@startanaicompany/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/PUBLISHING.md ADDED
@@ -0,0 +1,108 @@
1
+ # Publishing Guide for @startanaicompany/cli
2
+
3
+ ## Prerequisites
4
+
5
+ 1. **npm account** created
6
+ 2. **Organization @startanaicompany** created on npm
7
+ 3. Logged in to npm: `npm login`
8
+
9
+ ## Publishing Steps
10
+
11
+ ### 1. First Time Setup
12
+
13
+ ```bash
14
+ cd ~/projects/saac-cli
15
+
16
+ # Login to npm
17
+ npm login
18
+
19
+ # Test the package locally first
20
+ npm link
21
+
22
+ # Test the CLI
23
+ saac --help
24
+ saac register --help
25
+ ```
26
+
27
+ ### 2. Publish to npm
28
+
29
+ ```bash
30
+ # Make sure everything is committed
31
+ git status
32
+
33
+ # Publish as public package
34
+ npm publish --access public
35
+ ```
36
+
37
+ ### 3. Verify Publication
38
+
39
+ ```bash
40
+ # Check on npm
41
+ open https://www.npmjs.com/package/@startanaicompany/cli
42
+
43
+ # Test global installation
44
+ npm install -g @startanaicompany/cli
45
+
46
+ # Test it works
47
+ saac --help
48
+ ```
49
+
50
+ ## Updating the Package
51
+
52
+ ```bash
53
+ # Update version in package.json
54
+ npm version patch # or minor, or major
55
+
56
+ # Publish update
57
+ npm publish
58
+ ```
59
+
60
+ ## Version Numbers
61
+
62
+ - **patch** (1.0.0 → 1.0.1): Bug fixes
63
+ - **minor** (1.0.0 → 1.1.0): New features (backwards compatible)
64
+ - **major** (1.0.0 → 2.0.0): Breaking changes
65
+
66
+ ## Current Status
67
+
68
+ - ✅ Package structure created
69
+ - ✅ Core commands implemented (register, login, verify, deploy)
70
+ - ✅ Beautiful CLI with colors and spinners
71
+ - ✅ Configuration management
72
+ - ✅ API client
73
+ - ✅ Comprehensive README
74
+ - ⏳ Ready to publish!
75
+
76
+ ## Testing Before Publishing
77
+
78
+ ```bash
79
+ # Link locally
80
+ npm link
81
+
82
+ # Test all commands
83
+ saac --help
84
+ saac register
85
+ saac login
86
+ saac deploy
87
+
88
+ # Unlink when done
89
+ npm unlink -g @startanaicompany/cli
90
+ ```
91
+
92
+ ## Post-Publishing
93
+
94
+ After publishing, users can install with:
95
+
96
+ ```bash
97
+ npm install -g @startanaicompany/cli
98
+ ```
99
+
100
+ Then use:
101
+
102
+ ```bash
103
+ saac register
104
+ saac create my-site
105
+ saac deploy
106
+ ```
107
+
108
+ Perfect! 🚀
package/README.md ADDED
@@ -0,0 +1,325 @@
1
+ # @startanaicompany/cli
2
+
3
+ > Official CLI for StartAnAiCompany.com - Deploy AI recruitment sites with ease
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@startanaicompany/cli.svg)](https://www.npmjs.com/package/@startanaicompany/cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - ✨ **Simple & Intuitive** - Deploy with a single command
11
+ - 🔐 **Secure** - API key-based authentication
12
+ - 🚀 **Fast** - Optimized for quick deployments
13
+ - 📦 **Zero Configuration** - Works out of the box
14
+ - 🎨 **Beautiful CLI** - Color-coded output and progress indicators
15
+ - 🔄 **Auto-healing** - Automatically fixes common deployment issues
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g @startanaicompany/cli
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # 1. Register for an account
27
+ saac register
28
+
29
+ # 2. Verify your email (check MailHog)
30
+ saac verify <code>
31
+
32
+ # 3. Create a new application
33
+ saac create my-recruitment-site
34
+
35
+ # 4. Deploy!
36
+ saac deploy
37
+ ```
38
+
39
+ ## Commands
40
+
41
+ ### Authentication
42
+
43
+ #### `saac register`
44
+ Register for a new account
45
+
46
+ ```bash
47
+ saac register
48
+ saac register --email user@example.com
49
+ ```
50
+
51
+ #### `saac login`
52
+ Login with existing credentials
53
+
54
+ ```bash
55
+ saac login
56
+ saac login --email user@example.com --api-key cw_...
57
+ ```
58
+
59
+ #### `saac verify <code>`
60
+ Verify your email address
61
+
62
+ ```bash
63
+ saac verify 123456
64
+ ```
65
+
66
+ **Note:** Check MailHog at https://mailhog.goryan.io for verification codes
67
+
68
+ #### `saac logout`
69
+ Clear saved credentials
70
+
71
+ ```bash
72
+ saac logout
73
+ ```
74
+
75
+ ### Application Management
76
+
77
+ #### `saac init`
78
+ Initialize SAAC in an existing project
79
+
80
+ ```bash
81
+ saac init
82
+ saac init --name myapp --subdomain myapp
83
+ ```
84
+
85
+ #### `saac create [name]`
86
+ Create a new application
87
+
88
+ ```bash
89
+ saac create
90
+ saac create my-site --subdomain mysite
91
+ ```
92
+
93
+ Options:
94
+ - `-s, --subdomain <subdomain>` - Subdomain for your site
95
+ - `-d, --domain-suffix <suffix>` - Domain suffix (default: startanaicompany.com)
96
+ - `-r, --repository <url>` - Git repository URL
97
+ - `-b, --branch <branch>` - Git branch (default: master)
98
+
99
+ #### `saac deploy`
100
+ Deploy your application
101
+
102
+ ```bash
103
+ saac deploy
104
+ saac deploy --force
105
+ ```
106
+
107
+ #### `saac logs`
108
+ View application logs
109
+
110
+ ```bash
111
+ saac logs
112
+ saac logs --tail 50
113
+ saac logs --follow
114
+ ```
115
+
116
+ ### Environment Variables
117
+
118
+ #### `saac env set <vars...>`
119
+ Set environment variables
120
+
121
+ ```bash
122
+ saac env set KEY=value
123
+ saac env set KEY1=value1 KEY2=value2
124
+ ```
125
+
126
+ #### `saac env get [key]`
127
+ Get environment variable(s)
128
+
129
+ ```bash
130
+ saac env get
131
+ saac env get KEY
132
+ ```
133
+
134
+ #### `saac env list`
135
+ List all environment variables
136
+
137
+ ```bash
138
+ saac env list
139
+ ```
140
+
141
+ ### Domain Management
142
+
143
+ #### `saac domain set <subdomain>`
144
+ Change your application subdomain
145
+
146
+ ```bash
147
+ saac domain set newsubdomain
148
+ ```
149
+
150
+ #### `saac domain show`
151
+ Show current domain
152
+
153
+ ```bash
154
+ saac domain show
155
+ ```
156
+
157
+ ### Information
158
+
159
+ #### `saac list`
160
+ List all your applications
161
+
162
+ ```bash
163
+ saac list
164
+ saac ls
165
+ ```
166
+
167
+ #### `saac status`
168
+ Show current application status
169
+
170
+ ```bash
171
+ saac status
172
+ ```
173
+
174
+ #### `saac whoami`
175
+ Show current user information
176
+
177
+ ```bash
178
+ saac whoami
179
+ ```
180
+
181
+ ### Deletion
182
+
183
+ #### `saac delete`
184
+ Delete current application
185
+
186
+ ```bash
187
+ saac delete
188
+ saac delete --yes # Skip confirmation
189
+ saac rm # Alias
190
+ ```
191
+
192
+ ## Configuration
193
+
194
+ ### Global Configuration
195
+
196
+ Stored in `~/.saac/config.json`:
197
+
198
+ ```json
199
+ {
200
+ "apiUrl": "https://apps.startanaicompany.com/api/v1",
201
+ "user": {
202
+ "email": "user@example.com",
203
+ "userId": "...",
204
+ "apiKey": "cw_...",
205
+ "verified": true
206
+ }
207
+ }
208
+ ```
209
+
210
+ ### Project Configuration
211
+
212
+ Stored in `.saac/config.json` in your project:
213
+
214
+ ```json
215
+ {
216
+ "applicationUuid": "...",
217
+ "applicationName": "my-site",
218
+ "subdomain": "mysite",
219
+ "domainSuffix": "startanaicompany.com",
220
+ "gitRepository": "git@git.startanaicompany.com:user/repo.git"
221
+ }
222
+ ```
223
+
224
+ ## Workflow Example
225
+
226
+ ```bash
227
+ # Step 1: Register and verify
228
+ saac register --email dev@company.com
229
+ # Check MailHog for code
230
+ saac verify 123456
231
+
232
+ # Step 2: Clone or create your project
233
+ git clone git@git.startanaicompany.com:user/mysite.git
234
+ cd mysite
235
+
236
+ # Step 3: Initialize SAAC
237
+ saac init --subdomain mycompany
238
+
239
+ # Step 4: Deploy
240
+ saac deploy
241
+
242
+ # Step 5: View logs
243
+ saac logs --follow
244
+
245
+ # Step 6: Update environment variables
246
+ saac env set COMPANY_NAME="My Company"
247
+ saac deploy # Redeploy to apply changes
248
+ ```
249
+
250
+ ## Environment Variables
251
+
252
+ The CLI sets these automatically:
253
+ - `DOMAIN` - Your application domain
254
+ - `COOLIFY_*` - Coolify API configuration (managed by wrapper)
255
+
256
+ You can set custom variables:
257
+ ```bash
258
+ saac env set COMPANY_NAME="Acme Corp"
259
+ saac env set PRIMARY_COLOR="#2563EB"
260
+ saac env set CONTACT_EMAIL="contact@acme.com"
261
+ ```
262
+
263
+ ## Troubleshooting
264
+
265
+ ### "Not logged in"
266
+ ```bash
267
+ saac login
268
+ ```
269
+
270
+ ### "No application found"
271
+ ```bash
272
+ saac init
273
+ # or
274
+ saac create
275
+ ```
276
+
277
+ ### "Email not verified"
278
+ Check MailHog at https://mailhog.goryan.io and run:
279
+ ```bash
280
+ saac verify <code>
281
+ ```
282
+
283
+ ### Deployment fails
284
+ ```bash
285
+ # View logs to see what went wrong
286
+ saac logs
287
+
288
+ # Try force deploy
289
+ saac deploy --force
290
+ ```
291
+
292
+ ## Development
293
+
294
+ ```bash
295
+ # Clone repository
296
+ git clone https://github.com/startanaicompany/cli.git
297
+ cd cli
298
+
299
+ # Install dependencies
300
+ npm install
301
+
302
+ # Link for local development
303
+ npm link
304
+
305
+ # Now you can use `saac` command
306
+ saac --help
307
+ ```
308
+
309
+ ## Support
310
+
311
+ - 📧 Email: support@startanaicompany.com
312
+ - 🐛 Issues: https://github.com/startanaicompany/cli/issues
313
+ - 📚 Docs: https://startanaicompany.com/docs
314
+
315
+ ## License
316
+
317
+ MIT © StartAnAiCompany
318
+
319
+ ## Contributing
320
+
321
+ Pull requests are welcome! Please read our contributing guidelines first.
322
+
323
+ ---
324
+
325
+ Made with ❤️ by StartAnAiCompany
package/bin/saac.js ADDED
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SAAC CLI - StartAnAiCompany Official CLI
5
+ * Deploy AI recruitment sites with ease
6
+ */
7
+
8
+ const { program } = require('commander');
9
+ const chalk = require('chalk');
10
+ const pkg = require('../package.json');
11
+
12
+ // Import commands
13
+ const register = require('../src/commands/register');
14
+ const login = require('../src/commands/login');
15
+ const verify = require('../src/commands/verify');
16
+ const logout = require('../src/commands/logout');
17
+ const init = require('../src/commands/init');
18
+ const create = require('../src/commands/create');
19
+ const deploy = require('../src/commands/deploy');
20
+ const logs = require('../src/commands/logs');
21
+ const env = require('../src/commands/env');
22
+ const domain = require('../src/commands/domain');
23
+ const deleteCmd = require('../src/commands/delete');
24
+ const list = require('../src/commands/list');
25
+ const status = require('../src/commands/status');
26
+ const whoami = require('../src/commands/whoami');
27
+
28
+ // Configure CLI
29
+ program
30
+ .name('saac')
31
+ .description(chalk.cyan('Official CLI for StartAnAiCompany.com'))
32
+ .version(pkg.version, '-v, --version', 'Output the current version')
33
+ .helpOption('-h, --help', 'Display help for command');
34
+
35
+ // Authentication commands
36
+ program
37
+ .command('register')
38
+ .description('Register for a new account')
39
+ .option('-e, --email <email>', 'Email address')
40
+ .option('--gitea-username <username>', 'Gitea username (auto-detected if not provided)')
41
+ .action(register);
42
+
43
+ program
44
+ .command('login')
45
+ .description('Login with existing account')
46
+ .option('-e, --email <email>', 'Email address')
47
+ .option('-k, --api-key <key>', 'API key')
48
+ .action(login);
49
+
50
+ program
51
+ .command('verify <code>')
52
+ .description('Verify email with verification code')
53
+ .action(verify);
54
+
55
+ program
56
+ .command('logout')
57
+ .description('Clear saved credentials')
58
+ .action(logout);
59
+
60
+ // Application management
61
+ program
62
+ .command('init')
63
+ .description('Initialize a new SAAC project in current directory')
64
+ .option('-n, --name <name>', 'Application name')
65
+ .option('-s, --subdomain <subdomain>', 'Subdomain')
66
+ .option('-d, --domain-suffix <suffix>', 'Domain suffix', 'startanaicompany.com')
67
+ .option('-r, --repository <url>', 'Git repository URL')
68
+ .action(init);
69
+
70
+ program
71
+ .command('create [name]')
72
+ .description('Create a new application')
73
+ .option('-s, --subdomain <subdomain>', 'Subdomain')
74
+ .option('-d, --domain-suffix <suffix>', 'Domain suffix', 'startanaicompany.com')
75
+ .option('-r, --repository <url>', 'Git repository URL')
76
+ .option('-b, --branch <branch>', 'Git branch', 'master')
77
+ .action(create);
78
+
79
+ program
80
+ .command('deploy')
81
+ .description('Deploy current application')
82
+ .option('-f, --force', 'Force deployment')
83
+ .action(deploy);
84
+
85
+ program
86
+ .command('logs')
87
+ .description('View application logs')
88
+ .option('-t, --tail <lines>', 'Number of lines to show', '100')
89
+ .option('-f, --follow', 'Follow log output')
90
+ .action(logs);
91
+
92
+ // Environment variable commands
93
+ const envCommand = program
94
+ .command('env')
95
+ .description('Manage environment variables');
96
+
97
+ envCommand
98
+ .command('set <vars...>')
99
+ .description('Set environment variables (KEY=VALUE format)')
100
+ .action(env.set);
101
+
102
+ envCommand
103
+ .command('get [key]')
104
+ .description('Get environment variable(s)')
105
+ .action(env.get);
106
+
107
+ envCommand
108
+ .command('list')
109
+ .alias('ls')
110
+ .description('List all environment variables')
111
+ .action(env.list);
112
+
113
+ // Domain management
114
+ const domainCommand = program
115
+ .command('domain')
116
+ .description('Manage application domain');
117
+
118
+ domainCommand
119
+ .command('set <subdomain>')
120
+ .description('Change subdomain')
121
+ .option('-d, --domain-suffix <suffix>', 'Domain suffix', 'startanaicompany.com')
122
+ .action(domain.set);
123
+
124
+ domainCommand
125
+ .command('show')
126
+ .description('Show current domain')
127
+ .action(domain.show);
128
+
129
+ // Application info
130
+ program
131
+ .command('list')
132
+ .alias('ls')
133
+ .description('List all your applications')
134
+ .action(list);
135
+
136
+ program
137
+ .command('status')
138
+ .description('Show current application status')
139
+ .action(status);
140
+
141
+ program
142
+ .command('whoami')
143
+ .description('Show current user information')
144
+ .action(whoami);
145
+
146
+ // Deletion
147
+ program
148
+ .command('delete')
149
+ .alias('rm')
150
+ .description('Delete current application')
151
+ .option('-y, --yes', 'Skip confirmation')
152
+ .action(deleteCmd);
153
+
154
+ // Error handling
155
+ program.on('command:*', function () {
156
+ console.error(chalk.red('\n Invalid command: %s\n'), program.args.join(' '));
157
+ console.log(chalk.yellow(' Run'), chalk.cyan('saac --help'), chalk.yellow('to see available commands'));
158
+ process.exit(1);
159
+ });
160
+
161
+ // Parse arguments
162
+ program.parse(process.argv);
163
+
164
+ // Show help if no command provided
165
+ if (!process.argv.slice(2).length) {
166
+ program.outputHelp();
167
+ }
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@startanaicompany/cli",
3
+ "version": "1.0.0",
4
+ "description": "Official CLI for StartAnAiCompany.com - Deploy AI recruitment sites with ease",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "saac": "./bin/saac.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "dev": "node bin/saac.js",
12
+ "lint": "eslint src/"
13
+ },
14
+ "keywords": [
15
+ "saac",
16
+ "startanaicompany",
17
+ "deployment",
18
+ "cli",
19
+ "ai",
20
+ "recruitment",
21
+ "coolify"
22
+ ],
23
+ "author": "StartAnAiCompany <support@startanaicompany.com>",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/startanaicompany/cli.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/startanaicompany/cli/issues"
31
+ },
32
+ "homepage": "https://github.com/startanaicompany/cli#readme",
33
+ "engines": {
34
+ "node": ">=16.0.0"
35
+ },
36
+ "dependencies": {
37
+ "axios": "^1.6.0",
38
+ "chalk": "^4.1.2",
39
+ "commander": "^11.1.0",
40
+ "conf": "^10.2.0",
41
+ "inquirer": "^8.2.5",
42
+ "ora": "^5.4.1",
43
+ "boxen": "^5.1.2",
44
+ "table": "^6.8.1",
45
+ "dotenv": "^16.3.1",
46
+ "validator": "^13.11.0"
47
+ },
48
+ "devDependencies": {
49
+ "eslint": "^8.54.0"
50
+ }
51
+ }
@@ -0,0 +1,4 @@
1
+ // TODO: Implement create command
2
+ module.exports = async function() {
3
+ console.log('create command - Coming soon!');
4
+ };
@@ -0,0 +1,4 @@
1
+ // TODO: Implement delete command
2
+ module.exports = async function() {
3
+ console.log('delete command - Coming soon!');
4
+ };
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Deploy command
3
+ */
4
+
5
+ const api = require('../lib/api');
6
+ const { getProjectConfig, isAuthenticated } = require('../lib/config');
7
+ const logger = require('../lib/logger');
8
+
9
+ async function deploy(options) {
10
+ try {
11
+ // Check authentication
12
+ if (!isAuthenticated()) {
13
+ logger.error('Not logged in. Run: saac login');
14
+ process.exit(1);
15
+ }
16
+
17
+ // Check for project config
18
+ const projectConfig = getProjectConfig();
19
+ if (!projectConfig || !projectConfig.applicationUuid) {
20
+ logger.error('No application found in current directory');
21
+ logger.info('Run: saac init or saac create');
22
+ process.exit(1);
23
+ }
24
+
25
+ const { applicationUuid, applicationName } = projectConfig;
26
+
27
+ logger.section(`Deploying ${applicationName}`);
28
+
29
+ const spin = logger.spinner('Triggering deployment...').start();
30
+
31
+ try {
32
+ const result = await api.deployApplication(applicationUuid);
33
+
34
+ spin.succeed('Deployment triggered!');
35
+
36
+ logger.newline();
37
+ logger.success('Deployment started successfully');
38
+ logger.newline();
39
+ logger.field('Application', applicationName);
40
+ logger.field('Status', result.status);
41
+ if (result.domain) {
42
+ logger.field('Domain', result.domain);
43
+ }
44
+ logger.field('Deployment ID', result.deployment_id);
45
+ logger.newline();
46
+ logger.info(
47
+ `View logs with: ${logger.chalk.yellow('saac logs --follow')}`
48
+ );
49
+
50
+ } catch (error) {
51
+ spin.fail('Deployment failed');
52
+ throw error;
53
+ }
54
+ } catch (error) {
55
+ logger.error(error.response?.data?.message || error.message);
56
+ process.exit(1);
57
+ }
58
+ }
59
+
60
+ module.exports = deploy;
@@ -0,0 +1,4 @@
1
+ // TODO: Implement domain command
2
+ module.exports = async function() {
3
+ console.log('domain command - Coming soon!');
4
+ };
@@ -0,0 +1,4 @@
1
+ // TODO: Implement env command
2
+ module.exports = async function() {
3
+ console.log('env command - Coming soon!');
4
+ };
@@ -0,0 +1,4 @@
1
+ // TODO: Implement init command
2
+ module.exports = async function() {
3
+ console.log('init command - Coming soon!');
4
+ };
@@ -0,0 +1,4 @@
1
+ // TODO: Implement list command
2
+ module.exports = async function() {
3
+ console.log('list command - Coming soon!');
4
+ };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Login command
3
+ */
4
+
5
+ const inquirer = require('inquirer');
6
+ const validator = require('validator');
7
+ const api = require('../lib/api');
8
+ const { saveUser } = require('../lib/config');
9
+ const logger = require('../lib/logger');
10
+
11
+ async function login(options) {
12
+ try {
13
+ logger.section('Login to StartAnAiCompany');
14
+
15
+ // Get email
16
+ let email = options.email;
17
+ if (!email) {
18
+ const answers = await inquirer.prompt([
19
+ {
20
+ type: 'input',
21
+ name: 'email',
22
+ message: 'Email address:',
23
+ validate: (input) => {
24
+ if (!validator.isEmail(input)) {
25
+ return 'Please enter a valid email address';
26
+ }
27
+ return true;
28
+ },
29
+ },
30
+ ]);
31
+ email = answers.email;
32
+ }
33
+
34
+ // Get API key
35
+ let apiKey = options.apiKey;
36
+ if (!apiKey) {
37
+ const answers = await inquirer.prompt([
38
+ {
39
+ type: 'password',
40
+ name: 'apiKey',
41
+ message: 'API Key:',
42
+ mask: '*',
43
+ },
44
+ ]);
45
+ apiKey = answers.apiKey;
46
+ }
47
+
48
+ // Verify credentials by getting user info
49
+ const spin = logger.spinner('Verifying credentials...').start();
50
+
51
+ try {
52
+ // Save temporarily to make API call
53
+ saveUser({ email, apiKey });
54
+
55
+ const userInfo = await api.getUserInfo();
56
+
57
+ spin.succeed('Login successful!');
58
+
59
+ // Update with full user info
60
+ saveUser({
61
+ email: userInfo.email,
62
+ userId: userInfo.id,
63
+ apiKey,
64
+ verified: userInfo.email_verified,
65
+ });
66
+
67
+ logger.newline();
68
+ logger.success('You are now logged in!');
69
+ logger.newline();
70
+ logger.field('Email', userInfo.email);
71
+ logger.field('Verified', userInfo.email_verified ? 'Yes' : 'No');
72
+
73
+ } catch (error) {
74
+ spin.fail('Login failed');
75
+ throw error;
76
+ }
77
+ } catch (error) {
78
+ logger.error(
79
+ error.response?.data?.message || 'Invalid credentials'
80
+ );
81
+ process.exit(1);
82
+ }
83
+ }
84
+
85
+ module.exports = login;
@@ -0,0 +1,9 @@
1
+ const { clearUser } = require('../lib/config');
2
+ const logger = require('../lib/logger');
3
+
4
+ async function logout() {
5
+ clearUser();
6
+ logger.success('Logged out successfully');
7
+ }
8
+
9
+ module.exports = logout;
@@ -0,0 +1,4 @@
1
+ // TODO: Implement logs command
2
+ module.exports = async function() {
3
+ console.log('logs command - Coming soon!');
4
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Register command - Create a new account
3
+ */
4
+
5
+ const inquirer = require('inquirer');
6
+ const validator = require('validator');
7
+ const api = require('../lib/api');
8
+ const { saveUser } = require('../lib/config');
9
+ const logger = require('../lib/logger');
10
+
11
+ async function register(options) {
12
+ try {
13
+ logger.section('Register for StartAnAiCompany');
14
+
15
+ // Get email (from flag or prompt)
16
+ let email = options.email;
17
+ if (!email) {
18
+ const answers = await inquirer.prompt([
19
+ {
20
+ type: 'input',
21
+ name: 'email',
22
+ message: 'Email address:',
23
+ validate: (input) => {
24
+ if (!validator.isEmail(input)) {
25
+ return 'Please enter a valid email address';
26
+ }
27
+ return true;
28
+ },
29
+ },
30
+ ]);
31
+ email = answers.email;
32
+ }
33
+
34
+ // Get Gitea username (optional, auto-detected from git)
35
+ let giteaUsername = options.giteaUsername;
36
+ if (!giteaUsername) {
37
+ const answers = await inquirer.prompt([
38
+ {
39
+ type: 'input',
40
+ name: 'giteaUsername',
41
+ message: 'Gitea username (optional, press Enter to skip):',
42
+ },
43
+ ]);
44
+ giteaUsername = answers.giteaUsername || undefined;
45
+ }
46
+
47
+ // Register via API
48
+ const spin = logger.spinner('Creating account...').start();
49
+
50
+ try {
51
+ const result = await api.register(email, giteaUsername);
52
+
53
+ spin.succeed('Account created successfully!');
54
+
55
+ // Save user info
56
+ saveUser({
57
+ email: result.email || email,
58
+ userId: result.user_id,
59
+ apiKey: result.api_key,
60
+ verified: result.verified || false,
61
+ });
62
+
63
+ logger.newline();
64
+ logger.success('Registration complete!');
65
+ logger.newline();
66
+
67
+ if (!result.verified) {
68
+ logger.info(
69
+ `A verification code has been sent to ${logger.chalk.cyan(email)}`
70
+ );
71
+ logger.info(
72
+ `Check your email and run: ${logger.chalk.yellow(
73
+ 'saac verify <code>'
74
+ )}`
75
+ );
76
+ logger.newline();
77
+ logger.warn(
78
+ 'Note: Check MailHog at https://mailhog.goryan.io for the verification code'
79
+ );
80
+ }
81
+
82
+ logger.newline();
83
+ logger.field('Email', email);
84
+ logger.field('API Key', result.api_key.substring(0, 20) + '...');
85
+
86
+ } catch (error) {
87
+ spin.fail('Registration failed');
88
+ throw error;
89
+ }
90
+ } catch (error) {
91
+ logger.error(error.response?.data?.message || error.message);
92
+ process.exit(1);
93
+ }
94
+ }
95
+
96
+ module.exports = register;
@@ -0,0 +1,4 @@
1
+ // TODO: Implement status command
2
+ module.exports = async function() {
3
+ console.log('status command - Coming soon!');
4
+ };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Verify email command
3
+ */
4
+
5
+ const api = require('../lib/api');
6
+ const { getUser, saveUser } = require('../lib/config');
7
+ const logger = require('../lib/logger');
8
+
9
+ async function verify(code) {
10
+ try {
11
+ const user = getUser();
12
+
13
+ if (!user || !user.email) {
14
+ logger.error('No user found. Please register first with: saac register');
15
+ process.exit(1);
16
+ }
17
+
18
+ if (user.verified) {
19
+ logger.warn('Email already verified!');
20
+ return;
21
+ }
22
+
23
+ logger.section('Verify Email');
24
+
25
+ const spin = logger.spinner('Verifying email...').start();
26
+
27
+ try {
28
+ const result = await api.verifyEmail(user.email, code);
29
+
30
+ spin.succeed('Email verified successfully!');
31
+
32
+ // Update user verification status
33
+ saveUser({
34
+ ...user,
35
+ verified: true,
36
+ });
37
+
38
+ logger.newline();
39
+ logger.success('Your account is now verified!');
40
+ logger.info('You can now create and deploy applications.');
41
+
42
+ } catch (error) {
43
+ spin.fail('Verification failed');
44
+ throw error;
45
+ }
46
+ } catch (error) {
47
+ logger.error(
48
+ error.response?.data?.message || 'Invalid verification code'
49
+ );
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ module.exports = verify;
@@ -0,0 +1,4 @@
1
+ // TODO: Implement whoami command
2
+ module.exports = async function() {
3
+ console.log('whoami command - Coming soon!');
4
+ };
package/src/lib/api.js ADDED
@@ -0,0 +1,156 @@
1
+ /**
2
+ * API Client for SAAC Wrapper API
3
+ */
4
+
5
+ const axios = require('axios');
6
+ const { getApiUrl, getUser } = require('./config');
7
+
8
+ /**
9
+ * Create axios instance with base configuration
10
+ */
11
+ function createClient() {
12
+ const user = getUser();
13
+
14
+ return axios.create({
15
+ baseURL: getApiUrl(),
16
+ timeout: 30000,
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ ...(user?.apiKey && { 'X-API-Key': user.apiKey }),
20
+ },
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Register a new user
26
+ */
27
+ async function register(email, giteaUsername) {
28
+ const client = createClient();
29
+ const response = await client.post('/register', {
30
+ email,
31
+ gitea_username: giteaUsername,
32
+ });
33
+ return response.data;
34
+ }
35
+
36
+ /**
37
+ * Verify email with code
38
+ */
39
+ async function verifyEmail(email, code) {
40
+ const client = createClient();
41
+ const response = await client.post('/users/verify', {
42
+ email,
43
+ verification_code: code,
44
+ });
45
+ return response.data;
46
+ }
47
+
48
+ /**
49
+ * Get current user info
50
+ */
51
+ async function getUserInfo() {
52
+ const client = createClient();
53
+ const response = await client.get('/users/me');
54
+ return response.data;
55
+ }
56
+
57
+ /**
58
+ * Create a new application
59
+ */
60
+ async function createApplication(appData) {
61
+ const client = createClient();
62
+ const response = await client.post('/applications', appData);
63
+ return response.data;
64
+ }
65
+
66
+ /**
67
+ * List all applications
68
+ */
69
+ async function listApplications() {
70
+ const client = createClient();
71
+ const response = await client.get('/applications');
72
+ return response.data;
73
+ }
74
+
75
+ /**
76
+ * Get application details
77
+ */
78
+ async function getApplication(uuid) {
79
+ const client = createClient();
80
+ const response = await client.get(`/applications/${uuid}`);
81
+ return response.data;
82
+ }
83
+
84
+ /**
85
+ * Deploy application
86
+ */
87
+ async function deployApplication(uuid) {
88
+ const client = createClient();
89
+ const response = await client.post(`/applications/${uuid}/deploy`);
90
+ return response.data;
91
+ }
92
+
93
+ /**
94
+ * Get application logs
95
+ */
96
+ async function getApplicationLogs(uuid, params = {}) {
97
+ const client = createClient();
98
+ const response = await client.get(`/applications/${uuid}/logs`, { params });
99
+ return response.data;
100
+ }
101
+
102
+ /**
103
+ * Update environment variables
104
+ */
105
+ async function updateEnvironmentVariables(uuid, variables) {
106
+ const client = createClient();
107
+ const response = await client.patch(`/applications/${uuid}/env`, {
108
+ variables,
109
+ });
110
+ return response.data;
111
+ }
112
+
113
+ /**
114
+ * Update application domain
115
+ */
116
+ async function updateDomain(uuid, subdomain, domainSuffix) {
117
+ const client = createClient();
118
+ const response = await client.patch(`/applications/${uuid}/domain`, {
119
+ subdomain,
120
+ domain_suffix: domainSuffix,
121
+ });
122
+ return response.data;
123
+ }
124
+
125
+ /**
126
+ * Delete application
127
+ */
128
+ async function deleteApplication(uuid) {
129
+ const client = createClient();
130
+ const response = await client.delete(`/applications/${uuid}`);
131
+ return response.data;
132
+ }
133
+
134
+ /**
135
+ * Health check
136
+ */
137
+ async function healthCheck() {
138
+ const client = createClient();
139
+ const response = await client.get('/health');
140
+ return response.data;
141
+ }
142
+
143
+ module.exports = {
144
+ register,
145
+ verifyEmail,
146
+ getUserInfo,
147
+ createApplication,
148
+ listApplications,
149
+ getApplication,
150
+ deployApplication,
151
+ getApplicationLogs,
152
+ updateEnvironmentVariables,
153
+ updateDomain,
154
+ deleteApplication,
155
+ healthCheck,
156
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Configuration management
3
+ * Handles both global (~/.saac/config.json) and project (.saac/config.json) configs
4
+ */
5
+
6
+ const Conf = require('conf');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ // Global configuration (user credentials, API settings)
11
+ const globalConfig = new Conf({
12
+ projectName: 'saac',
13
+ configName: 'config',
14
+ defaults: {
15
+ apiUrl: 'https://apps.startanaicompany.com/api/v1',
16
+ giteaUrl: 'https://git.startanaicompany.com/api/v1',
17
+ },
18
+ });
19
+
20
+ /**
21
+ * Get project configuration (from .saac/config.json in current directory)
22
+ */
23
+ function getProjectConfig() {
24
+ const configPath = path.join(process.cwd(), '.saac', 'config.json');
25
+
26
+ if (!fs.existsSync(configPath)) {
27
+ return null;
28
+ }
29
+
30
+ try {
31
+ const data = fs.readFileSync(configPath, 'utf8');
32
+ return JSON.parse(data);
33
+ } catch (error) {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Save project configuration
40
+ */
41
+ function saveProjectConfig(config) {
42
+ const configDir = path.join(process.cwd(), '.saac');
43
+ const configPath = path.join(configDir, 'config.json');
44
+
45
+ // Create .saac directory if it doesn't exist
46
+ if (!fs.existsSync(configDir)) {
47
+ fs.mkdirSync(configDir, { recursive: true });
48
+ }
49
+
50
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
51
+ }
52
+
53
+ /**
54
+ * Check if user is authenticated
55
+ */
56
+ function isAuthenticated() {
57
+ const apiKey = globalConfig.get('user.apiKey');
58
+ const email = globalConfig.get('user.email');
59
+ return !!(apiKey && email);
60
+ }
61
+
62
+ /**
63
+ * Get user info
64
+ */
65
+ function getUser() {
66
+ return globalConfig.get('user') || null;
67
+ }
68
+
69
+ /**
70
+ * Save user info
71
+ */
72
+ function saveUser(user) {
73
+ globalConfig.set('user', user);
74
+ }
75
+
76
+ /**
77
+ * Clear user info (logout)
78
+ */
79
+ function clearUser() {
80
+ globalConfig.delete('user');
81
+ }
82
+
83
+ /**
84
+ * Get API URL
85
+ */
86
+ function getApiUrl() {
87
+ return globalConfig.get('apiUrl');
88
+ }
89
+
90
+ /**
91
+ * Get Gitea URL
92
+ */
93
+ function getGiteaUrl() {
94
+ return globalConfig.get('giteaUrl');
95
+ }
96
+
97
+ module.exports = {
98
+ globalConfig,
99
+ getProjectConfig,
100
+ saveProjectConfig,
101
+ isAuthenticated,
102
+ getUser,
103
+ saveUser,
104
+ clearUser,
105
+ getApiUrl,
106
+ getGiteaUrl,
107
+ };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Pretty logging with colors and symbols
3
+ */
4
+
5
+ const chalk = require('chalk');
6
+ const ora = require('ora');
7
+ const boxen = require('boxen');
8
+
9
+ /**
10
+ * Log success message
11
+ */
12
+ function success(message) {
13
+ console.log(chalk.green('✓'), message);
14
+ }
15
+
16
+ /**
17
+ * Log error message
18
+ */
19
+ function error(message) {
20
+ console.log(chalk.red('✗'), message);
21
+ }
22
+
23
+ /**
24
+ * Log warning message
25
+ */
26
+ function warn(message) {
27
+ console.log(chalk.yellow('⚠'), message);
28
+ }
29
+
30
+ /**
31
+ * Log info message
32
+ */
33
+ function info(message) {
34
+ console.log(chalk.blue('ℹ'), message);
35
+ }
36
+
37
+ /**
38
+ * Log plain message
39
+ */
40
+ function log(message) {
41
+ console.log(message);
42
+ }
43
+
44
+ /**
45
+ * Create a spinner
46
+ */
47
+ function spinner(text) {
48
+ return ora({
49
+ text,
50
+ color: 'cyan',
51
+ });
52
+ }
53
+
54
+ /**
55
+ * Log a boxed message
56
+ */
57
+ function box(message, options = {}) {
58
+ console.log(
59
+ boxen(message, {
60
+ padding: 1,
61
+ margin: 1,
62
+ borderStyle: 'round',
63
+ borderColor: 'cyan',
64
+ ...options,
65
+ })
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Log section header
71
+ */
72
+ function section(title) {
73
+ console.log('\n' + chalk.bold.cyan(title));
74
+ console.log(chalk.gray('─'.repeat(title.length)));
75
+ }
76
+
77
+ /**
78
+ * Log key-value pair
79
+ */
80
+ function field(key, value) {
81
+ console.log(chalk.gray(` ${key}:`), chalk.white(value));
82
+ }
83
+
84
+ /**
85
+ * Log newline
86
+ */
87
+ function newline() {
88
+ console.log('');
89
+ }
90
+
91
+ module.exports = {
92
+ success,
93
+ error,
94
+ warn,
95
+ info,
96
+ log,
97
+ spinner,
98
+ box,
99
+ section,
100
+ field,
101
+ newline,
102
+ chalk,
103
+ };