@sanity/runtime-cli 14.13.3 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +215 -118
  2. package/dist/actions/blueprints/blueprint.js +13 -11
  3. package/dist/actions/blueprints/logs.d.ts +2 -1
  4. package/dist/actions/blueprints/logs.js +4 -5
  5. package/dist/actions/blueprints/resources.js +1 -0
  6. package/dist/actions/blueprints/stacks.d.ts +3 -1
  7. package/dist/actions/blueprints/stacks.js +11 -2
  8. package/dist/actions/functions/test.js +1 -1
  9. package/dist/actions/sanity/access.d.ts +38 -0
  10. package/dist/actions/sanity/access.js +23 -0
  11. package/dist/actions/sanity/projects.d.ts +1 -1
  12. package/dist/baseCommands.d.ts +2 -0
  13. package/dist/baseCommands.js +8 -5
  14. package/dist/commands/blueprints/add.js +1 -1
  15. package/dist/commands/blueprints/deploy.d.ts +2 -0
  16. package/dist/commands/blueprints/deploy.js +6 -2
  17. package/dist/commands/blueprints/destroy.js +0 -2
  18. package/dist/commands/blueprints/info.d.ts +1 -0
  19. package/dist/commands/blueprints/info.js +3 -1
  20. package/dist/commands/blueprints/init.js +2 -0
  21. package/dist/commands/blueprints/logs.d.ts +5 -0
  22. package/dist/commands/blueprints/logs.js +26 -3
  23. package/dist/commands/blueprints/mint-deploy-token.d.ts +14 -0
  24. package/dist/commands/blueprints/mint-deploy-token.js +47 -0
  25. package/dist/commands/blueprints/plan.d.ts +2 -0
  26. package/dist/commands/blueprints/plan.js +8 -2
  27. package/dist/commands/blueprints/promote.d.ts +2 -1
  28. package/dist/commands/blueprints/promote.js +7 -2
  29. package/dist/commands/blueprints/stacks.js +1 -1
  30. package/dist/commands/functions/add.js +1 -1
  31. package/dist/cores/blueprints/config.js +34 -34
  32. package/dist/cores/blueprints/doctor.js +7 -7
  33. package/dist/cores/blueprints/init.js +99 -76
  34. package/dist/cores/blueprints/logs.d.ts +3 -0
  35. package/dist/cores/blueprints/logs.js +15 -9
  36. package/dist/cores/blueprints/mint-deploy-token.d.ts +15 -0
  37. package/dist/cores/blueprints/mint-deploy-token.js +111 -0
  38. package/dist/cores/blueprints/promote.d.ts +1 -0
  39. package/dist/cores/blueprints/promote.js +25 -4
  40. package/dist/cores/functions/add.js +4 -5
  41. package/dist/cores/index.d.ts +1 -2
  42. package/dist/cores/index.js +1 -3
  43. package/dist/index.d.ts +0 -18
  44. package/dist/index.js +0 -20
  45. package/dist/utils/clipboard.d.ts +14 -0
  46. package/dist/utils/clipboard.js +73 -0
  47. package/dist/utils/display/errors.d.ts +5 -1
  48. package/dist/utils/display/prompt.d.ts +55 -15
  49. package/dist/utils/display/prompt.js +271 -45
  50. package/oclif.manifest.json +320 -18
  51. package/package.json +21 -67
  52. package/dist/actions/blueprints/index.d.ts +0 -16
  53. package/dist/actions/blueprints/index.js +0 -10
  54. package/dist/actions/functions/index.d.ts +0 -4
  55. package/dist/actions/functions/index.js +0 -4
  56. package/dist/actions/sanity/index.d.ts +0 -1
  57. package/dist/actions/sanity/index.js +0 -1
  58. package/dist/cores/blueprints/index.d.ts +0 -20
  59. package/dist/cores/blueprints/index.js +0 -10
  60. package/dist/cores/functions/index.d.ts +0 -16
  61. package/dist/cores/functions/index.js +0 -8
  62. package/dist/utils/display/index.d.ts +0 -5
  63. package/dist/utils/display/index.js +0 -5
  64. package/dist/utils/index.d.ts +0 -8
  65. package/dist/utils/index.js +0 -8
@@ -87,11 +87,11 @@ export async function loadBlueprintFile(fileInfo) {
87
87
  break;
88
88
  }
89
89
  default:
90
- throw Error(`Unsupported blueprint file extension: ${extension}`);
90
+ throw Error(`Unsupported Blueprint manifest extension: ${extension}`);
91
91
  }
92
92
  }
93
93
  catch (err) {
94
- throw Error(`Unable to parse Blueprint file: ${fileName}\n${err}`);
94
+ throw Error(`Could not parse Blueprint manifest: ${fileName}\n${err}`);
95
95
  }
96
96
  // Dynamic modules: extract attached properties, then execute to get raw blueprint
97
97
  if (blueprintModule) {
@@ -100,15 +100,15 @@ export async function loadBlueprintFile(fileInfo) {
100
100
  rawBlueprint = blueprintModule();
101
101
  }
102
102
  catch {
103
- throw Error(`Error executing Blueprint file: ${fileName}`);
103
+ throw Error(`Error executing Blueprint manifest: ${fileName}`);
104
104
  }
105
105
  }
106
106
  else {
107
- throw Error(`Blueprint ${fileName} must export a default function`);
107
+ throw Error(`Blueprint manifest ${fileName} must export a default function.`);
108
108
  }
109
109
  }
110
110
  if (typeof rawBlueprint === 'undefined') {
111
- throw Error('Unable to read Blueprint');
111
+ throw Error('Could not read Blueprint manifest.');
112
112
  }
113
113
  return { rawBlueprint, module: blueprintModule };
114
114
  }
@@ -148,8 +148,9 @@ export function parseBlueprintContent(rawBlueprint, options) {
148
148
  */
149
149
  export async function loadAndParseBlueprint(blueprintPath, options = { validateResources: true }) {
150
150
  const fileInfo = findBlueprintFile(blueprintPath);
151
- if (!fileInfo)
152
- throw Error('Could not find Blueprint file! Use the `blueprints init` command.');
151
+ if (!fileInfo) {
152
+ throw Error('Could not find a Blueprint manifest (sanity.blueprint.ts, .js, or .json). Run `blueprints init` to create one.');
153
+ }
153
154
  const loaded = await loadBlueprintFile(fileInfo);
154
155
  const parsed = parseBlueprintContent(loaded.rawBlueprint, options);
155
156
  return {
@@ -169,8 +170,9 @@ export async function loadAndParseBlueprint(blueprintPath, options = { validateR
169
170
  */
170
171
  export async function readLocalBlueprint(logger, validate, blueprintPath) {
171
172
  const fileInfo = findBlueprintFile(blueprintPath);
172
- if (!fileInfo)
173
- throw Error('Could not find Blueprint file! Use the `blueprints init` command.');
173
+ if (!fileInfo) {
174
+ throw Error('Could not find a Blueprint manifest (sanity.blueprint.ts, .js, or .json). Run `blueprints init` to create one.');
175
+ }
174
176
  // 1. Load blueprint from disk
175
177
  const loaded = await loadBlueprintFile(fileInfo);
176
178
  const { rawBlueprint } = loaded;
@@ -226,7 +228,7 @@ export function writeBlueprintToDisk({ blueprintFilePath, jsonContent = JSON_BLU
226
228
  break;
227
229
  }
228
230
  default: {
229
- throw Error(`Unsupported blueprint file extension: ${extension}`);
231
+ throw Error(`Unsupported Blueprint manifest extension: ${extension}`);
230
232
  }
231
233
  }
232
234
  mkdirSync(dir, { recursive: true });
@@ -236,7 +238,7 @@ export function writeBlueprintToDisk({ blueprintFilePath, jsonContent = JSON_BLU
236
238
  export function addResourceToBlueprint({ blueprintFilePath, resource, }) {
237
239
  const blueprintFile = findBlueprintFile(blueprintFilePath);
238
240
  if (!blueprintFile)
239
- throw Error('Could not find Blueprint file');
241
+ throw Error('Could not find a Blueprint manifest.');
240
242
  const { blueprintFilePath: foundPath, extension } = blueprintFile;
241
243
  // modify .json files directly
242
244
  if (extension === '.json') {
@@ -6,8 +6,9 @@ export interface GetLogsParams {
6
6
  operationId?: string;
7
7
  limit?: number;
8
8
  before?: string;
9
+ after?: string;
9
10
  }
10
11
  export declare function getLogs(params: GetLogsParams, auth: AuthParams, logger: Logger): Promise<ActionResponse & {
11
12
  logs: BlueprintLog[];
13
+ hasMore: boolean;
12
14
  }>;
13
- export declare function getRecentLogs(logs: BlueprintLog[], limit?: number): BlueprintLog[];
@@ -14,19 +14,18 @@ export async function getLogs(params, auth, logger) {
14
14
  url.searchParams.append('limit', String(params.limit));
15
15
  if (params.before)
16
16
  url.searchParams.append('before', params.before);
17
+ if (params.after)
18
+ url.searchParams.append('after', params.after);
17
19
  const response = await fetchFn(url.toString(), {
18
20
  headers: getHeaders(auth),
19
21
  method: 'GET',
20
22
  });
21
23
  const result = await response.json();
24
+ const hasMore = response.headers.get('X-Has-More') === 'true';
22
25
  return {
23
26
  ok: response.ok,
24
27
  error: response.ok ? null : result.message || 'Unknown error',
25
28
  logs: response.ok ? result : [],
29
+ hasMore: response.ok ? hasMore : false,
26
30
  };
27
31
  }
28
- // Get most recent logs, up to a limit
29
- export function getRecentLogs(logs, limit = 10) {
30
- const sortedLogs = [...logs].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
31
- return sortedLogs.slice(-limit);
32
- }
@@ -125,6 +125,7 @@ export async function createFunctionResource(options, logger) {
125
125
  src: `functions/${name}`,
126
126
  type: SANITY_FUNCTION_SCHEDULED,
127
127
  event: {
128
+ // @ts-expect-error use shorthand
128
129
  expression: '0 0 * * *',
129
130
  },
130
131
  };
@@ -107,11 +107,13 @@ interface PromoteStackResponse extends ActionResponse {
107
107
  stack: Stack & {
108
108
  alreadyPromoted: boolean;
109
109
  };
110
+ response?: Response;
110
111
  }
111
- export declare function promoteStack({ stackId, auth, logger, }: {
112
+ export declare function promoteStack({ stackId, auth, logger, name, }: {
112
113
  stackId: string;
113
114
  auth: AuthParams;
114
115
  logger: Logger;
116
+ name?: string;
115
117
  }): Promise<PromoteStackResponse>;
116
118
  export declare function destroyStack({ stackId, auth, logger, }: {
117
119
  stackId: string;
@@ -45,6 +45,11 @@ export async function createStack({ stackMutation, auth, logger, }) {
45
45
  };
46
46
  }
47
47
  export async function createEmptyStack({ token, scopeType, scopeId, name, logger, }) {
48
+ if (!scopeId) {
49
+ const scopeLabel = scopeType === 'organization' ? 'organization' : 'project';
50
+ throw new Error(`Cannot create Stack: no ${scopeLabel} ID provided. ` +
51
+ 'Provide --project-id or --organization-id, or select a scope.');
52
+ }
48
53
  const stackMutation = {
49
54
  name,
50
55
  scopeType,
@@ -57,7 +62,8 @@ export async function createEmptyStack({ token, scopeType, scopeId, name, logger
57
62
  logger,
58
63
  });
59
64
  if (!response.ok) {
60
- throw new Error(response.error || 'Failed to create new Stack');
65
+ const scopeLabel = scopeType === 'organization' ? 'organization' : 'project';
66
+ throw new Error(response.error || `Failed to create Stack "${name}" for ${scopeLabel} "${scopeId}".`);
61
67
  }
62
68
  return response.stack;
63
69
  }
@@ -105,17 +111,20 @@ export async function resolveStackIdByNameOrId(value, auth, logger) {
105
111
  throw new Error(`No stack found with name "${value}"`);
106
112
  return match.id;
107
113
  }
108
- export async function promoteStack({ stackId, auth, logger, }) {
114
+ export async function promoteStack({ stackId, auth, logger, name, }) {
109
115
  const fetchFn = createTracedFetch(logger);
116
+ const hasName = typeof name === 'string' && name.length > 0;
110
117
  const response = await fetchFn(`${stacksUrl}/${stackId}/promote`, {
111
118
  method: 'POST',
112
119
  headers: getHeaders(auth),
120
+ body: hasName ? JSON.stringify({ name }) : undefined,
113
121
  });
114
122
  const data = await response.json();
115
123
  return {
116
124
  ok: response.ok,
117
125
  error: response.ok ? null : data.message,
118
126
  stack: data,
127
+ response,
119
128
  };
120
129
  }
121
130
  export async function destroyStack({ stackId, auth, logger, }) {
@@ -2,7 +2,7 @@ import invoke from '../../utils/invoke-local.js';
2
2
  export async function testAction(resource, payload, context, options) {
3
3
  try {
4
4
  const { json, logs, error } = await invoke(resource, payload, context, options);
5
- return { error: error, json, logs };
5
+ return { error, json, logs };
6
6
  }
7
7
  catch (error) {
8
8
  return { error: error, json: undefined, logs: undefined };
@@ -0,0 +1,38 @@
1
+ import type { Logger } from '../../utils/logger.js';
2
+ import type { ActionResponse, AuthParams, ScopeType } from '../../utils/types.js';
3
+ export declare const accessApiVersion = "v2025-07-11";
4
+ export declare const accessApiPath: string;
5
+ export interface RobotMembership {
6
+ resourceType: ScopeType;
7
+ resourceId: string;
8
+ roleNames: string[];
9
+ }
10
+ export interface CreateRobotInput {
11
+ label: string;
12
+ memberships: RobotMembership[];
13
+ }
14
+ export interface RobotResponse {
15
+ token: string;
16
+ id: string;
17
+ tokenId: string;
18
+ label: string;
19
+ createdAt: string;
20
+ expiresAt: string;
21
+ memberships: Array<RobotMembership & {
22
+ addedAt: string;
23
+ lastSeenAt?: string;
24
+ resourceUserId?: string;
25
+ }>;
26
+ }
27
+ interface CreateRobotResponse extends ActionResponse {
28
+ robot: RobotResponse | null;
29
+ }
30
+ /** Create a robot token via the Access API. */
31
+ export declare function createRobotToken({ auth, resourceType, resourceId, body, logger, }: {
32
+ auth: AuthParams;
33
+ resourceType: ScopeType;
34
+ resourceId: string;
35
+ body: CreateRobotInput;
36
+ logger: Logger;
37
+ }): Promise<CreateRobotResponse>;
38
+ export {};
@@ -0,0 +1,23 @@
1
+ import config from '../../config.js';
2
+ import getHeaders from '../../utils/get-headers.js';
3
+ import { createTracedFetch } from '../../utils/traced-fetch.js';
4
+ const { apiUrl } = config;
5
+ export const accessApiVersion = 'v2025-07-11';
6
+ export const accessApiPath = `${apiUrl}${accessApiVersion}/access`;
7
+ /** Create a robot token via the Access API. */
8
+ export async function createRobotToken({ auth, resourceType, resourceId, body, logger, }) {
9
+ const fetchFn = createTracedFetch(logger);
10
+ const url = `${accessApiPath}/${resourceType}/${encodeURIComponent(resourceId)}/robots`;
11
+ const response = await fetchFn(url, {
12
+ method: 'POST',
13
+ headers: getHeaders(auth),
14
+ body: JSON.stringify(body),
15
+ });
16
+ const raw = await response.json().catch(() => null);
17
+ const data = (raw && typeof raw === 'object' ? raw : {});
18
+ if (!response.ok) {
19
+ const errMessage = data.message || data.error?.message || response.statusText;
20
+ return { ok: false, error: errMessage, robot: null };
21
+ }
22
+ return { ok: true, error: null, robot: raw };
23
+ }
@@ -25,7 +25,7 @@ export declare function listProjects({ token, organizationId, logger, }: {
25
25
  interface GroupedProjectsByOrganizationResponse extends ActionResponse {
26
26
  organizations: GroupedProjects[];
27
27
  }
28
- interface GroupedProjects {
28
+ export interface GroupedProjects {
29
29
  organization: {
30
30
  id: string;
31
31
  name: string;
@@ -24,10 +24,12 @@ export declare const stackFlagConfig: {
24
24
  export declare const projectIdFlagConfig: {
25
25
  description: string;
26
26
  aliases: string[];
27
+ exclusive: string[];
27
28
  };
28
29
  export declare const organizationIdFlagConfig: {
29
30
  description: string;
30
31
  aliases: string[];
32
+ exclusive: string[];
31
33
  };
32
34
  /**
33
35
  * Guarantees flags and args.
@@ -40,6 +40,7 @@ export const baseFlags = {
40
40
  description: 'Validate resources',
41
41
  default: true,
42
42
  allowNo: true,
43
+ hidden: true,
43
44
  }),
44
45
  verbose: OclifFlags.boolean({
45
46
  description: 'Verbose output',
@@ -53,10 +54,12 @@ export const stackFlagConfig = {
53
54
  export const projectIdFlagConfig = {
54
55
  description: 'Sanity project ID used to scope Blueprint and Stack',
55
56
  aliases: ['project', 'projectId'],
57
+ exclusive: ['organization-id'],
56
58
  };
57
59
  export const organizationIdFlagConfig = {
58
60
  description: 'Sanity organization ID used to scope Blueprint and Stack',
59
61
  aliases: ['organization', 'organizationId', 'org'],
62
+ exclusive: ['project-id'],
60
63
  };
61
64
  /**
62
65
  * Guarantees flags and args.
@@ -216,7 +219,7 @@ export class ResolvedCommand extends RuntimeCommand {
216
219
  let blueprintModule;
217
220
  if (needsBlueprint) {
218
221
  if (!blueprintFileInfo) {
219
- this.error('Could not find Blueprint file! Use the `blueprints init` command.', {
222
+ this.error('Could not find a Blueprint manifest (sanity.blueprint.ts, .js, or .json).', {
220
223
  suggestions: [
221
224
  `Run \`${this.config.bin} blueprints init\` to create one.`,
222
225
  `Run \`${this.config.bin} blueprints doctor\` to check your configuration.`,
@@ -229,9 +232,9 @@ export class ResolvedCommand extends RuntimeCommand {
229
232
  });
230
233
  if (parsed.errors.length > 0) {
231
234
  log(presentBlueprintParserErrors(parsed.errors));
232
- this.error('Blueprint file contains errors.', {
235
+ this.error('Blueprint manifest contains errors.', {
233
236
  suggestions: [
234
- 'Fix the Blueprint errors listed above.',
237
+ 'Fix the manifest errors listed above.',
235
238
  `Run \`${this.config.bin} blueprints doctor\` to check your configuration.`,
236
239
  ],
237
240
  });
@@ -285,7 +288,7 @@ export class ResolvedCommand extends RuntimeCommand {
285
288
  // 5. Scope
286
289
  if (needsScope) {
287
290
  if (!resolved.scopeType || !resolved.scopeId) {
288
- this.error('Missing scope: provide --project-id or --organization-id, or configure a Blueprint.', {
291
+ this.error('Missing scope: provide --project-id or --organization-id, or run `blueprints init` to create a Blueprint config (.sanity/blueprint.config.json).', {
289
292
  suggestions: [
290
293
  `Run \`${this.config.bin} blueprints doctor\` to check your configuration.`,
291
294
  ],
@@ -307,7 +310,7 @@ export class ResolvedCommand extends RuntimeCommand {
307
310
  stackId = await resolveStackIdByNameOrId(stackOverride, auth, log);
308
311
  }
309
312
  if (!stackId) {
310
- this.error('Missing Stack: provide --stack, or configure a Blueprint with a Stack.', {
313
+ this.error(`Missing Stack: provide --stack, or set a Stack in your Blueprint config (\`${this.config.bin} blueprints config --edit\`).`, {
311
314
  suggestions: [
312
315
  `Run \`${this.config.bin} blueprints doctor\` to check your configuration.`,
313
316
  ],
@@ -5,7 +5,7 @@
5
5
  import { Args, Flags } from '@oclif/core';
6
6
  import { ResolvedCommand } from '../../baseCommands.js';
7
7
  import { FUNCTION_TYPES } from '../../constants.js';
8
- import { functionAddCore } from '../../cores/functions/index.js';
8
+ import { functionAddCore } from '../../cores/functions/add.js';
9
9
  import { warn } from '../../utils/display/presenters.js';
10
10
  import { Logger } from '../../utils/logger.js';
11
11
  import { INSTALLER_OPTIONS } from '../../utils/types.js';
@@ -6,6 +6,8 @@ export default class DeployCommand extends ResolvedCommand<typeof DeployCommand>
6
6
  static examples: string[];
7
7
  static flags: {
8
8
  stack: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
11
  message: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
12
  'fn-installer': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
13
  'no-wait': import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1,5 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { ResolvedCommand, stackFlagConfig } from '../../baseCommands.js';
2
+ import { organizationIdFlagConfig, projectIdFlagConfig, ResolvedCommand, stackFlagConfig, } from '../../baseCommands.js';
3
3
  import { blueprintDeployCore } from '../../cores/blueprints/deploy.js';
4
4
  import { Logger } from '../../utils/logger.js';
5
5
  import { INSTALLER_OPTIONS } from '../../utils/types.js';
@@ -20,9 +20,14 @@ Set SANITY_ASSET_TIMEOUT (seconds) to override the 60-second timeout for process
20
20
  '<%= config.bin %> <%= command.id %> --message "Enable staging dataset"',
21
21
  '<%= config.bin %> <%= command.id %> --no-wait',
22
22
  '<%= config.bin %> <%= command.id %> --fn-installer npm',
23
+ '<%= config.bin %> <%= command.id %> --stack <name-or-id>',
24
+ '<%= config.bin %> <%= command.id %> --organization-id <orgId> --stack <name-or-id>',
25
+ '<%= config.bin %> <%= command.id %> --new-stack-name <new-name>',
23
26
  ];
24
27
  static flags = {
25
28
  stack: Flags.string({ ...stackFlagConfig }),
29
+ 'project-id': Flags.string({ ...projectIdFlagConfig }),
30
+ 'organization-id': Flags.string({ ...organizationIdFlagConfig }),
26
31
  message: Flags.string({
27
32
  description: 'Message describing the deployment (e.g. reason for change)',
28
33
  char: 'm',
@@ -39,7 +44,6 @@ Set SANITY_ASSET_TIMEOUT (seconds) to override the 60-second timeout for process
39
44
  }),
40
45
  'new-stack-name': Flags.string({
41
46
  description: 'Set a new name for the Stack',
42
- hidden: true,
43
47
  }),
44
48
  };
45
49
  async run() {
@@ -23,12 +23,10 @@ Use this to clean up test environments or decommission a Stack you no longer nee
23
23
  'project-id': Flags.string({
24
24
  ...projectIdFlagConfig,
25
25
  dependsOn: ['stack', 'force'],
26
- exclusive: ['organization-id'],
27
26
  }),
28
27
  'organization-id': Flags.string({
29
28
  ...organizationIdFlagConfig,
30
29
  dependsOn: ['stack', 'force'],
31
- exclusive: ['project-id'],
32
30
  }),
33
31
  stack: Flags.string({
34
32
  description: 'Stack name or ID to destroy (defaults to the locally configured Stack)',
@@ -7,6 +7,7 @@ export default class InfoCommand extends ResolvedCommand<typeof InfoCommand> {
7
7
  static flags: {
8
8
  stack: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  };
11
12
  run(): Promise<Record<string, unknown> | undefined>;
12
13
  }
@@ -1,5 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { projectIdFlagConfig, ResolvedCommand } from '../../baseCommands.js';
2
+ import { organizationIdFlagConfig, projectIdFlagConfig, ResolvedCommand } from '../../baseCommands.js';
3
3
  import { blueprintInfoCore } from '../../cores/blueprints/info.js';
4
4
  import { Logger } from '../../utils/logger.js';
5
5
  export default class InfoCommand extends ResolvedCommand {
@@ -14,6 +14,7 @@ Run 'blueprints stacks' to see all available Stacks in your project or organizat
14
14
  '<%= config.bin %> <%= command.id %>',
15
15
  '<%= config.bin %> <%= command.id %> --stack <name-or-id>',
16
16
  '<%= config.bin %> <%= command.id %> --project-id <id> --stack <name-or-id>',
17
+ '<%= config.bin %> <%= command.id %> --organization-id <orgId> --stack <name-or-id>',
17
18
  ];
18
19
  static flags = {
19
20
  stack: Flags.string({
@@ -21,6 +22,7 @@ Run 'blueprints stacks' to see all available Stacks in your project or organizat
21
22
  aliases: ['id'],
22
23
  }),
23
24
  'project-id': Flags.string({ ...projectIdFlagConfig }),
25
+ 'organization-id': Flags.string({ ...organizationIdFlagConfig }),
24
26
  };
25
27
  async run() {
26
28
  const result = await blueprintInfoCore({
@@ -44,11 +44,13 @@ After initialization, use 'blueprints plan' to preview changes, then 'blueprints
44
44
  description: 'Existing Stack ID used to scope local Blueprint',
45
45
  aliases: ['stackId'],
46
46
  exclusive: ['stack-name'],
47
+ relationships: [{ type: 'some', flags: ['project-id', 'organization-id'] }],
47
48
  }),
48
49
  'stack-name': Flags.string({
49
50
  description: 'Name to use for a new Stack provisioned during initialization',
50
51
  aliases: ['name'],
51
52
  exclusive: ['stack-id'],
53
+ relationships: [{ type: 'some', flags: ['project-id', 'organization-id'] }],
52
54
  }),
53
55
  };
54
56
  async run() {
@@ -6,7 +6,12 @@ export default class LogsCommand extends ResolvedCommand<typeof LogsCommand> {
6
6
  static examples: string[];
7
7
  static flags: {
8
8
  stack: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
11
  watch: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ since: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ before: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
15
  };
11
16
  run(): Promise<void | Record<string, unknown>>;
12
17
  }
@@ -1,5 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { ResolvedCommand, stackFlagConfig } from '../../baseCommands.js';
2
+ import { organizationIdFlagConfig, projectIdFlagConfig, ResolvedCommand, stackFlagConfig, } from '../../baseCommands.js';
3
3
  import { blueprintLogsCore } from '../../cores/blueprints/logs.js';
4
4
  import { Logger } from '../../utils/logger.js';
5
5
  export default class LogsCommand extends ResolvedCommand {
@@ -7,20 +7,43 @@ export default class LogsCommand extends ResolvedCommand {
7
7
  static summary = "Display logs for the current Blueprint's Stack deployment";
8
8
  static description = `Retrieves Stack deployment logs, useful for debugging and monitoring deployment activity.
9
9
 
10
- Use --watch (-w) to stream logs in real-time.
10
+ Use --watch (-w) to tail logs in real-time.
11
+
12
+ Use --limit, --since, or --before to narrow the result set when not watching.
11
13
 
12
14
  If you're not seeing expected logs, verify your Stack is deployed with 'blueprints info'.`;
13
15
  static examples = [
14
16
  '<%= config.bin %> <%= command.id %>',
15
17
  '<%= config.bin %> <%= command.id %> --watch',
18
+ '<%= config.bin %> <%= command.id %> --stack <name-or-id>',
19
+ '<%= config.bin %> <%= command.id %> --limit 500',
20
+ '<%= config.bin %> <%= command.id %> --since 2026-05-01T00:00:00Z',
21
+ '<%= config.bin %> <%= command.id %> --before 2026-05-01T00:00:00Z',
16
22
  ];
17
23
  static flags = {
18
24
  stack: Flags.string({ ...stackFlagConfig }),
25
+ 'project-id': Flags.string({ ...projectIdFlagConfig }),
26
+ 'organization-id': Flags.string({ ...organizationIdFlagConfig }),
19
27
  watch: Flags.boolean({
20
28
  char: 'w',
21
- description: 'Watch for new Stack logs (streaming mode)',
29
+ description: 'Watch for new Stack logs',
22
30
  aliases: ['follow'],
23
31
  }),
32
+ limit: Flags.integer({
33
+ char: 'l',
34
+ description: 'Maximum number of log entries to retrieve (1-500)',
35
+ min: 1,
36
+ max: 500,
37
+ exclusive: ['watch'],
38
+ }),
39
+ since: Flags.string({
40
+ description: 'Only show logs after this ISO 8601 timestamp',
41
+ exclusive: ['watch'],
42
+ }),
43
+ before: Flags.string({
44
+ description: 'Only show logs before this ISO 8601 timestamp',
45
+ exclusive: ['watch'],
46
+ }),
24
47
  };
25
48
  async run() {
26
49
  const result = await blueprintLogsCore({
@@ -0,0 +1,14 @@
1
+ import { ResolvedCommand } from '../../baseCommands.js';
2
+ export default class MintDeployTokenCommand extends ResolvedCommand<typeof MintDeployTokenCommand> {
3
+ static needs: readonly ["scope"];
4
+ static summary: string;
5
+ static description: string;
6
+ static examples: string[];
7
+ static flags: {
8
+ label: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ print: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<Record<string, unknown> | undefined>;
14
+ }
@@ -0,0 +1,47 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { organizationIdFlagConfig, projectIdFlagConfig, ResolvedCommand } from '../../baseCommands.js';
3
+ import { blueprintMintDeployTokenCore } from '../../cores/blueprints/mint-deploy-token.js';
4
+ import { Logger } from '../../utils/logger.js';
5
+ export default class MintDeployTokenCommand extends ResolvedCommand {
6
+ static needs = ['scope'];
7
+ static summary = 'Create a robot API token for deploying Blueprints from CI/CD';
8
+ static description = `Mints a long-lived robot token with the role required to plan, deploy, and destroy Blueprints in this project or organization.
9
+
10
+ By default the command runs interactively and asks how you want to receive the token (clipboard, print, or exit). Use --print to emit only the raw token for shell pipelines, or --json for full API output.
11
+
12
+ The minted token is also visible in your Sanity Manage UI under Robots, where it can be revoked.`;
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %>',
15
+ '<%= config.bin %> <%= command.id %> --label "ci-deploy"',
16
+ '<%= config.bin %> <%= command.id %> --print',
17
+ 'export SANITY_AUTH_TOKEN=$(<%= config.bin %> <%= command.id %> --print)',
18
+ '<%= config.bin %> <%= command.id %> --json',
19
+ '<%= config.bin %> <%= command.id %> --project-id <projectId>',
20
+ '<%= config.bin %> <%= command.id %> --organization-id <orgId>',
21
+ ];
22
+ static flags = {
23
+ label: Flags.string({
24
+ description: 'Human-readable label for the robot. Defaults to a generated value.',
25
+ }),
26
+ 'project-id': Flags.string({ ...projectIdFlagConfig }),
27
+ 'organization-id': Flags.string({ ...organizationIdFlagConfig }),
28
+ print: Flags.boolean({
29
+ char: 'P',
30
+ description: 'Print only the raw token to stdout (suitable for shell substitution)',
31
+ exclusive: ['json'],
32
+ }),
33
+ };
34
+ async run() {
35
+ const result = await blueprintMintDeployTokenCore({
36
+ bin: this.config.bin,
37
+ log: Logger(this.log.bind(this), this.flags),
38
+ auth: this.auth,
39
+ scopeType: this.scopeType,
40
+ scopeId: this.scopeId,
41
+ flags: this.flags,
42
+ });
43
+ if (!result.success)
44
+ this.coreError(result);
45
+ return result.json;
46
+ }
47
+ }
@@ -6,6 +6,8 @@ export default class PlanCommand extends ResolvedCommand<typeof PlanCommand> {
6
6
  static examples: string[];
7
7
  static flags: {
8
8
  stack: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'organization-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
11
  };
10
12
  run(): Promise<Record<string, unknown> | undefined>;
11
13
  }
@@ -1,5 +1,5 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { ResolvedCommand, stackFlagConfig } from '../../baseCommands.js';
2
+ import { organizationIdFlagConfig, projectIdFlagConfig, ResolvedCommand, stackFlagConfig, } from '../../baseCommands.js';
3
3
  import { blueprintPlanCore } from '../../cores/blueprints/plan.js';
4
4
  import { Logger } from '../../utils/logger.js';
5
5
  export default class PlanCommand extends ResolvedCommand {
@@ -8,9 +8,15 @@ export default class PlanCommand extends ResolvedCommand {
8
8
  static description = `Use this command to preview what changes will be applied to your remote Stack before deploying. This is a safe, read-only operation—no resources are created, modified, or deleted.
9
9
 
10
10
  Run 'blueprints plan' after making local changes to your Blueprint manifest to verify the expected diff. When ready, run 'blueprints deploy' to apply changes.`;
11
- static examples = ['<%= config.bin %> <%= command.id %>'];
11
+ static examples = [
12
+ '<%= config.bin %> <%= command.id %>',
13
+ '<%= config.bin %> <%= command.id %> --stack <name-or-id>',
14
+ '<%= config.bin %> <%= command.id %> --organization-id <orgId> --stack <name-or-id>',
15
+ ];
12
16
  static flags = {
13
17
  stack: Flags.string({ ...stackFlagConfig }),
18
+ 'project-id': Flags.string({ ...projectIdFlagConfig }),
19
+ 'organization-id': Flags.string({ ...organizationIdFlagConfig }),
14
20
  };
15
21
  async run() {
16
22
  const result = await blueprintPlanCore({
@@ -3,11 +3,12 @@ export default class PromoteCommand extends ResolvedCommand<typeof PromoteComman
3
3
  static needs: readonly ["deployedStack", "blueprint"];
4
4
  static summary: string;
5
5
  static description: string;
6
- static hidden: boolean;
7
6
  static examples: string[];
8
7
  static flags: {
9
8
  stack: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'project-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
10
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ 'new-stack-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
12
13
  run(): Promise<Record<string, unknown> | undefined>;
13
14
  }