@sanity/cli 7.18.0 → 8.0.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.
Files changed (135) hide show
  1. package/README.md +74 -4
  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/init/sdkAppDependencies.js +1 -1
  29. package/dist/actions/init/sdkAppDependencies.js.map +1 -1
  30. package/dist/actions/init/studioDependencies.js +1 -1
  31. package/dist/actions/init/studioDependencies.js.map +1 -1
  32. package/dist/actions/schema/listSchemas.js +1 -1
  33. package/dist/actions/schema/listSchemas.js.map +1 -1
  34. package/dist/actions/tokens/constants.js +2 -2
  35. package/dist/actions/tokens/constants.js.map +1 -1
  36. package/dist/actions/tokens/expiry.js +35 -0
  37. package/dist/actions/tokens/expiry.js.map +1 -0
  38. package/dist/actions/tokens/types.js.map +1 -1
  39. package/dist/actions/tokens/validateRole.js +5 -2
  40. package/dist/actions/tokens/validateRole.js.map +1 -1
  41. package/dist/actions/typegen/count.js +6 -0
  42. package/dist/actions/typegen/count.js.map +1 -0
  43. package/dist/actions/typegen/formatPath.js +5 -0
  44. package/dist/actions/typegen/formatPath.js.map +1 -0
  45. package/dist/actions/typegen/percent.js +8 -0
  46. package/dist/actions/typegen/percent.js.map +1 -0
  47. package/dist/actions/typegen/renderTypegenProgress.js +88 -0
  48. package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
  49. package/dist/actions/undeploy/adapters.js +6 -9
  50. package/dist/actions/undeploy/adapters.js.map +1 -1
  51. package/dist/actions/undeploy/runUndeploy.js +12 -4
  52. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  53. package/dist/actions/undeploy/undeployPlan.js +6 -14
  54. package/dist/actions/undeploy/undeployPlan.js.map +1 -1
  55. package/dist/commands/assets/upload.js +160 -0
  56. package/dist/commands/assets/upload.js.map +1 -0
  57. package/dist/commands/backups/list.js +1 -1
  58. package/dist/commands/backups/list.js.map +1 -1
  59. package/dist/commands/datasets/copy.js +1 -1
  60. package/dist/commands/datasets/copy.js.map +1 -1
  61. package/dist/commands/login.js +2 -8
  62. package/dist/commands/login.js.map +1 -1
  63. package/dist/commands/mcp/configure.js.map +1 -1
  64. package/dist/commands/migrations/list.js +1 -1
  65. package/dist/commands/migrations/list.js.map +1 -1
  66. package/dist/commands/migrations/run.js +67 -3
  67. package/dist/commands/migrations/run.js.map +1 -1
  68. package/dist/commands/new.js +4 -3
  69. package/dist/commands/new.js.map +1 -1
  70. package/dist/commands/organizations/list.js +1 -1
  71. package/dist/commands/organizations/list.js.map +1 -1
  72. package/dist/commands/projects/list.js +39 -12
  73. package/dist/commands/projects/list.js.map +1 -1
  74. package/dist/commands/projects/unclaimed.js +39 -33
  75. package/dist/commands/projects/unclaimed.js.map +1 -1
  76. package/dist/commands/tokens/create.js +92 -7
  77. package/dist/commands/tokens/create.js.map +1 -1
  78. package/dist/commands/tokens/delete.js +2 -2
  79. package/dist/commands/tokens/delete.js.map +1 -1
  80. package/dist/commands/tokens/list.js +12 -11
  81. package/dist/commands/tokens/list.js.map +1 -1
  82. package/dist/commands/tokens/rotate.js +95 -0
  83. package/dist/commands/tokens/rotate.js.map +1 -0
  84. package/dist/commands/typegen/generate.js +222 -0
  85. package/dist/commands/typegen/generate.js.map +1 -0
  86. package/dist/commands/users/list.js +8 -8
  87. package/dist/commands/users/list.js.map +1 -1
  88. package/dist/exports/_internal.d.ts +29 -0
  89. package/dist/exports/_internal.js +1 -0
  90. package/dist/exports/_internal.js.map +1 -1
  91. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
  92. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  93. package/dist/exports/invokeSanityCli/help.js +4 -0
  94. package/dist/exports/invokeSanityCli/help.js.map +1 -1
  95. package/dist/exports/invokeSanityCli/index.js +2 -0
  96. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  97. package/dist/hooks/commandNotFound/topicAliases.js +13 -40
  98. package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
  99. package/dist/hooks/prerun/setupTelemetry.js +3 -2
  100. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  101. package/dist/server/vite/plugin-typegen.js +4 -16
  102. package/dist/server/vite/plugin-typegen.js.map +1 -1
  103. package/dist/services/assets.js +46 -0
  104. package/dist/services/assets.js.map +1 -0
  105. package/dist/services/tokens.js +81 -31
  106. package/dist/services/tokens.js.map +1 -1
  107. package/dist/topicAliases.js +43 -0
  108. package/dist/topicAliases.js.map +1 -1
  109. package/dist/util/parseArguments.js +7 -2
  110. package/dist/util/parseArguments.js.map +1 -1
  111. package/dist/util/readTokenFromStdin.js +17 -0
  112. package/dist/util/readTokenFromStdin.js.map +1 -0
  113. package/dist/util/responsiveTable.js +97 -0
  114. package/dist/util/responsiveTable.js.map +1 -0
  115. package/dist/util/telemetry/commandTelemetry.js +34 -0
  116. package/dist/util/telemetry/commandTelemetry.js.map +1 -0
  117. package/dist/util/unclaimedProjects.js +10 -0
  118. package/dist/util/unclaimedProjects.js.map +1 -1
  119. package/oclif.config.js +2 -4
  120. package/oclif.manifest.json +688 -476
  121. package/package.json +12 -10
  122. package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
  123. package/templates/shopify/docs/features.md +24 -0
  124. package/templates/shopify/schemaTypes/index.ts +2 -0
  125. package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
  126. package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
  127. package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
  128. package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
  129. package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
  130. package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
  131. package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
  132. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
  133. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
  134. package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
  135. package/templates/shopify-online-storefront/utils/formatMetafieldValue.ts +30 -0
@@ -1,10 +1,9 @@
1
- import { styleText } from 'node:util';
2
1
  import { Flags } from '@oclif/core';
3
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
4
- import size from 'lodash-es/size.js';
5
3
  import sortBy from 'lodash-es/sortBy.js';
6
4
  import { getManageUrl } from '../../actions/projects/getManageUrl.js';
7
5
  import { listProjects } from '../../services/projects.js';
6
+ import { Table } from '../../util/responsiveTable.js';
8
7
  const sortFields = [
9
8
  'id',
10
9
  'members',
@@ -60,17 +59,45 @@ export class List extends SanityCommand {
60
59
  sortFields.indexOf(sort)
61
60
  ]);
62
61
  const rows = order === 'asc' ? ordered : ordered.toReversed();
63
- // Initialize maxWidths with the width of each header
64
- const maxWidths = sortFields.map((str)=>size(str));
65
- // Calculate maximum width for each column
66
- for (const row of rows){
67
- for (const [i, element] of row.entries()){
68
- maxWidths[i] = Math.max(size(element), maxWidths[i]);
69
- }
62
+ const table = new Table({
63
+ columns: [
64
+ {
65
+ alignment: 'left',
66
+ name: 'id',
67
+ title: 'ID'
68
+ },
69
+ {
70
+ alignment: 'left',
71
+ name: 'members',
72
+ title: 'Members'
73
+ },
74
+ {
75
+ alignment: 'left',
76
+ name: 'name',
77
+ title: 'Name'
78
+ },
79
+ {
80
+ alignment: 'left',
81
+ name: 'url',
82
+ title: 'URL'
83
+ },
84
+ {
85
+ alignment: 'left',
86
+ name: 'created',
87
+ title: 'Created'
88
+ }
89
+ ]
90
+ });
91
+ for (const [id, members, name, url, created] of rows){
92
+ table.addRow({
93
+ created: created.split('T')[0],
94
+ id,
95
+ members,
96
+ name,
97
+ url
98
+ });
70
99
  }
71
- const printRow = (row)=>row.map((col, i)=>`${col}`.padEnd(maxWidths[i])).join(' ');
72
- this.log(styleText('cyan', printRow(sortFields)));
73
- for (const row of rows)this.log(printRow(row));
100
+ this.log(table.render());
74
101
  } catch (error) {
75
102
  projectsDebug('Error listing projects', error);
76
103
  this.error('Failed to list projects', {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/projects/list.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport size from 'lodash-es/size.js'\nimport sortBy from 'lodash-es/sortBy.js'\n\nimport {getManageUrl} from '../../actions/projects/getManageUrl.js'\nimport {listProjects} from '../../services/projects.js'\n\nconst sortFields = ['id', 'members', 'name', 'url', 'created']\n\nconst projectsDebug = subdebug('projects')\n\nexport class List extends SanityCommand<typeof List> {\n static override description = 'List your projects'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List projects',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sort=members --order=asc',\n description: 'List projects sorted by member count, ascending',\n },\n ]\n\n static override flags = {\n order: Flags.string({\n default: 'desc',\n description: 'Sort direction',\n options: ['asc', 'desc'],\n }),\n sort: Flags.string({\n default: 'created',\n description: 'Sort field',\n options: sortFields,\n }),\n }\n\n static override hiddenAliases: string[] = ['project:list']\n\n public async run() {\n const {order, sort} = this.flags\n\n try {\n const projects = await listProjects()\n const ordered = sortBy(\n projects.map(({createdAt, displayName, id, members = []}) => {\n const manage = getManageUrl(id)\n return [id, members.length, displayName, manage, createdAt].map(String)\n }),\n [sortFields.indexOf(sort)],\n )\n\n const rows = order === 'asc' ? ordered : ordered.toReversed()\n\n // Initialize maxWidths with the width of each header\n const maxWidths = sortFields.map((str) => size(str))\n\n // Calculate maximum width for each column\n for (const row of rows) {\n for (const [i, element] of row.entries()) {\n maxWidths[i] = Math.max(size(element), maxWidths[i])\n }\n }\n\n const printRow = (row: string[]) =>\n row.map((col, i) => `${col}`.padEnd(maxWidths[i])).join(' ')\n\n this.log(styleText('cyan', printRow(sortFields)))\n for (const row of rows) this.log(printRow(row))\n } catch (error) {\n projectsDebug('Error listing projects', error)\n this.error('Failed to list projects', {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["styleText","Flags","exitCodes","SanityCommand","subdebug","size","sortBy","getManageUrl","listProjects","sortFields","projectsDebug","List","description","examples","command","flags","order","string","default","options","sort","hiddenAliases","run","projects","ordered","map","createdAt","displayName","id","members","manage","length","String","indexOf","rows","toReversed","maxWidths","str","row","i","element","entries","Math","max","printRow","col","padEnd","join","log","error","exit","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,OAAOC,UAAU,oBAAmB;AACpC,OAAOC,YAAY,sBAAqB;AAExC,SAAQC,YAAY,QAAO,yCAAwC;AACnE,SAAQC,YAAY,QAAO,6BAA4B;AAEvD,MAAMC,aAAa;IAAC;IAAM;IAAW;IAAQ;IAAO;CAAU;AAE9D,MAAMC,gBAAgBN,SAAS;AAE/B,OAAO,MAAMO,aAAaR;IACxB,OAAgBS,cAAc,qBAAoB;IAClD,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtBC,OAAOf,MAAMgB,MAAM,CAAC;YAClBC,SAAS;YACTN,aAAa;YACbO,SAAS;gBAAC;gBAAO;aAAO;QAC1B;QACAC,MAAMnB,MAAMgB,MAAM,CAAC;YACjBC,SAAS;YACTN,aAAa;YACbO,SAASV;QACX;IACF,EAAC;IAED,OAAgBY,gBAA0B;QAAC;KAAe,CAAA;IAE1D,MAAaC,MAAM;QACjB,MAAM,EAACN,KAAK,EAAEI,IAAI,EAAC,GAAG,IAAI,CAACL,KAAK;QAEhC,IAAI;YACF,MAAMQ,WAAW,MAAMf;YACvB,MAAMgB,UAAUlB,OACdiB,SAASE,GAAG,CAAC,CAAC,EAACC,SAAS,EAAEC,WAAW,EAAEC,EAAE,EAAEC,UAAU,EAAE,EAAC;gBACtD,MAAMC,SAASvB,aAAaqB;gBAC5B,OAAO;oBAACA;oBAAIC,QAAQE,MAAM;oBAAEJ;oBAAaG;oBAAQJ;iBAAU,CAACD,GAAG,CAACO;YAClE,IACA;gBAACvB,WAAWwB,OAAO,CAACb;aAAM;YAG5B,MAAMc,OAAOlB,UAAU,QAAQQ,UAAUA,QAAQW,UAAU;YAE3D,qDAAqD;YACrD,MAAMC,YAAY3B,WAAWgB,GAAG,CAAC,CAACY,MAAQhC,KAAKgC;YAE/C,0CAA0C;YAC1C,KAAK,MAAMC,OAAOJ,KAAM;gBACtB,KAAK,MAAM,CAACK,GAAGC,QAAQ,IAAIF,IAAIG,OAAO,GAAI;oBACxCL,SAAS,CAACG,EAAE,GAAGG,KAAKC,GAAG,CAACtC,KAAKmC,UAAUJ,SAAS,CAACG,EAAE;gBACrD;YACF;YAEA,MAAMK,WAAW,CAACN,MAChBA,IAAIb,GAAG,CAAC,CAACoB,KAAKN,IAAM,GAAGM,KAAK,CAACC,MAAM,CAACV,SAAS,CAACG,EAAE,GAAGQ,IAAI,CAAC;YAE1D,IAAI,CAACC,GAAG,CAAChD,UAAU,QAAQ4C,SAASnC;YACpC,KAAK,MAAM6B,OAAOJ,KAAM,IAAI,CAACc,GAAG,CAACJ,SAASN;QAC5C,EAAE,OAAOW,OAAO;YACdvC,cAAc,0BAA0BuC;YACxC,IAAI,CAACA,KAAK,CAAC,2BAA2B;gBAACC,MAAMhD,UAAUiD,aAAa;YAAA;QACtE;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/projects/list.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport sortBy from 'lodash-es/sortBy.js'\n\nimport {getManageUrl} from '../../actions/projects/getManageUrl.js'\nimport {listProjects} from '../../services/projects.js'\nimport {Table} from '../../util/responsiveTable.js'\n\nconst sortFields = ['id', 'members', 'name', 'url', 'created']\n\nconst projectsDebug = subdebug('projects')\n\nexport class List extends SanityCommand<typeof List> {\n static override description = 'List your projects'\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List projects',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sort=members --order=asc',\n description: 'List projects sorted by member count, ascending',\n },\n ]\n\n static override flags = {\n order: Flags.string({\n default: 'desc',\n description: 'Sort direction',\n options: ['asc', 'desc'],\n }),\n sort: Flags.string({\n default: 'created',\n description: 'Sort field',\n options: sortFields,\n }),\n }\n\n static override hiddenAliases: string[] = ['project:list']\n\n public async run() {\n const {order, sort} = this.flags\n\n try {\n const projects = await listProjects()\n const ordered = sortBy(\n projects.map(({createdAt, displayName, id, members = []}) => {\n const manage = getManageUrl(id)\n return [id, members.length, displayName, manage, createdAt].map(String)\n }),\n [sortFields.indexOf(sort)],\n )\n\n const rows = order === 'asc' ? ordered : ordered.toReversed()\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'members', title: 'Members'},\n {alignment: 'left', name: 'name', title: 'Name'},\n {alignment: 'left', name: 'url', title: 'URL'},\n {alignment: 'left', name: 'created', title: 'Created'},\n ],\n })\n\n for (const [id, members, name, url, created] of rows) {\n table.addRow({created: created.split('T')[0], id, members, name, url})\n }\n\n this.log(table.render())\n } catch (error) {\n projectsDebug('Error listing projects', error)\n this.error('Failed to list projects', {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","subdebug","sortBy","getManageUrl","listProjects","Table","sortFields","projectsDebug","List","description","examples","command","flags","order","string","default","options","sort","hiddenAliases","run","projects","ordered","map","createdAt","displayName","id","members","manage","length","String","indexOf","rows","toReversed","table","columns","alignment","name","title","url","created","addRow","split","log","render","error","exit","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AACjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,OAAOC,YAAY,sBAAqB;AAExC,SAAQC,YAAY,QAAO,yCAAwC;AACnE,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,KAAK,QAAO,gCAA+B;AAEnD,MAAMC,aAAa;IAAC;IAAM;IAAW;IAAQ;IAAO;CAAU;AAE9D,MAAMC,gBAAgBN,SAAS;AAE/B,OAAO,MAAMO,aAAaR;IACxB,OAAgBS,cAAc,qBAAoB;IAClD,OAAgBC,WAAW;QACzB;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;KACD,CAAA;IAED,OAAgBG,QAAQ;QACtBC,OAAOf,MAAMgB,MAAM,CAAC;YAClBC,SAAS;YACTN,aAAa;YACbO,SAAS;gBAAC;gBAAO;aAAO;QAC1B;QACAC,MAAMnB,MAAMgB,MAAM,CAAC;YACjBC,SAAS;YACTN,aAAa;YACbO,SAASV;QACX;IACF,EAAC;IAED,OAAgBY,gBAA0B;QAAC;KAAe,CAAA;IAE1D,MAAaC,MAAM;QACjB,MAAM,EAACN,KAAK,EAAEI,IAAI,EAAC,GAAG,IAAI,CAACL,KAAK;QAEhC,IAAI;YACF,MAAMQ,WAAW,MAAMhB;YACvB,MAAMiB,UAAUnB,OACdkB,SAASE,GAAG,CAAC,CAAC,EAACC,SAAS,EAAEC,WAAW,EAAEC,EAAE,EAAEC,UAAU,EAAE,EAAC;gBACtD,MAAMC,SAASxB,aAAasB;gBAC5B,OAAO;oBAACA;oBAAIC,QAAQE,MAAM;oBAAEJ;oBAAaG;oBAAQJ;iBAAU,CAACD,GAAG,CAACO;YAClE,IACA;gBAACvB,WAAWwB,OAAO,CAACb;aAAM;YAG5B,MAAMc,OAAOlB,UAAU,QAAQQ,UAAUA,QAAQW,UAAU;YAC3D,MAAMC,QAAQ,IAAI5B,MAAM;gBACtB6B,SAAS;oBACP;wBAACC,WAAW;wBAAQC,MAAM;wBAAMC,OAAO;oBAAI;oBAC3C;wBAACF,WAAW;wBAAQC,MAAM;wBAAWC,OAAO;oBAAS;oBACrD;wBAACF,WAAW;wBAAQC,MAAM;wBAAQC,OAAO;oBAAM;oBAC/C;wBAACF,WAAW;wBAAQC,MAAM;wBAAOC,OAAO;oBAAK;oBAC7C;wBAACF,WAAW;wBAAQC,MAAM;wBAAWC,OAAO;oBAAS;iBACtD;YACH;YAEA,KAAK,MAAM,CAACZ,IAAIC,SAASU,MAAME,KAAKC,QAAQ,IAAIR,KAAM;gBACpDE,MAAMO,MAAM,CAAC;oBAACD,SAASA,QAAQE,KAAK,CAAC,IAAI,CAAC,EAAE;oBAAEhB;oBAAIC;oBAASU;oBAAME;gBAAG;YACtE;YAEA,IAAI,CAACI,GAAG,CAACT,MAAMU,MAAM;QACvB,EAAE,OAAOC,OAAO;YACdrC,cAAc,0BAA0BqC;YACxC,IAAI,CAACA,KAAK,CAAC,2BAA2B;gBAACC,MAAM9C,UAAU+C,aAAa;YAAA;QACtE;IACF;AACF"}
@@ -1,43 +1,13 @@
1
- import { styleText } from 'node:util';
2
1
  import { Flags } from '@oclif/core';
3
2
  import { exitCodes, SanityCommand } from '@sanity/cli-core';
4
3
  import { logSymbols } from '@sanity/cli-core/ux';
5
- import size from 'lodash-es/size.js';
6
4
  import { formatKeyValue, sectionHeader } from '../../actions/debug/output.js';
7
5
  import { getProjectClaimStatus } from '../../services/projects.js';
6
+ import { Table } from '../../util/responsiveTable.js';
8
7
  import { readUnclaimedProjects, removeUnclaimedProject } from '../../util/unclaimedProjects.js';
9
- const listFields = [
10
- 'id',
11
- 'dataset',
12
- 'created',
13
- 'claim deadline',
14
- 'claim url'
15
- ];
16
- function recordRow(record) {
17
- return [
18
- record.projectId,
19
- record.dataset,
20
- record.mintedAt,
21
- record.expiresAt,
22
- record.claimUrl
23
- ];
24
- }
25
8
  function isExpired(record) {
26
9
  return Date.parse(record.expiresAt) <= Date.now();
27
10
  }
28
- function formatTable(fields, rows) {
29
- const maxWidths = fields.map((field)=>size(field));
30
- for (const row of rows){
31
- for (const [index, value] of row.entries()){
32
- maxWidths[index] = Math.max(size(value), maxWidths[index]);
33
- }
34
- }
35
- const formatRow = (row)=>row.map((value, index)=>value.padEnd(maxWidths[index])).join(' ');
36
- return [
37
- styleText('cyan', formatRow(fields)),
38
- ...rows.map((row)=>formatRow(row))
39
- ];
40
- }
41
11
  export class UnclaimedProjectsCommand extends SanityCommand {
42
12
  static description = 'Recover details for unclaimed projects created on this machine';
43
13
  static examples = [
@@ -167,9 +137,45 @@ export class UnclaimedProjectsCommand extends SanityCommand {
167
137
  this.output.log('No locally recorded unclaimed projects. Create one with `sanity new`, then return here if you need its recovery details.');
168
138
  return;
169
139
  }
170
- for (const line of formatTable(listFields, records.map((record)=>recordRow(record)))){
171
- this.output.log(line);
140
+ const table = new Table({
141
+ columns: [
142
+ {
143
+ alignment: 'left',
144
+ name: 'id',
145
+ title: 'ID'
146
+ },
147
+ {
148
+ alignment: 'left',
149
+ name: 'dataset',
150
+ title: 'Dataset'
151
+ },
152
+ {
153
+ alignment: 'left',
154
+ name: 'created',
155
+ title: 'Created'
156
+ },
157
+ {
158
+ alignment: 'left',
159
+ name: 'claimDeadline',
160
+ title: 'Claim deadline'
161
+ },
162
+ {
163
+ alignment: 'left',
164
+ name: 'claimUrl',
165
+ title: 'Claim URL'
166
+ }
167
+ ]
168
+ });
169
+ for (const record of records){
170
+ table.addRow({
171
+ claimDeadline: record.expiresAt,
172
+ claimUrl: record.claimUrl,
173
+ created: record.mintedAt,
174
+ dataset: record.dataset,
175
+ id: record.projectId
176
+ });
172
177
  }
178
+ this.output.log(table.render());
173
179
  if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {
174
180
  this.output.log();
175
181
  for (const expiredProjectId of expiredProjectIds){
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/projects/unclaimed.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport size from 'lodash-es/size.js'\n\nimport {formatKeyValue, sectionHeader} from '../../actions/debug/output.js'\nimport {getProjectClaimStatus} from '../../services/projects.js'\nimport {\n readUnclaimedProjects,\n removeUnclaimedProject,\n type UnclaimedProjectRecord,\n} from '../../util/unclaimedProjects.js'\n\nconst listFields = ['id', 'dataset', 'created', 'claim deadline', 'claim url']\n\nfunction recordRow(record: UnclaimedProjectRecord): string[] {\n return [record.projectId, record.dataset, record.mintedAt, record.expiresAt, record.claimUrl]\n}\n\nfunction isExpired(record: UnclaimedProjectRecord): boolean {\n return Date.parse(record.expiresAt) <= Date.now()\n}\n\nfunction formatTable(fields: string[], rows: string[][]): string[] {\n const maxWidths = fields.map((field) => size(field))\n for (const row of rows) {\n for (const [index, value] of row.entries()) {\n maxWidths[index] = Math.max(size(value), maxWidths[index])\n }\n }\n\n const formatRow = (row: string[]) =>\n row.map((value, index) => value.padEnd(maxWidths[index])).join(' ')\n\n return [styleText('cyan', formatRow(fields)), ...rows.map((row) => formatRow(row))]\n}\n\nexport class UnclaimedProjectsCommand extends SanityCommand<typeof UnclaimedProjectsCommand> {\n static override description = 'Recover details for unclaimed projects created on this machine'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List locally recorded unclaimed projects',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Show recovery details for one project',\n },\n ]\n\n static override flags = {\n 'project-id': Flags.string({\n description: 'Project ID to recover',\n }),\n }\n\n static override hiddenAliases: string[] = ['project:unclaimed']\n\n public async run(): Promise<void> {\n let records: UnclaimedProjectRecord[]\n try {\n records = readUnclaimedProjects()\n } catch (error) {\n this.output.error(\n `Could not read local unclaimed projects: ${\n error instanceof Error ? error.message : String(error)\n } Remove or repair the \"unclaimedProjects\" entry in your Sanity user config.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n const projectId = this.flags['project-id']\n if (projectId) {\n const record = records.find((candidate) => candidate.projectId === projectId)\n if (!record) {\n this.output.error(\n `No local recovery record found for project \"${projectId}\". Run \\`sanity projects unclaimed\\` to list recorded projects.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n if (isExpired(record)) {\n if (this.removeExpiredProject(record)) {\n this.logExpiredProject(record.projectId)\n }\n return\n }\n\n const status = await getProjectClaimStatus(record.projectId, record.token)\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n this.logClaimedProject(record.projectId)\n }\n return\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n\n this.output.log(sectionHeader('Project'))\n const details = [\n ['Project ID', record.projectId],\n ['Dataset', record.dataset],\n ['Created', record.mintedAt],\n ['Claim deadline', record.expiresAt],\n ['Claim URL', record.claimUrl],\n ['Access token', record.token],\n ]\n for (const [label, value] of details) {\n this.output.log(formatKeyValue(label, value, {padTo: 14}))\n }\n this.output.log()\n return\n }\n\n const expiredProjectIds: string[] = []\n const activeRecords = records.filter((record) => {\n if (!isExpired(record)) return true\n if (this.removeExpiredProject(record)) {\n expiredProjectIds.push(record.projectId)\n }\n return false\n })\n const checked = await Promise.all(\n activeRecords.map(async (record) => ({\n record,\n status: await getProjectClaimStatus(record.projectId, record.token),\n })),\n )\n records = []\n const claimedProjectIds: string[] = []\n for (const {record, status} of checked) {\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n claimedProjectIds.push(record.projectId)\n }\n continue\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${record.projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n records.push(record)\n }\n\n if (records.length === 0) {\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n return\n }\n this.output.log(\n 'No locally recorded unclaimed projects. Create one with `sanity new`, then return here if you need its recovery details.',\n )\n return\n }\n\n for (const line of formatTable(\n listFields,\n records.map((record) => recordRow(record)),\n )) {\n this.output.log(line)\n }\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n this.output.log()\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n }\n }\n\n private logClaimedProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" is claimed. Local recovery details removed.`,\n )\n }\n\n private logExpiredProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" expired. Local recovery details removed.`,\n )\n }\n\n private removeClaimedProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" is claimed, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n\n private removeExpiredProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" expired, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n}\n"],"names":["styleText","Flags","exitCodes","SanityCommand","logSymbols","size","formatKeyValue","sectionHeader","getProjectClaimStatus","readUnclaimedProjects","removeUnclaimedProject","listFields","recordRow","record","projectId","dataset","mintedAt","expiresAt","claimUrl","isExpired","Date","parse","now","formatTable","fields","rows","maxWidths","map","field","row","index","value","entries","Math","max","formatRow","padEnd","join","UnclaimedProjectsCommand","description","examples","command","flags","string","hiddenAliases","run","records","error","output","Error","message","String","exit","RUNTIME_ERROR","find","candidate","removeExpiredProject","logExpiredProject","status","token","removeClaimedProject","logClaimedProject","warn","log","details","label","padTo","expiredProjectIds","activeRecords","filter","push","checked","Promise","all","claimedProjectIds","length","expiredProjectId","claimedProjectId","line","success","removed","claimToken"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/projects/unclaimed.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {formatKeyValue, sectionHeader} from '../../actions/debug/output.js'\nimport {getProjectClaimStatus} from '../../services/projects.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {\n readUnclaimedProjects,\n removeUnclaimedProject,\n type UnclaimedProjectRecord,\n} from '../../util/unclaimedProjects.js'\n\nfunction isExpired(record: UnclaimedProjectRecord): boolean {\n return Date.parse(record.expiresAt) <= Date.now()\n}\n\nexport class UnclaimedProjectsCommand extends SanityCommand<typeof UnclaimedProjectsCommand> {\n static override description = 'Recover details for unclaimed projects created on this machine'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List locally recorded unclaimed projects',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Show recovery details for one project',\n },\n ]\n\n static override flags = {\n 'project-id': Flags.string({\n description: 'Project ID to recover',\n }),\n }\n\n static override hiddenAliases: string[] = ['project:unclaimed']\n\n public async run(): Promise<void> {\n let records: UnclaimedProjectRecord[]\n try {\n records = readUnclaimedProjects()\n } catch (error) {\n this.output.error(\n `Could not read local unclaimed projects: ${\n error instanceof Error ? error.message : String(error)\n } Remove or repair the \"unclaimedProjects\" entry in your Sanity user config.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n const projectId = this.flags['project-id']\n if (projectId) {\n const record = records.find((candidate) => candidate.projectId === projectId)\n if (!record) {\n this.output.error(\n `No local recovery record found for project \"${projectId}\". Run \\`sanity projects unclaimed\\` to list recorded projects.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n return\n }\n\n if (isExpired(record)) {\n if (this.removeExpiredProject(record)) {\n this.logExpiredProject(record.projectId)\n }\n return\n }\n\n const status = await getProjectClaimStatus(record.projectId, record.token)\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n this.logClaimedProject(record.projectId)\n }\n return\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n\n this.output.log(sectionHeader('Project'))\n const details = [\n ['Project ID', record.projectId],\n ['Dataset', record.dataset],\n ['Created', record.mintedAt],\n ['Claim deadline', record.expiresAt],\n ['Claim URL', record.claimUrl],\n ['Access token', record.token],\n ]\n for (const [label, value] of details) {\n this.output.log(formatKeyValue(label, value, {padTo: 14}))\n }\n this.output.log()\n return\n }\n\n const expiredProjectIds: string[] = []\n const activeRecords = records.filter((record) => {\n if (!isExpired(record)) return true\n if (this.removeExpiredProject(record)) {\n expiredProjectIds.push(record.projectId)\n }\n return false\n })\n const checked = await Promise.all(\n activeRecords.map(async (record) => ({\n record,\n status: await getProjectClaimStatus(record.projectId, record.token),\n })),\n )\n records = []\n const claimedProjectIds: string[] = []\n for (const {record, status} of checked) {\n if (status === 'claimed') {\n if (this.removeClaimedProject(record)) {\n claimedProjectIds.push(record.projectId)\n }\n continue\n }\n if (status === 'unknown') {\n this.output.warn(\n `Could not verify whether project \"${record.projectId}\" is claimed. Local recovery details were kept. Run this command again to retry.`,\n )\n }\n records.push(record)\n }\n\n if (records.length === 0) {\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n return\n }\n this.output.log(\n 'No locally recorded unclaimed projects. Create one with `sanity new`, then return here if you need its recovery details.',\n )\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'ID'},\n {alignment: 'left', name: 'dataset', title: 'Dataset'},\n {alignment: 'left', name: 'created', title: 'Created'},\n {alignment: 'left', name: 'claimDeadline', title: 'Claim deadline'},\n {alignment: 'left', name: 'claimUrl', title: 'Claim URL'},\n ],\n })\n for (const record of records) {\n table.addRow({\n claimDeadline: record.expiresAt,\n claimUrl: record.claimUrl,\n created: record.mintedAt,\n dataset: record.dataset,\n id: record.projectId,\n })\n }\n this.output.log(table.render())\n if (claimedProjectIds.length > 0 || expiredProjectIds.length > 0) {\n this.output.log()\n for (const expiredProjectId of expiredProjectIds) {\n this.logExpiredProject(expiredProjectId)\n }\n for (const claimedProjectId of claimedProjectIds) {\n this.logClaimedProject(claimedProjectId)\n }\n }\n }\n\n private logClaimedProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" is claimed. Local recovery details removed.`,\n )\n }\n\n private logExpiredProject(projectId: string): void {\n this.output.log(\n `${logSymbols.success} Project \"${projectId}\" expired. Local recovery details removed.`,\n )\n }\n\n private removeClaimedProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" is claimed, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n\n private removeExpiredProject(record: UnclaimedProjectRecord): boolean {\n const removed = removeUnclaimedProject(record.projectId, record.claimToken)\n if (!removed) {\n this.output.warn(\n `Project \"${record.projectId}\" expired, but its local recovery details could not be removed. Run this command again to retry.`,\n )\n }\n return removed\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","logSymbols","formatKeyValue","sectionHeader","getProjectClaimStatus","Table","readUnclaimedProjects","removeUnclaimedProject","isExpired","record","Date","parse","expiresAt","now","UnclaimedProjectsCommand","description","examples","command","flags","string","hiddenAliases","run","records","error","output","Error","message","String","exit","RUNTIME_ERROR","projectId","find","candidate","removeExpiredProject","logExpiredProject","status","token","removeClaimedProject","logClaimedProject","warn","log","details","dataset","mintedAt","claimUrl","label","value","padTo","expiredProjectIds","activeRecords","filter","push","checked","Promise","all","map","claimedProjectIds","length","expiredProjectId","claimedProjectId","table","columns","alignment","name","title","addRow","claimDeadline","created","id","render","success","removed","claimToken"],"mappings":"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"}
@@ -1,11 +1,18 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { input, select } from '@sanity/cli-core/ux';
4
+ import { expiryInDays, parseExpiryDate } from '../../actions/tokens/expiry.js';
4
5
  import { validateRole } from '../../actions/tokens/validateRole.js';
5
6
  import { promptForProject } from '../../prompts/promptForProject.js';
6
7
  import { createToken, getTokenRoles } from '../../services/tokens.js';
7
8
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
8
9
  const tokensCreateDebug = subdebug('tokens:create');
10
+ // Mirrors the expiry presets offered by sanity.io/manage
11
+ const EXPIRY_PRESET_DAYS = [
12
+ 30,
13
+ 60,
14
+ 90
15
+ ];
9
16
  export class CreateTokenCommand extends SanityCommand {
10
17
  static args = {
11
18
  label: Args.string({
@@ -27,6 +34,10 @@ export class CreateTokenCommand extends SanityCommand {
27
34
  command: '<%= config.bin %> <%= command.id %> "CI Token" --role=editor --yes',
28
35
  description: 'Create a token in unattended mode'
29
36
  },
37
+ {
38
+ command: '<%= config.bin %> <%= command.id %> "Build Token" --expires-at 2027-01-01',
39
+ description: 'Create a token that expires on a given date'
40
+ },
30
41
  {
31
42
  command: '<%= config.bin %> <%= command.id %> "API Token" --json',
32
43
  description: 'Output token information as JSON'
@@ -41,6 +52,10 @@ export class CreateTokenCommand extends SanityCommand {
41
52
  description: 'Project ID to create token in',
42
53
  semantics: 'override'
43
54
  }),
55
+ 'expires-at': Flags.string({
56
+ description: 'Date or timestamp the token expires (ISO 8601; tokens never expire by default)',
57
+ helpValue: '2027-01-01'
58
+ }),
44
59
  json: Flags.boolean({
45
60
  default: false,
46
61
  description: 'Output as JSON'
@@ -70,6 +85,7 @@ export class CreateTokenCommand extends SanityCommand {
70
85
  exit: exitCodes.USAGE_ERROR
71
86
  });
72
87
  }
88
+ const flagExpiresAt = this.parseExpiryFlag(flags['expires-at']);
73
89
  const projectId = await this.getProjectId({
74
90
  fallback: ()=>promptForProject({
75
91
  requiredPermissions: [
@@ -84,16 +100,19 @@ export class CreateTokenCommand extends SanityCommand {
84
100
  ]
85
101
  })
86
102
  });
87
- const roleName = await (role ? validateRole(role, projectId, this.output) : this.promptForRole(projectId));
103
+ const selectedRole = await (role ? validateRole(role, projectId, this.output) : this.promptForRole(projectId));
104
+ const expiresAt = flags['expires-at'] === undefined ? await this.promptForExpiry() : flagExpiresAt;
88
105
  try {
89
106
  tokensCreateDebug(`Creating token for project ${projectId}`, {
107
+ expiresAt,
90
108
  label,
91
- roleName
109
+ roleName: selectedRole.name
92
110
  });
93
111
  const token = await createToken({
112
+ expiresAt,
94
113
  label,
95
114
  projectId,
96
- roleName
115
+ roleName: selectedRole.name
97
116
  });
98
117
  if (json) {
99
118
  this.log(JSON.stringify(token, null, 2));
@@ -102,8 +121,11 @@ export class CreateTokenCommand extends SanityCommand {
102
121
  this.log('API token created');
103
122
  this.log(`Label: ${token.label}`);
104
123
  this.log(`ID: ${token.id}`);
105
- this.log(`Role: ${token.roles.map((r)=>r.title).join(', ')}`);
106
- this.log(`Token: ${token.key}`);
124
+ this.log(`Role: ${selectedRole.title}`);
125
+ if (token.expiresAt) {
126
+ this.log(`Expires: ${token.expiresAt}`);
127
+ }
128
+ this.log(`Token: ${token.token}`);
107
129
  this.log('');
108
130
  this.log("Copy the token now. It won't be shown again.");
109
131
  } catch (error) {
@@ -114,6 +136,57 @@ export class CreateTokenCommand extends SanityCommand {
114
136
  });
115
137
  }
116
138
  }
139
+ parseExpiryFlag(value) {
140
+ if (value === undefined) {
141
+ return undefined;
142
+ }
143
+ const parsed = parseExpiryDate(value);
144
+ if ('error' in parsed) {
145
+ this.error(parsed.error, {
146
+ exit: exitCodes.USAGE_ERROR
147
+ });
148
+ }
149
+ return parsed.expiresAt;
150
+ }
151
+ async promptForExpiry() {
152
+ if (this.isUnattended()) {
153
+ return undefined // Tokens never expire by default
154
+ ;
155
+ }
156
+ const choice = await select({
157
+ choices: [
158
+ {
159
+ name: 'Never',
160
+ value: 'never'
161
+ },
162
+ ...EXPIRY_PRESET_DAYS.map((days)=>({
163
+ name: `${days} days (${expiryInDays(days).slice(0, 10)})`,
164
+ value: String(days)
165
+ })),
166
+ {
167
+ name: 'Custom date',
168
+ value: 'custom'
169
+ }
170
+ ],
171
+ default: 'never',
172
+ message: 'Token expiry:'
173
+ });
174
+ if (choice === 'never') {
175
+ return undefined;
176
+ }
177
+ if (choice === 'custom') {
178
+ const value = await input({
179
+ message: 'Expiry date (ISO 8601, e.g. 2027-01-01):',
180
+ validate: (input)=>{
181
+ const parsed = parseExpiryDate(input.trim());
182
+ return 'error' in parsed ? parsed.error : true;
183
+ }
184
+ });
185
+ const parsed = parseExpiryDate(value.trim());
186
+ return 'error' in parsed ? undefined : parsed.expiresAt;
187
+ }
188
+ return expiryInDays(Number(choice));
189
+ }
117
190
  async promptForLabel() {
118
191
  if (this.isUnattended()) {
119
192
  this.error('Token label is required. Pass it as the `<label>` argument.', {
@@ -133,7 +206,10 @@ export class CreateTokenCommand extends SanityCommand {
133
206
  }
134
207
  async promptForRole(projectId) {
135
208
  if (this.isUnattended()) {
136
- return 'viewer' // Default role for unattended mode
209
+ return {
210
+ name: 'viewer',
211
+ title: 'Viewer'
212
+ } // Default role for unattended mode
137
213
  ;
138
214
  }
139
215
  const roles = await getTokenRoles(projectId);
@@ -155,7 +231,16 @@ export class CreateTokenCommand extends SanityCommand {
155
231
  default: 'viewer',
156
232
  message: 'Select role for the token:'
157
233
  });
158
- return selectedRoleName;
234
+ const selectedRole = robotRoles.find((r)=>r.name === selectedRoleName);
235
+ if (!selectedRole) {
236
+ this.error('No roles available for tokens', {
237
+ exit: exitCodes.RUNTIME_ERROR
238
+ });
239
+ }
240
+ return {
241
+ name: selectedRole.name,
242
+ title: selectedRole.title
243
+ };
159
244
  }
160
245
  }
161
246
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createToken, getTokenRoles} from '../../services/tokens.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description = 'Create a new API token for the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token with a label',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\" --role=editor',\n description: 'Create a token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project-id abc123 --role=editor',\n description: 'Create a token for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output as JSON',\n }),\n role: Flags.string({\n description: 'Role to assign to the token (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n const {json, role} = flags\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n 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.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n\n const roleName = await (role\n ? validateRole(role, projectId, this.output)\n : this.promptForRole(projectId))\n\n try {\n tokensCreateDebug(`Creating token for project ${projectId}`, {\n label,\n roleName,\n })\n const token = await createToken({\n label,\n projectId,\n roleName,\n })\n\n if (json) {\n this.log(JSON.stringify(token, null, 2))\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n this.log(`Role: ${token.roles.map((r) => r.title).join(', ')}`)\n this.log(`Token: ${token.key}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n } catch (error) {\n const err = error as Error\n\n tokensCreateDebug(`Error creating token for project ${projectId}`, err)\n this.error(`Token creation failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async promptForRole(projectId: string): Promise<string> {\n if (this.isUnattended()) {\n return 'viewer' // Default role for unattended mode\n }\n\n const roles = await getTokenRoles(projectId)\n const robotRoles = roles.filter((role) => role.appliesToRobots)\n\n tokensCreateDebug('Robot roles', {robotRoles})\n\n if (robotRoles.length === 0) {\n this.error('No roles available for tokens', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const selectedRoleName = await select({\n choices: robotRoles.map((role) => ({\n name: `${role.title} (${role.name})`,\n short: role.title,\n value: role.name,\n })),\n default: 'viewer',\n message: 'Select role for the token:',\n })\n\n return selectedRoleName\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","input","select","validateRole","promptForProject","createToken","getTokenRoles","getProjectIdFlag","tokensCreateDebug","CreateTokenCommand","args","label","string","description","required","examples","command","flags","semantics","json","boolean","default","role","helpValue","yes","char","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","roleName","output","promptForRole","token","log","JSON","stringify","id","roles","map","r","title","join","key","err","message","RUNTIME_ERROR","isUnattended","validate","value","robotRoles","filter","appliesToRobots","length","selectedRoleName","choices","name","short"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,KAAK,EAAEC,MAAM,QAAO,sBAAqB;AAEjD,SAAQC,YAAY,QAAO,uCAAsC;AACjE,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,WAAW,EAAEC,aAAa,QAAO,2BAA0B;AACnE,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,oBAAoBR,SAAS;AAEnC,OAAO,MAAMS,2BAA2BV;IACtC,OAAgBW,OAAO;QACrBC,OAAOf,KAAKgB,MAAM,CAAC;YACjBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,yCAAwC;IAEtE,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGV,iBAAiB;YAClBM,aAAa;YACbK,WAAW;QACb,EAAE;QACFC,MAAMtB,MAAMuB,OAAO,CAAC;YAClBC,SAAS;YACTR,aAAa;QACf;QACAS,MAAMzB,MAAMe,MAAM,CAAC;YACjBC,aAAa;YACbU,WAAW;QACb;QACAC,KAAK3B,MAAMuB,OAAO,CAAC;YACjBK,MAAM;YACNJ,SAAS;YACTR,aAAa;QACf;IACF,EAAC;IAED,OAAgBa,gBAA0B;QAAC;QAAc;QAAa;KAAe,CAAA;IAErF,MAAaC,MAAqB;QAChC,MAAM,EAACjB,IAAI,EAAEO,KAAK,EAAC,GAAG,MAAM,IAAI,CAACW,KAAK,CAACnB;QACvC,MAAM,EAACE,OAAOkB,UAAU,EAAC,GAAGnB;QAC5B,MAAM,EAACS,IAAI,EAAEG,IAAI,EAAC,GAAGL;QAErB,MAAMN,QAAQkB,eAAeC,YAAY,MAAM,IAAI,CAACC,cAAc,KAAKF,WAAWG,IAAI;QACtF,IAAI,CAACrB,OAAO;YACV,IAAI,CAACsB,KAAK,CAAC,kFAAkF;gBAC3FC,MAAMpC,UAAUqC,WAAW;YAC7B;QACF;QAEA,MAAMC,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACRlC,iBAAiB;oBACfmC,qBAAqB;wBACnB;4BAACC,OAAO;4BAAQC,YAAY;wBAAsB;wBAClD;4BAACD,OAAO;4BAAUC,YAAY;wBAAuB;qBACtD;gBACH;QACJ;QAEA,MAAMC,WAAW,MAAOpB,CAAAA,OACpBnB,aAAamB,MAAMc,WAAW,IAAI,CAACO,MAAM,IACzC,IAAI,CAACC,aAAa,CAACR,UAAS;QAEhC,IAAI;YACF5B,kBAAkB,CAAC,2BAA2B,EAAE4B,WAAW,EAAE;gBAC3DzB;gBACA+B;YACF;YACA,MAAMG,QAAQ,MAAMxC,YAAY;gBAC9BM;gBACAyB;gBACAM;YACF;YAEA,IAAIvB,MAAM;gBACR,IAAI,CAAC2B,GAAG,CAACC,KAAKC,SAAS,CAACH,OAAO,MAAM;gBACrC;YACF;YAEA,IAAI,CAACC,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC,CAAC,OAAO,EAAED,MAAMlC,KAAK,EAAE;YAChC,IAAI,CAACmC,GAAG,CAAC,CAAC,IAAI,EAAED,MAAMI,EAAE,EAAE;YAC1B,IAAI,CAACH,GAAG,CAAC,CAAC,MAAM,EAAED,MAAMK,KAAK,CAACC,GAAG,CAAC,CAACC,IAAMA,EAAEC,KAAK,EAAEC,IAAI,CAAC,OAAO;YAC9D,IAAI,CAACR,GAAG,CAAC,CAAC,OAAO,EAAED,MAAMU,GAAG,EAAE;YAC9B,IAAI,CAACT,GAAG,CAAC;YACT,IAAI,CAACA,GAAG,CAAC;QACX,EAAE,OAAOb,OAAO;YACd,MAAMuB,MAAMvB;YAEZzB,kBAAkB,CAAC,iCAAiC,EAAE4B,WAAW,EAAEoB;YACnE,IAAI,CAACvB,KAAK,CAAC,CAAC,wBAAwB,EAAEuB,IAAIC,OAAO,EAAE,EAAE;gBAACvB,MAAMpC,UAAU4D,aAAa;YAAA;QACrF;IACF;IAEA,MAAc3B,iBAAkC;QAC9C,IAAI,IAAI,CAAC4B,YAAY,IAAI;YACvB,IAAI,CAAC1B,KAAK,CAAC,+DAA+D;gBACxEC,MAAMpC,UAAUqC,WAAW;YAC7B;QACF;QAEA,MAAMxB,QAAQ,MAAMV,MAAM;YACxBwD,SAAS;YACTG,UAAU,CAACC;gBACT,IAAI,CAACA,SAAS,CAACA,MAAM7B,IAAI,IAAI;oBAC3B,OAAO;gBACT;gBACA,OAAO;YACT;QACF;QAEA,OAAOrB,MAAMqB,IAAI;IACnB;IAEA,MAAcY,cAAcR,SAAiB,EAAmB;QAC9D,IAAI,IAAI,CAACuB,YAAY,IAAI;YACvB,OAAO,SAAS,mCAAmC;;QACrD;QAEA,MAAMT,QAAQ,MAAM5C,cAAc8B;QAClC,MAAM0B,aAAaZ,MAAMa,MAAM,CAAC,CAACzC,OAASA,KAAK0C,eAAe;QAE9DxD,kBAAkB,eAAe;YAACsD;QAAU;QAE5C,IAAIA,WAAWG,MAAM,KAAK,GAAG;YAC3B,IAAI,CAAChC,KAAK,CAAC,iCAAiC;gBAACC,MAAMpC,UAAU4D,aAAa;YAAA;QAC5E;QAEA,MAAMQ,mBAAmB,MAAMhE,OAAO;YACpCiE,SAASL,WAAWX,GAAG,CAAC,CAAC7B,OAAU,CAAA;oBACjC8C,MAAM,GAAG9C,KAAK+B,KAAK,CAAC,EAAE,EAAE/B,KAAK8C,IAAI,CAAC,CAAC,CAAC;oBACpCC,OAAO/C,KAAK+B,KAAK;oBACjBQ,OAAOvC,KAAK8C,IAAI;gBAClB,CAAA;YACA/C,SAAS;YACToC,SAAS;QACX;QAEA,OAAOS;IACT;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/tokens/create.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {input, select} from '@sanity/cli-core/ux'\n\nimport {expiryInDays, parseExpiryDate} from '../../actions/tokens/expiry.js'\nimport {type SelectedTokenRole} from '../../actions/tokens/types.js'\nimport {validateRole} from '../../actions/tokens/validateRole.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {createToken, getTokenRoles} from '../../services/tokens.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst tokensCreateDebug = subdebug('tokens:create')\n\n// Mirrors the expiry presets offered by sanity.io/manage\nconst EXPIRY_PRESET_DAYS = [30, 60, 90]\n\nexport class CreateTokenCommand extends SanityCommand<typeof CreateTokenCommand> {\n static override args = {\n label: Args.string({\n description: 'Label for the new token',\n required: false,\n }),\n }\n\n static override description = 'Create a new API token for the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\"',\n description: 'Create a token with a label',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My API Token\" --role=editor',\n description: 'Create a token with editor role',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"CI Token\" --role=editor --yes',\n description: 'Create a token in unattended mode',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"Build Token\" --expires-at 2027-01-01',\n description: 'Create a token that expires on a given date',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"API Token\" --json',\n description: 'Output token information as JSON',\n },\n {\n command: '<%= config.bin %> <%= command.id %> \"My Token\" --project-id abc123 --role=editor',\n description: 'Create a token for a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to create token in',\n semantics: 'override',\n }),\n 'expires-at': Flags.string({\n description: 'Date or timestamp the token expires (ISO 8601; tokens never expire by default)',\n helpValue: '2027-01-01',\n }),\n json: Flags.boolean({\n default: false,\n description: 'Output as JSON',\n }),\n role: Flags.string({\n description: 'Role to assign to the token (defaults to viewer in unattended mode)',\n helpValue: 'viewer',\n }),\n yes: Flags.boolean({\n char: 'y',\n default: false,\n description: 'Skip prompts and use defaults (unattended mode)',\n }),\n }\n\n static override hiddenAliases: string[] = ['tokens:add', 'token:add', 'token:create']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CreateTokenCommand)\n const {label: givenLabel} = args\n const {json, role} = flags\n\n const label = givenLabel === undefined ? await this.promptForLabel() : givenLabel.trim()\n if (!label) {\n this.error('Token label cannot be empty. Pass a non-empty value as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const flagExpiresAt = this.parseExpiryFlag(flags['expires-at'])\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.roles'},\n {grant: 'create', permission: 'sanity.project.tokens'},\n ],\n }),\n })\n\n const selectedRole = await (role\n ? validateRole(role, projectId, this.output)\n : this.promptForRole(projectId))\n\n const expiresAt =\n flags['expires-at'] === undefined ? await this.promptForExpiry() : flagExpiresAt\n\n try {\n tokensCreateDebug(`Creating token for project ${projectId}`, {\n expiresAt,\n label,\n roleName: selectedRole.name,\n })\n const token = await createToken({\n expiresAt,\n label,\n projectId,\n roleName: selectedRole.name,\n })\n\n if (json) {\n this.log(JSON.stringify(token, null, 2))\n return\n }\n\n this.log('API token created')\n this.log(`Label: ${token.label}`)\n this.log(`ID: ${token.id}`)\n this.log(`Role: ${selectedRole.title}`)\n if (token.expiresAt) {\n this.log(`Expires: ${token.expiresAt}`)\n }\n this.log(`Token: ${token.token}`)\n this.log('')\n this.log(\"Copy the token now. It won't be shown again.\")\n } catch (error) {\n const err = error as Error\n\n tokensCreateDebug(`Error creating token for project ${projectId}`, err)\n this.error(`Token creation failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private parseExpiryFlag(value: string | undefined): string | undefined {\n if (value === undefined) {\n return undefined\n }\n\n const parsed = parseExpiryDate(value)\n if ('error' in parsed) {\n this.error(parsed.error, {exit: exitCodes.USAGE_ERROR})\n }\n\n return parsed.expiresAt\n }\n\n private async promptForExpiry(): Promise<string | undefined> {\n if (this.isUnattended()) {\n return undefined // Tokens never expire by default\n }\n\n const choice = await select({\n choices: [\n {name: 'Never', value: 'never'},\n ...EXPIRY_PRESET_DAYS.map((days) => ({\n name: `${days} days (${expiryInDays(days).slice(0, 10)})`,\n value: String(days),\n })),\n {name: 'Custom date', value: 'custom'},\n ],\n default: 'never',\n message: 'Token expiry:',\n })\n\n if (choice === 'never') {\n return undefined\n }\n\n if (choice === 'custom') {\n const value = await input({\n message: 'Expiry date (ISO 8601, e.g. 2027-01-01):',\n validate: (input) => {\n const parsed = parseExpiryDate(input.trim())\n return 'error' in parsed ? parsed.error : true\n },\n })\n const parsed = parseExpiryDate(value.trim())\n return 'error' in parsed ? undefined : parsed.expiresAt\n }\n\n return expiryInDays(Number(choice))\n }\n\n private async promptForLabel(): Promise<string> {\n if (this.isUnattended()) {\n this.error('Token label is required. Pass it as the `<label>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const label = await input({\n message: 'Token label:',\n validate: (value) => {\n if (!value || !value.trim()) {\n return 'Label cannot be empty'\n }\n return true\n },\n })\n\n return label.trim()\n }\n\n private async promptForRole(projectId: string): Promise<SelectedTokenRole> {\n if (this.isUnattended()) {\n return {name: 'viewer', title: 'Viewer'} // Default role for unattended mode\n }\n\n const roles = await getTokenRoles(projectId)\n const robotRoles = roles.filter((role) => role.appliesToRobots)\n\n tokensCreateDebug('Robot roles', {robotRoles})\n\n if (robotRoles.length === 0) {\n this.error('No roles available for tokens', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const selectedRoleName = await select({\n choices: robotRoles.map((role) => ({\n name: `${role.title} (${role.name})`,\n short: role.title,\n value: role.name,\n })),\n default: 'viewer',\n message: 'Select role for the token:',\n })\n\n const selectedRole = robotRoles.find((r) => r.name === selectedRoleName)\n if (!selectedRole) {\n this.error('No roles available for tokens', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n return {name: selectedRole.name, title: selectedRole.title}\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","input","select","expiryInDays","parseExpiryDate","validateRole","promptForProject","createToken","getTokenRoles","getProjectIdFlag","tokensCreateDebug","EXPIRY_PRESET_DAYS","CreateTokenCommand","args","label","string","description","required","examples","command","flags","semantics","helpValue","json","boolean","default","role","yes","char","hiddenAliases","run","parse","givenLabel","undefined","promptForLabel","trim","error","exit","USAGE_ERROR","flagExpiresAt","parseExpiryFlag","projectId","getProjectId","fallback","requiredPermissions","grant","permission","selectedRole","output","promptForRole","expiresAt","promptForExpiry","roleName","name","token","log","JSON","stringify","id","title","err","message","RUNTIME_ERROR","value","parsed","isUnattended","choice","choices","map","days","slice","String","validate","Number","roles","robotRoles","filter","appliesToRobots","length","selectedRoleName","short","find","r"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { confirm, select } from '@sanity/cli-core/ux';
4
4
  import { ClientError } from '@sanity/client';
5
5
  import { promptForProject } from '../../prompts/promptForProject.js';
6
- import { deleteToken, getTokens } from '../../services/tokens.js';
6
+ import { deleteToken, getProjectMembership, getTokens } from '../../services/tokens.js';
7
7
  import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
8
8
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
9
9
  const deleteTokenDebug = subdebug('tokens:delete');
@@ -128,7 +128,7 @@ export class DeleteTokensCommand extends SanityCommand {
128
128
  });
129
129
  }
130
130
  const choices = tokens.map((token)=>({
131
- name: `${token.label} (${(token.roles || []).map((r)=>r.title).join(', ')})`,
131
+ name: `${token.label} (${getProjectMembership(token, this.projectId)?.roleNames.join(', ') ?? ''})`,
132
132
  value: token.id
133
133
  }));
134
134
  return select({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/tokens/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {deleteToken, getTokens} from '../../services/tokens.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteTokenDebug = subdebug('tokens:delete')\n\nexport class DeleteTokensCommand extends SanityCommand<typeof DeleteTokensCommand> {\n static override args = {\n tokenId: Args.string({\n description: 'Token ID to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete an API token from the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB',\n description: 'Delete a specific token by ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --yes',\n description: 'Delete a specific token without confirmation prompt',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Delete a token from a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to delete token from',\n semantics: 'override',\n }),\n yes: Flags.boolean({\n aliases: ['y'],\n description: 'Skip confirmation prompt (unattended mode)',\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['token:delete']\n\n private projectId!: string\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DeleteTokensCommand)\n\n const skipConfirmation = flags.yes\n const unattended = this.isUnattended()\n const {tokenId: givenTokenId} = args\n\n if (unattended) {\n const errors: string[] = []\n\n if (!givenTokenId) {\n errors.push('Token ID is required. Pass it as the `<tokenId>` argument.')\n }\n if (!skipConfirmation) {\n errors.push('Deletion requires confirmation. Pass `--yes` to delete the token.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n }\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'delete', permission: 'sanity.project.tokens'}],\n }),\n })\n\n this.projectId = projectId\n\n const tokenId = givenTokenId || (await this.getTokenIdFromList())\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message: `Delete API token \"${tokenId}\"?`,\n })\n\n if (!confirmed) {\n this.log('API token not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteToken({\n projectId: this.projectId,\n tokenId,\n })\n\n this.log('API token deleted')\n } catch (error) {\n if (error instanceof ClientError && error.response.statusCode === 404) {\n this.error(`Token with ID \"${tokenId}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const err = error as Error\n deleteTokenDebug(`Error deleting token`, err)\n this.error(`Token deletion failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private async getTokenIdFromList() {\n let tokens: Awaited<ReturnType<typeof getTokens>>\n try {\n tokens = await getTokens(this.projectId)\n } catch (error) {\n const err = error as Error\n deleteTokenDebug(`Error fetching tokens for project ${this.projectId}`, err)\n this.error(\n `Could not list API tokens:\\n${err.message}\\nCheck the project ID and your access permissions, then try again.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n if (tokens.length === 0) {\n this.error('No API tokens found for this project.', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const choices = tokens.map((token) => ({\n name: `${token.label} (${(token.roles || []).map((r) => r.title).join(', ')})`,\n value: token.id,\n }))\n\n return select({\n choices,\n message: 'Select token to delete:',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","confirm","select","ClientError","promptForProject","deleteToken","getTokens","formatCliErrorMessages","getProjectIdFlag","deleteTokenDebug","DeleteTokensCommand","args","tokenId","string","description","required","examples","command","flags","semantics","yes","boolean","aliases","hiddenAliases","projectId","run","parse","skipConfirmation","unattended","isUnattended","givenTokenId","errors","push","length","error","exit","USAGE_ERROR","getProjectId","fallback","requiredPermissions","grant","permission","getTokenIdFromList","confirmed","default","message","log","USER_ABORT","response","statusCode","RUNTIME_ERROR","err","tokens","choices","map","token","name","label","roles","r","title","join","value","id"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/tokens/delete.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {confirm, select} from '@sanity/cli-core/ux'\nimport {ClientError} from '@sanity/client'\n\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {deleteToken, getProjectMembership, getTokens} from '../../services/tokens.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst deleteTokenDebug = subdebug('tokens:delete')\n\nexport class DeleteTokensCommand extends SanityCommand<typeof DeleteTokensCommand> {\n static override args = {\n tokenId: Args.string({\n description: 'Token ID to delete (will prompt if not provided)',\n required: false,\n }),\n }\n\n static override description = 'Delete an API token from the project'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively select and delete a token',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB',\n description: 'Delete a specific token by ID',\n },\n {\n command: '<%= config.bin %> <%= command.id %> silJ2lFmK6dONB --yes',\n description: 'Delete a specific token without confirmation prompt',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --project-id abc123',\n description: 'Delete a token from a specific project',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to delete token from',\n semantics: 'override',\n }),\n yes: Flags.boolean({\n aliases: ['y'],\n description: 'Skip confirmation prompt (unattended mode)',\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['token:delete']\n\n private projectId!: string\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(DeleteTokensCommand)\n\n const skipConfirmation = flags.yes\n const unattended = this.isUnattended()\n const {tokenId: givenTokenId} = args\n\n if (unattended) {\n const errors: string[] = []\n\n if (!givenTokenId) {\n errors.push('Token ID is required. Pass it as the `<tokenId>` argument.')\n }\n if (!skipConfirmation) {\n errors.push('Deletion requires confirmation. Pass `--yes` to delete the token.')\n }\n\n if (errors.length > 0) {\n this.error(formatCliErrorMessages(errors), {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n }\n\n // Ensure we have project context\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'delete', permission: 'sanity.project.tokens'}],\n }),\n })\n\n this.projectId = projectId\n\n const tokenId = givenTokenId || (await this.getTokenIdFromList())\n\n if (!skipConfirmation) {\n const confirmed = await confirm({\n default: false,\n message: `Delete API token \"${tokenId}\"?`,\n })\n\n if (!confirmed) {\n this.log('API token not deleted')\n this.exit(exitCodes.USER_ABORT)\n }\n }\n\n try {\n await deleteToken({\n projectId: this.projectId,\n tokenId,\n })\n\n this.log('API token deleted')\n } catch (error) {\n if (error instanceof ClientError && error.response.statusCode === 404) {\n this.error(`Token with ID \"${tokenId}\" not found`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n const err = error as Error\n deleteTokenDebug(`Error deleting token`, err)\n this.error(`Token deletion failed:\\n${err.message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private async getTokenIdFromList() {\n let tokens: Awaited<ReturnType<typeof getTokens>>\n try {\n tokens = await getTokens(this.projectId)\n } catch (error) {\n const err = error as Error\n deleteTokenDebug(`Error fetching tokens for project ${this.projectId}`, err)\n this.error(\n `Could not list API tokens:\\n${err.message}\\nCheck the project ID and your access permissions, then try again.`,\n {exit: exitCodes.RUNTIME_ERROR},\n )\n }\n\n if (tokens.length === 0) {\n this.error('No API tokens found for this project.', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const choices = tokens.map((token) => ({\n name: `${token.label} (${getProjectMembership(token, this.projectId)?.roleNames.join(', ') ?? ''})`,\n value: token.id,\n }))\n\n return select({\n choices,\n message: 'Select token to delete:',\n })\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","confirm","select","ClientError","promptForProject","deleteToken","getProjectMembership","getTokens","formatCliErrorMessages","getProjectIdFlag","deleteTokenDebug","DeleteTokensCommand","args","tokenId","string","description","required","examples","command","flags","semantics","yes","boolean","aliases","hiddenAliases","projectId","run","parse","skipConfirmation","unattended","isUnattended","givenTokenId","errors","push","length","error","exit","USAGE_ERROR","getProjectId","fallback","requiredPermissions","grant","permission","getTokenIdFromList","confirmed","default","message","log","USER_ABORT","response","statusCode","RUNTIME_ERROR","err","tokens","choices","map","token","name","label","roleNames","join","value","id"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
3
  import { getErrorMessage } from '@sanity/cli-core/errors';
4
- import { Table } from 'console-table-printer';
5
4
  import { promptForProject } from '../../prompts/promptForProject.js';
6
- import { getTokens } from '../../services/tokens.js';
5
+ import { getProjectMembership, getTokens } from '../../services/tokens.js';
6
+ import { Table } from '../../util/responsiveTable.js';
7
7
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
8
8
  const listTokenDebug = subdebug('tokens:list');
9
9
  export class TokensListCommand extends SanityCommand {
@@ -72,33 +72,34 @@ export class TokensListCommand extends SanityCommand {
72
72
  columns: [
73
73
  {
74
74
  alignment: 'left',
75
- maxLen: 40,
76
75
  name: 'label',
77
76
  title: 'Label'
78
77
  },
79
78
  {
80
79
  alignment: 'left',
81
- maxLen: 20,
82
80
  name: 'id',
83
- title: 'Token ID'
81
+ title: 'ID'
84
82
  },
85
83
  {
86
84
  alignment: 'left',
87
- maxLen: 30,
88
85
  name: 'roles',
89
86
  title: 'Roles'
87
+ },
88
+ {
89
+ alignment: 'left',
90
+ name: 'expires',
91
+ title: 'Expires'
90
92
  }
91
93
  ],
92
94
  title: `Found ${tokens.length} API tokens`
93
95
  });
94
96
  for (const token of tokens){
95
- const roles = token.roles?.map((role)=>role.title).join(', ') || 'No roles';
96
- const truncatedLabel = token.label.length > 37 ? `${token.label.slice(0, 37)}...` : token.label;
97
- const truncatedRoles = roles.length > 27 ? `${roles.slice(0, 27)}...` : roles;
97
+ const roles = getProjectMembership(token, projectId)?.roleNames.join(', ') || 'No roles';
98
98
  table.addRow({
99
+ expires: token.expiresAt ? token.expiresAt.slice(0, 10) : 'Never',
99
100
  id: token.id,
100
- label: truncatedLabel,
101
- roles: truncatedRoles
101
+ label: token.label,
102
+ roles
102
103
  });
103
104
  }
104
105
  this.log(table.render());