@sanity/cli 7.17.0 → 8.0.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.
Files changed (133) hide show
  1. package/README.md +81 -6
  2. package/dist/SanityHelp.js +1 -35
  3. package/dist/SanityHelp.js.map +1 -1
  4. package/dist/actions/assets/assetFileError.js +9 -0
  5. package/dist/actions/assets/assetFileError.js.map +1 -0
  6. package/dist/actions/assets/uploadAssetFromFile.js +40 -0
  7. package/dist/actions/assets/uploadAssetFromFile.js.map +1 -0
  8. package/dist/actions/assets/uploadAssetWithProgress.js +76 -0
  9. package/dist/actions/assets/uploadAssetWithProgress.js.map +1 -0
  10. package/dist/actions/deploy/deployApp.js +39 -44
  11. package/dist/actions/deploy/deployApp.js.map +1 -1
  12. package/dist/actions/deploy/deployChecks.js +21 -13
  13. package/dist/actions/deploy/deployChecks.js.map +1 -1
  14. package/dist/actions/deploy/deployRunner.js +8 -7
  15. package/dist/actions/deploy/deployRunner.js.map +1 -1
  16. package/dist/actions/deploy/deployStudio.js +55 -35
  17. package/dist/actions/deploy/deployStudio.js.map +1 -1
  18. package/dist/actions/deploy/deploymentPlan.js +17 -29
  19. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  20. package/dist/actions/deploy/resolveDeployTarget.js +2 -8
  21. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  22. package/dist/actions/dev/servers/getDevServerConfig.js +2 -8
  23. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  24. package/dist/actions/dev/servers/startStudioDevServer.js +7 -1
  25. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  26. package/dist/actions/init/bootstrapRemoteTemplate.js +2 -2
  27. package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -1
  28. package/dist/actions/schema/listSchemas.js +1 -1
  29. package/dist/actions/schema/listSchemas.js.map +1 -1
  30. package/dist/actions/tokens/constants.js +2 -2
  31. package/dist/actions/tokens/constants.js.map +1 -1
  32. package/dist/actions/tokens/expiry.js +35 -0
  33. package/dist/actions/tokens/expiry.js.map +1 -0
  34. package/dist/actions/tokens/types.js.map +1 -1
  35. package/dist/actions/tokens/validateRole.js +5 -2
  36. package/dist/actions/tokens/validateRole.js.map +1 -1
  37. package/dist/actions/typegen/count.js +6 -0
  38. package/dist/actions/typegen/count.js.map +1 -0
  39. package/dist/actions/typegen/formatPath.js +5 -0
  40. package/dist/actions/typegen/formatPath.js.map +1 -0
  41. package/dist/actions/typegen/percent.js +8 -0
  42. package/dist/actions/typegen/percent.js.map +1 -0
  43. package/dist/actions/typegen/renderTypegenProgress.js +88 -0
  44. package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
  45. package/dist/actions/undeploy/adapters.js +6 -9
  46. package/dist/actions/undeploy/adapters.js.map +1 -1
  47. package/dist/actions/undeploy/runUndeploy.js +12 -4
  48. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  49. package/dist/actions/undeploy/undeployPlan.js +6 -14
  50. package/dist/actions/undeploy/undeployPlan.js.map +1 -1
  51. package/dist/commands/assets/upload.js +126 -0
  52. package/dist/commands/assets/upload.js.map +1 -0
  53. package/dist/commands/backups/list.js +1 -1
  54. package/dist/commands/backups/list.js.map +1 -1
  55. package/dist/commands/datasets/copy.js +1 -1
  56. package/dist/commands/datasets/copy.js.map +1 -1
  57. package/dist/commands/login.js +2 -8
  58. package/dist/commands/login.js.map +1 -1
  59. package/dist/commands/mcp/configure.js.map +1 -1
  60. package/dist/commands/migrations/list.js +1 -1
  61. package/dist/commands/migrations/list.js.map +1 -1
  62. package/dist/commands/migrations/run.js +67 -3
  63. package/dist/commands/migrations/run.js.map +1 -1
  64. package/dist/commands/new.js +56 -4
  65. package/dist/commands/new.js.map +1 -1
  66. package/dist/commands/organizations/list.js +1 -1
  67. package/dist/commands/organizations/list.js.map +1 -1
  68. package/dist/commands/projects/list.js +39 -12
  69. package/dist/commands/projects/list.js.map +1 -1
  70. package/dist/commands/projects/unclaimed.js +39 -33
  71. package/dist/commands/projects/unclaimed.js.map +1 -1
  72. package/dist/commands/tokens/create.js +92 -7
  73. package/dist/commands/tokens/create.js.map +1 -1
  74. package/dist/commands/tokens/delete.js +2 -2
  75. package/dist/commands/tokens/delete.js.map +1 -1
  76. package/dist/commands/tokens/list.js +12 -11
  77. package/dist/commands/tokens/list.js.map +1 -1
  78. package/dist/commands/tokens/rotate.js +95 -0
  79. package/dist/commands/tokens/rotate.js.map +1 -0
  80. package/dist/commands/typegen/generate.js +222 -0
  81. package/dist/commands/typegen/generate.js.map +1 -0
  82. package/dist/commands/users/list.js +8 -8
  83. package/dist/commands/users/list.js.map +1 -1
  84. package/dist/exports/_internal.d.ts +29 -0
  85. package/dist/exports/_internal.js +1 -0
  86. package/dist/exports/_internal.js.map +1 -1
  87. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +29 -14
  88. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  89. package/dist/exports/invokeSanityCli/help.js +4 -0
  90. package/dist/exports/invokeSanityCli/help.js.map +1 -1
  91. package/dist/exports/invokeSanityCli/index.js +2 -0
  92. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  93. package/dist/hooks/commandNotFound/topicAliases.js +13 -40
  94. package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
  95. package/dist/hooks/prerun/setupTelemetry.js +3 -2
  96. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  97. package/dist/server/vite/plugin-typegen.js +4 -16
  98. package/dist/server/vite/plugin-typegen.js.map +1 -1
  99. package/dist/services/assets.js +46 -0
  100. package/dist/services/assets.js.map +1 -0
  101. package/dist/services/newInstructions.js +48 -0
  102. package/dist/services/newInstructions.js.map +1 -0
  103. package/dist/services/tokens.js +81 -31
  104. package/dist/services/tokens.js.map +1 -1
  105. package/dist/topicAliases.js +43 -0
  106. package/dist/topicAliases.js.map +1 -1
  107. package/dist/util/parseArguments.js +7 -2
  108. package/dist/util/parseArguments.js.map +1 -1
  109. package/dist/util/readTokenFromStdin.js +17 -0
  110. package/dist/util/readTokenFromStdin.js.map +1 -0
  111. package/dist/util/responsiveTable.js +97 -0
  112. package/dist/util/responsiveTable.js.map +1 -0
  113. package/dist/util/telemetry/commandTelemetry.js +34 -0
  114. package/dist/util/telemetry/commandTelemetry.js.map +1 -0
  115. package/dist/util/unclaimedProjects.js +10 -0
  116. package/dist/util/unclaimedProjects.js.map +1 -1
  117. package/oclif.config.js +2 -4
  118. package/oclif.manifest.json +224 -2
  119. package/package.json +12 -10
  120. package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
  121. package/templates/shopify/docs/features.md +24 -0
  122. package/templates/shopify/schemaTypes/index.ts +2 -0
  123. package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
  124. package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
  125. package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
  126. package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
  127. package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
  128. package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
  129. package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
  130. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
  131. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
  132. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
  133. package/templates/shopify-online-storefront/utils/formatMetafieldValue.ts +30 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/tokens/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {Table} from 'console-table-printer'\n\nimport {type Token} from '../../actions/tokens/types.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getTokens} from '../../services/tokens.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listTokenDebug = subdebug('tokens:list')\n\nexport class TokensListCommand extends SanityCommand<typeof TokensListCommand> {\n static override description = 'List API tokens for the project'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List tokens for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'List tokens in JSON format',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'List tokens for a specific project',\n },\n ]\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list tokens for',\n semantics: 'override',\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output tokens in JSON format',\n }),\n }\n\n static override hiddenAliases: string[] = ['token:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(TokensListCommand)\n const {json} = flags\n const outputJson = json ?? false\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.tokens'}],\n }),\n })\n\n let tokens: Token[]\n try {\n tokens = await getTokens(projectId)\n } catch (error) {\n const message = getErrorMessage(error)\n listTokenDebug(`Error fetching tokens for project ${projectId}`, error)\n this.error(`Token list retrieval failed:\\n${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (outputJson) {\n this.log(JSON.stringify(tokens, null, 2))\n return\n }\n\n if (tokens.length === 0) {\n this.log('No API tokens found for this project.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', maxLen: 40, name: 'label', title: 'Label'},\n {alignment: 'left', maxLen: 20, name: 'id', title: 'Token ID'},\n {alignment: 'left', maxLen: 30, name: 'roles', title: 'Roles'},\n ],\n title: `Found ${tokens.length} API tokens`,\n })\n\n for (const token of tokens) {\n const roles = token.roles?.map((role) => role.title).join(', ') || 'No roles'\n const truncatedLabel =\n token.label.length > 37 ? `${token.label.slice(0, 37)}...` : token.label\n const truncatedRoles = roles.length > 27 ? `${roles.slice(0, 27)}...` : roles\n\n table.addRow({\n id: token.id,\n label: truncatedLabel,\n roles: truncatedRoles,\n })\n }\n\n this.log(table.render())\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","Table","promptForProject","getTokens","getProjectIdFlag","listTokenDebug","TokensListCommand","description","examples","command","flags","semantics","json","boolean","default","hiddenAliases","run","parse","outputJson","projectId","getProjectId","fallback","requiredPermissions","grant","permission","tokens","error","message","exit","RUNTIME_ERROR","log","JSON","stringify","length","table","columns","alignment","maxLen","name","title","token","roles","map","role","join","truncatedLabel","label","slice","truncatedRoles","addRow","id","render"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,KAAK,QAAO,wBAAuB;AAG3C,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,SAAS,QAAO,2BAA0B;AAClD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,iBAAiBN,SAAS;AAEhC,OAAO,MAAMO,0BAA0BR;IACrC,OAAgBS,cAAc,kCAAiC;IAC/D,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IACD,OAAgBG,QAAQ;QACtB,GAAGN,iBAAiB;YAClBG,aAAa;YACbI,WAAW;QACb,EAAE;QACFC,MAAMhB,MAAMiB,OAAO,CAAC;YAClBC,SAAS;YACTP,aAAa;QACf;IACF,EAAC;IAED,OAAgBQ,gBAA0B;QAAC;KAAa,CAAA;IAExD,MAAaC,MAAqB;QAChC,MAAM,EAACN,KAAK,EAAC,GAAG,MAAM,IAAI,CAACO,KAAK,CAACX;QACjC,MAAM,EAACM,IAAI,EAAC,GAAGF;QACf,MAAMQ,aAAaN,QAAQ;QAE3B,iCAAiC;QACjC,MAAMO,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRnB,iBAAiB;oBACfoB,qBAAqB;wBAAC;4BAACC,OAAO;4BAAQC,YAAY;wBAAuB;qBAAE;gBAC7E;QACJ;QAEA,IAAIC;QACJ,IAAI;YACFA,SAAS,MAAMtB,UAAUgB;QAC3B,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAU3B,gBAAgB0B;YAChCrB,eAAe,CAAC,kCAAkC,EAAEc,WAAW,EAAEO;YACjE,IAAI,CAACA,KAAK,CAAC,CAAC,8BAA8B,EAAEC,SAAS,EAAE;gBAACC,MAAM/B,UAAUgC,aAAa;YAAA;QACvF;QAEA,IAAIX,YAAY;YACd,IAAI,CAACY,GAAG,CAACC,KAAKC,SAAS,CAACP,QAAQ,MAAM;YACtC;QACF;QAEA,IAAIA,OAAOQ,MAAM,KAAK,GAAG;YACvB,IAAI,CAACH,GAAG,CAAC;YACT;QACF;QAEA,MAAMI,QAAQ,IAAIjC,MAAM;YACtBkC,SAAS;gBACP;oBAACC,WAAW;oBAAQC,QAAQ;oBAAIC,MAAM;oBAASC,OAAO;gBAAO;gBAC7D;oBAACH,WAAW;oBAAQC,QAAQ;oBAAIC,MAAM;oBAAMC,OAAO;gBAAU;gBAC7D;oBAACH,WAAW;oBAAQC,QAAQ;oBAAIC,MAAM;oBAASC,OAAO;gBAAO;aAC9D;YACDA,OAAO,CAAC,MAAM,EAAEd,OAAOQ,MAAM,CAAC,WAAW,CAAC;QAC5C;QAEA,KAAK,MAAMO,SAASf,OAAQ;YAC1B,MAAMgB,QAAQD,MAAMC,KAAK,EAAEC,IAAI,CAACC,OAASA,KAAKJ,KAAK,EAAEK,KAAK,SAAS;YACnE,MAAMC,iBACJL,MAAMM,KAAK,CAACb,MAAM,GAAG,KAAK,GAAGO,MAAMM,KAAK,CAACC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,GAAGP,MAAMM,KAAK;YAC1E,MAAME,iBAAiBP,MAAMR,MAAM,GAAG,KAAK,GAAGQ,MAAMM,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,GAAGN;YAExEP,MAAMe,MAAM,CAAC;gBACXC,IAAIV,MAAMU,EAAE;gBACZJ,OAAOD;gBACPJ,OAAOO;YACT;QACF;QAEA,IAAI,CAAClB,GAAG,CAACI,MAAMiB,MAAM;IACvB;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/tokens/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\n\nimport {type Robot} from '../../actions/tokens/types.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getProjectMembership, getTokens} from '../../services/tokens.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listTokenDebug = subdebug('tokens:list')\n\nexport class TokensListCommand extends SanityCommand<typeof TokensListCommand> {\n static override description = 'List API tokens for the project'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List tokens for the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --json',\n description: 'List tokens in JSON format',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'List tokens for a specific project',\n },\n ]\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list tokens for',\n semantics: 'override',\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output tokens in JSON format',\n }),\n }\n\n static override hiddenAliases: string[] = ['token:list']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(TokensListCommand)\n const {json} = flags\n const outputJson = json ?? false\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.tokens'}],\n }),\n })\n\n let tokens: Robot[]\n try {\n tokens = await getTokens(projectId)\n } catch (error) {\n const message = getErrorMessage(error)\n listTokenDebug(`Error fetching tokens for project ${projectId}`, error)\n this.error(`Token list retrieval failed:\\n${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (outputJson) {\n this.log(JSON.stringify(tokens, null, 2))\n return\n }\n\n if (tokens.length === 0) {\n this.log('No API tokens found for this project.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'label', title: 'Label'},\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'roles', title: 'Roles'},\n {alignment: 'left', name: 'expires', title: 'Expires'},\n ],\n title: `Found ${tokens.length} API tokens`,\n })\n\n for (const token of tokens) {\n const roles = getProjectMembership(token, projectId)?.roleNames.join(', ') || 'No roles'\n\n table.addRow({\n expires: token.expiresAt ? token.expiresAt.slice(0, 10) : 'Never',\n id: token.id,\n label: token.label,\n roles,\n })\n }\n\n this.log(table.render())\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","promptForProject","getProjectMembership","getTokens","Table","getProjectIdFlag","listTokenDebug","TokensListCommand","description","examples","command","flags","semantics","json","boolean","default","hiddenAliases","run","parse","outputJson","projectId","getProjectId","fallback","requiredPermissions","grant","permission","tokens","error","message","exit","RUNTIME_ERROR","log","JSON","stringify","length","table","columns","alignment","name","title","token","roles","roleNames","join","addRow","expires","expiresAt","slice","id","label","render"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AAGvD,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,oBAAoB,EAAEC,SAAS,QAAO,2BAA0B;AACxE,SAAQC,KAAK,QAAO,gCAA+B;AACnD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,iBAAiBP,SAAS;AAEhC,OAAO,MAAMQ,0BAA0BT;IACrC,OAAgBU,cAAc,kCAAiC;IAC/D,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IACD,OAAgBG,QAAQ;QACtB,GAAGN,iBAAiB;YAClBG,aAAa;YACbI,WAAW;QACb,EAAE;QACFC,MAAMjB,MAAMkB,OAAO,CAAC;YAClBC,SAAS;YACTP,aAAa;QACf;IACF,EAAC;IAED,OAAgBQ,gBAA0B;QAAC;KAAa,CAAA;IAExD,MAAaC,MAAqB;QAChC,MAAM,EAACN,KAAK,EAAC,GAAG,MAAM,IAAI,CAACO,KAAK,CAACX;QACjC,MAAM,EAACM,IAAI,EAAC,GAAGF;QACf,MAAMQ,aAAaN,QAAQ;QAE3B,iCAAiC;QACjC,MAAMO,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRrB,iBAAiB;oBACfsB,qBAAqB;wBAAC;4BAACC,OAAO;4BAAQC,YAAY;wBAAuB;qBAAE;gBAC7E;QACJ;QAEA,IAAIC;QACJ,IAAI;YACFA,SAAS,MAAMvB,UAAUiB;QAC3B,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAU5B,gBAAgB2B;YAChCrB,eAAe,CAAC,kCAAkC,EAAEc,WAAW,EAAEO;YACjE,IAAI,CAACA,KAAK,CAAC,CAAC,8BAA8B,EAAEC,SAAS,EAAE;gBAACC,MAAMhC,UAAUiC,aAAa;YAAA;QACvF;QAEA,IAAIX,YAAY;YACd,IAAI,CAACY,GAAG,CAACC,KAAKC,SAAS,CAACP,QAAQ,MAAM;YACtC;QACF;QAEA,IAAIA,OAAOQ,MAAM,KAAK,GAAG;YACvB,IAAI,CAACH,GAAG,CAAC;YACT;QACF;QAEA,MAAMI,QAAQ,IAAI/B,MAAM;YACtBgC,SAAS;gBACP;oBAACC,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAMC,OAAO;gBAAI;gBAC3C;oBAACF,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAWC,OAAO;gBAAS;aACtD;YACDA,OAAO,CAAC,MAAM,EAAEb,OAAOQ,MAAM,CAAC,WAAW,CAAC;QAC5C;QAEA,KAAK,MAAMM,SAASd,OAAQ;YAC1B,MAAMe,QAAQvC,qBAAqBsC,OAAOpB,YAAYsB,UAAUC,KAAK,SAAS;YAE9ER,MAAMS,MAAM,CAAC;gBACXC,SAASL,MAAMM,SAAS,GAAGN,MAAMM,SAAS,CAACC,KAAK,CAAC,GAAG,MAAM;gBAC1DC,IAAIR,MAAMQ,EAAE;gBACZC,OAAOT,MAAMS,KAAK;gBAClBR;YACF;QACF;QAEA,IAAI,CAACV,GAAG,CAACI,MAAMe,MAAM;IACvB;AACF"}
@@ -0,0 +1,95 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { getErrorMessage } from '@sanity/cli-core/errors';
4
+ import { isHttpError } from '@sanity/client';
5
+ import { rotateToken } from '../../services/tokens.js';
6
+ import { readTokenFromStdin } from '../../util/readTokenFromStdin.js';
7
+ const tokensRotateDebug = subdebug('tokens:rotate');
8
+ const TOKEN_USAGE_HINT = 'Token is required. Pipe it on standard input (`echo "$SANITY_TOKEN" | sanity tokens rotate`) to keep it out of shell history, or pass it with `--token`.';
9
+ export class RotateTokenCommand extends SanityCommand {
10
+ static description = 'Rotate an API token, replacing its secret with a new one';
11
+ static examples = [
12
+ {
13
+ command: 'echo "$SANITY_TOKEN" | <%= config.bin %> <%= command.id %>',
14
+ description: 'Rotate the token piped on standard input'
15
+ },
16
+ {
17
+ command: '<%= config.bin %> <%= command.id %> < token.txt',
18
+ description: 'Rotate a token read from a file'
19
+ },
20
+ {
21
+ command: 'echo "$SANITY_TOKEN" | <%= config.bin %> <%= command.id %> --json',
22
+ description: 'Output the rotated token as JSON'
23
+ }
24
+ ];
25
+ static flags = {
26
+ json: Flags.boolean({
27
+ default: false,
28
+ description: 'Output as JSON'
29
+ }),
30
+ token: Flags.string({
31
+ char: 't',
32
+ description: 'Token to rotate (prefer standard input to keep it out of shell history)',
33
+ helpValue: '<token>'
34
+ })
35
+ };
36
+ static hiddenAliases = [
37
+ 'token:rotate'
38
+ ];
39
+ async run() {
40
+ const { flags } = await this.parse(RotateTokenCommand);
41
+ const { json } = flags;
42
+ const token = flags.token?.trim() || await this.readTokenArg();
43
+ try {
44
+ tokensRotateDebug('Rotating API token');
45
+ const robot = await rotateToken(token);
46
+ if (json) {
47
+ this.log(JSON.stringify(robot, null, 2));
48
+ return;
49
+ }
50
+ this.log('API token rotated');
51
+ this.log(`Label: ${robot.label}`);
52
+ this.log(`ID: ${robot.id}`);
53
+ if (robot.expiresAt) {
54
+ this.log(`Expires: ${robot.expiresAt}`);
55
+ }
56
+ this.log(`Token: ${robot.token}`);
57
+ this.log('');
58
+ this.log("Copy the token now. It won't be shown again.");
59
+ this.log('The previous token was revoked and no longer works.');
60
+ } catch (error) {
61
+ tokensRotateDebug('Error rotating token', error);
62
+ if (isHttpError(error) && error.statusCode === 401) {
63
+ this.error('Token is invalid, expired, or was already rotated or revoked. Check that you are passing the current token.', {
64
+ exit: exitCodes.RUNTIME_ERROR
65
+ });
66
+ }
67
+ if (isHttpError(error) && error.statusCode === 403) {
68
+ this.error('The token is not an API token and cannot be rotated. Personal tokens are renewed by logging in again.', {
69
+ exit: exitCodes.RUNTIME_ERROR
70
+ });
71
+ }
72
+ this.error(`Token rotation failed:\n${getErrorMessage(error)}`, {
73
+ exit: exitCodes.RUNTIME_ERROR
74
+ });
75
+ }
76
+ }
77
+ async readTokenArg() {
78
+ let token;
79
+ try {
80
+ token = await readTokenFromStdin(TOKEN_USAGE_HINT);
81
+ } catch (error) {
82
+ return this.error(getErrorMessage(error), {
83
+ exit: exitCodes.USAGE_ERROR
84
+ });
85
+ }
86
+ if (!token) {
87
+ this.error(TOKEN_USAGE_HINT, {
88
+ exit: exitCodes.USAGE_ERROR
89
+ });
90
+ }
91
+ return token;
92
+ }
93
+ }
94
+
95
+ //# sourceMappingURL=rotate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/tokens/rotate.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {rotateToken} from '../../services/tokens.js'\nimport {readTokenFromStdin} from '../../util/readTokenFromStdin.js'\n\nconst tokensRotateDebug = subdebug('tokens:rotate')\n\nconst TOKEN_USAGE_HINT =\n 'Token is required. Pipe it on standard input (`echo \"$SANITY_TOKEN\" | sanity tokens rotate`) to keep it out of shell history, or pass it with `--token`.'\n\nexport class RotateTokenCommand extends SanityCommand<typeof RotateTokenCommand> {\n static override description = 'Rotate an API token, replacing its secret with a new one'\n\n static override examples = [\n {\n command: 'echo \"$SANITY_TOKEN\" | <%= config.bin %> <%= command.id %>',\n description: 'Rotate the token piped on standard input',\n },\n {\n command: '<%= config.bin %> <%= command.id %> < token.txt',\n description: 'Rotate a token read from a file',\n },\n {\n command: 'echo \"$SANITY_TOKEN\" | <%= config.bin %> <%= command.id %> --json',\n description: 'Output the rotated token as JSON',\n },\n ]\n\n static override flags = {\n json: Flags.boolean({\n default: false,\n description: 'Output as JSON',\n }),\n token: Flags.string({\n char: 't',\n description: 'Token to rotate (prefer standard input to keep it out of shell history)',\n helpValue: '<token>',\n }),\n }\n\n static override hiddenAliases: string[] = ['token:rotate']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(RotateTokenCommand)\n const {json} = flags\n\n const token = flags.token?.trim() || (await this.readTokenArg())\n\n try {\n tokensRotateDebug('Rotating API token')\n const robot = await rotateToken(token)\n\n if (json) {\n this.log(JSON.stringify(robot, null, 2))\n return\n }\n\n this.log('API token rotated')\n this.log(`Label: ${robot.label}`)\n this.log(`ID: ${robot.id}`)\n if (robot.expiresAt) {\n this.log(`Expires: ${robot.expiresAt}`)\n }\n this.log(`Token: ${robot.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n this.log('The previous token was revoked and no longer works.')\n } catch (error) {\n tokensRotateDebug('Error rotating token', error)\n\n if (isHttpError(error) && error.statusCode === 401) {\n this.error(\n 'Token is invalid, expired, or was already rotated or revoked. Check that you are passing the current token.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n if (isHttpError(error) && error.statusCode === 403) {\n this.error(\n 'The token is not an API token and cannot be rotated. Personal tokens are renewed by logging in again.',\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n this.error(`Token rotation failed:\\n${getErrorMessage(error)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async readTokenArg(): Promise<string> {\n let token: string\n try {\n token = await readTokenFromStdin(TOKEN_USAGE_HINT)\n } catch (error) {\n return this.error(getErrorMessage(error), {exit: exitCodes.USAGE_ERROR})\n }\n\n if (!token) {\n this.error(TOKEN_USAGE_HINT, {exit: exitCodes.USAGE_ERROR})\n }\n\n return token\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","rotateToken","readTokenFromStdin","tokensRotateDebug","TOKEN_USAGE_HINT","RotateTokenCommand","description","examples","command","flags","json","boolean","default","token","string","char","helpValue","hiddenAliases","run","parse","trim","readTokenArg","robot","log","JSON","stringify","label","id","expiresAt","error","statusCode","exit","RUNTIME_ERROR","USAGE_ERROR"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,WAAW,QAAO,2BAA0B;AACpD,SAAQC,kBAAkB,QAAO,mCAAkC;AAEnE,MAAMC,oBAAoBL,SAAS;AAEnC,MAAMM,mBACJ;AAEF,OAAO,MAAMC,2BAA2BR;IACtC,OAAgBS,cAAc,2DAA0D;IAExF,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtBC,MAAMf,MAAMgB,OAAO,CAAC;YAClBC,SAAS;YACTN,aAAa;QACf;QACAO,OAAOlB,MAAMmB,MAAM,CAAC;YAClBC,MAAM;YACNT,aAAa;YACbU,WAAW;QACb;IACF,EAAC;IAED,OAAgBC,gBAA0B;QAAC;KAAe,CAAA;IAE1D,MAAaC,MAAqB;QAChC,MAAM,EAACT,KAAK,EAAC,GAAG,MAAM,IAAI,CAACU,KAAK,CAACd;QACjC,MAAM,EAACK,IAAI,EAAC,GAAGD;QAEf,MAAMI,QAAQJ,MAAMI,KAAK,EAAEO,UAAW,MAAM,IAAI,CAACC,YAAY;QAE7D,IAAI;YACFlB,kBAAkB;YAClB,MAAMmB,QAAQ,MAAMrB,YAAYY;YAEhC,IAAIH,MAAM;gBACR,IAAI,CAACa,GAAG,CAACC,KAAKC,SAAS,CAACH,OAAO,MAAM;gBACrC;YACF;YAEA,IAAI,CAACC,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC,CAAC,OAAO,EAAED,MAAMI,KAAK,EAAE;YAChC,IAAI,CAACH,GAAG,CAAC,CAAC,IAAI,EAAED,MAAMK,EAAE,EAAE;YAC1B,IAAIL,MAAMM,SAAS,EAAE;gBACnB,IAAI,CAACL,GAAG,CAAC,CAAC,SAAS,EAAED,MAAMM,SAAS,EAAE;YACxC;YACA,IAAI,CAACL,GAAG,CAAC,CAAC,OAAO,EAAED,MAAMT,KAAK,EAAE;YAChC,IAAI,CAACU,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC;QACX,EAAE,OAAOM,OAAO;YACd1B,kBAAkB,wBAAwB0B;YAE1C,IAAI7B,YAAY6B,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CACR,+GACA;oBAACE,MAAMnC,UAAUoC,aAAa;gBAAA;YAElC;YACA,IAAIhC,YAAY6B,UAAUA,MAAMC,UAAU,KAAK,KAAK;gBAClD,IAAI,CAACD,KAAK,CACR,yGACA;oBAACE,MAAMnC,UAAUoC,aAAa;gBAAA;YAElC;YAEA,IAAI,CAACH,KAAK,CAAC,CAAC,wBAAwB,EAAE9B,gBAAgB8B,QAAQ,EAAE;gBAC9DE,MAAMnC,UAAUoC,aAAa;YAC/B;QACF;IACF;IAEA,MAAcX,eAAgC;QAC5C,IAAIR;QACJ,IAAI;YACFA,QAAQ,MAAMX,mBAAmBE;QACnC,EAAE,OAAOyB,OAAO;YACd,OAAO,IAAI,CAACA,KAAK,CAAC9B,gBAAgB8B,QAAQ;gBAACE,MAAMnC,UAAUqC,WAAW;YAAA;QACxE;QAEA,IAAI,CAACpB,OAAO;YACV,IAAI,CAACgB,KAAK,CAACzB,kBAAkB;gBAAC2B,MAAMnC,UAAUqC,WAAW;YAAA;QAC3D;QAEA,OAAOpB;IACT;AACF"}
@@ -0,0 +1,222 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { styleText } from 'node:util';
3
+ import { Flags } from '@oclif/core';
4
+ import { CLIError } from '@oclif/core/errors';
5
+ import { SanityCommand } from '@sanity/cli-core';
6
+ import { spinner } from '@sanity/cli-core/ux';
7
+ import { configDefinition, readConfig, runTypegenGenerate, runTypegenWatcher, TypegenWatchModeTrace, TypesGeneratedTrace } from '@sanity/codegen';
8
+ import omit from 'lodash-es/omit.js';
9
+ import once from 'lodash-es/once.js';
10
+ import { createTypegenProgressRenderer } from '../../actions/typegen/renderTypegenProgress.js';
11
+ const description = `Sanity TypeGen
12
+
13
+ ${styleText('bold', 'Configuration:')}
14
+ This command can utilize configuration settings defined in a \`sanity-typegen.json\` file. These settings include:
15
+
16
+ - "path": Specifies a glob pattern to locate your TypeScript or JavaScript files.
17
+ Default: "./src/**/*.{ts,tsx,js,jsx}"
18
+
19
+ - "schema": Defines the path to your Sanity schema file. This file should be generated using the \`sanity schema extract\` command.
20
+ Default: "schema.json"
21
+
22
+ - "generates": Indicates the path where the generated TypeScript type definitions will be saved.
23
+ Default: "./sanity.types.ts"
24
+
25
+ The default configuration values listed above are used if not overridden in your \`sanity-typegen.json\` configuration file. To customize the behavior of the type generation, adjust these properties in the configuration file according to your project's needs.
26
+
27
+ ${styleText('bold', 'Note:')}
28
+ - The \`sanity schema extract\` command is a prerequisite for extracting your Sanity Studio schema into a \`schema.json\` file, which is then used by the \`sanity typegen generate\` command to generate type definitions.`.trim();
29
+ export class TypegenGenerateCommand extends SanityCommand {
30
+ static description = description;
31
+ static examples = [
32
+ {
33
+ command: '<%= config.bin %> <%= command.id %>',
34
+ description: `Generate TypeScript type definitions from a Sanity Studio schema extracted using the \`sanity schema extract\` command.`
35
+ }
36
+ ];
37
+ static flags = {
38
+ 'config-path': Flags.string({
39
+ description: '[Default: sanity-typegen.json] Specifies the path to the typegen configuration file. This file should be a JSON file that contains settings for the type generation process.'
40
+ }),
41
+ watch: Flags.boolean({
42
+ default: false,
43
+ description: '[Default: false] Run the typegen in watch mode'
44
+ })
45
+ };
46
+ static hiddenAliases = [
47
+ 'typegen:generate'
48
+ ];
49
+ async run() {
50
+ const { flags } = await this.parse(TypegenGenerateCommand);
51
+ if (flags.watch) {
52
+ await this.runWatcher();
53
+ return;
54
+ }
55
+ await this.runSingle();
56
+ }
57
+ async getConfig() {
58
+ const spin = spinner({}).start('Loading config…');
59
+ try {
60
+ const { flags } = await this.parse(TypegenGenerateCommand);
61
+ const rootDir = await this.getProjectRoot();
62
+ const config = await this.getCliConfig();
63
+ const configPath = flags['config-path'];
64
+ const workDir = rootDir.directory;
65
+ const legacyConfigPath = configPath || 'sanity-typegen.json';
66
+ let hasLegacyConfig = false;
67
+ try {
68
+ const file = await stat(legacyConfigPath);
69
+ hasLegacyConfig = file.isFile();
70
+ } catch (err) {
71
+ if (err instanceof Error && 'code' in err && err.code === 'ENOENT' && configPath) {
72
+ spin.fail();
73
+ this.error(`Typegen config file not found: ${configPath}`, {
74
+ exit: 1
75
+ });
76
+ }
77
+ if (err instanceof Error && 'code' in err && err.code !== 'ENOENT') {
78
+ spin.fail();
79
+ this.error(`Error when checking if typegen config file exists: ${legacyConfigPath}`, {
80
+ exit: 1
81
+ });
82
+ }
83
+ }
84
+ if (config?.typegen && hasLegacyConfig) {
85
+ spin.warn(styleText('yellow', `You've specified typegen in your Sanity CLI config, but also have a typegen config.
86
+
87
+ The config from the Sanity CLI config is used.
88
+ `));
89
+ return {
90
+ config: configDefinition.parse(config.typegen || {}),
91
+ path: rootDir.path,
92
+ type: 'cli',
93
+ workDir
94
+ };
95
+ }
96
+ if (hasLegacyConfig) {
97
+ spin.warn(styleText('yellow', `The separate typegen config has been deprecated. Use \`typegen\` in the sanity CLI config instead.
98
+
99
+ See: https://www.sanity.io/docs/help/configuring-typegen-in-sanity-cli-config`));
100
+ return {
101
+ config: await readConfig(legacyConfigPath),
102
+ path: legacyConfigPath,
103
+ type: 'legacy',
104
+ workDir
105
+ };
106
+ }
107
+ spin.succeed(`Config loaded from sanity.cli.ts`);
108
+ return {
109
+ config: configDefinition.parse(config.typegen || {}),
110
+ path: rootDir.path,
111
+ type: 'cli',
112
+ workDir
113
+ };
114
+ } catch (err) {
115
+ // The config-file checks above already reported a user-facing error via
116
+ // `this.error()` (an oclif CLIError). Re-throw those as-is instead of
117
+ // wrapping them a second time and failing the spinner twice.
118
+ if (err instanceof CLIError) throw err;
119
+ spin.fail();
120
+ this.error(`An error occurred during config loading: ${err}`, {
121
+ exit: 1
122
+ });
123
+ }
124
+ }
125
+ async runSingle() {
126
+ const trace = this.telemetry.trace(TypesGeneratedTrace);
127
+ let spin;
128
+ try {
129
+ const { config: typegenConfig, type: typegenConfigMethod, workDir } = await this.getConfig();
130
+ trace.start();
131
+ spin = spinner({}).start('Loading schema…');
132
+ const result = await runTypegenGenerate({
133
+ config: typegenConfig,
134
+ onProgress: createTypegenProgressRenderer(spin, {
135
+ formatGeneratedCode: typegenConfig.formatGeneratedCode,
136
+ generates: typegenConfig.generates,
137
+ schema: typegenConfig.schema
138
+ }),
139
+ workDir
140
+ });
141
+ const traceStats = omit(result, 'code', 'duration');
142
+ trace.log({
143
+ configMethod: typegenConfigMethod,
144
+ configOverloadClientMethods: typegenConfig.overloadClientMethods,
145
+ ...traceStats
146
+ });
147
+ trace.complete();
148
+ } catch (error) {
149
+ if (spin?.isSpinning) {
150
+ spin.fail();
151
+ }
152
+ trace.error(error instanceof Error ? error : new Error(String(error)));
153
+ this.error(`${error instanceof Error ? error.message : 'Unknown error'}`, {
154
+ exit: 1
155
+ });
156
+ }
157
+ }
158
+ async runWatcher() {
159
+ const trace = this.telemetry.trace(TypegenWatchModeTrace);
160
+ let spin;
161
+ try {
162
+ const { config: typegenConfig, workDir } = await this.getConfig();
163
+ trace.start();
164
+ let resolve = ()=>{};
165
+ const promise = new Promise((res)=>{
166
+ resolve = res;
167
+ });
168
+ const watchSpin = spinner({}).start('Loading schema…');
169
+ spin = watchSpin;
170
+ const rendererContext = {
171
+ formatGeneratedCode: typegenConfig.formatGeneratedCode,
172
+ generates: typegenConfig.generates,
173
+ schema: typegenConfig.schema
174
+ };
175
+ let renderProgress = createTypegenProgressRenderer(watchSpin, rendererContext);
176
+ const typegenWatcher = runTypegenWatcher({
177
+ config: typegenConfig,
178
+ onProgress: (event)=>{
179
+ // 'schemaLoaded' is the first event of every generation cycle; the
180
+ // renderer accumulates per-run state, so start each cycle fresh.
181
+ if (event.type === 'schemaLoaded') {
182
+ renderProgress = createTypegenProgressRenderer(watchSpin, rendererContext);
183
+ }
184
+ renderProgress(event);
185
+ },
186
+ workDir
187
+ });
188
+ const stop = once(async ()=>{
189
+ process.off('SIGINT', stop);
190
+ process.off('SIGTERM', stop);
191
+ if (watchSpin.isSpinning) {
192
+ watchSpin.stop();
193
+ }
194
+ trace.log({
195
+ step: 'stopped',
196
+ ...typegenWatcher.getStats()
197
+ });
198
+ trace.complete();
199
+ try {
200
+ await typegenWatcher.stop();
201
+ } catch {
202
+ // We're shutting down; a failure to close the watcher shouldn't hang
203
+ // the process or surface as an unhandled rejection.
204
+ }
205
+ resolve();
206
+ });
207
+ process.on('SIGINT', stop);
208
+ process.on('SIGTERM', stop);
209
+ await promise;
210
+ } catch (error) {
211
+ if (spin?.isSpinning) {
212
+ spin.fail();
213
+ }
214
+ trace.error(error instanceof Error ? error : new Error(String(error)));
215
+ this.error(`${error instanceof Error ? error.message : 'Unknown error'}`, {
216
+ exit: 1
217
+ });
218
+ }
219
+ }
220
+ }
221
+
222
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/commands/typegen/generate.ts"],"sourcesContent":["import {stat} from 'node:fs/promises'\nimport {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {CLIError} from '@oclif/core/errors'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {spinner, type SpinnerInstance} from '@sanity/cli-core/ux'\nimport {\n configDefinition,\n readConfig,\n runTypegenGenerate,\n runTypegenWatcher,\n type TypeGenConfig,\n TypegenWatchModeTrace,\n TypesGeneratedTrace,\n} from '@sanity/codegen'\nimport omit from 'lodash-es/omit.js'\nimport once from 'lodash-es/once.js'\n\nimport {createTypegenProgressRenderer} from '../../actions/typegen/renderTypegenProgress.js'\n\nconst description = `Sanity TypeGen\n\n${styleText('bold', 'Configuration:')}\nThis command can utilize configuration settings defined in a \\`sanity-typegen.json\\` file. These settings include:\n\n- \"path\": Specifies a glob pattern to locate your TypeScript or JavaScript files.\n Default: \"./src/**/*.{ts,tsx,js,jsx}\"\n\n- \"schema\": Defines the path to your Sanity schema file. This file should be generated using the \\`sanity schema extract\\` command.\n Default: \"schema.json\"\n\n- \"generates\": Indicates the path where the generated TypeScript type definitions will be saved.\n Default: \"./sanity.types.ts\"\n\nThe default configuration values listed above are used if not overridden in your \\`sanity-typegen.json\\` configuration file. To customize the behavior of the type generation, adjust these properties in the configuration file according to your project's needs.\n\n${styleText('bold', 'Note:')}\n- The \\`sanity schema extract\\` command is a prerequisite for extracting your Sanity Studio schema into a \\`schema.json\\` file, which is then used by the \\`sanity typegen generate\\` command to generate type definitions.`.trim()\n\nexport class TypegenGenerateCommand extends SanityCommand<typeof TypegenGenerateCommand> {\n static override description = description\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: `Generate TypeScript type definitions from a Sanity Studio schema extracted using the \\`sanity schema extract\\` command.`,\n },\n ]\n\n static override flags = {\n 'config-path': Flags.string({\n description:\n '[Default: sanity-typegen.json] Specifies the path to the typegen configuration file. This file should be a JSON file that contains settings for the type generation process.',\n }),\n watch: Flags.boolean({\n default: false,\n description: '[Default: false] Run the typegen in watch mode',\n }),\n }\n\n static override hiddenAliases: string[] = ['typegen:generate']\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(TypegenGenerateCommand)\n\n if (flags.watch) {\n await this.runWatcher()\n return\n }\n\n await this.runSingle()\n }\n\n private async getConfig(): Promise<{\n config: TypeGenConfig\n path?: string\n type: 'cli' | 'legacy'\n workDir: string\n }> {\n const spin = spinner({}).start('Loading config…')\n\n try {\n const {flags} = await this.parse(TypegenGenerateCommand)\n const rootDir = await this.getProjectRoot()\n const config = await this.getCliConfig()\n\n const configPath = flags['config-path']\n const workDir = rootDir.directory\n\n const legacyConfigPath = configPath || 'sanity-typegen.json'\n let hasLegacyConfig = false\n\n try {\n const file = await stat(legacyConfigPath)\n hasLegacyConfig = file.isFile()\n } catch (err) {\n if (err instanceof Error && 'code' in err && err.code === 'ENOENT' && configPath) {\n spin.fail()\n this.error(`Typegen config file not found: ${configPath}`, {exit: 1})\n }\n\n if (err instanceof Error && 'code' in err && err.code !== 'ENOENT') {\n spin.fail()\n this.error(`Error when checking if typegen config file exists: ${legacyConfigPath}`, {\n exit: 1,\n })\n }\n }\n\n if (config?.typegen && hasLegacyConfig) {\n spin.warn(\n styleText(\n 'yellow',\n `You've specified typegen in your Sanity CLI config, but also have a typegen config.\n\n The config from the Sanity CLI config is used.\n `,\n ),\n )\n\n return {\n config: configDefinition.parse(config.typegen || {}),\n path: rootDir.path,\n type: 'cli',\n workDir,\n }\n }\n\n if (hasLegacyConfig) {\n spin.warn(\n styleText(\n 'yellow',\n `The separate typegen config has been deprecated. Use \\`typegen\\` in the sanity CLI config instead.\n\n See: https://www.sanity.io/docs/help/configuring-typegen-in-sanity-cli-config`,\n ),\n )\n return {\n config: await readConfig(legacyConfigPath),\n path: legacyConfigPath,\n type: 'legacy',\n workDir,\n }\n }\n\n spin.succeed(`Config loaded from sanity.cli.ts`)\n\n return {\n config: configDefinition.parse(config.typegen || {}),\n path: rootDir.path,\n type: 'cli',\n workDir,\n }\n } catch (err) {\n // The config-file checks above already reported a user-facing error via\n // `this.error()` (an oclif CLIError). Re-throw those as-is instead of\n // wrapping them a second time and failing the spinner twice.\n if (err instanceof CLIError) throw err\n spin.fail()\n this.error(`An error occurred during config loading: ${err}`, {exit: 1})\n }\n }\n\n private async runSingle(): Promise<void> {\n const trace = this.telemetry.trace(TypesGeneratedTrace)\n let spin: SpinnerInstance | undefined\n\n try {\n const {config: typegenConfig, type: typegenConfigMethod, workDir} = await this.getConfig()\n trace.start()\n\n spin = spinner({}).start('Loading schema…')\n const result = await runTypegenGenerate({\n config: typegenConfig,\n onProgress: createTypegenProgressRenderer(spin, {\n formatGeneratedCode: typegenConfig.formatGeneratedCode,\n generates: typegenConfig.generates,\n schema: typegenConfig.schema,\n }),\n workDir,\n })\n\n const traceStats = omit(result, 'code', 'duration')\n\n trace.log({\n configMethod: typegenConfigMethod,\n configOverloadClientMethods: typegenConfig.overloadClientMethods,\n ...traceStats,\n })\n trace.complete()\n } catch (error) {\n if (spin?.isSpinning) {\n spin.fail()\n }\n trace.error(error instanceof Error ? error : new Error(String(error)))\n this.error(`${error instanceof Error ? error.message : 'Unknown error'}`, {exit: 1})\n }\n }\n\n private async runWatcher(): Promise<void> {\n const trace = this.telemetry.trace(TypegenWatchModeTrace)\n let spin: SpinnerInstance | undefined\n\n try {\n const {config: typegenConfig, workDir} = await this.getConfig()\n trace.start()\n\n let resolve: () => void = () => {}\n const promise = new Promise<void>((res) => {\n resolve = res\n })\n\n const watchSpin = spinner({}).start('Loading schema…')\n spin = watchSpin\n const rendererContext = {\n formatGeneratedCode: typegenConfig.formatGeneratedCode,\n generates: typegenConfig.generates,\n schema: typegenConfig.schema,\n }\n let renderProgress = createTypegenProgressRenderer(watchSpin, rendererContext)\n\n const typegenWatcher = runTypegenWatcher({\n config: typegenConfig,\n onProgress: (event) => {\n // 'schemaLoaded' is the first event of every generation cycle; the\n // renderer accumulates per-run state, so start each cycle fresh.\n if (event.type === 'schemaLoaded') {\n renderProgress = createTypegenProgressRenderer(watchSpin, rendererContext)\n }\n renderProgress(event)\n },\n workDir,\n })\n\n const stop = once(async () => {\n process.off('SIGINT', stop)\n process.off('SIGTERM', stop)\n\n if (watchSpin.isSpinning) {\n watchSpin.stop()\n }\n\n trace.log({\n step: 'stopped',\n ...typegenWatcher.getStats(),\n })\n trace.complete()\n\n try {\n await typegenWatcher.stop()\n } catch {\n // We're shutting down; a failure to close the watcher shouldn't hang\n // the process or surface as an unhandled rejection.\n }\n resolve()\n })\n\n process.on('SIGINT', stop)\n process.on('SIGTERM', stop)\n\n await promise\n } catch (error) {\n if (spin?.isSpinning) {\n spin.fail()\n }\n trace.error(error instanceof Error ? error : new Error(String(error)))\n this.error(`${error instanceof Error ? error.message : 'Unknown error'}`, {exit: 1})\n }\n }\n}\n"],"names":["stat","styleText","Flags","CLIError","SanityCommand","spinner","configDefinition","readConfig","runTypegenGenerate","runTypegenWatcher","TypegenWatchModeTrace","TypesGeneratedTrace","omit","once","createTypegenProgressRenderer","description","trim","TypegenGenerateCommand","examples","command","flags","string","watch","boolean","default","hiddenAliases","run","parse","runWatcher","runSingle","getConfig","spin","start","rootDir","getProjectRoot","config","getCliConfig","configPath","workDir","directory","legacyConfigPath","hasLegacyConfig","file","isFile","err","Error","code","fail","error","exit","typegen","warn","path","type","succeed","trace","telemetry","typegenConfig","typegenConfigMethod","result","onProgress","formatGeneratedCode","generates","schema","traceStats","log","configMethod","configOverloadClientMethods","overloadClientMethods","complete","isSpinning","String","message","resolve","promise","Promise","res","watchSpin","rendererContext","renderProgress","typegenWatcher","event","stop","process","off","step","getStats","on"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { styleText } from 'node:util';
2
2
  import { Flags } from '@oclif/core';
3
3
  import { SanityCommand } from '@sanity/cli-core';
4
- import { Table } from 'console-table-printer';
5
4
  import sortBy from 'lodash-es/sortBy.js';
6
5
  import { getMembersForProject } from '../../actions/users/getMembersForProject.js';
7
6
  import { promptForProject } from '../../prompts/promptForProject.js';
7
+ import { Table } from '../../util/responsiveTable.js';
8
8
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
9
9
  const sortFields = [
10
10
  'id',
@@ -105,35 +105,35 @@ export class List extends SanityCommand {
105
105
  ]), [
106
106
  sortFields.indexOf(sort)
107
107
  ]);
108
- const rows = order === 'asc' ? ordered : ordered.toReversed();
108
+ const rows = (order === 'asc' ? ordered : ordered.toReversed()).map(([id, name, roles, date])=>[
109
+ id,
110
+ name,
111
+ roles,
112
+ date.split('T')[0]
113
+ ]);
109
114
  const table = new Table({
110
115
  columns: [
111
116
  {
112
117
  alignment: 'left',
113
- maxLen: 30,
114
118
  name: 'id',
115
119
  title: 'ID'
116
120
  },
117
121
  {
118
122
  alignment: 'left',
119
- maxLen: 40,
120
123
  name: 'name',
121
124
  title: 'Name'
122
125
  },
123
126
  {
124
127
  alignment: 'left',
125
- maxLen: 30,
126
128
  name: 'roles',
127
129
  title: 'Roles'
128
130
  },
129
131
  {
130
132
  alignment: 'left',
131
- maxLen: 12,
132
133
  name: 'date',
133
134
  title: 'Date'
134
135
  }
135
- ],
136
- rowSeparator: true
136
+ ]
137
137
  });
138
138
  for (const [id, name, roles, date] of rows){
139
139
  const isPending = id === '<pending>';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/users/list.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {Table} from 'console-table-printer'\nimport sortBy from 'lodash-es/sortBy.js'\n\nimport {getMembersForProject} from '../../actions/users/getMembersForProject.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst sortFields = ['id', 'name', 'role', 'date']\n\nfunction dimText(value: string, isDim: boolean): string {\n return isDim ? styleText('dim', value) : value\n}\n\nexport class List extends SanityCommand<typeof List> {\n static override description = 'List project members'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all users of the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --no-invitations --no-robots',\n description: 'List all users of the project, but exclude pending invitations and robots',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sort role',\n description: 'List all users, sorted by role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'List users for a specific project',\n },\n ]\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list users for',\n semantics: 'override',\n }),\n invitations: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Includes or excludes pending invitations',\n }),\n order: Flags.string({\n default: 'asc',\n description: 'Sort output ascending/descending',\n options: ['asc', 'desc'],\n }),\n robots: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Includes or excludes robots (token users)',\n }),\n sort: Flags.string({\n default: 'date',\n description: 'Sort users by specified column',\n options: ['id', 'name', 'role', 'date'],\n }),\n }\n\n static override hiddenAliases: string[] = ['user:list']\n\n public async run(): Promise<void> {\n const {invitations, order, robots, sort} = this.flags\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project'},\n {grant: 'read', permission: 'sanity.project.members'},\n ],\n }),\n })\n\n const members = await getMembersForProject({\n includeInvitations: invitations,\n includeRobots: robots,\n projectId,\n })\n\n if (members.length === 0) {\n this.log('No members found for this project.')\n return\n }\n\n const ordered = sortBy(\n members.map(({date, id, name, roles}) => [\n id,\n name,\n roles\n ?.map((role) => role.title)\n .join(', ')\n .trim() || '-',\n date,\n ]),\n [sortFields.indexOf(sort)],\n )\n\n const rows = order === 'asc' ? ordered : ordered.toReversed()\n\n const table = new Table({\n columns: [\n {alignment: 'left', maxLen: 30, name: 'id', title: 'ID'},\n {alignment: 'left', maxLen: 40, name: 'name', title: 'Name'},\n {alignment: 'left', maxLen: 30, name: 'roles', title: 'Roles'},\n {alignment: 'left', maxLen: 12, name: 'date', title: 'Date'},\n ],\n rowSeparator: true,\n })\n\n for (const [id, name, roles, date] of rows) {\n const isPending = id === '<pending>'\n table.addRow({\n date: dimText(date, isPending),\n id: dimText(id, isPending),\n name: dimText(name, isPending),\n roles: dimText(roles, isPending),\n })\n }\n\n this.log(table.render())\n }\n}\n"],"names":["styleText","Flags","SanityCommand","Table","sortBy","getMembersForProject","promptForProject","getProjectIdFlag","sortFields","dimText","value","isDim","List","description","examples","command","flags","semantics","invitations","boolean","allowNo","default","order","string","options","robots","sort","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","members","includeInvitations","includeRobots","length","log","ordered","map","date","id","name","roles","role","title","join","trim","indexOf","rows","toReversed","table","columns","alignment","maxLen","rowSeparator","isPending","addRow","render"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/users/list.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport sortBy from 'lodash-es/sortBy.js'\n\nimport {getMembersForProject} from '../../actions/users/getMembersForProject.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst sortFields = ['id', 'name', 'role', 'date']\n\nfunction dimText(value: string, isDim: boolean): string {\n return isDim ? styleText('dim', value) : value\n}\n\nexport class List extends SanityCommand<typeof List> {\n static override description = 'List project members'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List all users of the project',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --no-invitations --no-robots',\n description: 'List all users of the project, but exclude pending invitations and robots',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sort role',\n description: 'List all users, sorted by role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'List users for a specific project',\n },\n ]\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list users for',\n semantics: 'override',\n }),\n invitations: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Includes or excludes pending invitations',\n }),\n order: Flags.string({\n default: 'asc',\n description: 'Sort output ascending/descending',\n options: ['asc', 'desc'],\n }),\n robots: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Includes or excludes robots (token users)',\n }),\n sort: Flags.string({\n default: 'date',\n description: 'Sort users by specified column',\n options: ['id', 'name', 'role', 'date'],\n }),\n }\n\n static override hiddenAliases: string[] = ['user:list']\n\n public async run(): Promise<void> {\n const {invitations, order, robots, sort} = this.flags\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project'},\n {grant: 'read', permission: 'sanity.project.members'},\n ],\n }),\n })\n\n const members = await getMembersForProject({\n includeInvitations: invitations,\n includeRobots: robots,\n projectId,\n })\n\n if (members.length === 0) {\n this.log('No members found for this project.')\n return\n }\n\n const ordered = sortBy(\n members.map(({date, id, name, roles}) => [\n id,\n name,\n roles\n ?.map((role) => role.title)\n .join(', ')\n .trim() || '-',\n date,\n ]),\n [sortFields.indexOf(sort)],\n )\n\n const rows = (order === 'asc' ? ordered : ordered.toReversed()).map(\n ([id, name, roles, date]) => [id, name, roles, date.split('T')[0]],\n )\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'name', title: 'Name'},\n {alignment: 'left', name: 'roles', title: 'Roles'},\n {alignment: 'left', name: 'date', title: 'Date'},\n ],\n })\n\n for (const [id, name, roles, date] of rows) {\n const isPending = id === '<pending>'\n table.addRow({\n date: dimText(date, isPending),\n id: dimText(id, isPending),\n name: dimText(name, isPending),\n roles: dimText(roles, isPending),\n })\n }\n\n this.log(table.render())\n }\n}\n"],"names":["styleText","Flags","SanityCommand","sortBy","getMembersForProject","promptForProject","Table","getProjectIdFlag","sortFields","dimText","value","isDim","List","description","examples","command","flags","semantics","invitations","boolean","allowNo","default","order","string","options","robots","sort","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","members","includeInvitations","includeRobots","length","log","ordered","map","date","id","name","roles","role","title","join","trim","indexOf","rows","toReversed","split","table","columns","alignment","isPending","addRow","render"],"mappings":"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"}
@@ -1,7 +1,22 @@
1
1
  import { getStudioEnvironmentVariables } from "@sanity/cli-build/_internal/env";
2
+ import { Interfaces } from "@oclif/core";
2
3
  import { Schema } from "@sanity/types";
3
4
  import { StudioEnvVariablesOptions } from "@sanity/cli-build/_internal/env";
4
5
 
6
+ /** @internal */
7
+ export declare interface CommandTelemetry {
8
+ redactedFlags: readonly RedactedFlag[];
9
+ }
10
+
11
+ declare type CommandTelemetryConfig<Flags extends Interfaces.FlagInput> = {
12
+ redact?: readonly OptionFlagName<Flags>[];
13
+ };
14
+
15
+ /** @internal */
16
+ export declare function defineCommandTelemetry<
17
+ const Flags extends Interfaces.FlagInput,
18
+ >(flags: Flags, config: CommandTelemetryConfig<Flags>): CommandTelemetry;
19
+
5
20
  /**
6
21
  * Extracts all serializable properties from userland schema types,
7
22
  * so they best-effort can be used as definitions for Schema.compile.
@@ -85,6 +100,20 @@ declare type ManifestValidationRule = {
85
100
  flag: string;
86
101
  };
87
102
 
103
+ declare type OptionFlagName<Flags extends Interfaces.FlagInput> = string &
104
+ {
105
+ [Name in keyof Flags]: Flags[Name] extends {
106
+ type: "option";
107
+ }
108
+ ? Name
109
+ : never;
110
+ }[keyof Flags];
111
+
112
+ declare interface RedactedFlag {
113
+ canonical: string;
114
+ flags: readonly string[];
115
+ }
116
+
88
117
  export { StudioEnvVariablesOptions };
89
118
 
90
119
  export {};
@@ -1,4 +1,5 @@
1
1
  export { extractManifestSchemaTypes } from '../actions/manifest/extractWorkspaceManifest.js';
2
+ export { defineCommandTelemetry } from '../util/telemetry/commandTelemetry.js';
2
3
  export { getStudioEnvironmentVariables } from '@sanity/cli-build/_internal/env';
3
4
 
4
5
  //# sourceMappingURL=_internal.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/exports/_internal.ts"],"sourcesContent":["export {extractManifestSchemaTypes} from '../actions/manifest/extractWorkspaceManifest.js'\nexport {\n getStudioEnvironmentVariables,\n type StudioEnvVariablesOptions,\n} from '@sanity/cli-build/_internal/env'\n"],"names":["extractManifestSchemaTypes","getStudioEnvironmentVariables"],"mappings":"AAAA,SAAQA,0BAA0B,QAAO,kDAAiD;AAC1F,SACEC,6BAA6B,QAExB,kCAAiC"}
1
+ {"version":3,"sources":["../../src/exports/_internal.ts"],"sourcesContent":["export {extractManifestSchemaTypes} from '../actions/manifest/extractWorkspaceManifest.js'\nexport {type CommandTelemetry, defineCommandTelemetry} from '../util/telemetry/commandTelemetry.js'\nexport {\n getStudioEnvironmentVariables,\n type StudioEnvVariablesOptions,\n} from '@sanity/cli-build/_internal/env'\n"],"names":["extractManifestSchemaTypes","defineCommandTelemetry","getStudioEnvironmentVariables"],"mappings":"AAAA,SAAQA,0BAA0B,QAAO,kDAAiD;AAC1F,SAA+BC,sBAAsB,QAAO,wCAAuC;AACnG,SACEC,6BAA6B,QAExB,kCAAiC"}
@@ -1,16 +1,24 @@
1
1
  import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.js';
2
- /**
3
- * A typed `-F/--field` value of `@<file>` or `@-` makes the `api` command
4
- * read the host's filesystem or stdin (raw `-f` fields are always verbatim).
5
- */ function fieldReadsFromHost(field) {
6
- if (typeof field !== 'string') return false;
7
- const separatorIndex = field.indexOf('=');
8
- return separatorIndex > 0 && field[separatorIndex + 1] === '@';
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';
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` and an Authorization header replace the MCP user's 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,8 +42,10 @@ import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.j
33
42
  'input',
34
43
  'token'
35
44
  ],
36
- validate: ({ flags })=>(!Array.isArray(flags.field) || !flags.field.some((field)=>fieldReadsFromHost(field))) && (!Array.isArray(flags.header) || !flags.header.some((header)=>headerOverridesAuthentication(header)))
45
+ validate: apiValidator
37
46
  }),
47
+ // Reads a file from the machine running the command and must only run locally.
48
+ 'assets:upload': deny,
38
49
  'backups:disable': allow,
39
50
  // Writes a downloaded backup to the local filesystem.
40
51
  'backups:download': deny,
@@ -166,6 +177,10 @@ import { allow, conditionalDenyFlags, conditionalPolicy, deny } from './policy.j
166
177
  'tokens:delete': deny,
167
178
  // Exposes authentication credential metadata.
168
179
  'tokens:list': deny,
180
+ // Replaces authentication credentials and exposes the new secret.
181
+ 'tokens:rotate': deny,
182
+ // Reads the local schema and source files and writes generated types to the project.
183
+ 'typegen:generate': deny,
169
184
  // Loads local CLI and workbench configuration to identify the deployed Studio or application.
170
185
  undeploy: deny,
171
186
  // Grants a user access to a project.