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.
@@ -0,0 +1 @@
1
+ export declare function runAudit(projectRoot: string): void;
@@ -0,0 +1,194 @@
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.runAudit = runAudit;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ function runAudit(projectRoot) {
40
+ console.log('\n🔍 Auditing project for production readiness...\n');
41
+ const items = [];
42
+ // 1. Check Dockerfile
43
+ const dockerfilePath = path.join(projectRoot, 'Dockerfile');
44
+ if (!fs.existsSync(dockerfilePath)) {
45
+ items.push({
46
+ name: 'Dockerfile Exists',
47
+ passed: false,
48
+ type: 'error',
49
+ message: 'No Dockerfile found. Run `npx mysystem init` to generate one.',
50
+ });
51
+ }
52
+ else {
53
+ items.push({
54
+ name: 'Dockerfile Exists',
55
+ passed: true,
56
+ type: 'error',
57
+ message: 'Dockerfile found.',
58
+ });
59
+ const dockerfileContent = fs.readFileSync(dockerfilePath, 'utf8');
60
+ // Check for non-root USER
61
+ const hasUser = dockerfileContent.includes('USER ');
62
+ items.push({
63
+ name: 'Secure Container User (Non-Root)',
64
+ passed: hasUser,
65
+ type: 'error',
66
+ message: hasUser
67
+ ? 'Dockerfile specifies a non-root USER.'
68
+ : 'Dockerfile executes as root. Add a USER instruction for security container hardening.',
69
+ });
70
+ // Check for healthcheck
71
+ const hasHealthcheck = dockerfileContent.includes('HEALTHCHECK ');
72
+ items.push({
73
+ name: 'Container Health Check',
74
+ passed: hasHealthcheck,
75
+ type: 'warning',
76
+ message: hasHealthcheck
77
+ ? 'Dockerfile specifies a HEALTHCHECK instruction.'
78
+ : 'No HEALTHCHECK found in Dockerfile. ECS needs health checks to detect failing tasks.',
79
+ });
80
+ }
81
+ // 2. Check CI/CD Workflows
82
+ const workflowPath = path.join(projectRoot, '.github', 'workflows', 'mysystem-deploy.yml');
83
+ const hasWorkflow = fs.existsSync(workflowPath);
84
+ items.push({
85
+ name: 'GitHub Actions CI/CD Pipeline',
86
+ passed: hasWorkflow,
87
+ type: 'error',
88
+ message: hasWorkflow
89
+ ? 'GitHub Actions deploy workflow configured.'
90
+ : 'Deployment workflow missing. Make sure `.github/workflows/mysystem-deploy.yml` exists.',
91
+ });
92
+ // 3. Check Terraform Infrastructure as Code
93
+ const terraformPath = path.join(projectRoot, 'terraform');
94
+ const hasTerraform = fs.existsSync(terraformPath) && fs.readdirSync(terraformPath).some(file => file.endsWith('.tf'));
95
+ items.push({
96
+ name: 'Infrastructure as Code (Terraform)',
97
+ passed: hasTerraform,
98
+ type: 'error',
99
+ message: hasTerraform
100
+ ? 'Terraform modules found in `/terraform` directory.'
101
+ : 'Terraform configuration files not found in `/terraform`. Run `npx mysystem init`.',
102
+ });
103
+ // 4. Check AGENTS.md rulebook
104
+ const agentsPath = path.join(projectRoot, 'AGENTS.md');
105
+ const hasAgents = fs.existsSync(agentsPath);
106
+ items.push({
107
+ name: 'AI Agent Guidelines (AGENTS.md)',
108
+ passed: hasAgents,
109
+ type: 'warning',
110
+ message: hasAgents
111
+ ? 'AGENTS.md rules file configured in root.'
112
+ : 'No AGENTS.md rules found. AI agents won\'t have constraints for production-readiness.',
113
+ });
114
+ // 5. Secret Scanning (Simple checks for common API Keys/Credentials)
115
+ let foundSecrets = false;
116
+ const filesToScan = scanDirForCodeFiles(projectRoot);
117
+ for (const file of filesToScan) {
118
+ // Skip node_modules, git, terraform, dist, etc.
119
+ const content = fs.readFileSync(file, 'utf8');
120
+ const hasSmdPattern = /aws_access_key_id\s*=\s*['"][A-Z0-9]{20}['"]/gi.test(content) ||
121
+ /aws_secret_access_key\s*=\s*['"][A-Za-z0-9/+=]{40}['"]/gi.test(content) ||
122
+ /db_password\s*=\s*['"][^'"]{6,}['"]/gi.test(content);
123
+ if (hasSmdPattern) {
124
+ foundSecrets = true;
125
+ console.log(`\x1b[31m⚠️ Potential secret/credential leak in file: ${path.relative(projectRoot, file)}\x1b[0m`);
126
+ }
127
+ }
128
+ items.push({
129
+ name: 'No Hardcoded Secrets',
130
+ passed: !foundSecrets,
131
+ type: 'error',
132
+ message: !foundSecrets
133
+ ? 'No plain-text credentials found in repository files.'
134
+ : 'Potential plain-text credentials detected in codebase. Clean secrets and use environment variables.',
135
+ });
136
+ // Output Audit Dashboard
137
+ console.log('----------------------------------------------------');
138
+ console.log('\x1b[1mAUDIT REPORT RESULTS\x1b[0m');
139
+ console.log('----------------------------------------------------');
140
+ let passedCount = 0;
141
+ let errorCount = 0;
142
+ let warningCount = 0;
143
+ for (const item of items) {
144
+ if (item.passed) {
145
+ console.log(` ✅ \x1b[32m[PASS]\x1b[0m \x1b[1m${item.name}\x1b[0m`);
146
+ console.log(` ${item.message}`);
147
+ passedCount++;
148
+ }
149
+ else {
150
+ const color = item.type === 'error' ? '\x1b[31m[FAIL]\x1b[0m' : '\x1b[33m[WARN]\x1b[0m';
151
+ console.log(` ❌ ${color} \x1b[1m${item.name}\x1b[0m`);
152
+ console.log(` ${item.message}`);
153
+ if (item.type === 'error')
154
+ errorCount++;
155
+ else
156
+ warningCount++;
157
+ }
158
+ console.log('');
159
+ }
160
+ const score = Math.round((passedCount / items.length) * 100);
161
+ console.log('----------------------------------------------------');
162
+ console.log(`Readiness Score: \x1b[1m${score === 100 ? '\x1b[32m' : score >= 70 ? '\x1b[33m' : '\x1b[31m'}${score}%\x1b[0m`);
163
+ console.log(`Summary: ${passedCount} passed | ${errorCount} errors | ${warningCount} warnings`);
164
+ console.log('----------------------------------------------------');
165
+ if (errorCount > 0) {
166
+ console.log('\x1b[31m❌ Fix all errors before deploying to production.\x1b[0m\n');
167
+ }
168
+ else if (warningCount > 0) {
169
+ console.log('\x1b[33m⚠️ Resolving warnings is recommended for full production readiness.\x1b[0m\n');
170
+ }
171
+ else {
172
+ console.log('\x1b[32m🚀 Your application is 100% production ready! Ready to deploy to AWS.\x1b[0m\n');
173
+ }
174
+ }
175
+ function scanDirForCodeFiles(dir, fileList = []) {
176
+ const files = fs.readdirSync(dir);
177
+ for (const file of files) {
178
+ const filePath = path.join(dir, file);
179
+ if (fs.statSync(filePath).isDirectory()) {
180
+ // Exclude build, node, git, terraform folders
181
+ if (['node_modules', '.git', 'terraform', 'dist', 'build', '.next', 'out'].includes(file)) {
182
+ continue;
183
+ }
184
+ scanDirForCodeFiles(filePath, fileList);
185
+ }
186
+ else {
187
+ // Only scan code files
188
+ if (/\.(js|ts|tsx|jsx|json|py|env|tfvars)$/.test(file) && file !== 'package-lock.json') {
189
+ fileList.push(filePath);
190
+ }
191
+ }
192
+ }
193
+ return fileList;
194
+ }
@@ -0,0 +1 @@
1
+ export declare function runDestroy(projectRoot: string): Promise<void>;
@@ -0,0 +1,92 @@
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.runDestroy = runDestroy;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const readline = __importStar(require("readline/promises"));
40
+ const child_process_1 = require("child_process");
41
+ async function runDestroy(projectRoot) {
42
+ const configPath = path.join(projectRoot, 'mysystem.json');
43
+ if (!fs.existsSync(configPath)) {
44
+ console.error('\x1b[31mError: MySystem is not initialized in this directory.\x1b[0m');
45
+ console.error('Run `npx mysystem init` first.');
46
+ process.exit(1);
47
+ }
48
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
49
+ console.log(`\n\x1b[31m⚠️ WARNING: You are about to DESTROY all AWS infrastructure for "${config.name}".\x1b[0m`);
50
+ console.log('This will delete the database (RDS), cache (Redis), server (Fargate), and load balancers.');
51
+ console.log('\x1b[1mALL DATA WILL BE PERMANENTLY LOST.\x1b[0m\n');
52
+ const rl = readline.createInterface({
53
+ input: process.stdin,
54
+ output: process.stdout,
55
+ });
56
+ const confirm = await rl.question('Are you absolutely sure? Type the application name to confirm: ');
57
+ rl.close();
58
+ if (confirm.trim() !== config.name) {
59
+ console.log('\n❌ Confirmation failed. Destruction cancelled.');
60
+ return;
61
+ }
62
+ console.log('\n🔥 Initiating infrastructure destruction. Please wait...');
63
+ const tfDir = path.join(projectRoot, 'terraform');
64
+ if (!fs.existsSync(tfDir)) {
65
+ console.error('\x1b[31mError: terraform directory not found.\x1b[0m');
66
+ process.exit(1);
67
+ }
68
+ // Execute terraform destroy
69
+ // Set stdio: 'inherit' to stream Terraform output directly to user's terminal
70
+ const tf = (0, child_process_1.spawn)('terraform', ['destroy', '-auto-approve'], {
71
+ cwd: tfDir,
72
+ stdio: 'inherit',
73
+ shell: true,
74
+ });
75
+ tf.on('close', (code) => {
76
+ if (code === 0) {
77
+ console.log('\n\x1b[32m✅ Successfully destroyed all AWS resources for this application.\x1b[0m');
78
+ console.log('Your AWS billing for this project has been stopped.');
79
+ // Remove local generated files if they want, or keep configuration
80
+ try {
81
+ if (fs.existsSync(path.join(projectRoot, 'mysystem.json'))) {
82
+ fs.unlinkSync(path.join(projectRoot, 'mysystem.json'));
83
+ }
84
+ console.log('Deleted local mysystem.json configuration.');
85
+ }
86
+ catch (e) { }
87
+ }
88
+ else {
89
+ console.error(`\n\x1b[31m❌ Terraform destroy failed with exit code ${code}.\x1b[0m`);
90
+ }
91
+ });
92
+ }
@@ -0,0 +1 @@
1
+ export declare function runInit(projectRoot: string): Promise<void>;
@@ -0,0 +1,255 @@
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.runInit = runInit;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const readline = __importStar(require("readline/promises"));
40
+ const detector_1 = require("../utils/detector");
41
+ async function runInit(projectRoot) {
42
+ console.log('\n🔍 Scanning project codebase...');
43
+ const detected = (0, detector_1.detectProject)(projectRoot);
44
+ console.log(`Detected application type: \x1b[36m${detected.type}\x1b[0m`);
45
+ console.log(`Default container port: \x1b[36m${detected.port}\x1b[0m`);
46
+ console.log(`Requires database: \x1b[36m${detected.hasDatabase ? 'Yes' : 'No'}\x1b[0m`);
47
+ console.log(`Requires Redis: \x1b[36m${detected.hasRedis ? 'Yes' : 'No'}\x1b[0m\n`);
48
+ // Prompt the user for overrides using native readline
49
+ const rl = readline.createInterface({
50
+ input: process.stdin,
51
+ output: process.stdout,
52
+ });
53
+ try {
54
+ const appNameInput = await rl.question(`Enter application name [${detected.name}]: `);
55
+ const appName = appNameInput.trim() || detected.name;
56
+ const awsRegionInput = await rl.question(`Enter AWS region [us-east-1]: `);
57
+ const awsRegion = awsRegionInput.trim() || 'us-east-1';
58
+ console.log('Select your AWS hosting tier:');
59
+ console.log(' \x1b[36m1. Production\x1b[0m [ECS Fargate + RDS + ALB + WAF] (~$17/mo free-tier, ~$51/mo standard)');
60
+ console.log(' \x1b[36m2. Hobbyist\x1b[0m [Single EC2 + Docker Compose + Postgres] ($0/mo free-tier, ~$3.20/mo standard)');
61
+ const tierInput = await rl.question('Choose tier [1]: ');
62
+ const isProductionTier = tierInput.trim() !== '2';
63
+ let enableDatabase = detected.hasDatabase;
64
+ let enableRedis = detected.hasRedis;
65
+ let enableRdsProxy = false;
66
+ if (isProductionTier) {
67
+ const enableDbInput = await rl.question(`Enable RDS PostgreSQL database? (y/n) [${detected.hasDatabase ? 'y' : 'n'}]: `);
68
+ enableDatabase = enableDbInput.trim() ? enableDbInput.trim().toLowerCase() === 'y' : detected.hasDatabase;
69
+ const enableRedisInput = await rl.question(`Enable ElastiCache Redis? (y/n) [${detected.hasRedis ? 'y' : 'n'}]: `);
70
+ enableRedis = enableRedisInput.trim() ? enableRedisInput.trim().toLowerCase() === 'y' : detected.hasRedis;
71
+ if (enableDatabase) {
72
+ const enableProxyInput = await rl.question(`Enable RDS Proxy (PgBouncer connection pooler)? (y/n) [n]: `);
73
+ enableRdsProxy = enableProxyInput.trim().toLowerCase() === 'y';
74
+ }
75
+ }
76
+ else {
77
+ // Hobby tier runs Postgres inside Docker Compose on the same instance (flat cost)
78
+ enableDatabase = true;
79
+ enableRedis = false;
80
+ enableRdsProxy = false;
81
+ }
82
+ const billingEmailInput = await rl.question(`Enter email for AWS budget alerts (press Enter to skip): `);
83
+ const billingEmail = billingEmailInput.trim();
84
+ const customDomainInput = await rl.question(`Enable custom domain & HTTPS SSL certificate? (y/n) [n]: `);
85
+ const enableCustomDomain = customDomainInput.trim().toLowerCase() === 'y';
86
+ let domainName = '';
87
+ let dnsProvider = 'external';
88
+ if (enableCustomDomain) {
89
+ const domainInput = await rl.question(`Enter custom domain (e.g., app.myproduct.com): `);
90
+ domainName = domainInput.trim();
91
+ const providerInput = await rl.question(`Is your domain DNS managed on AWS Route 53? (y/n) [n]: `);
92
+ dnsProvider = providerInput.trim().toLowerCase() === 'y' ? 'route53' : 'external';
93
+ }
94
+ const enableSentryInput = await rl.question(`Enable Sentry Error Tracking? (y/n) [n]: `);
95
+ const enableSentry = enableSentryInput.trim().toLowerCase() === 'y';
96
+ let sentryDsn = '';
97
+ if (enableSentry) {
98
+ const dsnInput = await rl.question(`Enter Sentry DSN (press Enter to skip and configure later): `);
99
+ sentryDsn = dsnInput.trim();
100
+ }
101
+ rl.close();
102
+ console.log('\n🚀 Generating deployment assets...');
103
+ // Find templates directory relative to CLI source or dist
104
+ let templatesDir = '';
105
+ const pathsToCheck = [
106
+ path.join(__dirname, '../../templates'), // Packaged location relative to dist/commands/
107
+ path.join(__dirname, '../../../templates'), // Local dev relative to src/commands/
108
+ path.join(__dirname, '../../../../templates'), // Local dev relative to packages/cli/dist/commands
109
+ path.join(__dirname, '../../../../../templates'), // Fallback
110
+ ];
111
+ for (const p of pathsToCheck) {
112
+ if (fs.existsSync(p)) {
113
+ templatesDir = p;
114
+ break;
115
+ }
116
+ }
117
+ if (!fs.existsSync(templatesDir)) {
118
+ throw new Error(`Templates directory not found. Looked in: ${templatesDir}`);
119
+ }
120
+ // 1. Copy Dockerfile
121
+ let dockerfileTemplate = '';
122
+ switch (detected.type) {
123
+ case 'nextjs':
124
+ dockerfileTemplate = 'nextjs.Dockerfile';
125
+ break;
126
+ case 'react-vite':
127
+ dockerfileTemplate = 'react.Dockerfile';
128
+ break;
129
+ case 'fastapi':
130
+ dockerfileTemplate = 'fastapi.Dockerfile';
131
+ break;
132
+ default:
133
+ dockerfileTemplate = 'node.Dockerfile';
134
+ }
135
+ const srcDockerfile = path.join(templatesDir, 'docker', dockerfileTemplate);
136
+ const destDockerfile = path.join(projectRoot, 'Dockerfile');
137
+ if (fs.existsSync(srcDockerfile)) {
138
+ fs.copyFileSync(srcDockerfile, destDockerfile);
139
+ console.log(' ✅ Created Dockerfile');
140
+ }
141
+ // 2. Create GitHub Actions folder & copy deploy.yml and destroy.yml
142
+ const githubWorkflowDir = path.join(projectRoot, '.github', 'workflows');
143
+ fs.mkdirSync(githubWorkflowDir, { recursive: true });
144
+ const srcDeployWorkflow = path.join(templatesDir, 'github', isProductionTier ? 'deploy.yml' : 'deploy-ec2.yml');
145
+ const destDeployWorkflow = path.join(githubWorkflowDir, 'mysystem-deploy.yml');
146
+ if (fs.existsSync(srcDeployWorkflow)) {
147
+ let workflowContent = fs.readFileSync(srcDeployWorkflow, 'utf8');
148
+ workflowContent = workflowContent.replace(/aws-region: us-east-1/g, `aws-region: ${awsRegion}`);
149
+ fs.writeFileSync(destDeployWorkflow, workflowContent, 'utf8');
150
+ console.log(' ✅ Created .github/workflows/mysystem-deploy.yml');
151
+ }
152
+ const srcDestroyWorkflow = path.join(templatesDir, 'github', 'destroy.yml');
153
+ const destDestroyWorkflow = path.join(githubWorkflowDir, 'mysystem-destroy.yml');
154
+ if (fs.existsSync(srcDestroyWorkflow)) {
155
+ let workflowContent = fs.readFileSync(srcDestroyWorkflow, 'utf8');
156
+ workflowContent = workflowContent.replace(/aws-region: us-east-1/g, `aws-region: ${awsRegion}`);
157
+ fs.writeFileSync(destDestroyWorkflow, workflowContent, 'utf8');
158
+ console.log(' ✅ Created .github/workflows/mysystem-destroy.yml');
159
+ }
160
+ // 3. Create Terraform directory & copy templates
161
+ const terraformDir = path.join(projectRoot, 'terraform');
162
+ fs.mkdirSync(terraformDir, { recursive: true });
163
+ const srcTerraformDir = path.join(templatesDir, isProductionTier ? 'terraform' : 'terraform-ec2');
164
+ if (fs.existsSync(srcTerraformDir)) {
165
+ const files = fs.readdirSync(srcTerraformDir);
166
+ for (const file of files) {
167
+ const filePath = path.join(srcTerraformDir, file);
168
+ if (fs.statSync(filePath).isFile() && file !== 'bootstrap-oidc.yaml') {
169
+ fs.copyFileSync(filePath, path.join(terraformDir, file));
170
+ }
171
+ }
172
+ console.log(' ✅ Created Terraform modules in /terraform');
173
+ }
174
+ // 4. Write terraform.tfvars
175
+ const tfvarsContent = `aws_region = "${awsRegion}"
176
+ app_name = "${appName}"
177
+ container_port = ${detected.port}
178
+ enable_database = ${enableDatabase}
179
+ enable_redis = ${enableRedis}
180
+ enable_rds_proxy = ${enableRdsProxy}
181
+ billing_email = "${billingEmail}"
182
+ enable_custom_domain = ${enableCustomDomain}
183
+ domain_name = "${domainName}"
184
+ dns_provider = "${dnsProvider}"
185
+ sentry_dsn = "${sentryDsn}"
186
+ `;
187
+ fs.writeFileSync(path.join(terraformDir, 'terraform.tfvars'), tfvarsContent, 'utf8');
188
+ console.log(' ✅ Created terraform/terraform.tfvars');
189
+ // 5. Copy AGENTS.md
190
+ let srcAgentsMd = '';
191
+ const agentsPathsToCheck = [
192
+ path.join(__dirname, '../../AGENTS.md'),
193
+ path.join(__dirname, '../../../AGENTS.md'),
194
+ path.join(__dirname, '../../../../AGENTS.md'),
195
+ path.join(__dirname, '../../../../../AGENTS.md'),
196
+ ];
197
+ for (const p of agentsPathsToCheck) {
198
+ if (fs.existsSync(p)) {
199
+ srcAgentsMd = p;
200
+ break;
201
+ }
202
+ }
203
+ const destAgentsMd = path.join(projectRoot, 'AGENTS.md');
204
+ if (fs.existsSync(srcAgentsMd)) {
205
+ fs.copyFileSync(srcAgentsMd, destAgentsMd);
206
+ console.log(' ✅ Created AGENTS.md');
207
+ }
208
+ // 6. Write project configuration mysystem.json
209
+ const config = {
210
+ name: appName,
211
+ region: awsRegion,
212
+ port: detected.port,
213
+ tier: isProductionTier ? 'production' : 'hobbyist',
214
+ database: enableDatabase,
215
+ redis: enableRedis,
216
+ rdsProxy: enableRdsProxy,
217
+ billingEmail: billingEmail,
218
+ customDomain: enableCustomDomain,
219
+ domainName: domainName,
220
+ dnsProvider: dnsProvider,
221
+ sentryDsn: sentryDsn,
222
+ type: detected.type,
223
+ initializedAt: new Date().toISOString(),
224
+ };
225
+ fs.writeFileSync(path.join(projectRoot, 'mysystem.json'), JSON.stringify(config, null, 2), 'utf8');
226
+ console.log(' ✅ Created mysystem.json');
227
+ // Output Setup Guidance
228
+ const cfUrl = `https://console.aws.amazon.com/cloudformation/home?region=${awsRegion}#/stacks/create/review?templateURL=https://raw.githubusercontent.com/ai-production-standard/mysystem/main/templates/terraform/bootstrap-oidc.yaml&stackName=mysystem-oidc-${appName}`;
229
+ console.log('\n\x1b[32m✨ MySystem deployment files initialized successfully!\x1b[0m\n');
230
+ console.log('\x1b[1m🚀 NEXT STEPS TO DEPLOY:\x1b[0m\n');
231
+ console.log('\x1b[33mStep 1: Connect AWS to GitHub (One-Time Setup)\x1b[0m');
232
+ console.log('--------------------------------------------');
233
+ console.log('Click this link to configure a secure OIDC Trust stack in AWS:');
234
+ console.log(`\x1b[36m${cfUrl}\x1b[0m\n`);
235
+ console.log('Fill in parameters:');
236
+ console.log(' - GitHubOrg: Your GitHub Org or username (case-sensitive)');
237
+ console.log(' - GitHubRepo: Your GitHub repository name (case-sensitive)');
238
+ console.log('\nClick "Create Stack". Once complete, copy the "RoleARN" from outputs.');
239
+ console.log('\n\x1b[33mStep 2: Save Role ARN as a GitHub Secret\x1b[0m');
240
+ console.log('----------------------------------------');
241
+ console.log('Go to your GitHub repository -> Settings -> Secrets and variables -> Actions.');
242
+ console.log('Add a new secret:');
243
+ console.log(' - Name: \x1b[1mAWS_ROLE_ARN\x1b[0m');
244
+ console.log(' - Value: \x1b[32m<copied-role-arn>\x1b[0m');
245
+ console.log('\n\x1b[33mStep 3: Tell your AI coding agent to push and deploy!\x1b[0m');
246
+ console.log('----------------------------------------------------');
247
+ console.log('Simply type in your AI chat:');
248
+ console.log(' \x1b[32m"I have set up the AWS secrets. Push changes to deploy."\x1b[0m');
249
+ console.log('\nThe agent will commit, push, and initiate the GitHub Actions pipeline.\n');
250
+ }
251
+ catch (e) {
252
+ rl.close();
253
+ console.error(`\x1b[31mError during initialization: ${e.message}\x1b[0m`);
254
+ }
255
+ }
@@ -0,0 +1 @@
1
+ export declare function runLogs(projectRoot: string): Promise<void>;
@@ -0,0 +1,76 @@
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.runLogs = runLogs;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const child_process_1 = require("child_process");
40
+ const installer_1 = require("../utils/installer");
41
+ async function runLogs(projectRoot) {
42
+ const configPath = path.join(projectRoot, 'mysystem.json');
43
+ if (!fs.existsSync(configPath)) {
44
+ console.error('\x1b[31mError: MySystem is not initialized in this directory.\x1b[0m');
45
+ console.error('Run `npx mysystem init` first.');
46
+ process.exit(1);
47
+ }
48
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
49
+ const logGroupName = `/ecs/${config.name}`;
50
+ const region = config.region;
51
+ // 1. Ensure AWS CLI is installed
52
+ const hasAwsCli = await (0, installer_1.ensureAwsCli)();
53
+ if (!hasAwsCli) {
54
+ console.log('\nAlternatively, you can view logs in your browser via the AWS CloudWatch Console:');
55
+ console.log(`👉 \x1b[36mhttps://console.aws.amazon.com/cloudwatch/home?region=${region}#logsV2:log-groups/log-group/%252Fecs%252F${config.name}\x1b[0m\n`);
56
+ process.exit(1);
57
+ }
58
+ console.log(`\n☁️ Streaming logs for \x1b[36m${config.name}\x1b[0m [Log Group: ${logGroupName}] in \x1b[32m${region}\x1b[0m...`);
59
+ console.log('Press \x1b[33mCtrl+C\x1b[0m to stop streaming.\n');
60
+ // Spawn AWS CLI log tailing command
61
+ const awsLog = (0, child_process_1.spawn)('aws', ['logs', 'tail', logGroupName, '--follow', '--region', region], {
62
+ stdio: 'inherit',
63
+ shell: true,
64
+ });
65
+ awsLog.on('close', (code) => {
66
+ if (code !== 0 && code !== null) {
67
+ console.error(`\n\x1b[31m❌ AWS CLI log tailing exited with code ${code}.\x1b[0m`);
68
+ console.log('Make sure your AWS credentials are configured locally by running `aws configure`.');
69
+ }
70
+ });
71
+ // Handle Ctrl+C gracefully
72
+ process.on('SIGINT', () => {
73
+ awsLog.kill();
74
+ process.exit(0);
75
+ });
76
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};