mysystem-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const init_1 = require("./commands/init");
5
+ const audit_1 = require("./commands/audit");
6
+ const destroy_1 = require("./commands/destroy");
7
+ const logs_1 = require("./commands/logs");
8
+ const args = process.argv.slice(2);
9
+ const command = args[0];
10
+ function printHelp() {
11
+ console.log(`
12
+ \x1b[1mMySystem CLI - Production Deployment for Vibecoders\x1b[0m
13
+
14
+ Usage:
15
+ npx mysystem-cli <command> [options]
16
+
17
+ Commands:
18
+ \x1b[36minit\x1b[0m Initialize AWS Terraform configs, Dockerfiles, and GitHub workflows.
19
+ \x1b[36maudit\x1b[0m Audit local files for production-readiness, security, and compliance.
20
+ \x1b[36mlogs\x1b[0m Stream container logs from AWS CloudWatch directly to your terminal.
21
+ \x1b[36mdestroy\x1b[0m Teardown all AWS infrastructure resources for this application.
22
+ \x1b[36mhelp\x1b[0m Print this help menu.
23
+
24
+ Examples:
25
+ $ npx mysystem-cli init
26
+ $ npx mysystem-cli audit
27
+ $ npx mysystem-cli logs
28
+ $ npx mysystem-cli destroy
29
+ `);
30
+ }
31
+ async function main() {
32
+ switch (command) {
33
+ case 'init':
34
+ await (0, init_1.runInit)(process.cwd());
35
+ break;
36
+ case 'audit':
37
+ (0, audit_1.runAudit)(process.cwd());
38
+ break;
39
+ case 'logs':
40
+ await (0, logs_1.runLogs)(process.cwd());
41
+ break;
42
+ case 'destroy':
43
+ await (0, destroy_1.runDestroy)(process.cwd());
44
+ break;
45
+ case 'help':
46
+ case '--help':
47
+ case '-h':
48
+ printHelp();
49
+ break;
50
+ default:
51
+ if (!command) {
52
+ printHelp();
53
+ }
54
+ else {
55
+ console.error(`\x1b[31mUnknown command: ${command}\x1b[0m`);
56
+ printHelp();
57
+ process.exit(1);
58
+ }
59
+ }
60
+ }
61
+ main().catch(err => {
62
+ console.error('\x1b[31mFatal error occurred:\x1b[0m', err);
63
+ process.exit(1);
64
+ });
@@ -0,0 +1,8 @@
1
+ export interface ProjectInfo {
2
+ type: 'nextjs' | 'react-vite' | 'node' | 'fastapi' | 'unknown';
3
+ port: number;
4
+ hasDatabase: boolean;
5
+ hasRedis: boolean;
6
+ name: string;
7
+ }
8
+ export declare function detectProject(projectRoot: string): ProjectInfo;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.detectProject = detectProject;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ function detectProject(projectRoot) {
40
+ const info = {
41
+ type: 'unknown',
42
+ port: 3000,
43
+ hasDatabase: false,
44
+ hasRedis: false,
45
+ name: path.basename(projectRoot) || 'mysystem-app',
46
+ };
47
+ // 1. Read package.json if it exists
48
+ const packageJsonPath = path.join(projectRoot, 'package.json');
49
+ if (fs.existsSync(packageJsonPath)) {
50
+ try {
51
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
52
+ if (packageJson.name) {
53
+ info.name = packageJson.name;
54
+ }
55
+ const deps = {
56
+ ...packageJson.dependencies,
57
+ ...packageJson.devDependencies,
58
+ };
59
+ // Detect database dependencies (pg, prisma, typeorm, sequelize, knex, sqlite3, mysql2)
60
+ const dbDeps = ['pg', 'postgres', 'prisma', 'typeorm', 'sequelize', 'knex', 'sqlite3', 'mysql2'];
61
+ if (Object.keys(deps).some(dep => dbDeps.includes(dep))) {
62
+ info.hasDatabase = true;
63
+ }
64
+ // Detect Redis dependencies
65
+ const redisDeps = ['redis', 'ioredis', 'bull', 'bullmq', 'handy-redis', 'keyv'];
66
+ if (Object.keys(deps).some(dep => redisDeps.includes(dep))) {
67
+ info.hasRedis = true;
68
+ }
69
+ // Framework detection
70
+ if (deps['next']) {
71
+ info.type = 'nextjs';
72
+ info.port = 3000;
73
+ }
74
+ else if (deps['vite'] || deps['react']) {
75
+ // A Vite React app (or standard React)
76
+ info.type = 'react-vite';
77
+ info.port = 80; // React SPAs get served on port 80 via Nginx in production
78
+ }
79
+ else if (deps['express'] || deps['koa'] || deps['fastify'] || deps['nest']) {
80
+ info.type = 'node';
81
+ info.port = 3000;
82
+ }
83
+ else {
84
+ info.type = 'node';
85
+ info.port = 3000;
86
+ }
87
+ }
88
+ catch (e) {
89
+ // Ignore JSON parse errors and continue
90
+ }
91
+ }
92
+ // 2. Read requirements.txt or main.py if Python
93
+ const reqTxtPath = path.join(projectRoot, 'requirements.txt');
94
+ const mainPyPath = path.join(projectRoot, 'main.py');
95
+ if (fs.existsSync(reqTxtPath) || fs.existsSync(mainPyPath)) {
96
+ info.type = 'fastapi';
97
+ info.port = 8000;
98
+ if (fs.existsSync(reqTxtPath)) {
99
+ const reqs = fs.readFileSync(reqTxtPath, 'utf8');
100
+ const dbTerms = ['postgresql', 'psycopg2', 'sqlalchemy', 'tortoise-orm', 'peewee', 'asyncpg'];
101
+ if (dbTerms.some(term => reqs.toLowerCase().includes(term))) {
102
+ info.hasDatabase = true;
103
+ }
104
+ const redisTerms = ['redis', 'django-redis', 'celery'];
105
+ if (redisTerms.some(term => reqs.toLowerCase().includes(term))) {
106
+ info.hasRedis = true;
107
+ }
108
+ }
109
+ }
110
+ return info;
111
+ }
@@ -0,0 +1 @@
1
+ export declare function ensureAwsCli(): Promise<boolean>;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ensureAwsCli = ensureAwsCli;
37
+ const child_process_1 = require("child_process");
38
+ const readline = __importStar(require("readline/promises"));
39
+ async function ensureAwsCli() {
40
+ // 1. Check if AWS CLI is already installed
41
+ try {
42
+ (0, child_process_1.execSync)('aws --version', { stdio: 'ignore' });
43
+ return true;
44
+ }
45
+ catch (e) {
46
+ // AWS CLI not installed
47
+ }
48
+ console.log('\n\x1b[33m⚠️ AWS CLI is not installed on your system.\x1b[0m');
49
+ console.log('The AWS CLI is required to stream container logs and manage local credentials.');
50
+ const rl = readline.createInterface({
51
+ input: process.stdin,
52
+ output: process.stdout,
53
+ });
54
+ const answer = await rl.question('\nWould you like MySystem to install the AWS CLI automatically? (y/n) [y]: ');
55
+ rl.close();
56
+ if (answer.trim().toLowerCase() === 'n') {
57
+ return false;
58
+ }
59
+ const platform = process.platform;
60
+ console.log(`\n⚙️ Installing AWS CLI for \x1b[36m${platform}\x1b[0m...`);
61
+ try {
62
+ if (platform === 'win32') {
63
+ // Windows: Use winget (native Windows Package Manager)
64
+ console.log('Running winget installer...');
65
+ const res = (0, child_process_1.spawnSync)('winget', ['install', '--id', 'Amazon.AWSCLI', '--silent', '--accept-source-agreements', '--accept-package-agreements'], {
66
+ stdio: 'inherit',
67
+ shell: true,
68
+ });
69
+ if (res.status === 0) {
70
+ console.log('\x1b[32m✅ AWS CLI installed successfully! You may need to restart your terminal for changes to take effect.\x1b[0m');
71
+ return true;
72
+ }
73
+ }
74
+ else if (platform === 'darwin') {
75
+ // macOS: Try homebrew first
76
+ let hasBrew = false;
77
+ try {
78
+ (0, child_process_1.execSync)('brew --version', { stdio: 'ignore' });
79
+ hasBrew = true;
80
+ }
81
+ catch (e) { }
82
+ if (hasBrew) {
83
+ console.log('Running: brew install awscli...');
84
+ const res = (0, child_process_1.spawnSync)('brew', ['install', 'awscli'], { stdio: 'inherit' });
85
+ if (res.status === 0) {
86
+ console.log('\x1b[32m✅ AWS CLI installed successfully via Homebrew!\x1b[0m');
87
+ return true;
88
+ }
89
+ }
90
+ else {
91
+ // Fallback: Download pkg installer
92
+ console.log('Downloading AWS CLI macOS package...');
93
+ (0, child_process_1.execSync)('curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "/tmp/AWSCLIV2.pkg"', { stdio: 'inherit' });
94
+ console.log('Installing package (requires sudo privileges)...');
95
+ const res = (0, child_process_1.spawnSync)('sudo', ['installer', '-pkg', '/tmp/AWSCLIV2.pkg', '-target', '/'], { stdio: 'inherit' });
96
+ if (res.status === 0) {
97
+ console.log('\x1b[32m✅ AWS CLI installed successfully!\x1b[0m');
98
+ return true;
99
+ }
100
+ }
101
+ }
102
+ else if (platform === 'linux') {
103
+ // Linux install
104
+ console.log('Downloading AWS CLI Linux package...');
105
+ (0, child_process_1.execSync)('curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "/tmp/awscliv2.zip"', { stdio: 'inherit' });
106
+ (0, child_process_1.execSync)('unzip -q /tmp/awscliv2.zip -d /tmp', { stdio: 'inherit' });
107
+ console.log('Installing package (requires sudo privileges)...');
108
+ const res = (0, child_process_1.spawnSync)('sudo', ['/tmp/aws/install', '--update'], { stdio: 'inherit' });
109
+ if (res.status === 0) {
110
+ console.log('\x1b[32m✅ AWS CLI installed successfully!\x1b[0m');
111
+ return true;
112
+ }
113
+ }
114
+ }
115
+ catch (err) {
116
+ console.error(`\x1b[31mInstallation failed: ${err.message}\x1b[0m`);
117
+ }
118
+ console.log('\n\x1b[31m❌ Automatic installation failed.\x1b[0m');
119
+ console.log('Please install the AWS CLI manually:');
120
+ console.log('👉 \x1b[36mhttps://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html\x1b[0m\n');
121
+ return false;
122
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "mysystem-cli",
3
+ "version": "1.0.0",
4
+ "description": "Zero-config deployment standard and CLI for AI-generated applications on AWS Fargate",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "mysystem": "dist/index.js",
8
+ "mysystem-cli": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "aws",
16
+ "fargate",
17
+ "ecs",
18
+ "deploy",
19
+ "terraform",
20
+ "docker",
21
+ "cursor",
22
+ "ai",
23
+ "agents"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "typescript": "^5.0.0"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ }
34
+ }
@@ -0,0 +1,176 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ interface AuditItem {
5
+ name: string;
6
+ passed: boolean;
7
+ type: 'error' | 'warning';
8
+ message: string;
9
+ }
10
+
11
+ export function runAudit(projectRoot: string) {
12
+ console.log('\n🔍 Auditing project for production readiness...\n');
13
+
14
+ const items: AuditItem[] = [];
15
+
16
+ // 1. Check Dockerfile
17
+ const dockerfilePath = path.join(projectRoot, 'Dockerfile');
18
+ if (!fs.existsSync(dockerfilePath)) {
19
+ items.push({
20
+ name: 'Dockerfile Exists',
21
+ passed: false,
22
+ type: 'error',
23
+ message: 'No Dockerfile found. Run `npx mysystem init` to generate one.',
24
+ });
25
+ } else {
26
+ items.push({
27
+ name: 'Dockerfile Exists',
28
+ passed: true,
29
+ type: 'error',
30
+ message: 'Dockerfile found.',
31
+ });
32
+
33
+ const dockerfileContent = fs.readFileSync(dockerfilePath, 'utf8');
34
+
35
+ // Check for non-root USER
36
+ const hasUser = dockerfileContent.includes('USER ');
37
+ items.push({
38
+ name: 'Secure Container User (Non-Root)',
39
+ passed: hasUser,
40
+ type: 'error',
41
+ message: hasUser
42
+ ? 'Dockerfile specifies a non-root USER.'
43
+ : 'Dockerfile executes as root. Add a USER instruction for security container hardening.',
44
+ });
45
+
46
+ // Check for healthcheck
47
+ const hasHealthcheck = dockerfileContent.includes('HEALTHCHECK ');
48
+ items.push({
49
+ name: 'Container Health Check',
50
+ passed: hasHealthcheck,
51
+ type: 'warning',
52
+ message: hasHealthcheck
53
+ ? 'Dockerfile specifies a HEALTHCHECK instruction.'
54
+ : 'No HEALTHCHECK found in Dockerfile. ECS needs health checks to detect failing tasks.',
55
+ });
56
+ }
57
+
58
+ // 2. Check CI/CD Workflows
59
+ const workflowPath = path.join(projectRoot, '.github', 'workflows', 'mysystem-deploy.yml');
60
+ const hasWorkflow = fs.existsSync(workflowPath);
61
+ items.push({
62
+ name: 'GitHub Actions CI/CD Pipeline',
63
+ passed: hasWorkflow,
64
+ type: 'error',
65
+ message: hasWorkflow
66
+ ? 'GitHub Actions deploy workflow configured.'
67
+ : 'Deployment workflow missing. Make sure `.github/workflows/mysystem-deploy.yml` exists.',
68
+ });
69
+
70
+ // 3. Check Terraform Infrastructure as Code
71
+ const terraformPath = path.join(projectRoot, 'terraform');
72
+ const hasTerraform = fs.existsSync(terraformPath) && fs.readdirSync(terraformPath).some(file => file.endsWith('.tf'));
73
+ items.push({
74
+ name: 'Infrastructure as Code (Terraform)',
75
+ passed: hasTerraform,
76
+ type: 'error',
77
+ message: hasTerraform
78
+ ? 'Terraform modules found in `/terraform` directory.'
79
+ : 'Terraform configuration files not found in `/terraform`. Run `npx mysystem init`.',
80
+ });
81
+
82
+ // 4. Check AGENTS.md rulebook
83
+ const agentsPath = path.join(projectRoot, 'AGENTS.md');
84
+ const hasAgents = fs.existsSync(agentsPath);
85
+ items.push({
86
+ name: 'AI Agent Guidelines (AGENTS.md)',
87
+ passed: hasAgents,
88
+ type: 'warning',
89
+ message: hasAgents
90
+ ? 'AGENTS.md rules file configured in root.'
91
+ : 'No AGENTS.md rules found. AI agents won\'t have constraints for production-readiness.',
92
+ });
93
+
94
+ // 5. Secret Scanning (Simple checks for common API Keys/Credentials)
95
+ let foundSecrets = false;
96
+ const filesToScan = scanDirForCodeFiles(projectRoot);
97
+ for (const file of filesToScan) {
98
+ // Skip node_modules, git, terraform, dist, etc.
99
+ const content = fs.readFileSync(file, 'utf8');
100
+ const hasSmdPattern = /aws_access_key_id\s*=\s*['"][A-Z0-9]{20}['"]/gi.test(content) ||
101
+ /aws_secret_access_key\s*=\s*['"][A-Za-z0-9/+=]{40}['"]/gi.test(content) ||
102
+ /db_password\s*=\s*['"][^'"]{6,}['"]/gi.test(content);
103
+ if (hasSmdPattern) {
104
+ foundSecrets = true;
105
+ console.log(`\x1b[31m⚠️ Potential secret/credential leak in file: ${path.relative(projectRoot, file)}\x1b[0m`);
106
+ }
107
+ }
108
+
109
+ items.push({
110
+ name: 'No Hardcoded Secrets',
111
+ passed: !foundSecrets,
112
+ type: 'error',
113
+ message: !foundSecrets
114
+ ? 'No plain-text credentials found in repository files.'
115
+ : 'Potential plain-text credentials detected in codebase. Clean secrets and use environment variables.',
116
+ });
117
+
118
+ // Output Audit Dashboard
119
+ console.log('----------------------------------------------------');
120
+ console.log('\x1b[1mAUDIT REPORT RESULTS\x1b[0m');
121
+ console.log('----------------------------------------------------');
122
+
123
+ let passedCount = 0;
124
+ let errorCount = 0;
125
+ let warningCount = 0;
126
+
127
+ for (const item of items) {
128
+ if (item.passed) {
129
+ console.log(` ✅ \x1b[32m[PASS]\x1b[0m \x1b[1m${item.name}\x1b[0m`);
130
+ console.log(` ${item.message}`);
131
+ passedCount++;
132
+ } else {
133
+ const color = item.type === 'error' ? '\x1b[31m[FAIL]\x1b[0m' : '\x1b[33m[WARN]\x1b[0m';
134
+ console.log(` ❌ ${color} \x1b[1m${item.name}\x1b[0m`);
135
+ console.log(` ${item.message}`);
136
+ if (item.type === 'error') errorCount++;
137
+ else warningCount++;
138
+ }
139
+ console.log('');
140
+ }
141
+
142
+ const score = Math.round((passedCount / items.length) * 100);
143
+
144
+ console.log('----------------------------------------------------');
145
+ console.log(`Readiness Score: \x1b[1m${score === 100 ? '\x1b[32m' : score >= 70 ? '\x1b[33m' : '\x1b[31m'}${score}%\x1b[0m`);
146
+ console.log(`Summary: ${passedCount} passed | ${errorCount} errors | ${warningCount} warnings`);
147
+ console.log('----------------------------------------------------');
148
+
149
+ if (errorCount > 0) {
150
+ console.log('\x1b[31m❌ Fix all errors before deploying to production.\x1b[0m\n');
151
+ } else if (warningCount > 0) {
152
+ console.log('\x1b[33m⚠️ Resolving warnings is recommended for full production readiness.\x1b[0m\n');
153
+ } else {
154
+ console.log('\x1b[32m🚀 Your application is 100% production ready! Ready to deploy to AWS.\x1b[0m\n');
155
+ }
156
+ }
157
+
158
+ function scanDirForCodeFiles(dir: string, fileList: string[] = []): string[] {
159
+ const files = fs.readdirSync(dir);
160
+ for (const file of files) {
161
+ const filePath = path.join(dir, file);
162
+ if (fs.statSync(filePath).isDirectory()) {
163
+ // Exclude build, node, git, terraform folders
164
+ if (['node_modules', '.git', 'terraform', 'dist', 'build', '.next', 'out'].includes(file)) {
165
+ continue;
166
+ }
167
+ scanDirForCodeFiles(filePath, fileList);
168
+ } else {
169
+ // Only scan code files
170
+ if (/\.(js|ts|tsx|jsx|json|py|env|tfvars)$/.test(file) && file !== 'package-lock.json') {
171
+ fileList.push(filePath);
172
+ }
173
+ }
174
+ }
175
+ return fileList;
176
+ }
@@ -0,0 +1,65 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as readline from 'readline/promises';
4
+ import { spawn } from 'child_process';
5
+
6
+ export async function runDestroy(projectRoot: string) {
7
+ const configPath = path.join(projectRoot, 'mysystem.json');
8
+ if (!fs.existsSync(configPath)) {
9
+ console.error('\x1b[31mError: MySystem is not initialized in this directory.\x1b[0m');
10
+ console.error('Run `npx mysystem init` first.');
11
+ process.exit(1);
12
+ }
13
+
14
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
15
+
16
+ console.log(`\n\x1b[31m⚠️ WARNING: You are about to DESTROY all AWS infrastructure for "${config.name}".\x1b[0m`);
17
+ console.log('This will delete the database (RDS), cache (Redis), server (Fargate), and load balancers.');
18
+ console.log('\x1b[1mALL DATA WILL BE PERMANENTLY LOST.\x1b[0m\n');
19
+
20
+ const rl = readline.createInterface({
21
+ input: process.stdin,
22
+ output: process.stdout,
23
+ });
24
+
25
+ const confirm = await rl.question('Are you absolutely sure? Type the application name to confirm: ');
26
+ rl.close();
27
+
28
+ if (confirm.trim() !== config.name) {
29
+ console.log('\n❌ Confirmation failed. Destruction cancelled.');
30
+ return;
31
+ }
32
+
33
+ console.log('\n🔥 Initiating infrastructure destruction. Please wait...');
34
+
35
+ const tfDir = path.join(projectRoot, 'terraform');
36
+ if (!fs.existsSync(tfDir)) {
37
+ console.error('\x1b[31mError: terraform directory not found.\x1b[0m');
38
+ process.exit(1);
39
+ }
40
+
41
+ // Execute terraform destroy
42
+ // Set stdio: 'inherit' to stream Terraform output directly to user's terminal
43
+ const tf = spawn('terraform', ['destroy', '-auto-approve'], {
44
+ cwd: tfDir,
45
+ stdio: 'inherit',
46
+ shell: true,
47
+ });
48
+
49
+ tf.on('close', (code) => {
50
+ if (code === 0) {
51
+ console.log('\n\x1b[32m✅ Successfully destroyed all AWS resources for this application.\x1b[0m');
52
+ console.log('Your AWS billing for this project has been stopped.');
53
+
54
+ // Remove local generated files if they want, or keep configuration
55
+ try {
56
+ if (fs.existsSync(path.join(projectRoot, 'mysystem.json'))) {
57
+ fs.unlinkSync(path.join(projectRoot, 'mysystem.json'));
58
+ }
59
+ console.log('Deleted local mysystem.json configuration.');
60
+ } catch (e) {}
61
+ } else {
62
+ console.error(`\n\x1b[31m❌ Terraform destroy failed with exit code ${code}.\x1b[0m`);
63
+ }
64
+ });
65
+ }