@nitrostack/cli 1.0.6 → 1.0.8

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 (34) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +39 -96
  3. package/dist/commands/build.d.ts.map +1 -1
  4. package/dist/commands/build.js +4 -1
  5. package/dist/commands/dev.d.ts.map +1 -1
  6. package/dist/commands/dev.js +24 -52
  7. package/dist/commands/generate-types.d.ts +0 -1
  8. package/dist/commands/generate-types.d.ts.map +1 -1
  9. package/dist/commands/generate-types.js +18 -39
  10. package/dist/commands/generate.d.ts.map +1 -1
  11. package/dist/commands/generate.js +12 -4
  12. package/dist/commands/init.d.ts +3 -1
  13. package/dist/commands/init.d.ts.map +1 -1
  14. package/dist/commands/init.js +27 -15
  15. package/dist/commands/install.d.ts.map +1 -1
  16. package/dist/commands/install.js +3 -1
  17. package/dist/commands/start.d.ts.map +1 -1
  18. package/dist/commands/start.js +4 -4
  19. package/dist/commands/upgrade.d.ts.map +1 -1
  20. package/dist/commands/upgrade.js +92 -77
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +3 -1
  23. package/dist/ui/branding.d.ts +21 -4
  24. package/dist/ui/branding.d.ts.map +1 -1
  25. package/dist/ui/branding.js +121 -52
  26. package/package.json +5 -6
  27. package/templates/typescript-oauth/.env.example +27 -0
  28. package/templates/typescript-oauth/README.md +36 -231
  29. package/templates/typescript-pizzaz/.env.example +8 -0
  30. package/templates/typescript-pizzaz/README.md +42 -217
  31. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.module.ts +2 -1
  32. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.tasks.ts +294 -0
  33. package/templates/typescript-starter/.env.example +7 -0
  34. package/templates/typescript-starter/README.md +51 -284
@@ -1,24 +1,21 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { dirname } from 'path';
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
3
+ import { createHeader, createSuccessBox, NitroSpinner, spacer } from '../ui/branding.js';
7
4
  /**
8
5
  * Generates TypeScript types from tool definitions
9
- * Scans all *.tools.ts files and extracts types from Zod schemas and examples
10
6
  */
11
7
  export async function generateTypes(options = {}) {
12
8
  const projectRoot = process.cwd();
13
9
  const outputPath = options.output || path.join(projectRoot, 'src', 'types', 'generated-tools.ts');
14
- console.log('🔍 Scanning for tool definitions...');
10
+ console.log(createHeader('Types', 'Generate TypeScript definitions'));
11
+ const spinner = new NitroSpinner('Scanning for tool definitions...').start();
15
12
  // Find all *.tools.ts files
16
13
  const toolFiles = await findToolFiles(path.join(projectRoot, 'src'));
17
14
  if (toolFiles.length === 0) {
18
- console.log('⚠️ No tool files found');
15
+ spinner.fail('No tool files found');
19
16
  return;
20
17
  }
21
- console.log(`📝 Found ${toolFiles.length} tool file(s)`);
18
+ spinner.update(`Found ${toolFiles.length} tool file(s)`);
22
19
  // Parse tools and generate types
23
20
  const types = await generateTypesFromFiles(toolFiles);
24
21
  // Write output file
@@ -27,7 +24,12 @@ export async function generateTypes(options = {}) {
27
24
  fs.mkdirSync(outputDir, { recursive: true });
28
25
  }
29
26
  fs.writeFileSync(outputPath, types);
30
- console.log(`✅ Generated types at: ${outputPath}`);
27
+ spinner.succeed('Type generation complete');
28
+ spacer();
29
+ console.log(createSuccessBox('Types Generated', [
30
+ `Location: ${path.relative(projectRoot, outputPath)}`,
31
+ `Count: ${toolFiles.length} tools processed`
32
+ ]));
31
33
  }
32
34
  async function findToolFiles(dir) {
33
35
  const files = [];
@@ -135,9 +137,7 @@ function toPascalCase(str) {
135
137
  }
136
138
  function generateTypeFromZodSchema(zodContent) {
137
139
  // Simple parser for basic Zod schemas
138
- // This is a simplified version - in production, you might want to use actual Zod parsing
139
140
  const fields = [];
140
- // Match field definitions like: field_name: z.string().describe('...')
141
141
  const fieldRegex = /(\w+):\s*z\.(\w+)\(\)(?:\.optional\(\))?(?:\.describe\([^)]*\))?/g;
142
142
  let match;
143
143
  while ((match = fieldRegex.exec(zodContent)) !== null) {
@@ -156,14 +156,7 @@ function generateTypeFromZodSchema(zodContent) {
156
156
  tsType = 'boolean';
157
157
  break;
158
158
  case 'enum':
159
- // Extract enum values if possible
160
- const enumMatch = zodContent.match(new RegExp(`${fieldName}:\\s*z\\.enum\\(\\[([^\\]]+)\\]\\)`));
161
- if (enumMatch) {
162
- tsType = enumMatch[1].replace(/'/g, '"');
163
- }
164
- else {
165
- tsType = 'string';
166
- }
159
+ tsType = 'string';
167
160
  break;
168
161
  case 'array':
169
162
  tsType = 'unknown[]';
@@ -178,11 +171,7 @@ function generateTypeFromZodSchema(zodContent) {
178
171
  }
179
172
  function generateTypeFromExample(exampleJson) {
180
173
  try {
181
- // Parse the example JSON to infer types
182
- // This is a simplified version
183
- const cleaned = exampleJson
184
- .replace(/\/\/.*/g, '') // Remove comments
185
- .trim();
174
+ const cleaned = exampleJson.replace(/\/\/.*/g, '').trim();
186
175
  const obj = eval('(' + cleaned + ')');
187
176
  return generateTypeFromObject(obj);
188
177
  }
@@ -191,29 +180,19 @@ function generateTypeFromExample(exampleJson) {
191
180
  }
192
181
  }
193
182
  function generateTypeFromObject(obj, indent = ' ') {
194
- if (obj === null || obj === undefined) {
183
+ if (obj === null || obj === undefined)
195
184
  return 'unknown';
196
- }
197
185
  if (Array.isArray(obj)) {
198
- if (obj.length === 0) {
186
+ if (obj.length === 0)
199
187
  return 'unknown[]';
200
- }
201
- const itemType = generateTypeFromObject(obj[0], indent + ' ');
202
- return `Array<${itemType}>`;
188
+ return `Array<${generateTypeFromObject(obj[0], indent + ' ')}>`;
203
189
  }
204
190
  if (typeof obj === 'object') {
205
191
  const fields = [];
206
192
  for (const [key, value] of Object.entries(obj)) {
207
- const type = generateTypeFromObject(value, indent + ' ');
208
- fields.push(`${key}: ${type}`);
193
+ fields.push(`${key}: ${generateTypeFromObject(value, indent + ' ')}`);
209
194
  }
210
195
  return `{\n${indent}${fields.join(`;\n${indent}`)};\n${indent.slice(2)}}`;
211
196
  }
212
- if (typeof obj === 'string')
213
- return 'string';
214
- if (typeof obj === 'number')
215
- return 'number';
216
- if (typeof obj === 'boolean')
217
- return 'boolean';
218
- return 'unknown';
197
+ return typeof obj;
219
198
  }
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AA8TA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6FvG"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAgUA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsGvG"}
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
- import { createHeader, createSuccessBox, createErrorBox, log, spacer, nextSteps } from '../ui/branding.js';
4
+ import { createHeader, createSuccessBox, createErrorBox, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
5
5
  /**
6
6
  * Generator templates
7
7
  */
@@ -295,6 +295,7 @@ function getFilePath(type, name, module) {
295
295
  * Generate command
296
296
  */
297
297
  export async function generate(type, name, options = {}) {
298
+ console.log(NITRO_BANNER_FULL);
298
299
  // Special case for types generation
299
300
  if (type === 'types') {
300
301
  const { generateTypes } = await import('./generate-types.js');
@@ -305,6 +306,7 @@ export async function generate(type, name, options = {}) {
305
306
  // Validate type
306
307
  const validTypes = Object.keys(TEMPLATES);
307
308
  if (!validTypes.includes(type)) {
309
+ spacer();
308
310
  console.log(createErrorBox('Invalid Type', `Valid types: ${validTypes.join(', ')}, types`));
309
311
  process.exit(1);
310
312
  }
@@ -332,9 +334,9 @@ export async function generate(type, name, options = {}) {
332
334
  }
333
335
  // Check if file already exists
334
336
  if (fs.existsSync(filePath) && !options.force) {
335
- console.log(createErrorBox('File Exists', 'Use --force to overwrite'));
336
337
  spacer();
337
- log(`Path: ${filePath}`, 'dim');
338
+ console.log(createErrorBox('File Exists', 'Use --force to overwrite if you want to replace the existing file.'));
339
+ log(`Path: ${path.relative(process.cwd(), filePath)}`, 'dim');
338
340
  process.exit(1);
339
341
  }
340
342
  // Write file
@@ -366,10 +368,16 @@ export async function generate(type, name, options = {}) {
366
368
  }
367
369
  // Summary
368
370
  spacer();
369
- console.log(createSuccessBox('Generation Complete', generatedFiles));
371
+ console.log(createSuccessBox('Generation Complete', [
372
+ ...generatedFiles,
373
+ '',
374
+ chalk.dim(`Type: ${type}`),
375
+ chalk.dim(`Name: ${name}`)
376
+ ]));
370
377
  nextSteps([
371
378
  'Open the generated file and implement the TODOs',
372
379
  'Register the component in your module',
373
380
  'Build and test your changes',
374
381
  ]);
382
+ showFooter();
375
383
  }
@@ -1,5 +1,7 @@
1
1
  interface InitOptions {
2
- template: string;
2
+ template?: string;
3
+ description?: string;
4
+ author?: string;
3
5
  skipInstall?: boolean;
4
6
  }
5
7
  export declare function initCommand(projectName: string | undefined, options: InitOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAiDA,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA+MtF"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoDA,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA4NtF"}
@@ -5,7 +5,7 @@ import { dirname } from 'path';
5
5
  import chalk from 'chalk';
6
6
  import inquirer from 'inquirer';
7
7
  import { execSync } from 'child_process';
8
- import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand } from '../ui/branding.js';
8
+ import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand, showFooter } from '../ui/branding.js';
9
9
  // ES module equivalent of __dirname
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = dirname(__filename);
@@ -40,7 +40,7 @@ export async function initCommand(projectName, options) {
40
40
  try {
41
41
  // Show banner
42
42
  console.log(NITRO_BANNER_FULL);
43
- console.log(chalk.dim(' Create a new MCP server project\n'));
43
+ spacer();
44
44
  // Prompt for project name if not provided
45
45
  if (!projectName) {
46
46
  const answers = await inquirer.prompt([
@@ -77,7 +77,7 @@ export async function initCommand(projectName, options) {
77
77
  }
78
78
  fs.removeSync(targetDir);
79
79
  }
80
- // Prompt for template and details
80
+ // Prompt for template and details if not provided via flags
81
81
  const answers = await inquirer.prompt([
82
82
  {
83
83
  type: 'list',
@@ -85,53 +85,64 @@ export async function initCommand(projectName, options) {
85
85
  message: chalk.white('Choose a template:'),
86
86
  choices: [
87
87
  {
88
- name: `${brand.accent('Starter')} ${chalk.dim('Simple calculator for learning basics')}`,
88
+ name: `${brand.signal('Starter')} ${chalk.dim('Simple calculator for learning basics')}`,
89
89
  value: 'typescript-starter',
90
90
  },
91
91
  {
92
- name: `${brand.accent('Advanced')} ${chalk.dim('Pizza shop finder with maps & widgets')}`,
92
+ name: `${brand.signal('Advanced')} ${chalk.dim('Pizza shop finder with maps & widgets')}`,
93
93
  value: 'typescript-pizzaz',
94
94
  },
95
95
  {
96
- name: `${brand.accent('OAuth')} ${chalk.dim('Flight booking with OAuth 2.1 auth')}`,
96
+ name: `${brand.signal('OAuth')} ${chalk.dim('Flight booking with OAuth 2.1 auth')}`,
97
97
  value: 'typescript-oauth',
98
98
  },
99
99
  ],
100
100
  default: 'typescript-starter',
101
+ when: !options.template,
101
102
  },
102
103
  {
103
104
  type: 'input',
104
105
  name: 'description',
105
106
  message: chalk.white('Description:'),
106
107
  default: 'My awesome MCP server',
108
+ when: !options.description,
107
109
  },
108
110
  {
109
111
  type: 'input',
110
112
  name: 'author',
111
113
  message: chalk.white('Author:'),
112
114
  default: '',
115
+ when: !options.author,
113
116
  },
114
117
  ]);
118
+ // Merge flag values with prompt answers
119
+ const finalTemplate = options.template || answers.template || 'typescript-starter';
120
+ const finalDescription = options.description || answers.description || 'My awesome MCP server';
121
+ const finalAuthor = options.author || answers.author || '';
115
122
  spacer();
116
123
  spinner = new NitroSpinner('Creating project structure...').start();
117
124
  // Create project directory
118
125
  fs.mkdirSync(targetDir, { recursive: true });
119
126
  // Get template path - templates are in the CLI package root
120
- const templateDir = path.join(__dirname, '../../templates', answers.template);
127
+ const templateDir = path.join(__dirname, '../../templates', finalTemplate);
121
128
  // Copy template files
122
129
  if (fs.existsSync(templateDir)) {
123
130
  fs.copySync(templateDir, targetDir);
124
131
  }
125
132
  else {
126
- await createProjectFromScratch(targetDir, finalProjectName, answers);
133
+ await createProjectFromScratch(targetDir, finalProjectName, {
134
+ template: finalTemplate,
135
+ description: finalDescription,
136
+ author: finalAuthor,
137
+ });
127
138
  }
128
139
  // Update package.json
129
140
  const packageJsonPath = path.join(targetDir, 'package.json');
130
141
  if (fs.existsSync(packageJsonPath)) {
131
142
  const packageJson = fs.readJSONSync(packageJsonPath);
132
143
  packageJson.name = finalProjectName;
133
- packageJson.description = answers.description;
134
- packageJson.author = answers.author;
144
+ packageJson.description = finalDescription;
145
+ packageJson.author = finalAuthor;
135
146
  fs.writeJSONSync(packageJsonPath, packageJson, { spaces: 2 });
136
147
  }
137
148
  spinner.succeed('Project created');
@@ -150,7 +161,7 @@ export async function initCommand(projectName, options) {
150
161
  }
151
162
  }
152
163
  // Handle widgets
153
- if (!options.skipInstall && ['typescript-starter', 'typescript-pizzaz', 'typescript-oauth'].includes(answers.template)) {
164
+ if (!options.skipInstall && ['typescript-starter', 'typescript-pizzaz', 'typescript-oauth'].includes(finalTemplate)) {
154
165
  const fromNpm = isNitrostackFromNpm();
155
166
  const isLocalDev = isLocalDevelopment();
156
167
  if (isLocalDev && !fromNpm) {
@@ -192,7 +203,7 @@ export async function initCommand(projectName, options) {
192
203
  spacer();
193
204
  console.log(createSuccessBox('Project Ready', [
194
205
  `Name: ${finalProjectName}`,
195
- `Template: ${answers.template}`,
206
+ `Template: ${finalTemplate}`,
196
207
  `Path: ${targetDir}`,
197
208
  ]));
198
209
  // Next steps
@@ -202,19 +213,20 @@ export async function initCommand(projectName, options) {
202
213
  if (options.skipInstall) {
203
214
  steps.push('npm install');
204
215
  }
205
- if (answers.template === 'typescript-oauth') {
216
+ if (finalTemplate === 'typescript-oauth') {
206
217
  steps.push('cp .env.example .env # Configure OAuth');
207
218
  }
208
219
  steps.push('npm run dev');
209
220
  nextSteps(steps);
210
221
  // Template-specific tips
211
- if (answers.template === 'typescript-oauth') {
222
+ if (finalTemplate === 'typescript-oauth') {
212
223
  console.log(chalk.dim(' OAuth Setup: See OAUTH_SETUP.md for provider guides\n'));
213
224
  }
214
- else if (answers.template === 'typescript-pizzaz') {
225
+ else if (finalTemplate === 'typescript-pizzaz') {
215
226
  console.log(chalk.dim(' Mapbox (optional): Get free key from mapbox.com\n'));
216
227
  }
217
228
  console.log(chalk.dim(' Happy coding! 🎉\n'));
229
+ showFooter();
218
230
  }
219
231
  catch (error) {
220
232
  if (spinner) {
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAcA,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA+D3E"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAiBA,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAkE3E"}
@@ -1,7 +1,7 @@
1
1
  import { execSync } from 'child_process';
2
2
  import path from 'path';
3
3
  import fs from 'fs-extra';
4
- import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps } from '../ui/branding.js';
4
+ import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
5
5
  /**
6
6
  * Run npm install silently
7
7
  */
@@ -22,6 +22,7 @@ function hasPackageJson(directory) {
22
22
  * Main install command handler
23
23
  */
24
24
  export async function installCommand(options) {
25
+ console.log(NITRO_BANNER_FULL);
25
26
  console.log(createHeader('Install', 'Install project dependencies'));
26
27
  const projectRoot = process.cwd();
27
28
  const rootPackageJsonPath = path.join(projectRoot, 'package.json');
@@ -77,4 +78,5 @@ export async function installCommand(options) {
77
78
  'npm run build - Build for production',
78
79
  'npm run upgrade - Upgrade nitrostack',
79
80
  ]);
81
+ showFooter();
80
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAaA,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBA4EvD"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAgBA,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBA6EvD"}
@@ -2,8 +2,9 @@ import chalk from 'chalk';
2
2
  import { spawn } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
5
- import { createHeader, createInfoBox, createErrorBox, log, spacer, brand } from '../ui/branding.js';
5
+ import { createHeader, createBox, createInfoBox, createErrorBox, log, spacer, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
6
  export async function startCommand(options) {
7
+ console.log(NITRO_BANNER_FULL);
7
8
  console.log(createHeader('Start', 'Production mode'));
8
9
  const port = options.port || '3000';
9
10
  // Check if dist/index.js exists
@@ -53,9 +54,7 @@ export async function startCommand(options) {
53
54
  // Handle graceful shutdown
54
55
  const shutdown = () => {
55
56
  spacer();
56
- console.log(brand.accentBold('┌──────────────────────────────────────────────────────────────────┐'));
57
- console.log(brand.accentBold('│') + chalk.yellow.bold(' Shutting down... ') + brand.accentBold('│'));
58
- console.log(brand.accentBold('└──────────────────────────────────────────────────────────────────┘'));
57
+ console.log(createBox([chalk.yellow.bold('Shutting down...')]));
59
58
  serverProcess.kill('SIGTERM');
60
59
  setTimeout(() => {
61
60
  serverProcess.kill('SIGKILL');
@@ -65,6 +64,7 @@ export async function startCommand(options) {
65
64
  };
66
65
  process.on('SIGINT', shutdown);
67
66
  process.on('SIGTERM', shutdown);
67
+ showFooter();
68
68
  // Keep process alive
69
69
  await new Promise(() => { });
70
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAsBA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA2GD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8I3E"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAyBA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAwHD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqI3E"}