pixelstart 0.0.1 → 1.1.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.
@@ -1,77 +1,232 @@
1
1
  import { execSync } from 'child_process';
2
+ import inquirer from 'inquirer';
2
3
  import chalk from 'chalk';
3
4
  import { success, error, warn, info, section } from './ui.js';
5
+ const P = '#7C3AED';
6
+ const R = '#EF4444';
7
+ const G = '#22C55E';
8
+ const Y = '#EAB308';
9
+ const C = '#06B6D4';
10
+ function c(hex, text) {
11
+ return String(chalk.hex(hex)(text));
12
+ }
13
+ function cb(hex, text) {
14
+ return String(chalk.hex(hex).bold(text));
15
+ }
16
+ function getPlatform() {
17
+ return process.platform;
18
+ }
19
+ function isRoot() {
20
+ if (getPlatform() === 'win32')
21
+ return true;
22
+ try {
23
+ execSync('id -u', { stdio: 'pipe' });
24
+ return execSync('id -u', { encoding: 'utf-8', stdio: 'pipe' }).trim() === '0';
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ function run(cmd, opts = {}) {
31
+ try {
32
+ const full = opts.sudo && !isRoot() ? `sudo ${cmd}` : cmd;
33
+ execSync(full, { stdio: 'pipe', timeout: 120000 });
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ function parseVersion(output) {
41
+ return output.replace(/^[^\d]*/, '').split('+')[0].trim();
42
+ }
43
+ function compareVersions(a, b) {
44
+ const pa = a.split('.').map(Number);
45
+ const pb = b.split('.').map(Number);
46
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
47
+ const na = pa[i] || 0;
48
+ const nb = pb[i] || 0;
49
+ if (na > nb)
50
+ return 1;
51
+ if (na < nb)
52
+ return -1;
53
+ }
54
+ return 0;
55
+ }
56
+ // ── Install functions per platform ──────────────────────────────────────────
57
+ function getNodeInstallCmd(platform) {
58
+ switch (platform) {
59
+ case 'linux':
60
+ return [
61
+ 'curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -',
62
+ 'sudo apt-get install -y nodejs',
63
+ ];
64
+ case 'darwin':
65
+ return ['brew install node@22'];
66
+ case 'win32':
67
+ return ['winget install OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements'];
68
+ }
69
+ }
70
+ function getDockerInstallCmd(platform) {
71
+ switch (platform) {
72
+ case 'linux':
73
+ return [
74
+ 'curl -fsSL https://get.docker.com | sudo sh',
75
+ 'sudo usermod -aG docker $USER',
76
+ ];
77
+ case 'darwin':
78
+ return ['brew install --cask docker'];
79
+ case 'win32':
80
+ return ['winget install Docker.DockerDesktop --accept-package-agreements --accept-source-agreements'];
81
+ }
82
+ }
83
+ function getPnpmInstallCmd() {
84
+ return ['npm install -g pnpm@9'];
85
+ }
4
86
  const REQUIREMENTS = [
5
87
  {
6
88
  name: 'Node.js',
7
89
  command: 'node --version',
8
90
  minVersion: '22.0.0',
9
91
  required: true,
10
- installHint: 'Install Node.js 22+: https://nodejs.org/',
92
+ getInstallCmd: getNodeInstallCmd,
93
+ installHint: 'https://nodejs.org/',
11
94
  },
12
95
  {
13
96
  name: 'Docker',
14
97
  command: 'docker --version',
15
98
  required: true,
16
- installHint: 'Install Docker: https://docs.docker.com/get-docker/',
99
+ getInstallCmd: getDockerInstallCmd,
100
+ installHint: 'https://docs.docker.com/get-docker/',
17
101
  },
18
102
  {
19
103
  name: 'Docker Compose',
20
104
  command: 'docker compose version',
21
105
  required: true,
22
- installHint: 'Install Docker Compose: https://docs.docker.com/compose/install/',
106
+ getInstallCmd: () => [],
107
+ installHint: 'Included with Docker Desktop',
23
108
  },
24
109
  {
25
110
  name: 'pnpm',
26
111
  command: 'pnpm --version',
27
112
  minVersion: '9.0.0',
28
113
  required: false,
29
- installHint: 'Install pnpm: npm install -g pnpm@9',
114
+ getInstallCmd: getPnpmInstallCmd,
115
+ installHint: 'npm install -g pnpm@9',
30
116
  },
31
117
  ];
32
- function parseVersion(output) {
33
- return output.replace(/^[^\d]*/, '').split('+')[0].trim();
34
- }
35
- function compareVersions(a, b) {
36
- const pa = a.split('.').map(Number);
37
- const pb = b.split('.').map(Number);
38
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
39
- const na = pa[i] || 0;
40
- const nb = pb[i] || 0;
41
- if (na > nb)
42
- return 1;
43
- if (na < nb)
44
- return -1;
45
- }
46
- return 0;
47
- }
48
- function checkRequirement(req) {
118
+ function checkRequirement(req, platform) {
49
119
  try {
50
120
  const output = execSync(req.command, { encoding: 'utf-8', stdio: 'pipe' }).trim();
51
121
  const version = parseVersion(output);
52
122
  const meetsMin = req.minVersion ? compareVersions(version, req.minVersion) >= 0 : true;
53
- return { name: req.name, installed: true, version, meetsMin, required: req.required, minVersion: req.minVersion, installHint: req.installHint };
123
+ return {
124
+ name: req.name, installed: true, version, meetsMin,
125
+ required: req.required, minVersion: req.minVersion,
126
+ installCmds: req.getInstallCmd(platform), installHint: req.installHint,
127
+ };
54
128
  }
55
129
  catch {
56
- return { name: req.name, installed: false, required: req.required, installHint: req.installHint };
130
+ return {
131
+ name: req.name, installed: false,
132
+ required: req.required, minVersion: req.minVersion,
133
+ installCmds: req.getInstallCmd(platform), installHint: req.installHint,
134
+ };
57
135
  }
58
136
  }
137
+ async function autoInstall(name, cmds) {
138
+ console.log('');
139
+ info(c(C, `Installing ${name}...`));
140
+ console.log('');
141
+ for (const cmd of cmds) {
142
+ console.log(c(P, ` $ ${cmd}`));
143
+ const sudo = cmd.startsWith('sudo');
144
+ const ok = run(cmd, { sudo });
145
+ if (!ok) {
146
+ error(c(R, `Failed to install ${name}.`));
147
+ info('You may need to run this command manually or check your internet connection.');
148
+ return false;
149
+ }
150
+ }
151
+ console.log('');
152
+ success(c(G, `${name} installed successfully!`));
153
+ return true;
154
+ }
59
155
  export async function checkRequirements() {
60
156
  section('System Requirements');
61
- const results = REQUIREMENTS.map(checkRequirement);
157
+ const platform = getPlatform();
158
+ const platformNames = { linux: 'Linux', darwin: 'macOS', win32: 'Windows' };
159
+ info(c(C, `Detected platform: ${platformNames[platform]}`));
160
+ if (!isRoot() && platform !== 'win32') {
161
+ info(c(Y, 'Some operations may require sudo (password may be prompted)'));
162
+ }
163
+ console.log('');
164
+ const results = REQUIREMENTS.map(r => checkRequirement(r, platform));
62
165
  let allPassed = true;
63
166
  for (const r of results) {
64
167
  if (!r.installed) {
65
- error(`${chalk.bold(r.name)} not found`);
66
- info(r.installHint);
67
- if (r.required)
68
- allPassed = false;
168
+ if (r.installCmds.length > 0) {
169
+ const { wantInstall } = await inquirer.prompt([{
170
+ type: 'confirm',
171
+ name: 'wantInstall',
172
+ message: cb(R, ` ${r.name} is not installed. Install it now?`),
173
+ default: true,
174
+ }]);
175
+ if (wantInstall) {
176
+ const ok = await autoInstall(r.name, r.installCmds);
177
+ if (!ok) {
178
+ if (r.required)
179
+ allPassed = false;
180
+ }
181
+ else {
182
+ // Re-check after install
183
+ const recheck = checkRequirement(REQUIREMENTS.find(x => x.name === r.name), platform);
184
+ if (recheck.installed && recheck.meetsMin !== false) {
185
+ success(cb(G, `${r.name} ${recheck.version || ''}`));
186
+ }
187
+ else if (r.required) {
188
+ allPassed = false;
189
+ }
190
+ }
191
+ }
192
+ else {
193
+ error(`${chalk.bold(r.name)} is required but not installed.`);
194
+ info(r.installHint);
195
+ if (r.required)
196
+ allPassed = false;
197
+ }
198
+ }
199
+ else {
200
+ error(`${chalk.bold(r.name)} not found`);
201
+ info(r.installHint);
202
+ if (r.required)
203
+ allPassed = false;
204
+ }
69
205
  }
70
206
  else if (r.meetsMin === false) {
71
- warn(`${chalk.bold(r.name)} ${r.version} requires ${r.minVersion}+`);
72
- info(r.installHint);
73
- if (r.required)
74
- allPassed = false;
207
+ if (r.installCmds.length > 0) {
208
+ const { wantUpgrade } = await inquirer.prompt([{
209
+ type: 'confirm',
210
+ name: 'wantUpgrade',
211
+ message: cb(Y, ` ${r.name} ${r.version} is too old (need ${r.minVersion}+). Upgrade?`),
212
+ default: true,
213
+ }]);
214
+ if (wantUpgrade) {
215
+ const ok = await autoInstall(r.name, r.installCmds);
216
+ if (!ok && r.required)
217
+ allPassed = false;
218
+ }
219
+ else {
220
+ if (r.required)
221
+ allPassed = false;
222
+ }
223
+ }
224
+ else {
225
+ warn(`${chalk.bold(r.name)} ${r.version} — requires ${r.minVersion}+`);
226
+ info(r.installHint);
227
+ if (r.required)
228
+ allPassed = false;
229
+ }
75
230
  }
76
231
  else {
77
232
  success(`${chalk.bold(r.name)} ${chalk.gray(r.version || '')}`);
@@ -79,7 +234,8 @@ export async function checkRequirements() {
79
234
  }
80
235
  console.log('');
81
236
  if (!allPassed) {
82
- error('Missing required dependencies. Please install them and try again.');
237
+ error('Some required dependencies could not be installed.');
238
+ info('Please install them manually and try again.');
83
239
  return false;
84
240
  }
85
241
  return true;
@@ -4,4 +4,5 @@ export declare function error(msg: string): void;
4
4
  export declare function warn(msg: string): void;
5
5
  export declare function info(msg: string): void;
6
6
  export declare function divider(): void;
7
+ export declare function dividerLine(): string;
7
8
  export declare function section(title: string): void;
@@ -1,9 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  export function banner() {
3
- console.log(chalk.hex('#7C3AED')('┌─────────────────────────────────────────────┐'));
4
- console.log(chalk.hex('#7C3AED')('│') + chalk.white.bold(' PixelStack Lite Installer ') + chalk.hex('#7C3AED')('│'));
5
- console.log(chalk.hex('#7C3AED')('│') + chalk.gray(' v0.1.0 — Single Container ') + chalk.hex('#7C3AED')('│'));
6
- console.log(chalk.hex('#7C3AED')('└─────────────────────────────────────────────┘'));
3
+ console.log(chalk.hex('#7C3AED').bold(' PIXELSTACK LITE'));
4
+ console.log(chalk.gray(' ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
7
5
  console.log('');
8
6
  }
9
7
  export function success(msg) {
@@ -21,6 +19,9 @@ export function info(msg) {
21
19
  export function divider() {
22
20
  console.log(chalk.gray(' ───────────────────────────────────────────'));
23
21
  }
22
+ export function dividerLine() {
23
+ return String(chalk.gray(' ───────────────────────────────────────────'));
24
+ }
24
25
  export function section(title) {
25
26
  console.log('');
26
27
  console.log(chalk.hex('#7C3AED').bold(` ▸ ${title}`));
package/package.json CHANGED
@@ -1,18 +1,23 @@
1
1
  {
2
2
  "name": "pixelstart",
3
- "version": "0.0.1",
4
- "description": "PixelStack CLI — scaffold and manage PixelStack projects",
3
+ "version": "1.1.0",
4
+ "description": "PixelStack Lite CLI — scaffold, license, and deploy PixelStack projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "pixelstart": "dist/index.js"
9
9
  },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
10
14
  "scripts": {
11
15
  "dev": "tsx src/index.ts",
12
16
  "build": "tsc && chmod +x dist/index.js",
13
17
  "start": "node dist/index.js",
14
18
  "type-check": "tsc --noEmit",
15
- "prepublishOnly": "npm run build"
19
+ "prepublishOnly": "npm run build",
20
+ "pack": "npm run build && npm pack"
16
21
  },
17
22
  "engines": {
18
23
  "node": ">=22.0.0"
@@ -22,10 +27,17 @@
22
27
  "cli",
23
28
  "dxp",
24
29
  "scaffold",
25
- "cms"
30
+ "cms",
31
+ "docker",
32
+ "license"
26
33
  ],
27
- "author": "",
28
- "license": "MIT",
34
+ "author": "soroosh-assa",
35
+ "license": "UNLICENSED",
36
+ "private": false,
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/soroosh-assa/pixel-cicd"
40
+ },
29
41
  "devDependencies": {
30
42
  "@types/inquirer": "^9.0.10",
31
43
  "@types/node": "^22.0.0",
package/src/index.ts DELETED
@@ -1,95 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import chalk from 'chalk';
4
- import { showLogo } from './modules/logo.js';
5
- import { banner, section, success, error, info, divider } from './modules/ui.js';
6
- import { checkRequirements } from './modules/requirements.js';
7
- import { showNDA } from './modules/nda.js';
8
- import { showLicense } from './modules/license.js';
9
- import { initProject } from './modules/init.js';
10
-
11
- const VERSION = '0.1.0';
12
-
13
- async function main(): Promise<void> {
14
- const args = process.argv.slice(2);
15
- const command = args[0];
16
-
17
- // --help / --version work without logo
18
- if (command === '--help' || command === '-h') {
19
- printHelp();
20
- return;
21
- }
22
- if (command === '--version' || command === '-v') {
23
- console.log(`pixelstart v${VERSION}`);
24
- return;
25
- }
26
-
27
- // Interactive flow — show logo + banner
28
- showLogo();
29
- banner();
30
-
31
- // Step 1: Check requirements
32
- const reqsOk = await checkRequirements();
33
- if (!reqsOk) {
34
- process.exit(1);
35
- }
36
-
37
- // Step 2: NDA
38
- const ndaOk = await showNDA();
39
- if (!ndaOk) {
40
- process.exit(0);
41
- }
42
-
43
- // Step 3: License
44
- const tier = await showLicense();
45
- if (!tier) {
46
- process.exit(0);
47
- }
48
-
49
- // Step 4: Init project (if `init` command or no command)
50
- if (!command || command === 'init') {
51
- await initProject();
52
- } else if (command === 'up' || command === 'down' || command === 'status' || command === 'logs') {
53
- section('Service Management');
54
- info(`Command "${command}" will be available after project initialization.`);
55
- console.log('');
56
- } else {
57
- error(`Unknown command: ${command}`);
58
- info('Run `pixelstart --help` for usage.');
59
- process.exit(1);
60
- }
61
- }
62
-
63
- function printHelp(): void {
64
- console.log(`
65
- ${chalk.hex('#7C3AED').bold('pixelstart')} v${VERSION}
66
-
67
- ${chalk.gray('Usage:')}
68
- pixelstart [command]
69
-
70
- ${chalk.gray('Commands:')}
71
- init Scaffold a new PixelStack Lite project
72
- up Start all services
73
- down Stop all services
74
- status Show service status
75
- logs Stream service logs
76
-
77
- ${chalk.gray('Options:')}
78
- -h, --help Show help
79
- -v, --version Show version
80
-
81
- ${chalk.gray('Examples:')}
82
- pixelstart Launch interactive installer
83
- pixelstart init Create a new project
84
- pixelstart up Start services
85
- `);
86
- }
87
-
88
- // Catch unhandled errors gracefully
89
- process.on('unhandledRejection', (err) => {
90
- console.error('');
91
- error(`Unexpected error: ${err}`);
92
- process.exit(1);
93
- });
94
-
95
- main();
@@ -1,100 +0,0 @@
1
- import { execSync } from 'child_process';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import inquirer from 'inquirer';
5
- import chalk from 'chalk';
6
- import { section, success, error, warn, info } from './ui.js';
7
-
8
- interface ProjectConfig {
9
- name: string;
10
- directory: string;
11
- port: number;
12
- dbPassword: string;
13
- }
14
-
15
- export async function initProject(): Promise<void> {
16
- section('Project Setup');
17
-
18
- const config = await inquirer.prompt<ProjectConfig>([
19
- {
20
- type: 'input',
21
- name: 'name',
22
- message: chalk.white('Project name:'),
23
- default: 'my-pixelstack',
24
- validate: (input: string) => {
25
- if (!/^[a-z0-9-_]+$/.test(input)) return 'Use lowercase, numbers, hyphens only';
26
- return true;
27
- },
28
- },
29
- {
30
- type: 'input',
31
- name: 'directory',
32
- message: chalk.white('Install directory:'),
33
- default: (answers: { name: string }) => `./${answers.name}`,
34
- },
35
- {
36
- type: 'number',
37
- name: 'port',
38
- message: chalk.white('Base port (HTTP):'),
39
- default: 80,
40
- },
41
- {
42
- type: 'password',
43
- name: 'dbPassword',
44
- message: chalk.white('Database password:'),
45
- mask: '*',
46
- validate: (input: string) => input.length >= 8 || 'Min 8 characters',
47
- },
48
- ]);
49
-
50
- console.log('');
51
- info(`Creating project in ${chalk.bold(config.directory)}`);
52
-
53
- try {
54
- fs.mkdirSync(config.directory, { recursive: true });
55
-
56
- const envContent = `# PixelStack Lite Configuration
57
- NODE_ENV=production
58
- POSTGRES_USER=pixelstack
59
- POSTGRES_PASSWORD=${config.dbPassword}
60
- POSTGRES_DB=pixelstack_db
61
- STRAPI_DB_NAME=strapi_core_db
62
- REDIS_HOST=localhost
63
- REDIS_PORT=6379
64
- MINIO_ROOT_USER=minioadmin
65
- MINIO_ROOT_PASSWORD=minioadmin
66
- MINIO_BUCKET=strapi-uploads
67
- MINIO_ENDPOINT=http://localhost:9000
68
- MINIO_PUBLIC_URL=http://localhost/media
69
- API_PORT=3000
70
- STRAPI_PORT=1337
71
- WEB_PORT=3001
72
- ARTEMIS_PORT=3100
73
- ADMIN_PORT=4028
74
- ARTEMIS_FE_PORT=4029
75
- BASE_PORT=${config.port}
76
- `;
77
-
78
- fs.writeFileSync(path.join(config.directory, '.env'), envContent);
79
- success('Created .env');
80
-
81
- fs.writeFileSync(path.join(config.directory, '.gitignore'), `node_modules/
82
- .env
83
- *.log
84
- data/
85
- `);
86
- success('Created .gitignore');
87
-
88
- console.log('');
89
- success(`Project "${chalk.bold(config.name)}" created!`);
90
- console.log('');
91
- info(`Next steps:`);
92
- console.log(chalk.white(` cd ${config.directory}`));
93
- console.log(chalk.white(' pixelstart up'));
94
- console.log('');
95
-
96
- } catch (err) {
97
- error(`Failed to create project: ${err}`);
98
- process.exit(1);
99
- }
100
- }
@@ -1,84 +0,0 @@
1
- import inquirer from 'inquirer';
2
- import chalk from 'chalk';
3
- import { section, divider, success, info } from './ui.js';
4
-
5
- const LICENSE_TIERS = [
6
- {
7
- name: 'Community — Free',
8
- value: 'community',
9
- description: 'Basic features, single project, community support',
10
- },
11
- {
12
- name: 'Pro — $29/mo',
13
- value: 'pro',
14
- description: 'Unlimited projects, priority support, custom themes',
15
- },
16
- {
17
- name: 'Enterprise — Custom',
18
- value: 'enterprise',
19
- description: 'Self-hosted, SLA, dedicated support, custom integrations',
20
- },
21
- ];
22
-
23
- export async function showLicense(): Promise<string | null> {
24
- section('License');
25
-
26
- console.log(chalk.white(' Choose your PixelStack Lite license tier:'));
27
- console.log('');
28
-
29
- for (const tier of LICENSE_TIERS) {
30
- const isActive = tier.value === 'community';
31
- const marker = isActive ? chalk.green('●') : chalk.gray('○');
32
- console.log(` ${marker} ${chalk.bold(tier.name)}`);
33
- console.log(` ${chalk.gray(tier.description)}`);
34
- console.log('');
35
- }
36
-
37
- const { tier } = await inquirer.prompt([
38
- {
39
- type: 'list',
40
- name: 'tier',
41
- message: chalk.white('Select a license tier:'),
42
- choices: LICENSE_TIERS,
43
- default: 'community',
44
- },
45
- ]);
46
-
47
- console.log('');
48
-
49
- if (tier === 'community') {
50
- success('Community license activated');
51
- info('No key required. Run `pixelstart init` to get started.');
52
- } else if (tier === 'pro') {
53
- const { key } = await inquirer.prompt([
54
- {
55
- type: 'input',
56
- name: 'key',
57
- message: chalk.white('Enter your Pro license key:'),
58
- validate: (input: string) => input.length > 0 || 'License key is required',
59
- },
60
- ]);
61
-
62
- if (key) {
63
- success('Pro license activated');
64
- info('Your license key has been validated and saved.');
65
- }
66
- } else {
67
- info('Enterprise inquiries: contact@pixelstack.dev');
68
- }
69
-
70
- console.log('');
71
- return tier;
72
- }
73
-
74
- export function getLicenseInfo(): { tier: string; features: string[] } {
75
- return {
76
- tier: 'community',
77
- features: [
78
- 'PixelStack Lite (single container)',
79
- 'Up to 1 project',
80
- 'Community support via GitHub Issues',
81
- 'Standard themes and components',
82
- ],
83
- };
84
- }
@@ -1,21 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- export const logo = `
4
- ${chalk.hex('#7C3AED').bold(' ╔══════════════════════════════════╗')}
5
- ${chalk.hex('#7C3AED').bold(' ║ ║')}
6
- ${chalk.hex('#7C3AED').bold(' ║ ██████╗ ██╗██╗ ██╗███████╗██╗ ██████╗ ██████╗ ')}
7
- ${chalk.hex('#7C3AED').bold(' ║ ██╔══██╗██║╚██╗██╔╝██╔════╝██║ ██╔═══██╗██╔════╝ ')}
8
- ${chalk.hex('#7C3AED').bold(' ║ ██████╔╝██║ ╚███╔╝ █████╗ ██║ ██║ ██║██║ ███╗')}
9
- ${chalk.hex('#7C3AED').bold(' ║ ██╔═══╝ ██║ ██╔██╗ ██╔══╝ ██║ ██║ ██║██║ ██║')}
10
- ${chalk.hex('#7C3AED').bold(' ║ ██║ ██║██╔╝ ██╗███████╗███████╗╚██████╔╝╚██████╔╝')}
11
- ${chalk.hex('#7C3AED').bold(' ║ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ')}
12
- ${chalk.hex('#7C3AED').bold(' ║ ║')}
13
- ${chalk.hex('#7C3AED').bold(' ╚══════════════════════════════════╝')}
14
- ${chalk.gray(' Digital Experience Platform')}
15
- `;
16
-
17
- export function showLogo(): void {
18
- console.clear();
19
- console.log(logo);
20
- console.log('');
21
- }