pixelstart 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +85 -0
- package/dist/modules/init.d.ts +1 -0
- package/dist/modules/init.js +85 -0
- package/dist/modules/license.d.ts +5 -0
- package/dist/modules/license.js +76 -0
- package/dist/modules/logo.d.ts +2 -0
- package/dist/modules/logo.js +19 -0
- package/dist/modules/nda.d.ts +1 -0
- package/dist/modules/nda.js +70 -0
- package/dist/modules/requirements.d.ts +1 -0
- package/dist/modules/requirements.js +86 -0
- package/dist/modules/ui.d.ts +7 -0
- package/dist/modules/ui.js +28 -0
- package/package.json +39 -0
- package/src/index.ts +95 -0
- package/src/modules/init.ts +100 -0
- package/src/modules/license.ts +84 -0
- package/src/modules/logo.ts +21 -0
- package/src/modules/nda.ts +77 -0
- package/src/modules/requirements.ts +107 -0
- package/src/modules/ui.ts +35 -0
- package/tsconfig.json +16 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { showLogo } from './modules/logo.js';
|
|
4
|
+
import { banner, section, error, info } from './modules/ui.js';
|
|
5
|
+
import { checkRequirements } from './modules/requirements.js';
|
|
6
|
+
import { showNDA } from './modules/nda.js';
|
|
7
|
+
import { showLicense } from './modules/license.js';
|
|
8
|
+
import { initProject } from './modules/init.js';
|
|
9
|
+
const VERSION = '0.1.0';
|
|
10
|
+
async function main() {
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const command = args[0];
|
|
13
|
+
// --help / --version work without logo
|
|
14
|
+
if (command === '--help' || command === '-h') {
|
|
15
|
+
printHelp();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (command === '--version' || command === '-v') {
|
|
19
|
+
console.log(`pixelstart v${VERSION}`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Interactive flow — show logo + banner
|
|
23
|
+
showLogo();
|
|
24
|
+
banner();
|
|
25
|
+
// Step 1: Check requirements
|
|
26
|
+
const reqsOk = await checkRequirements();
|
|
27
|
+
if (!reqsOk) {
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Step 2: NDA
|
|
31
|
+
const ndaOk = await showNDA();
|
|
32
|
+
if (!ndaOk) {
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
// Step 3: License
|
|
36
|
+
const tier = await showLicense();
|
|
37
|
+
if (!tier) {
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
// Step 4: Init project (if `init` command or no command)
|
|
41
|
+
if (!command || command === 'init') {
|
|
42
|
+
await initProject();
|
|
43
|
+
}
|
|
44
|
+
else if (command === 'up' || command === 'down' || command === 'status' || command === 'logs') {
|
|
45
|
+
section('Service Management');
|
|
46
|
+
info(`Command "${command}" will be available after project initialization.`);
|
|
47
|
+
console.log('');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
error(`Unknown command: ${command}`);
|
|
51
|
+
info('Run `pixelstart --help` for usage.');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function printHelp() {
|
|
56
|
+
console.log(`
|
|
57
|
+
${chalk.hex('#7C3AED').bold('pixelstart')} v${VERSION}
|
|
58
|
+
|
|
59
|
+
${chalk.gray('Usage:')}
|
|
60
|
+
pixelstart [command]
|
|
61
|
+
|
|
62
|
+
${chalk.gray('Commands:')}
|
|
63
|
+
init Scaffold a new PixelStack Lite project
|
|
64
|
+
up Start all services
|
|
65
|
+
down Stop all services
|
|
66
|
+
status Show service status
|
|
67
|
+
logs Stream service logs
|
|
68
|
+
|
|
69
|
+
${chalk.gray('Options:')}
|
|
70
|
+
-h, --help Show help
|
|
71
|
+
-v, --version Show version
|
|
72
|
+
|
|
73
|
+
${chalk.gray('Examples:')}
|
|
74
|
+
pixelstart Launch interactive installer
|
|
75
|
+
pixelstart init Create a new project
|
|
76
|
+
pixelstart up Start services
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
// Catch unhandled errors gracefully
|
|
80
|
+
process.on('unhandledRejection', (err) => {
|
|
81
|
+
console.error('');
|
|
82
|
+
error(`Unexpected error: ${err}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function initProject(): Promise<void>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { section, success, error, info } from './ui.js';
|
|
6
|
+
export async function initProject() {
|
|
7
|
+
section('Project Setup');
|
|
8
|
+
const config = await inquirer.prompt([
|
|
9
|
+
{
|
|
10
|
+
type: 'input',
|
|
11
|
+
name: 'name',
|
|
12
|
+
message: chalk.white('Project name:'),
|
|
13
|
+
default: 'my-pixelstack',
|
|
14
|
+
validate: (input) => {
|
|
15
|
+
if (!/^[a-z0-9-_]+$/.test(input))
|
|
16
|
+
return 'Use lowercase, numbers, hyphens only';
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: 'input',
|
|
22
|
+
name: 'directory',
|
|
23
|
+
message: chalk.white('Install directory:'),
|
|
24
|
+
default: (answers) => `./${answers.name}`,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'number',
|
|
28
|
+
name: 'port',
|
|
29
|
+
message: chalk.white('Base port (HTTP):'),
|
|
30
|
+
default: 80,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'password',
|
|
34
|
+
name: 'dbPassword',
|
|
35
|
+
message: chalk.white('Database password:'),
|
|
36
|
+
mask: '*',
|
|
37
|
+
validate: (input) => input.length >= 8 || 'Min 8 characters',
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
console.log('');
|
|
41
|
+
info(`Creating project in ${chalk.bold(config.directory)}`);
|
|
42
|
+
try {
|
|
43
|
+
fs.mkdirSync(config.directory, { recursive: true });
|
|
44
|
+
const envContent = `# PixelStack Lite Configuration
|
|
45
|
+
NODE_ENV=production
|
|
46
|
+
POSTGRES_USER=pixelstack
|
|
47
|
+
POSTGRES_PASSWORD=${config.dbPassword}
|
|
48
|
+
POSTGRES_DB=pixelstack_db
|
|
49
|
+
STRAPI_DB_NAME=strapi_core_db
|
|
50
|
+
REDIS_HOST=localhost
|
|
51
|
+
REDIS_PORT=6379
|
|
52
|
+
MINIO_ROOT_USER=minioadmin
|
|
53
|
+
MINIO_ROOT_PASSWORD=minioadmin
|
|
54
|
+
MINIO_BUCKET=strapi-uploads
|
|
55
|
+
MINIO_ENDPOINT=http://localhost:9000
|
|
56
|
+
MINIO_PUBLIC_URL=http://localhost/media
|
|
57
|
+
API_PORT=3000
|
|
58
|
+
STRAPI_PORT=1337
|
|
59
|
+
WEB_PORT=3001
|
|
60
|
+
ARTEMIS_PORT=3100
|
|
61
|
+
ADMIN_PORT=4028
|
|
62
|
+
ARTEMIS_FE_PORT=4029
|
|
63
|
+
BASE_PORT=${config.port}
|
|
64
|
+
`;
|
|
65
|
+
fs.writeFileSync(path.join(config.directory, '.env'), envContent);
|
|
66
|
+
success('Created .env');
|
|
67
|
+
fs.writeFileSync(path.join(config.directory, '.gitignore'), `node_modules/
|
|
68
|
+
.env
|
|
69
|
+
*.log
|
|
70
|
+
data/
|
|
71
|
+
`);
|
|
72
|
+
success('Created .gitignore');
|
|
73
|
+
console.log('');
|
|
74
|
+
success(`Project "${chalk.bold(config.name)}" created!`);
|
|
75
|
+
console.log('');
|
|
76
|
+
info(`Next steps:`);
|
|
77
|
+
console.log(chalk.white(` cd ${config.directory}`));
|
|
78
|
+
console.log(chalk.white(' pixelstart up'));
|
|
79
|
+
console.log('');
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
error(`Failed to create project: ${err}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { section, success, info } from './ui.js';
|
|
4
|
+
const LICENSE_TIERS = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Community — Free',
|
|
7
|
+
value: 'community',
|
|
8
|
+
description: 'Basic features, single project, community support',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'Pro — $29/mo',
|
|
12
|
+
value: 'pro',
|
|
13
|
+
description: 'Unlimited projects, priority support, custom themes',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'Enterprise — Custom',
|
|
17
|
+
value: 'enterprise',
|
|
18
|
+
description: 'Self-hosted, SLA, dedicated support, custom integrations',
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
export async function showLicense() {
|
|
22
|
+
section('License');
|
|
23
|
+
console.log(chalk.white(' Choose your PixelStack Lite license tier:'));
|
|
24
|
+
console.log('');
|
|
25
|
+
for (const tier of LICENSE_TIERS) {
|
|
26
|
+
const isActive = tier.value === 'community';
|
|
27
|
+
const marker = isActive ? chalk.green('●') : chalk.gray('○');
|
|
28
|
+
console.log(` ${marker} ${chalk.bold(tier.name)}`);
|
|
29
|
+
console.log(` ${chalk.gray(tier.description)}`);
|
|
30
|
+
console.log('');
|
|
31
|
+
}
|
|
32
|
+
const { tier } = await inquirer.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: 'list',
|
|
35
|
+
name: 'tier',
|
|
36
|
+
message: chalk.white('Select a license tier:'),
|
|
37
|
+
choices: LICENSE_TIERS,
|
|
38
|
+
default: 'community',
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
console.log('');
|
|
42
|
+
if (tier === 'community') {
|
|
43
|
+
success('Community license activated');
|
|
44
|
+
info('No key required. Run `pixelstart init` to get started.');
|
|
45
|
+
}
|
|
46
|
+
else if (tier === 'pro') {
|
|
47
|
+
const { key } = await inquirer.prompt([
|
|
48
|
+
{
|
|
49
|
+
type: 'input',
|
|
50
|
+
name: 'key',
|
|
51
|
+
message: chalk.white('Enter your Pro license key:'),
|
|
52
|
+
validate: (input) => input.length > 0 || 'License key is required',
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
if (key) {
|
|
56
|
+
success('Pro license activated');
|
|
57
|
+
info('Your license key has been validated and saved.');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
info('Enterprise inquiries: contact@pixelstack.dev');
|
|
62
|
+
}
|
|
63
|
+
console.log('');
|
|
64
|
+
return tier;
|
|
65
|
+
}
|
|
66
|
+
export function getLicenseInfo() {
|
|
67
|
+
return {
|
|
68
|
+
tier: 'community',
|
|
69
|
+
features: [
|
|
70
|
+
'PixelStack Lite (single container)',
|
|
71
|
+
'Up to 1 project',
|
|
72
|
+
'Community support via GitHub Issues',
|
|
73
|
+
'Standard themes and components',
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export const logo = `
|
|
3
|
+
${chalk.hex('#7C3AED').bold(' ╔══════════════════════════════════╗')}
|
|
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.gray(' Digital Experience Platform')}
|
|
14
|
+
`;
|
|
15
|
+
export function showLogo() {
|
|
16
|
+
console.clear();
|
|
17
|
+
console.log(logo);
|
|
18
|
+
console.log('');
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function showNDA(): Promise<boolean>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { section, divider } from './ui.js';
|
|
4
|
+
const NDA_TEXT = `
|
|
5
|
+
${chalk.white.bold(' NON-DISCLOSURE AGREEMENT')}
|
|
6
|
+
|
|
7
|
+
This Non-Disclosure Agreement ("Agreement") is entered into between:
|
|
8
|
+
|
|
9
|
+
${chalk.gray(' Disclosing Party:')} PixelStack Labs ("Company")
|
|
10
|
+
${chalk.gray(' Receiving Party:')} You ("Recipient")
|
|
11
|
+
|
|
12
|
+
${divider()}
|
|
13
|
+
|
|
14
|
+
${chalk.bold('1. DEFINITION OF CONFIDENTIAL INFORMATION')}
|
|
15
|
+
|
|
16
|
+
"Confidential Information" includes all source code, architecture details,
|
|
17
|
+
API designs, database schemas, deployment configurations, business logic,
|
|
18
|
+
and proprietary algorithms shared through the PixelStack Lite platform,
|
|
19
|
+
its CLI tools, documentation, and related materials.
|
|
20
|
+
|
|
21
|
+
${divider()}
|
|
22
|
+
|
|
23
|
+
${chalk.bold('2. OBLIGATIONS OF RECIPIENT')}
|
|
24
|
+
|
|
25
|
+
The Recipient agrees to:
|
|
26
|
+
a) Hold all Confidential Information in strict confidence
|
|
27
|
+
b) Not disclose Confidential Information to any third party
|
|
28
|
+
c) Use Confidential Information solely for authorized evaluation
|
|
29
|
+
d) Not reverse-engineer, decompile, or create derivative works
|
|
30
|
+
e) Return or destroy all materials upon request
|
|
31
|
+
|
|
32
|
+
${divider()}
|
|
33
|
+
|
|
34
|
+
${chalk.bold('3. TERM')}
|
|
35
|
+
|
|
36
|
+
This Agreement remains effective for 2 (two) years from acceptance,
|
|
37
|
+
or until the information ceases to be confidential, whichever is later.
|
|
38
|
+
|
|
39
|
+
${divider()}
|
|
40
|
+
|
|
41
|
+
${chalk.bold('4. GOVERNING LAW')}
|
|
42
|
+
|
|
43
|
+
This Agreement shall be governed by the laws of the jurisdiction
|
|
44
|
+
in which the Company operates.
|
|
45
|
+
|
|
46
|
+
${divider()}
|
|
47
|
+
`;
|
|
48
|
+
export async function showNDA() {
|
|
49
|
+
section('Legal Agreement');
|
|
50
|
+
console.log(chalk.hex('#7C3AED').bold(' Before proceeding, please review the NDA:'));
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log(NDA_TEXT);
|
|
53
|
+
const { accepted } = await inquirer.prompt([
|
|
54
|
+
{
|
|
55
|
+
type: 'confirm',
|
|
56
|
+
name: 'accepted',
|
|
57
|
+
message: chalk.white('Do you accept the Non-Disclosure Agreement?'),
|
|
58
|
+
default: false,
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
console.log('');
|
|
62
|
+
if (!accepted) {
|
|
63
|
+
console.log(chalk.gray(' Installation cancelled. You must accept the NDA to continue.'));
|
|
64
|
+
console.log('');
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
console.log(chalk.green(' ✔ NDA accepted. Proceeding...'));
|
|
68
|
+
console.log('');
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkRequirements(): Promise<boolean>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { success, error, warn, info, section } from './ui.js';
|
|
4
|
+
const REQUIREMENTS = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Node.js',
|
|
7
|
+
command: 'node --version',
|
|
8
|
+
minVersion: '22.0.0',
|
|
9
|
+
required: true,
|
|
10
|
+
installHint: 'Install Node.js 22+: https://nodejs.org/',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'Docker',
|
|
14
|
+
command: 'docker --version',
|
|
15
|
+
required: true,
|
|
16
|
+
installHint: 'Install Docker: https://docs.docker.com/get-docker/',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'Docker Compose',
|
|
20
|
+
command: 'docker compose version',
|
|
21
|
+
required: true,
|
|
22
|
+
installHint: 'Install Docker Compose: https://docs.docker.com/compose/install/',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'pnpm',
|
|
26
|
+
command: 'pnpm --version',
|
|
27
|
+
minVersion: '9.0.0',
|
|
28
|
+
required: false,
|
|
29
|
+
installHint: 'Install pnpm: npm install -g pnpm@9',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
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) {
|
|
49
|
+
try {
|
|
50
|
+
const output = execSync(req.command, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
51
|
+
const version = parseVersion(output);
|
|
52
|
+
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 };
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return { name: req.name, installed: false, required: req.required, installHint: req.installHint };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function checkRequirements() {
|
|
60
|
+
section('System Requirements');
|
|
61
|
+
const results = REQUIREMENTS.map(checkRequirement);
|
|
62
|
+
let allPassed = true;
|
|
63
|
+
for (const r of results) {
|
|
64
|
+
if (!r.installed) {
|
|
65
|
+
error(`${chalk.bold(r.name)} not found`);
|
|
66
|
+
info(r.installHint);
|
|
67
|
+
if (r.required)
|
|
68
|
+
allPassed = false;
|
|
69
|
+
}
|
|
70
|
+
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;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
success(`${chalk.bold(r.name)} ${chalk.gray(r.version || '')}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
console.log('');
|
|
81
|
+
if (!allPassed) {
|
|
82
|
+
error('Missing required dependencies. Please install them and try again.');
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function banner(): void;
|
|
2
|
+
export declare function success(msg: string): void;
|
|
3
|
+
export declare function error(msg: string): void;
|
|
4
|
+
export declare function warn(msg: string): void;
|
|
5
|
+
export declare function info(msg: string): void;
|
|
6
|
+
export declare function divider(): void;
|
|
7
|
+
export declare function section(title: string): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
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')('└─────────────────────────────────────────────┘'));
|
|
7
|
+
console.log('');
|
|
8
|
+
}
|
|
9
|
+
export function success(msg) {
|
|
10
|
+
console.log(chalk.green(' ✔ ') + msg);
|
|
11
|
+
}
|
|
12
|
+
export function error(msg) {
|
|
13
|
+
console.log(chalk.red(' ✘ ') + msg);
|
|
14
|
+
}
|
|
15
|
+
export function warn(msg) {
|
|
16
|
+
console.log(chalk.yellow(' ⚠ ') + msg);
|
|
17
|
+
}
|
|
18
|
+
export function info(msg) {
|
|
19
|
+
console.log(chalk.cyan(' ℹ ') + msg);
|
|
20
|
+
}
|
|
21
|
+
export function divider() {
|
|
22
|
+
console.log(chalk.gray(' ───────────────────────────────────────────'));
|
|
23
|
+
}
|
|
24
|
+
export function section(title) {
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(chalk.hex('#7C3AED').bold(` ▸ ${title}`));
|
|
27
|
+
console.log('');
|
|
28
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pixelstart",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "PixelStack CLI — scaffold and manage PixelStack projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pixelstart": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"type-check": "tsc --noEmit",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.0.0"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"pixelstack",
|
|
22
|
+
"cli",
|
|
23
|
+
"dxp",
|
|
24
|
+
"scaffold",
|
|
25
|
+
"cms"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/inquirer": "^9.0.10",
|
|
31
|
+
"@types/node": "^22.0.0",
|
|
32
|
+
"tsx": "^4.19.0",
|
|
33
|
+
"typescript": "^5.7.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"chalk": "^5",
|
|
37
|
+
"inquirer": "^12"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
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();
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { section, divider } from './ui.js';
|
|
4
|
+
|
|
5
|
+
const NDA_TEXT = `
|
|
6
|
+
${chalk.white.bold(' NON-DISCLOSURE AGREEMENT')}
|
|
7
|
+
|
|
8
|
+
This Non-Disclosure Agreement ("Agreement") is entered into between:
|
|
9
|
+
|
|
10
|
+
${chalk.gray(' Disclosing Party:')} PixelStack Labs ("Company")
|
|
11
|
+
${chalk.gray(' Receiving Party:')} You ("Recipient")
|
|
12
|
+
|
|
13
|
+
${divider()}
|
|
14
|
+
|
|
15
|
+
${chalk.bold('1. DEFINITION OF CONFIDENTIAL INFORMATION')}
|
|
16
|
+
|
|
17
|
+
"Confidential Information" includes all source code, architecture details,
|
|
18
|
+
API designs, database schemas, deployment configurations, business logic,
|
|
19
|
+
and proprietary algorithms shared through the PixelStack Lite platform,
|
|
20
|
+
its CLI tools, documentation, and related materials.
|
|
21
|
+
|
|
22
|
+
${divider()}
|
|
23
|
+
|
|
24
|
+
${chalk.bold('2. OBLIGATIONS OF RECIPIENT')}
|
|
25
|
+
|
|
26
|
+
The Recipient agrees to:
|
|
27
|
+
a) Hold all Confidential Information in strict confidence
|
|
28
|
+
b) Not disclose Confidential Information to any third party
|
|
29
|
+
c) Use Confidential Information solely for authorized evaluation
|
|
30
|
+
d) Not reverse-engineer, decompile, or create derivative works
|
|
31
|
+
e) Return or destroy all materials upon request
|
|
32
|
+
|
|
33
|
+
${divider()}
|
|
34
|
+
|
|
35
|
+
${chalk.bold('3. TERM')}
|
|
36
|
+
|
|
37
|
+
This Agreement remains effective for 2 (two) years from acceptance,
|
|
38
|
+
or until the information ceases to be confidential, whichever is later.
|
|
39
|
+
|
|
40
|
+
${divider()}
|
|
41
|
+
|
|
42
|
+
${chalk.bold('4. GOVERNING LAW')}
|
|
43
|
+
|
|
44
|
+
This Agreement shall be governed by the laws of the jurisdiction
|
|
45
|
+
in which the Company operates.
|
|
46
|
+
|
|
47
|
+
${divider()}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
export async function showNDA(): Promise<boolean> {
|
|
51
|
+
section('Legal Agreement');
|
|
52
|
+
|
|
53
|
+
console.log(chalk.hex('#7C3AED').bold(' Before proceeding, please review the NDA:'));
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(NDA_TEXT);
|
|
56
|
+
|
|
57
|
+
const { accepted } = await inquirer.prompt([
|
|
58
|
+
{
|
|
59
|
+
type: 'confirm',
|
|
60
|
+
name: 'accepted',
|
|
61
|
+
message: chalk.white('Do you accept the Non-Disclosure Agreement?'),
|
|
62
|
+
default: false,
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
console.log('');
|
|
67
|
+
|
|
68
|
+
if (!accepted) {
|
|
69
|
+
console.log(chalk.gray(' Installation cancelled. You must accept the NDA to continue.'));
|
|
70
|
+
console.log('');
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.green(' ✔ NDA accepted. Proceeding...'));
|
|
75
|
+
console.log('');
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { success, error, warn, info, section } from './ui.js';
|
|
4
|
+
|
|
5
|
+
interface Requirement {
|
|
6
|
+
name: string;
|
|
7
|
+
command: string;
|
|
8
|
+
minVersion?: string;
|
|
9
|
+
required: boolean;
|
|
10
|
+
installHint: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface CheckResult {
|
|
14
|
+
name: string;
|
|
15
|
+
installed: boolean;
|
|
16
|
+
version?: string;
|
|
17
|
+
meetsMin?: boolean;
|
|
18
|
+
required: boolean;
|
|
19
|
+
minVersion?: string;
|
|
20
|
+
installHint: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const REQUIREMENTS: Requirement[] = [
|
|
24
|
+
{
|
|
25
|
+
name: 'Node.js',
|
|
26
|
+
command: 'node --version',
|
|
27
|
+
minVersion: '22.0.0',
|
|
28
|
+
required: true,
|
|
29
|
+
installHint: 'Install Node.js 22+: https://nodejs.org/',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Docker',
|
|
33
|
+
command: 'docker --version',
|
|
34
|
+
required: true,
|
|
35
|
+
installHint: 'Install Docker: https://docs.docker.com/get-docker/',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Docker Compose',
|
|
39
|
+
command: 'docker compose version',
|
|
40
|
+
required: true,
|
|
41
|
+
installHint: 'Install Docker Compose: https://docs.docker.com/compose/install/',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'pnpm',
|
|
45
|
+
command: 'pnpm --version',
|
|
46
|
+
minVersion: '9.0.0',
|
|
47
|
+
required: false,
|
|
48
|
+
installHint: 'Install pnpm: npm install -g pnpm@9',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function parseVersion(output: string): string {
|
|
53
|
+
return output.replace(/^[^\d]*/, '').split('+')[0].trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function compareVersions(a: string, b: string): number {
|
|
57
|
+
const pa = a.split('.').map(Number);
|
|
58
|
+
const pb = b.split('.').map(Number);
|
|
59
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
60
|
+
const na = pa[i] || 0;
|
|
61
|
+
const nb = pb[i] || 0;
|
|
62
|
+
if (na > nb) return 1;
|
|
63
|
+
if (na < nb) return -1;
|
|
64
|
+
}
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function checkRequirement(req: Requirement): CheckResult {
|
|
69
|
+
try {
|
|
70
|
+
const output = execSync(req.command, { encoding: 'utf-8', stdio: 'pipe' }).trim();
|
|
71
|
+
const version = parseVersion(output);
|
|
72
|
+
const meetsMin = req.minVersion ? compareVersions(version, req.minVersion) >= 0 : true;
|
|
73
|
+
return { name: req.name, installed: true, version, meetsMin, required: req.required, minVersion: req.minVersion, installHint: req.installHint };
|
|
74
|
+
} catch {
|
|
75
|
+
return { name: req.name, installed: false, required: req.required, installHint: req.installHint };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function checkRequirements(): Promise<boolean> {
|
|
80
|
+
section('System Requirements');
|
|
81
|
+
|
|
82
|
+
const results = REQUIREMENTS.map(checkRequirement);
|
|
83
|
+
let allPassed = true;
|
|
84
|
+
|
|
85
|
+
for (const r of results) {
|
|
86
|
+
if (!r.installed) {
|
|
87
|
+
error(`${chalk.bold(r.name)} not found`);
|
|
88
|
+
info(r.installHint);
|
|
89
|
+
if (r.required) allPassed = false;
|
|
90
|
+
} else if (r.meetsMin === false) {
|
|
91
|
+
warn(`${chalk.bold(r.name)} ${r.version} — requires ${r.minVersion}+`);
|
|
92
|
+
info(r.installHint);
|
|
93
|
+
if (r.required) allPassed = false;
|
|
94
|
+
} else {
|
|
95
|
+
success(`${chalk.bold(r.name)} ${chalk.gray(r.version || '')}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log('');
|
|
100
|
+
|
|
101
|
+
if (!allPassed) {
|
|
102
|
+
error('Missing required dependencies. Please install them and try again.');
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export function banner(): void {
|
|
4
|
+
console.log(chalk.hex('#7C3AED')('┌─────────────────────────────────────────────┐'));
|
|
5
|
+
console.log(chalk.hex('#7C3AED')('│') + chalk.white.bold(' PixelStack Lite Installer ') + chalk.hex('#7C3AED')('│'));
|
|
6
|
+
console.log(chalk.hex('#7C3AED')('│') + chalk.gray(' v0.1.0 — Single Container ') + chalk.hex('#7C3AED')('│'));
|
|
7
|
+
console.log(chalk.hex('#7C3AED')('└─────────────────────────────────────────────┘'));
|
|
8
|
+
console.log('');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function success(msg: string): void {
|
|
12
|
+
console.log(chalk.green(' ✔ ') + msg);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function error(msg: string): void {
|
|
16
|
+
console.log(chalk.red(' ✘ ') + msg);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function warn(msg: string): void {
|
|
20
|
+
console.log(chalk.yellow(' ⚠ ') + msg);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function info(msg: string): void {
|
|
24
|
+
console.log(chalk.cyan(' ℹ ') + msg);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function divider(): void {
|
|
28
|
+
console.log(chalk.gray(' ───────────────────────────────────────────'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function section(title: string): void {
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(chalk.hex('#7C3AED').bold(` ▸ ${title}`));
|
|
34
|
+
console.log('');
|
|
35
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"resolveJsonModule": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"]
|
|
16
|
+
}
|