@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.
- package/README.md +74 -4
- package/dist/SanityHelp.js +1 -35
- package/dist/SanityHelp.js.map +1 -1
- package/dist/actions/assets/assetFileError.js +9 -0
- package/dist/actions/assets/assetFileError.js.map +1 -0
- package/dist/actions/assets/uploadAssetFromFile.js +40 -0
- package/dist/actions/assets/uploadAssetFromFile.js.map +1 -0
- package/dist/actions/assets/uploadAssetWithProgress.js +76 -0
- package/dist/actions/assets/uploadAssetWithProgress.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +39 -44
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +21 -13
- package/dist/actions/deploy/deployChecks.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +8 -7
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +55 -35
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +17 -29
- package/dist/actions/deploy/deploymentPlan.js.map +1 -1
- package/dist/actions/deploy/resolveDeployTarget.js +2 -8
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +2 -8
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +7 -1
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/init/bootstrapRemoteTemplate.js +2 -2
- package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -1
- package/dist/actions/init/sdkAppDependencies.js +1 -1
- package/dist/actions/init/sdkAppDependencies.js.map +1 -1
- package/dist/actions/init/studioDependencies.js +1 -1
- package/dist/actions/init/studioDependencies.js.map +1 -1
- package/dist/actions/schema/listSchemas.js +1 -1
- package/dist/actions/schema/listSchemas.js.map +1 -1
- package/dist/actions/tokens/constants.js +2 -2
- package/dist/actions/tokens/constants.js.map +1 -1
- package/dist/actions/tokens/expiry.js +35 -0
- package/dist/actions/tokens/expiry.js.map +1 -0
- package/dist/actions/tokens/types.js.map +1 -1
- package/dist/actions/tokens/validateRole.js +5 -2
- package/dist/actions/tokens/validateRole.js.map +1 -1
- package/dist/actions/typegen/count.js +6 -0
- package/dist/actions/typegen/count.js.map +1 -0
- package/dist/actions/typegen/formatPath.js +5 -0
- package/dist/actions/typegen/formatPath.js.map +1 -0
- package/dist/actions/typegen/percent.js +8 -0
- package/dist/actions/typegen/percent.js.map +1 -0
- package/dist/actions/typegen/renderTypegenProgress.js +88 -0
- package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
- package/dist/actions/undeploy/adapters.js +6 -9
- package/dist/actions/undeploy/adapters.js.map +1 -1
- package/dist/actions/undeploy/runUndeploy.js +12 -4
- package/dist/actions/undeploy/runUndeploy.js.map +1 -1
- package/dist/actions/undeploy/undeployPlan.js +6 -14
- package/dist/actions/undeploy/undeployPlan.js.map +1 -1
- package/dist/commands/assets/upload.js +160 -0
- package/dist/commands/assets/upload.js.map +1 -0
- package/dist/commands/backups/list.js +1 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/datasets/copy.js +1 -1
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/login.js +2 -8
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/migrations/list.js +1 -1
- package/dist/commands/migrations/list.js.map +1 -1
- package/dist/commands/migrations/run.js +67 -3
- package/dist/commands/migrations/run.js.map +1 -1
- package/dist/commands/new.js +4 -3
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/organizations/list.js +1 -1
- package/dist/commands/organizations/list.js.map +1 -1
- package/dist/commands/projects/list.js +39 -12
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/projects/unclaimed.js +39 -33
- package/dist/commands/projects/unclaimed.js.map +1 -1
- package/dist/commands/tokens/create.js +92 -7
- package/dist/commands/tokens/create.js.map +1 -1
- package/dist/commands/tokens/delete.js +2 -2
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/tokens/list.js +12 -11
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/tokens/rotate.js +95 -0
- package/dist/commands/tokens/rotate.js.map +1 -0
- package/dist/commands/typegen/generate.js +222 -0
- package/dist/commands/typegen/generate.js.map +1 -0
- package/dist/commands/users/list.js +8 -8
- package/dist/commands/users/list.js.map +1 -1
- package/dist/exports/_internal.d.ts +29 -0
- package/dist/exports/_internal.js +1 -0
- package/dist/exports/_internal.js.map +1 -1
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/exports/invokeSanityCli/help.js +4 -0
- package/dist/exports/invokeSanityCli/help.js.map +1 -1
- package/dist/exports/invokeSanityCli/index.js +2 -0
- package/dist/exports/invokeSanityCli/index.js.map +1 -1
- package/dist/hooks/commandNotFound/topicAliases.js +13 -40
- package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
- package/dist/hooks/prerun/setupTelemetry.js +3 -2
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/server/vite/plugin-typegen.js +4 -16
- package/dist/server/vite/plugin-typegen.js.map +1 -1
- package/dist/services/assets.js +46 -0
- package/dist/services/assets.js.map +1 -0
- package/dist/services/tokens.js +81 -31
- package/dist/services/tokens.js.map +1 -1
- package/dist/topicAliases.js +43 -0
- package/dist/topicAliases.js.map +1 -1
- package/dist/util/parseArguments.js +7 -2
- package/dist/util/parseArguments.js.map +1 -1
- package/dist/util/readTokenFromStdin.js +17 -0
- package/dist/util/readTokenFromStdin.js.map +1 -0
- package/dist/util/responsiveTable.js +97 -0
- package/dist/util/responsiveTable.js.map +1 -0
- package/dist/util/telemetry/commandTelemetry.js +34 -0
- package/dist/util/telemetry/commandTelemetry.js.map +1 -0
- package/dist/util/unclaimedProjects.js +10 -0
- package/dist/util/unclaimedProjects.js.map +1 -1
- package/oclif.config.js +2 -4
- package/oclif.manifest.json +688 -476
- package/package.json +12 -10
- package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
- package/templates/shopify/docs/features.md +24 -0
- package/templates/shopify/schemaTypes/index.ts +2 -0
- package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
- package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
- package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
- package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
- package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
- package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
- 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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
171
|
-
|
|
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
|
|
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: ${
|
|
106
|
-
|
|
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
|
|
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
|
-
|
|
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":"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"}
|
|
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
|
|
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
|
|
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: '
|
|
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
|
|
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:
|
|
101
|
-
roles
|
|
101
|
+
label: token.label,
|
|
102
|
+
roles
|
|
102
103
|
});
|
|
103
104
|
}
|
|
104
105
|
this.log(table.render());
|