@sanity/runtime-cli 1.2.1 → 1.3.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.
@@ -1,68 +1,74 @@
1
1
  import { Command } from '@oclif/core';
2
- import readBlueprint from '../../actions/blueprints/read-blueprint.js';
3
- import { getStack, listStacks } from '../../actions/blueprints/stacks.js';
2
+ import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
3
+ import { getStackByName } from '../../actions/blueprints/stacks.js';
4
+ import { formatResourceTree, formatStacksList, formatTitle, } from '../../utils/display/blueprints-formatting.js';
5
+ import { bold, green, red, yellow } from '../../utils/display/colors.js';
6
+ import { formatDate, formatDuration } from '../../utils/display/dates.js';
4
7
  import Spinner from '../../utils/spinner.js';
5
8
  export default class Info extends Command {
6
9
  static description = 'Show information about a Blueprint';
7
10
  static examples = ['<%= config.bin %> <%= command.id %>'];
8
11
  async run() {
9
- const { displayName, name, projectId, resources } = readBlueprint();
12
+ const { document } = readBlueprintOnDisk();
13
+ const { displayName, name, projectId } = document;
10
14
  const s = new Spinner();
11
15
  if (!projectId) {
12
- this.log(`No project for Blueprint "${displayName}"`);
16
+ this.log(`Cannot determine project for Blueprint "${displayName}"`);
13
17
  return;
14
18
  }
15
19
  s.start(`Retrieving info for "${displayName}"`);
16
20
  try {
17
- const { ok, stacks, error } = await listStacks();
21
+ const { ok, stack, stackId: foundStackId, error, availableStacks, } = await getStackByName({
22
+ name,
23
+ projectId,
24
+ });
18
25
  if (!ok) {
19
- s.stop('Failed to retrieve stacks');
26
+ s.stop(`${red('Failed')} to retrieve stacks`);
20
27
  this.log(`Error: ${error || 'Unknown error'}`);
21
28
  return;
22
29
  }
23
- if (stacks.length === 0) {
24
- s.stop(`No stacks found for Blueprint "${displayName}"`);
30
+ if (!foundStackId || !stack) {
31
+ s.stop(`${red('Failed')} to find Stack for ${formatTitle('Blueprint', displayName)}`);
32
+ if (availableStacks && availableStacks.length > 0) {
33
+ this.log(formatStacksList(availableStacks));
34
+ }
25
35
  return;
26
36
  }
27
- const foundStack = stacks.find((st) => st.name === name);
28
- const { ok: stackOk, stack, error: stackError } = await getStack(foundStack.id);
29
- if (!stackOk) {
30
- s.stop('Failed to retrieve stack');
31
- this.log(`Error: ${stackError || 'Unknown error'}`);
32
- return;
37
+ const foundStack = { id: foundStackId };
38
+ s.stop(`${formatTitle('Blueprint', displayName)} Info\n`);
39
+ this.log(`Stack name: ${bold(stack.name)}`);
40
+ this.log(`Stack ID: ${yellow(foundStack.id)}`);
41
+ if (stack.createdAt) {
42
+ this.log(`Created: ${formatDate(stack.createdAt)}`);
33
43
  }
34
- s.stop(`Stack "${stack.name}" (${stack.recentOperation.status})`);
35
- // Show resources
36
- if (stack?.resources) {
37
- const { resources: stackResources } = stack;
38
- const functionResources = stackResources.filter((r) => r.kind === 'function');
39
- const otherResources = stackResources.filter((r) => r.kind !== 'function');
40
- if (functionResources.length > 0) {
41
- this.log(`├─ Functions (${functionResources.length})`);
42
- for (const fn of functionResources) {
43
- this.log(`│ ├─ ${fn.name} <${fn.externalId}>`);
44
- }
44
+ if (stack.updatedAt) {
45
+ this.log(`Updated: ${formatDate(stack.updatedAt)}`);
46
+ }
47
+ if (stack.recentOperation) {
48
+ const operation = stack.recentOperation;
49
+ if (operation.id) {
50
+ this.log(`Recent Operation <${yellow(operation.id)}>:`);
45
51
  }
46
- if (otherResources.length > 0) {
47
- this.log(`└─Other Resources (${otherResources.length})`);
48
- for (const other of otherResources) {
49
- this.log(` ├─ ${other.name || other.src}`);
50
- }
52
+ if (operation.status) {
53
+ const operationColor = operation.status === 'COMPLETED' ? green : red;
54
+ const status = operation.status || 'UNKNOWN';
55
+ this.log(` Status: ${operationColor(status)}`);
51
56
  }
52
- }
53
- // Show operation details if not completed
54
- if (stack.recentOperation.status !== 'COMPLETED') {
55
- this.log('\nOperation Details:');
56
- if (stack.recentOperation.error) {
57
- this.log(`Error: ${stack.recentOperation.error}`);
57
+ if (operation.createdAt) {
58
+ this.log(` Started : ${formatDate(operation.createdAt)}`);
58
59
  }
59
- if (stack.recentOperation.progress) {
60
- this.log(`Progress: ${stack.recentOperation.progress}`);
60
+ if (operation.status === 'COMPLETED' && operation.completedAt && operation.createdAt) {
61
+ this.log(` Completed: ${formatDate(operation.completedAt)}`);
62
+ this.log(` Duration: ${yellow(formatDuration(operation.createdAt, operation.completedAt))}`);
61
63
  }
62
64
  }
65
+ this.log('');
66
+ if (stack?.resources) {
67
+ formatResourceTree(stack.resources, this.log.bind(this));
68
+ }
63
69
  }
64
70
  catch (err) {
65
- s.stop('Failed to retrieve info');
71
+ s.stop('Failed to parse Stack info');
66
72
  if (err instanceof Error) {
67
73
  this.log(`Error: ${err.message}`);
68
74
  }
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Logs extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'stack-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ watch: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,166 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { getLogs, streamLogs } from '../../actions/blueprints/logs.js';
3
+ import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
4
+ import { getStackByName } from '../../actions/blueprints/stacks.js';
5
+ import config from '../../config.js';
6
+ import { formatStacksList, formatTitle } from '../../utils/display/blueprints-formatting.js';
7
+ import { blue, bold, green, red, yellow } from '../../utils/display/colors.js';
8
+ import Spinner from '../../utils/spinner.js';
9
+ export default class Logs extends Command {
10
+ static description = 'Display logs for a Blueprint stack';
11
+ static examples = [
12
+ '<%= config.bin %> <%= command.id %>',
13
+ '<%= config.bin %> <%= command.id %> --stack-id <stack-id>',
14
+ '<%= config.bin %> <%= command.id %> --watch',
15
+ ];
16
+ static flags = {
17
+ 'stack-id': Flags.string({
18
+ char: 's',
19
+ description: 'Stack ID to fetch logs for (optional if running in a blueprint directory)',
20
+ required: false,
21
+ }),
22
+ watch: Flags.boolean({
23
+ char: 'w',
24
+ description: 'Watch for new logs (streaming mode)',
25
+ required: false,
26
+ default: false,
27
+ }),
28
+ };
29
+ async run() {
30
+ const { flags } = await this.parse(Logs);
31
+ const s = new Spinner();
32
+ const watchMode = flags.watch;
33
+ try {
34
+ const { document } = readBlueprintOnDisk();
35
+ const { name, displayName, projectId } = document;
36
+ if (!projectId) {
37
+ this.log('Cannot determine project ID for this Blueprint');
38
+ return;
39
+ }
40
+ let stackId = flags['stack-id'];
41
+ if (!stackId) {
42
+ s.start(`Finding stack for blueprint "${displayName}"`);
43
+ const { ok, stackId: foundStackId, error, availableStacks, } = await getStackByName({
44
+ name,
45
+ projectId,
46
+ });
47
+ if (!ok) {
48
+ s.stop(`${red('Failed')} to retrieve stacks`);
49
+ this.log(`Error: ${error || 'Unknown error'}`);
50
+ return;
51
+ }
52
+ if (!foundStackId) {
53
+ s.stop(`${red('Failed')} to find Stack for ${formatTitle('Blueprint', displayName)}`);
54
+ if (availableStacks && availableStacks.length > 0) {
55
+ this.log(formatStacksList(availableStacks));
56
+ }
57
+ return;
58
+ }
59
+ stackId = foundStackId;
60
+ s.stop(`Found stack ${stackId} for blueprint "${displayName}"`);
61
+ }
62
+ if (!stackId) {
63
+ s.stop(`${red('Failed')} to determine stack ID`);
64
+ return;
65
+ }
66
+ if (watchMode) {
67
+ // recent logs
68
+ s.start(`Fetching recent logs for stack ${stackId}`);
69
+ const { ok, logs, error } = await getLogs(stackId, projectId, config.token);
70
+ if (!ok) {
71
+ s.stop(`${red('Failed')} to retrieve logs`);
72
+ this.log(`Error: ${error || 'Unknown error'}`);
73
+ return;
74
+ }
75
+ s.stop(`${formatTitle('Blueprint', displayName)} ${bold('Live')} Logs`);
76
+ if (logs.length > 0) {
77
+ this.log('\nMost recent logs:');
78
+ const sortedLogs = [...logs].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
79
+ const recentLogs = sortedLogs.slice(-10);
80
+ for (const log of recentLogs) {
81
+ const date = new Date(log.timestamp);
82
+ const time = date.toLocaleTimeString();
83
+ const day = date.toLocaleDateString();
84
+ this.log(` ${bold(day)} ${yellow(time)} ${log.message}`);
85
+ }
86
+ }
87
+ else {
88
+ this.log(`No recent logs found for stack ${yellow(stackId)}`);
89
+ }
90
+ const onOpen = () => {
91
+ this.log(`\n${bold('Watching')} for new logs...`);
92
+ this.log(`Press ${bold('Ctrl+C')} to stop\n`);
93
+ };
94
+ let newestTimestamp = 0;
95
+ if (logs.length > 0) {
96
+ for (const log of logs) {
97
+ const timestamp = new Date(log.timestamp).getTime();
98
+ if (timestamp > newestTimestamp) {
99
+ newestTimestamp = timestamp;
100
+ }
101
+ }
102
+ }
103
+ const renderLog = (log) => {
104
+ const logTimestamp = new Date(log.timestamp).getTime();
105
+ if (logTimestamp <= newestTimestamp) {
106
+ return;
107
+ }
108
+ newestTimestamp = logTimestamp;
109
+ const date = new Date(log.timestamp);
110
+ const time = date.toLocaleTimeString();
111
+ const day = date.toLocaleDateString();
112
+ this.log(`${green('>')} ${bold(day)} ${yellow(time)} ${log.message}`);
113
+ };
114
+ this.debug(`${yellow('Debug:')} Connecting to streaming endpoint for stack ${stackId}...`);
115
+ streamLogs(stackId, projectId, config.token, renderLog, onOpen, (error) => this.log(`${red('Error:')} ${error}`));
116
+ return new Promise(() => {
117
+ // hold the line until the user terminates with Ctrl+C
118
+ });
119
+ }
120
+ s.start(`Fetching logs for stack ${stackId}`);
121
+ const { ok, logs, error } = await getLogs(stackId, projectId, config.token);
122
+ if (!ok) {
123
+ s.stop(`${red('Failed')} to retrieve logs`);
124
+ this.log(`Error: ${error || 'Unknown error'}`);
125
+ return;
126
+ }
127
+ if (logs.length === 0) {
128
+ s.stop(`No logs found for stack ${stackId}`);
129
+ return;
130
+ }
131
+ s.stop(`${formatTitle('Blueprint', displayName)} Logs`);
132
+ this.log(`Found ${bold(logs.length.toString())} log entries for stack ${yellow(stackId)}\n`);
133
+ const logsByDay = new Map();
134
+ for (const log of logs) {
135
+ const date = new Date(log.timestamp);
136
+ const day = date.toLocaleDateString();
137
+ if (!logsByDay.has(day)) {
138
+ logsByDay.set(day, []);
139
+ }
140
+ logsByDay.get(day)?.push(log);
141
+ }
142
+ const sortedDays = Array.from(logsByDay.keys()).sort((a, b) => {
143
+ return new Date(a).getTime() - new Date(b).getTime();
144
+ });
145
+ for (const day of sortedDays) {
146
+ this.log(`${blue('Date:')} ${bold(day)}`);
147
+ const dayLogs = logsByDay.get(day) || [];
148
+ dayLogs.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
149
+ for (const [i, log] of dayLogs.entries()) {
150
+ const date = new Date(log.timestamp);
151
+ const time = date.toLocaleTimeString();
152
+ const isLast = i === dayLogs.length - 1;
153
+ this.log(` ${isLast ? '└─' : '├─'} ${yellow(time)} ${log.message}`);
154
+ }
155
+ // new line between days
156
+ this.log('');
157
+ }
158
+ }
159
+ catch (err) {
160
+ s.stop('Failed to retrieve logs');
161
+ if (err instanceof Error) {
162
+ this.log(`Error: ${err.message}`);
163
+ }
164
+ }
165
+ }
166
+ }
@@ -1,31 +1,18 @@
1
1
  import { Command } from '@oclif/core';
2
- import readBlueprint from '../../actions/blueprints/read-blueprint.js';
2
+ import readBlueprintOnDisk from '../../actions/blueprints/read-blueprint.js';
3
+ import { formatResourceTree, formatTitle } from '../../utils/display/blueprints-formatting.js';
3
4
  export default class Plan extends Command {
4
5
  static description = 'Enumerate resources to be deployed - will not modify any resources';
5
6
  static examples = ['<%= config.bin %> <%= command.id %>'];
6
7
  async run() {
7
- const { displayName, resources } = readBlueprint();
8
- this.log(`Blueprint: "${displayName}"`);
9
- const functionResources = resources.filter((r) => r.kind === 'function');
10
- const otherResources = resources.filter((r) => r.kind === 'test');
11
- this.log('Tracked resources:');
12
- if (functionResources.length > 0) {
13
- this.log(`├─ Functions (${functionResources.length})`);
14
- for (const fn of functionResources) {
15
- this.log(`│ ├─ ${fn.name}: ${fn.src}`);
16
- }
17
- }
18
- if (otherResources.length > 0) {
19
- this.log(`└─Other (${otherResources.length})`);
20
- for (const other of otherResources) {
21
- this.log(` ├─ ${other.name}`);
22
- }
23
- }
24
- if (resources.length === 0) {
25
- this.log('No resources to deploy');
26
- }
27
- else {
28
- this.log('\nRun `blueprints deploy` to deploy these changes');
8
+ const { document, fileName } = readBlueprintOnDisk();
9
+ const { displayName, name, resources } = document;
10
+ this.log(`${formatTitle('Blueprint', displayName)} Plan\n`);
11
+ this.log(`Blueprint document: "${name}" (${fileName})`);
12
+ this.log('');
13
+ formatResourceTree(resources, this.log.bind(this));
14
+ if (resources.length > 0) {
15
+ this.log('\nRun `sanity blueprints deploy` to deploy these changes');
29
16
  }
30
17
  }
31
18
  }
package/dist/config.js CHANGED
@@ -9,8 +9,8 @@ const functionsUrls = {
9
9
  default: 'http://localhost:4567',
10
10
  };
11
11
  const blueprintsUrls = {
12
- production: 'https://api.sanity.io/',
13
- staging: 'https://api.sanity.work/',
12
+ production: 'https://api.sanity.io',
13
+ staging: 'https://api.sanity.work',
14
14
  default: 'http://localhost:4567',
15
15
  };
16
16
  const functionsUrl = new URL(functionsUrls[nodeEnv] ?? functionsUrls.default);
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { cwd } from 'node:process';
4
2
  import { default as mime } from 'mime-types';
3
+ import readBlueprintOnDisk from '../actions/blueprints/read-blueprint.js';
5
4
  import invoke from '../utils/invoke-local.js';
6
5
  import * as http from 'node:http';
7
6
  const host = 'localhost';
@@ -10,10 +9,16 @@ const app = (port) => {
10
9
  res.setHeader('Content-Type', 'application/json');
11
10
  switch (req.url) {
12
11
  case '/blueprint': {
13
- res.setHeader('Content-Type', 'application/json');
14
- const blueprint = readFileSync(join(cwd(), './blueprint.json')).toString();
15
- res.writeHead(200);
16
- res.end(blueprint);
12
+ try {
13
+ const blueprint = readBlueprintOnDisk();
14
+ res.setHeader('Content-Type', 'application/json');
15
+ res.writeHead(200);
16
+ res.end(JSON.stringify(blueprint));
17
+ }
18
+ catch {
19
+ res.writeHead(404);
20
+ res.end();
21
+ }
17
22
  break;
18
23
  }
19
24
  case '/invoke': {
@@ -42,9 +42,12 @@ function blueprint() {
42
42
  fetch('/blueprint')
43
43
  .then((response) => response.json())
44
44
  .then((json) => {
45
- const functions = json?.resources.filter((resource) => resource.kind === 'function')
45
+ const functions = json?.document?.resources.filter((resource) => resource.kind === 'function')
46
46
 
47
47
  store.functions = functions
48
48
  store.selectedIndex = functions[0].src
49
49
  })
50
+ .catch(() => {
51
+ store.functions = []
52
+ })
50
53
  }
@@ -15,18 +15,23 @@ class FunctionList extends ApiBaseElement {
15
15
  this.api.store.selectedIndex = event.srcElement.value
16
16
  }
17
17
  renderFunctions = () => {
18
- this.list.innerHTML = this.api.store.functions
19
- .map((func) => {
20
- const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
21
- return `<li class="pad-sm ${selected}">${func.name}</li>`
22
- })
23
- .join('')
24
- this.select.innerHTML = this.api.store.functions
25
- .map((func) => {
26
- const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
27
- return `<option value="${func.src}" ${selected}>${func.name}</option>`
28
- })
29
- .join('')
18
+ if (this.api.store.functions.length > 0) {
19
+ this.list.innerHTML = this.api.store.functions
20
+ .map((func) => {
21
+ const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
22
+ return `<li class="pad-sm ${selected}">${func.name}</li>`
23
+ })
24
+ .join('')
25
+ this.select.innerHTML = this.api.store.functions
26
+ .map((func) => {
27
+ const selected = this.api.store.selectedIndex === func.src ? 'selected' : ''
28
+ return `<option value="${func.src}" ${selected}>${func.name}</option>`
29
+ })
30
+ .join('')
31
+ } else {
32
+ this.list.innerHTML = '<option class="pad-sm">No blueprint.json file found</li>'
33
+ this.select.innerHTML = '<option>No blueprint.json file found</option>'
34
+ }
30
35
  }
31
36
 
32
37
  connectedCallback() {
@@ -0,0 +1,4 @@
1
+ import type { BlueprintResource } from '../types.js';
2
+ export declare function formatTitle(title: string, name: string): string;
3
+ export declare function formatStacksList(stacks: string[]): string;
4
+ export declare function formatResourceTree(resources: BlueprintResource[], logger: (msg: string) => void): void;
@@ -0,0 +1,42 @@
1
+ import { blue, bold, boldnblue, green, yellow } from './colors.js';
2
+ export function formatTitle(title, name) {
3
+ return `${boldnblue(title)} ${bold(`"${name}"`)}`;
4
+ }
5
+ export function formatStacksList(stacks) {
6
+ if (!stacks || stacks.length === 0) {
7
+ return 'No stacks found';
8
+ }
9
+ let result = '\nFound Stacks:\n';
10
+ for (const name of stacks) {
11
+ result += ` ${name}\n`;
12
+ }
13
+ return result;
14
+ }
15
+ export function formatResourceTree(resources, logger) {
16
+ if (!resources || resources.length === 0) {
17
+ logger(' No resources in this stack');
18
+ return;
19
+ }
20
+ logger(`${blue('Stack Resources')} [${resources.length}]`);
21
+ const functionResources = resources.filter((r) => r.kind === 'function');
22
+ const otherResources = resources.filter((r) => r.kind !== 'function');
23
+ const hasOtherResources = otherResources.length > 0;
24
+ if (functionResources.length > 0) {
25
+ logger(` ${hasOtherResources ? '├─' : '└─'} ${bold('Functions')} [${functionResources.length}]`);
26
+ for (const [i, fn] of functionResources.entries()) {
27
+ const isLast = i === functionResources.length - 1;
28
+ const prefix = hasOtherResources ? '│' : ' ';
29
+ const connector = isLast ? '└─' : '├─';
30
+ const name = green(fn.displayName || fn.name);
31
+ const externalId = fn.externalId ? `Fn<${yellow(fn.externalId)}>` : '';
32
+ logger(` ${prefix} ${connector} "${name}" ${externalId}`);
33
+ }
34
+ }
35
+ if (hasOtherResources) {
36
+ logger(` └─ ${bold('Other Resources')} [${otherResources.length}]`);
37
+ for (const [i, other] of otherResources.entries()) {
38
+ const isLast = i === otherResources.length - 1;
39
+ logger(` ${isLast ? '└─' : '├─'} "${yellow(other.displayName || other.name || other.src)}"`);
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,6 @@
1
+ export declare function bold(str: string): string;
2
+ export declare function blue(str: string): string;
3
+ export declare function green(str: string): string;
4
+ export declare function red(str: string): string;
5
+ export declare function yellow(str: string): string;
6
+ export declare function boldnblue(str: string): string;
@@ -0,0 +1,18 @@
1
+ export function bold(str) {
2
+ return `\x1b[1m${str}\x1b[0m`;
3
+ }
4
+ export function blue(str) {
5
+ return `\x1b[34m${str}\x1b[0m`;
6
+ }
7
+ export function green(str) {
8
+ return `\x1b[32m${str}\x1b[0m`;
9
+ }
10
+ export function red(str) {
11
+ return `\x1b[31m${str}\x1b[0m`;
12
+ }
13
+ export function yellow(str) {
14
+ return `\x1b[33m${str}\x1b[0m`;
15
+ }
16
+ export function boldnblue(str) {
17
+ return bold(blue(str));
18
+ }
@@ -0,0 +1,2 @@
1
+ export declare function formatDate(dateString: string): string;
2
+ export declare function formatDuration(startDate: string, endDate: string): string;
@@ -0,0 +1,26 @@
1
+ export function formatDate(dateString) {
2
+ const date = new Date(dateString);
3
+ return date.toLocaleString();
4
+ }
5
+ export function formatDuration(startDate, endDate) {
6
+ const start = new Date(startDate).getTime();
7
+ const end = new Date(endDate).getTime();
8
+ if (start > end) {
9
+ return 'Invalid duration';
10
+ }
11
+ const durationMs = end - start;
12
+ if (durationMs < 1000) {
13
+ return `${durationMs}ms`;
14
+ }
15
+ if (durationMs < 60000) {
16
+ return `${Math.round(durationMs / 1000)}s`;
17
+ }
18
+ if (durationMs < 3600000) {
19
+ const minutes = Math.floor(durationMs / 60000);
20
+ const seconds = Math.floor((durationMs % 60000) / 1000);
21
+ return `${minutes}m ${seconds}s`;
22
+ }
23
+ const hours = Math.floor(durationMs / 3600000);
24
+ const minutes = Math.floor((durationMs % 3600000) / 60000);
25
+ return `${hours}h ${minutes}m`;
26
+ }
@@ -48,17 +48,36 @@ export interface BlueprintJob {
48
48
  * @internal
49
49
  */
50
50
  export interface BlueprintResource {
51
+ id: string;
52
+ displayName: string;
51
53
  name: string;
52
54
  kind: string;
53
55
  src: string;
54
- assetId: string;
56
+ externalId: string;
55
57
  }
56
58
  /**
57
59
  * @internal
58
60
  */
61
+ export interface BlueprintOperation {
62
+ id: string;
63
+ status: string;
64
+ createdAt?: string;
65
+ completedAt?: string;
66
+ }
59
67
  export interface BlueprintStack {
68
+ id: string;
60
69
  name: string;
61
- kind: string;
62
- src: string;
70
+ displayName: string;
71
+ resources: Array<BlueprintResource>;
72
+ createdAt?: string;
73
+ updatedAt?: string;
74
+ recentOperation?: BlueprintOperation;
75
+ }
76
+ /**
77
+ * @internal
78
+ */
79
+ export interface BlueprintLog {
80
+ timestamp: string;
81
+ message: string;
63
82
  }
64
83
  export {};
@@ -48,6 +48,50 @@
48
48
  "info.js"
49
49
  ]
50
50
  },
51
+ "blueprints:logs": {
52
+ "aliases": [],
53
+ "args": {},
54
+ "description": "Display logs for a Blueprint stack",
55
+ "examples": [
56
+ "<%= config.bin %> <%= command.id %>",
57
+ "<%= config.bin %> <%= command.id %> --stack-id <stack-id>",
58
+ "<%= config.bin %> <%= command.id %> --watch"
59
+ ],
60
+ "flags": {
61
+ "stack-id": {
62
+ "char": "s",
63
+ "description": "Stack ID to fetch logs for (optional if running in a blueprint directory)",
64
+ "name": "stack-id",
65
+ "required": false,
66
+ "hasDynamicHelp": false,
67
+ "multiple": false,
68
+ "type": "option"
69
+ },
70
+ "watch": {
71
+ "char": "w",
72
+ "description": "Watch for new logs (streaming mode)",
73
+ "name": "watch",
74
+ "required": false,
75
+ "allowNo": false,
76
+ "type": "boolean"
77
+ }
78
+ },
79
+ "hasDynamicHelp": false,
80
+ "hiddenAliases": [],
81
+ "id": "blueprints:logs",
82
+ "pluginAlias": "@sanity/runtime-cli",
83
+ "pluginName": "@sanity/runtime-cli",
84
+ "pluginType": "core",
85
+ "strict": true,
86
+ "enableJsonFlag": false,
87
+ "isESM": true,
88
+ "relativePath": [
89
+ "dist",
90
+ "commands",
91
+ "blueprints",
92
+ "logs.js"
93
+ ]
94
+ },
51
95
  "blueprints:plan": {
52
96
  "aliases": [],
53
97
  "args": {},
@@ -247,5 +291,5 @@
247
291
  ]
248
292
  }
249
293
  },
250
- "version": "1.2.1"
294
+ "version": "1.3.1"
251
295
  }