@startanaicompany/cli 1.6.0 → 1.8.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/CLAUDE.md CHANGED
@@ -1,196 +1,59 @@
1
1
  # CLAUDE.md
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3
+ This file provides guidance to Claude Code when working with the SAAC CLI codebase.
4
4
 
5
5
  ## Project Overview
6
6
 
7
- SAAC CLI is the official command-line interface for StartAnAiCompany.com, enabling users to deploy AI recruitment sites through a wrapper API that interfaces with Coolify. The CLI is built with Node.js and uses the Commander.js framework for command structure.
7
+ SAAC CLI is the official command-line interface for StartAnAiCompany.com. Built with Node.js and Commander.js, it interfaces with a wrapper API that manages Coolify deployments.
8
8
 
9
- ## Key Commands
9
+ **Current Version:** 1.6.0
10
10
 
11
- ### Development
12
- ```bash
13
- # Run CLI locally during development
14
- npm run dev
15
-
16
- # Lint code
17
- npm run lint
11
+ ## Development Commands
18
12
 
19
- # Link for local testing
20
- npm link
21
- ```
22
-
23
- ### After linking, test with:
24
13
  ```bash
25
- saac --help
26
- saac register
27
- saac login
14
+ npm run dev # Run CLI locally
15
+ npm run lint # Lint code
16
+ npm link # Link for local testing
28
17
  ```
29
18
 
30
19
  ## Architecture
31
20
 
32
- ### Configuration System (Dual-Level)
33
-
34
- The CLI maintains two separate configuration files:
21
+ ### Configuration System
35
22
 
36
- 1. **Global Config** (`~/.config/startanaicompany/config.json`)
37
- - Managed by the `conf` package with custom cwd path
38
- - Stores user credentials (email, userId, sessionToken, expiresAt)
39
- - Stores API URLs (wrapper API, Git server)
40
- - Persists across all projects
41
- - Note: Uses explicit path to avoid `-nodejs` suffix that Conf adds by default
23
+ **Global Config** (`~/.config/startanaicompany/config.json`):
24
+ - User credentials (email, userId, sessionToken, expiresAt)
25
+ - API URLs (wrapper API, Git server)
26
+ - Uses `conf` package with explicit path (avoids `-nodejs` suffix)
42
27
 
43
- 2. **Project Config** (`.saac/config.json` in project directory)
44
- - Manually managed via fs operations
45
- - Stores application-specific data (applicationUuid, applicationName, subdomain, gitRepository)
46
- - Required for deployment commands
47
-
48
- **Important Pattern**: Commands check both configs:
49
- - Authentication commands → global config only
50
- - Deployment/management commands → require BOTH global config (for auth) and project config (for app UUID)
28
+ **Project Config** (`.saac/config.json`):
29
+ - Application data (applicationUuid, applicationName, subdomain, gitRepository)
30
+ - Required for deployment commands
51
31
 
52
32
  ### API Client (`src/lib/api.js`)
53
33
 
54
- Creates axios instances with:
55
- - Base URL from global config
56
- - X-API-Key header automatically injected from global config
34
+ - Axios-based HTTP client
35
+ - Auto-injects auth headers (X-Session-Token or X-API-Key)
57
36
  - 30-second timeout
58
- - All API functions are async and return response.data
59
-
60
- **API Wrapper Endpoints**:
61
- - `/register` - Create new user account
62
- - `/users/verify` - Email verification
63
- - `/users/me` - Get current user info
64
- - `/applications` - CRUD operations for apps
65
- - `/applications/:uuid/deploy` - Trigger deployment
66
- - `/applications/:uuid/logs` - Fetch logs
67
- - `/applications/:uuid/env` - Manage environment variables
68
- - `/applications/:uuid/domain` - Update domain settings
37
+ - Returns response.data directly
69
38
 
70
39
  ### Logger (`src/lib/logger.js`)
71
40
 
72
- Centralized logging utility using chalk, ora, and boxen:
73
- - `logger.success()` - Green checkmark messages
74
- - `logger.error()` - Red X messages
75
- - `logger.warn()` - Yellow warning symbol
76
- - `logger.info()` - Blue info symbol
77
- - `logger.spinner(text)` - Returns ora spinner instance
78
- - `logger.section(title)` - Bold cyan headers with underline
79
- - `logger.field(key, value)` - Key-value pair display
80
- - `logger.box(message, options)` - Boxed messages
41
+ Uses chalk, ora, and boxen for formatted output:
42
+ - `logger.success()`, `logger.error()`, `logger.warn()`, `logger.info()`
43
+ - `logger.spinner(text)` - For async operations
44
+ - `logger.section(title)` - Bold headers
45
+ - `logger.field(key, value)` - Key-value pairs
81
46
 
82
- **Pattern**: Use spinners for async operations:
83
- ```javascript
84
- const spin = logger.spinner('Processing...').start();
85
- try {
86
- await someAsyncOperation();
87
- spin.succeed('Done!');
88
- } catch (error) {
89
- spin.fail('Failed');
90
- throw error;
91
- }
92
- ```
93
-
94
- ### Command Structure
47
+ ### Command Structure Pattern
95
48
 
96
49
  All commands follow this pattern:
97
- 1. **Validate required flags** - Show usage if missing (commands are non-interactive by default)
98
- 2. Import required libraries (api, config, logger)
99
- 3. Check authentication with `isAuthenticated()` (validates session token expiration)
100
- 4. Check project config if needed with `getProjectConfig()`
101
- 5. Execute operation with spinner feedback
102
- 6. Handle errors and exit with appropriate code
103
-
104
- **Important:** Commands require flags and do NOT use interactive prompts. This makes them LLM and automation-friendly.
105
-
106
- **Exceptions to Non-Interactive Rule:**
107
- - `saac init` - Uses inquirer to select from existing applications (intentionally interactive)
108
- - `saac logout-all` - Confirmation prompt (can be skipped with `-y` flag)
109
- - `saac git connect` - Interactive provider selection when no host specified
110
-
111
- **Examples:**
112
- ```bash
113
- # ✅ Correct - with flags
114
- saac register -e user@example.com
115
- saac login -e user@example.com -k cw_api_key
116
-
117
- # ❌ Wrong - will show usage error
118
- saac register
119
- saac login
120
- ```
121
-
122
- **Command Files**:
123
- - Authentication: `register.js`, `login.js`, `verify.js`, `logout.js`, `logoutAll.js`, `sessions.js`, `whoami.js` (stub)
124
- - Git OAuth: `git.js` (exports object with `connect`, `list`, `disconnect` methods)
125
- - App Management: `create.js`, `init.js`, `deploy.js`, `update.js`, `delete.js` (stub), `list.js`, `status.js`
126
- - Configuration: `env.js` (stub), `domain.js` (stub)
127
- - Logs: `logs.js` (stub)
128
-
129
- ### Entry Point (`bin/saac.js`)
130
-
131
- Uses Commander.js to define:
132
- - Commands with options and aliases
133
- - Nested commands (e.g., `env set`, `env get`, `domain set`)
134
- - Help text and version info
135
- - Error handling for invalid commands
136
-
137
- ## Important Patterns
138
-
139
- ### Authentication Flow
140
-
141
- **Session Token Flow (Primary):**
142
- 1. User registers with email → API returns session token (1 year expiration)
143
- 2. Session token stored in global config with expiration timestamp
144
- 3. Verification code sent to MailHog (not real email)
145
- 4. User verifies → verified flag set to true
146
- 5. All subsequent API requests include X-Session-Token header
147
- 6. Token expires after 1 year → user must login again
148
-
149
- **Auto-Login Flow (CI/CD & Automation):**
150
- 1. User sets `SAAC_USER_API_KEY` and `SAAC_USER_EMAIL` environment variables
151
- 2. Any command checks `ensureAuthenticated()` instead of `isAuthenticated()`
152
- 3. If not logged in, `ensureAuthenticated()` checks for env vars
153
- 4. If both present, automatically calls login API
154
- 5. Session token saved to config, cached for subsequent commands
155
- 6. Subsequent commands use cached session (fast path, no API call)
156
-
157
- **Authentication Priority:**
158
- ```javascript
159
- // In commands (e.g., deploy.js, list.js, etc.)
160
- if (!(await ensureAuthenticated())) {
161
- // Not logged in and auto-login failed
162
- logger.error('Not logged in');
163
- logger.info('Run: saac login -e <email> -k <api-key>');
164
- logger.info('Or set: SAAC_USER_API_KEY and SAAC_USER_EMAIL');
165
- process.exit(1);
166
- }
167
-
168
- // In api.js createClient() - Header priority:
169
- if (process.env.SAAC_API_KEY) {
170
- headers['X-API-Key'] = SAAC_API_KEY; // 1st priority (legacy)
171
- } else if (user.sessionToken) {
172
- headers['X-Session-Token'] = sessionToken; // 2nd priority
173
- } else if (user.apiKey) {
174
- headers['X-API-Key'] = apiKey; // 3rd priority (backward compat)
175
- }
176
- ```
177
-
178
- **Environment Variables:**
179
- - `SAAC_USER_API_KEY` - API key for auto-login (NEW)
180
- - `SAAC_USER_EMAIL` - Email for auto-login (NEW)
181
- - `SAAC_API_KEY` - Legacy API key (used directly in API headers, bypasses login)
182
-
183
- ### Application Lifecycle
184
-
185
- 1. **Create/Init** → applicationUuid saved to `.saac/config.json`
186
- 2. **Deploy** → POST to `/applications/:uuid/deploy`
187
- 3. **Monitor** → GET `/applications/:uuid/logs`
188
- 4. **Update** → PATCH environment or domain
189
- 5. **Delete** → DELETE `/applications/:uuid`
50
+ 1. Validate required flags (no interactive prompts except `init`, `logout-all`, `git connect`)
51
+ 2. Check authentication with `ensureAuthenticated()` (auto-login via env vars)
52
+ 3. Check project config with `getProjectConfig()` (if needed)
53
+ 4. Execute with spinner feedback
54
+ 5. Handle errors and exit with code 0/1
190
55
 
191
- ### Error Handling Convention
192
-
193
- All commands use try-catch with:
56
+ **Error Handling:**
194
57
  ```javascript
195
58
  try {
196
59
  // command logic
@@ -200,1180 +63,160 @@ try {
200
63
  }
201
64
  ```
202
65
 
203
- This pattern extracts API error messages or falls back to generic error message.
204
-
205
- ## Development Notes
66
+ ## Authentication Flow
206
67
 
207
- ### Session Token Implementation
68
+ ### Session Tokens (Primary Method)
69
+ - 1-year expiration
70
+ - Stored in global config
71
+ - Header: `X-Session-Token`
208
72
 
209
- The CLI now uses session tokens instead of storing permanent API keys:
73
+ ### Auto-Login (CI/CD)
74
+ - Set `SAAC_USER_API_KEY` and `SAAC_USER_EMAIL` env vars
75
+ - `ensureAuthenticated()` checks env vars and auto-logs in
76
+ - Subsequent commands use cached session (fast path)
210
77
 
211
- **Config Storage** (`~/.saac/config.json`):
212
- ```json
213
- {
214
- "user": {
215
- "email": "user@example.com",
216
- "userId": "uuid",
217
- "sessionToken": "st_...",
218
- "expiresAt": "2026-01-25T12:00:00Z",
219
- "verified": true
220
- }
221
- }
222
- ```
223
-
224
- **Token Validation Functions** (in `config.js`):
225
- - `isAuthenticated()` - Checks if user has valid, non-expired token (synchronous)
226
- - `ensureAuthenticated()` - **NEW**: Checks authentication + auto-login via env vars (async)
227
- - `isTokenExpired()` - Checks if session token has expired
228
- - `isTokenExpiringSoon()` - Checks if token expires within 7 days
229
-
230
- **ensureAuthenticated() Function:**
78
+ ### Authentication Priority
231
79
  ```javascript
232
- async function ensureAuthenticated() {
233
- // Step 1: Check if already authenticated (fast path)
234
- if (isAuthenticated()) {
235
- return true;
236
- }
237
-
238
- // Step 2: Check for environment variables
239
- const apiKey = process.env.SAAC_USER_API_KEY;
240
- const email = process.env.SAAC_USER_EMAIL;
241
-
242
- if (!apiKey || !email) {
243
- return false; // No env vars - cannot auto-login
244
- }
245
-
246
- // Step 3: Attempt auto-login via API
247
- try {
248
- const api = require('./api');
249
- const result = await api.login(email, apiKey);
250
-
251
- // Step 4: Save session token to config
252
- saveUser({
253
- email: result.user.email,
254
- userId: result.user.id,
255
- sessionToken: result.session_token,
256
- expiresAt: result.expires_at,
257
- verified: result.user.verified,
258
- });
259
-
260
- return true; // Auto-login successful
261
- } catch (error) {
262
- return false; // Auto-login failed
263
- }
264
- }
265
- ```
266
-
267
- **When to use:**
268
- - Use `ensureAuthenticated()` in ALL non-auth commands (deploy, list, create, etc.)
269
- - Use `isAuthenticated()` only when you don't want auto-login behavior
270
- - Commands that should NOT use `ensureAuthenticated()`: login, logout, register, verify
271
-
272
- **Backend Requirements:**
273
- - `POST /auth/login` - Accepts `X-API-Key` + email, returns session token
274
- - Middleware must accept both `X-Session-Token` and `X-API-Key` headers
275
- - Session tokens expire after 1 year
276
-
277
- ### Create Command Implementation
278
-
279
- The `create` command is fully implemented with ALL backend features:
280
-
281
- **Required Options:**
282
- - `-s, --subdomain` - Subdomain for the application
283
- - `-r, --repository` - Git repository URL (SSH format)
284
- - `-t, --git-token` - Git API token for repository access
285
-
286
- **Optional Basic Configuration:**
287
- - `-b, --branch` - Git branch (default: master)
288
- - `-d, --domain-suffix` - Domain suffix (default: startanaicompany.com)
289
- - `-p, --port` - Port to expose (default: 3000)
290
-
291
- **Build Pack Options:**
292
- - `--build-pack` - Build system: dockercompose, nixpacks, dockerfile, static
293
- - `--install-cmd` - Custom install command (e.g., "pnpm install")
294
- - `--build-cmd` - Custom build command (e.g., "npm run build")
295
- - `--start-cmd` - Custom start command (e.g., "node server.js")
296
- - `--pre-deploy-cmd` - Pre-deployment hook (e.g., "npm run migrate")
297
- - `--post-deploy-cmd` - Post-deployment hook (e.g., "npm run seed")
298
-
299
- **Resource Limits:**
300
- - `--cpu-limit` - CPU limit (e.g., "1", "2.5")
301
- - `--memory-limit` - Memory limit (e.g., "512M", "2G")
302
- - Note: Free tier limited to 1 vCPU, 1024M RAM
303
-
304
- **Health Checks:**
305
- - `--health-check` - Enable health checks
306
- - `--health-path` - Health check endpoint (default: /health)
307
- - `--health-interval` - Check interval in seconds
308
- - `--health-timeout` - Check timeout in seconds
309
- - `--health-retries` - Number of retries (1-10)
310
-
311
- **Environment Variables:**
312
- - `--env KEY=VALUE` - Can be used multiple times (max 50 variables)
313
-
314
- **Examples:**
315
- ```bash
316
- # Basic application
317
- saac create my-app -s myapp -r git@git.startanaicompany.com:user/repo.git -t abc123
318
-
319
- # With build pack and custom port
320
- saac create api -s api -r git@git... -t abc123 --build-pack nixpacks --port 8080
321
-
322
- # With health checks and pre-deployment migration
323
- saac create web -s web -r git@git... -t abc123 \
324
- --health-check \
325
- --pre-deploy-cmd "npm run migrate" \
326
- --env NODE_ENV=production \
327
- --env LOG_LEVEL=info
328
- ```
329
-
330
- **Implementation Details:**
331
- - Validates all required fields before API call
332
- - Shows configuration summary before creation
333
- - Handles tier-based quota errors (403)
334
- - Handles validation errors (400) with field-specific messages
335
- - Saves project config to `.saac/config.json` after successful creation
336
- - Displays next steps and useful commands
337
-
338
- **Deployment Behavior (NEW):**
339
-
340
- The `create` command now **waits for the initial deployment to complete** (up to 5 minutes) before returning.
341
-
342
- **Response Time:**
343
- - Typical: 30-120 seconds
344
- - Maximum: 5 minutes (timeout)
345
-
346
- **Success Response:**
347
- ```json
348
- {
349
- "success": true,
350
- "coolify_app_uuid": "...",
351
- "app_name": "my-app",
352
- "domain": "https://myapp.startanaicompany.com",
353
- "deployment_status": "finished",
354
- "deployment_uuid": "...",
355
- "git_branch": "master"
356
- }
357
- ```
358
-
359
- **Failure Response (HTTP 200 with success: false):**
360
- ```json
361
- {
362
- "success": false,
363
- "coolify_app_uuid": "...",
364
- "app_name": "my-app",
365
- "deployment_status": "failed",
366
- "message": "Port 8080 is already in use. Remove host port bindings...",
367
- "errors": [
368
- {
369
- "type": "PORT_CONFLICT",
370
- "message": "Port 8080 is already in use...",
371
- "detail": "Bind for 0.0.0.0:8080 failed..."
372
- }
373
- ],
374
- "relevant_logs": [...],
375
- "last_logs": [...]
376
- }
377
- ```
378
-
379
- **Error Types:**
380
- - `PORT_CONFLICT` - Host port binding conflict in docker-compose.yml
381
- - `BUILD_FAILED` - Build process returned non-zero exit code
382
- - `TIMEOUT` - Deployment didn't complete in 5 minutes
383
- - `UNKNOWN` - Generic deployment failure
384
-
385
- **Important Notes:**
386
- 1. **Application is created even if deployment fails** - the UUID is saved to `.saac/config.json`
387
- 2. Failed deployments return HTTP 200 (not 4xx) with `success: false`
388
- 3. CLI must check the `success` field, not just HTTP status code
389
- 4. Detailed error information is displayed with actionable advice
390
- 5. User can fix the issue and run `saac deploy` to retry
391
-
392
- **Error Display:**
393
- The CLI displays comprehensive error information:
394
- - Error summary message
395
- - Structured error details with types
396
- - Relevant error logs (filtered)
397
- - Last log lines for context
398
- - Actionable advice based on error type:
399
- - `PORT_CONFLICT`: Remove host port bindings from docker-compose.yml
400
- - `BUILD_FAILED`: Check Dockerfile, run `docker build .` locally
401
- - `TIMEOUT`: Check `saac status` and `saac logs`, may still be running
402
-
403
- ### Update Command Implementation
404
-
405
- The `update` command allows modifying application configuration after deployment using `PATCH /api/v1/applications/:uuid`.
406
-
407
- **All Configuration Fields Can Be Updated:**
408
- - Basic: name, branch, port
409
- - Build pack and custom commands
410
- - Resource limits (CPU, memory) - capped at tier limits
411
- - Health checks (enable/disable, path, interval, timeout, retries)
412
- - Restart policy (always, on-failure, unless-stopped, no)
413
- - Environment variables
414
-
415
- **Important Notes:**
416
- - Only fields you specify will be updated (partial updates)
417
- - Resource limits are enforced by user tier (free: 1 vCPU, 1GB RAM)
418
- - Changes require redeployment to take effect: `saac deploy`
419
- - Requires project config (`.saac/config.json`)
420
-
421
- **Examples:**
422
- ```bash
423
- # Update port and enable health checks
424
- saac update --port 8080 --health-check --health-path /api/health
425
-
426
- # Switch to Nixpacks and update resource limits
427
- saac update --build-pack nixpacks --cpu-limit 2 --memory-limit 2G
428
-
429
- # Update custom commands
430
- saac update --pre-deploy-cmd "npm run migrate" --start-cmd "node dist/server.js"
431
-
432
- # Disable health checks
433
- saac update --no-health-check
434
-
435
- # Update environment variables
436
- saac update --env NODE_ENV=production --env LOG_LEVEL=debug
437
-
438
- # Change restart policy
439
- saac update --restart on-failure
440
- ```
441
-
442
- **Response Behavior:**
443
- - Shows which fields were updated
444
- - Warns if tier limits were applied (resource caps)
445
- - Reminds user to redeploy for changes to take effect
446
-
447
- ### Git OAuth Commands (NEW in 1.4.0)
448
-
449
- The `git` command manages Git account OAuth connections, allowing users to deploy applications without providing tokens repeatedly.
450
-
451
- **Subcommands:**
452
- 1. `saac git connect [host]` - Connect Git account via OAuth
453
- 2. `saac git list` - List all connected Git accounts
454
- 3. `saac git disconnect <host>` - Disconnect a Git account
455
-
456
- **OAuth Flow:**
457
- 1. User runs `saac git connect` or creates app without token
458
- 2. Browser opens to OAuth authorization page
459
- 3. User authorizes on Git provider (Gitea, GitHub, GitLab)
460
- 4. CLI polls for completion every 2 seconds (max 5 minutes)
461
- 5. Connection saved on server (encrypted AES-256-GCM)
462
- 6. Future app creations use OAuth token automatically
463
-
464
- **Usage Examples:**
465
- ```bash
466
- # Connect to default Gitea instance
467
- saac git connect git.startanaicompany.com
468
-
469
- # Connect from repository URL
470
- saac git connect git@git.startanaicompany.com:user/repo.git
471
-
472
- # Interactive mode - select provider
473
- saac git connect
474
-
475
- # List connections
476
- saac git list
477
-
478
- # Disconnect
479
- saac git disconnect git.startanaicompany.com
480
- ```
481
-
482
- **Integration with Create Command:**
483
- - If Git account connected → Uses OAuth automatically
484
- - If NOT connected and no `--git-token` → Prompts to connect
485
- - If `--git-token` provided → Uses manual token (fallback)
486
-
487
- **Benefits:**
488
- - ✅ Connect once, deploy unlimited apps
489
- - ✅ No need to copy/paste tokens repeatedly
490
- - ✅ Tokens stored encrypted on server
491
- - ✅ Auto-refresh for expired tokens
492
- - ✅ Supports multiple Git providers
493
-
494
- **Implementation Files:**
495
- - `src/lib/oauth.js` - OAuth helper functions (exports: `extractGitHost`, `connectGitAccount`, `getConnection`, `listConnections`, `revokeConnection`)
496
- - `src/commands/git.js` - Git command implementation (exports object with `connect`, `list`, `disconnect` methods)
497
- - Updated `src/commands/create.js` - OAuth integration (checks for existing OAuth connection before requiring `--git-token`)
498
-
499
- ### Deploy Command Implementation
500
-
501
- The `deploy` command triggers deployment and **waits for completion** (up to 5 minutes).
502
-
503
- **Usage:**
504
- ```bash
505
- saac deploy
506
- saac deploy --force
507
- ```
508
-
509
- **How It Works:**
510
- 1. Validates authentication (session token not expired)
511
- 2. Checks for project config (`.saac/config.json`)
512
- 3. Makes POST request to `/api/v1/applications/:uuid/deploy`
513
- 4. **Waits for deployment to complete** (up to 5 minutes)
514
- 5. Displays deployment status with detailed error information on failure
515
-
516
- **Response Time:**
517
- - Typical: 30-120 seconds
518
- - Maximum: 5 minutes (timeout)
519
-
520
- **Success Response:**
521
- ```json
522
- {
523
- "success": true,
524
- "status": "finished",
525
- "deployment_uuid": "...",
526
- "git_branch": "master",
527
- "domain": "https://myapp.startanaicompany.com",
528
- "traefik_status": "queued"
529
- }
530
- ```
531
-
532
- **Failure Response:**
533
- ```json
534
- {
535
- "success": false,
536
- "status": "failed",
537
- "message": "Build failed with exit code 1",
538
- "errors": [
539
- {
540
- "type": "BUILD_FAILED",
541
- "message": "Build failed with exit code 1",
542
- "detail": "npm ERR! code ELIFECYCLE..."
543
- }
544
- ],
545
- "relevant_logs": [...],
546
- "last_logs": [...]
547
- }
548
- ```
549
-
550
- **Error Types:**
551
- - `PORT_CONFLICT` - Host port binding conflict
552
- - `BUILD_FAILED` - Build process failed
553
- - `TIMEOUT` - Deployment didn't complete in 5 minutes
554
- - `UNKNOWN` - Generic failure
555
-
556
- **Error Display:**
557
- The CLI displays:
558
- 1. Error summary message
559
- 2. Structured error details with types
560
- 3. Relevant logs (filtered error logs)
561
- 4. Last 5 log lines for context
562
- 5. Actionable advice based on error type
563
- 6. Suggestion to view full logs with `saac logs --follow`
564
-
565
- **Note:** The `--force` flag is defined in the CLI but not currently used by the API.
566
-
567
- ### OTP-Based Login (NEW in 1.4.16)
568
-
569
- The login command now supports two authentication methods:
570
-
571
- **Method 1: Login with API Key (Fast Path)**
572
- ```bash
573
- saac login -e user@example.com -k cw_abc123
574
- # → Immediate session token, no email verification needed
575
- ```
576
-
577
- **Method 2: Login with OTP (Recovery Path)**
578
- ```bash
579
- # Step 1: Request OTP
580
- saac login -e user@example.com
581
- # → Verification code sent to email (6 digits, 5-minute expiration)
582
-
583
- # Step 2: Verify OTP
584
- saac login -e user@example.com --otp 123456
585
- # → Session token created, user is now logged in
586
- ```
587
-
588
- **Why This Feature?**
589
-
590
- Solves the **API key lockout problem**:
591
- - User loses API key
592
- - All sessions expire
593
- - Cannot login (no API key)
594
- - Cannot regenerate key (requires login)
595
- - **LOCKED OUT** ❌
596
-
597
- With OTP login:
598
- - User can always recover via email ✅
599
- - No support tickets needed ✅
600
- - Self-service account recovery ✅
601
-
602
- **Implementation Details:**
603
-
604
- The `login.js` command now has three modes:
605
- 1. **API key login** - If `-k` flag provided (existing flow)
606
- 2. **OTP request** - If no `-k` or `--otp` flag (new flow)
607
- 3. **OTP verification** - If `--otp` flag provided (new flow)
608
-
609
- **Backend Requirements:**
610
- - `POST /api/v1/auth/login-otp` - Generate and send OTP
611
- - `POST /api/v1/auth/verify-otp` - Verify OTP and create session
612
-
613
- See `/data/sharedinfo/login-feature-update.md` for complete API specifications.
614
-
615
- ### API Key Management (NEW in 1.4.16)
616
-
617
- Users can now regenerate their API keys if lost or compromised.
618
-
619
- **Commands:**
620
-
621
- ```bash
622
- # Regenerate API key (requires authentication)
623
- saac keys regenerate
624
- # → Shows new API key (only once!)
625
-
626
- # Show API key info
627
- saac keys show
628
- # → Displays key prefix, created date, last used
629
- ```
630
-
631
- **Recovery Flow:**
632
-
633
- ```bash
634
- # 1. User loses API key but is not logged in
635
- saac login -e user@example.com
636
- # → OTP sent to email
637
-
638
- # 2. Verify OTP
639
- saac login -e user@example.com --otp 123456
640
- # → Logged in with session token
641
-
642
- # 3. Generate new API key
643
- saac keys regenerate
644
- # → New API key: cw_new_key_xyz...
645
-
646
- # 4. On next machine, use new API key
647
- saac login -e user@example.com -k cw_new_key_xyz...
648
- ```
649
-
650
- **Security Notes:**
651
- - Regenerating API key invalidates the old key immediately
652
- - Existing session tokens remain valid (no disruption)
653
- - Email notification sent when key is regenerated
654
- - Full API key shown only once (must be saved)
655
-
656
- **Backend Requirements:**
657
- - `POST /api/v1/users/regenerate-key` - Generate new API key
658
- - `GET /api/v1/users/api-key` - Get API key info (optional)
659
-
660
- See `/data/sharedinfo/login-feature-update.md` for complete specifications.
661
-
662
- ### Init Command Implementation
663
-
664
- The `init` command links an existing SAAC application to the current directory.
665
-
666
- **Primary Use Case:** When you clone a Git repository or have an existing project and want to link it to a SAAC application for deployment.
667
-
668
- **How It Works:**
669
- 1. Checks if directory is already initialized (has `.saac/config.json`)
670
- 2. Fetches all user applications from the API
671
- 3. Shows interactive list to select which application to link
672
- 4. Saves selected application info to `.saac/config.json`
673
-
674
- **Usage:**
675
- ```bash
676
- # Interactive mode - select from existing applications
677
- cd my-project
678
- saac init
679
- # Select application from list
680
- ```
681
-
682
- **Behavior:**
683
- - If directory is already initialized, asks for confirmation to re-link
684
- - If user has no applications, suggests using `saac create`
685
- - After initialization, shows available commands (deploy, logs, status, update)
686
-
687
- **Note:** The `init` command options (`-n, --name`, etc.) are currently not implemented. To create a new application, use `saac create` instead.
688
-
689
- ### List Command Implementation
690
-
691
- The `list` command displays all user applications in a formatted table.
692
-
693
- **Usage:**
694
- ```bash
695
- saac list
696
- saac ls # Alias
697
- ```
698
-
699
- **How It Works:**
700
- 1. Validates authentication
701
- 2. Fetches all applications from `/api/v1/applications`
702
- 3. Displays table with columns: Name, Domain, Status, Branch, Created
703
- 4. Shows total count and helpful next-step commands
704
-
705
- **Status Display:**
706
- Uses the same status display logic as the status command (see "Application Status Values" section).
707
-
708
- **Table Formatting:**
709
- - Uses the `table` npm package
710
- - Header shows total application count
711
- - Domains fallback to `{subdomain}.startanaicompany.com` if not set
712
- - Branch defaults to 'master' if not specified
713
-
714
- ### Deployments Command Implementation
715
-
716
- The `deployments` command displays deployment history for the current application in a formatted table.
717
-
718
- **Usage:**
719
- ```bash
720
- saac deployments
721
- saac deploys # Alias
722
-
723
- # With pagination
724
- saac deployments --limit 10
725
- saac deployments --limit 20 --offset 20
726
- ```
727
-
728
- **How It Works:**
729
- 1. Validates authentication (session token not expired)
730
- 2. Checks for project config (`.saac/config.json`)
731
- 3. Fetches deployment history from `/api/v1/applications/:uuid/deployments`
732
- 4. Displays table with columns: UUID, Status, Branch, Commit, Duration, Trigger, Date
733
- 5. Shows pagination info if more deployments available
734
-
735
- **Options:**
736
- - `-l, --limit <number>` - Number of deployments to show (default: 20)
737
- - `-o, --offset <number>` - Offset for pagination (default: 0)
738
-
739
- **Status Display:**
740
- - ✅ `finished` - Displayed in green
741
- - ✗ `failed` - Displayed in red
742
- - ⏳ `running`, `queued` - Displayed in yellow
743
- - `unknown` - Displayed in gray
744
-
745
- **Table Formatting:**
746
- - UUID truncated to 26 characters for readability
747
- - Commit SHA truncated to 7 characters
748
- - Duration shown in seconds
749
- - Date formatted with `toLocaleString()`
750
-
751
- **Response Fields:**
752
- ```json
753
- {
754
- "deployments": [
755
- {
756
- "deployment_uuid": "...",
757
- "status": "finished",
758
- "git_branch": "master",
759
- "git_commit": "abc1234",
760
- "duration_seconds": 45,
761
- "triggered_by": "api",
762
- "started_at": "2024-01-20T12:00:00Z"
763
- }
764
- ],
765
- "total": 100
766
- }
767
- ```
768
-
769
- **Next Steps:**
770
- After viewing deployment history, use:
771
- - `saac logs --deployment` - View latest deployment logs
772
- - `saac logs --deployment <uuid>` - View specific deployment logs
773
-
774
- ### Logs Command Implementation
775
-
776
- The `logs` command displays application logs with support for both deployment logs (build logs) and runtime logs (container logs).
777
-
778
- **Usage:**
779
- ```bash
780
- # View latest deployment logs (build logs)
781
- saac logs --deployment
782
- saac logs -d
783
-
784
- # View specific deployment logs
785
- saac logs --deployment abc123-def456-...
786
- saac logs abc123-def456-... --deployment
787
-
788
- # View deployment logs in raw format
789
- saac logs --deployment --raw
790
-
791
- # View runtime logs (container logs)
792
- saac logs
793
- saac logs --tail 200
794
- saac logs --follow
795
- saac logs --since 1h
796
- ```
797
-
798
- **Two Modes:**
799
-
800
- **1. Deployment Logs Mode (Build Logs)**
801
- - Enabled with `--deployment` flag
802
- - Shows logs from the build/deployment process
803
- - Includes build output, errors, and deployment status
804
- - Supports both parsed (colorized) and raw formats
805
-
806
- **Options:**
807
- - `--deployment [uuid]` - View deployment logs (omit UUID for latest)
808
- - `--raw` - Show raw log output (no parsing or colorization)
809
- - `--include-hidden` - Include hidden log lines
810
-
811
- **Display:**
812
- - Header with deployment UUID, status, commit, duration
813
- - Parsed logs with stderr highlighted in red
814
- - Error summary if deployment failed
815
- - Structured error information with types and details
816
-
817
- **2. Runtime Logs Mode (Container Logs)**
818
- - Default mode when no `--deployment` flag
819
- - Shows logs from the running container
820
- - Real-time application output
821
-
822
- **Options:**
823
- - `-t, --tail <lines>` - Number of lines to show (default: 100)
824
- - `-f, --follow` - Follow log output (not yet implemented)
825
- - `--since <time>` - Show logs since timestamp
826
-
827
- **Error Handling:**
828
- - 404 error → No deployments found, suggests `saac deploy`
829
- - 501 error → Runtime logs not implemented, suggests deployment logs
830
-
831
- **Response Fields (Deployment Logs):**
832
- ```json
833
- {
834
- "deployment_uuid": "...",
835
- "status": "finished",
836
- "commit": "abc1234",
837
- "commit_message": "Fix bug",
838
- "started_at": "2024-01-20T12:00:00Z",
839
- "finished_at": "2024-01-20T12:02:00Z",
840
- "duration_seconds": 120,
841
- "log_count": 150,
842
- "logs": [
843
- {
844
- "type": "stdout",
845
- "output": "Installing dependencies..."
846
- },
847
- {
848
- "type": "stderr",
849
- "output": "npm WARN deprecated package@1.0.0"
850
- }
851
- ],
852
- "raw_logs": "...",
853
- "errors": [
854
- {
855
- "type": "BUILD_FAILED",
856
- "message": "Build failed with exit code 1",
857
- "detail": "npm ERR! code ELIFECYCLE"
858
- }
859
- ]
860
- }
861
- ```
862
-
863
- ### Local Development Commands (NEW in 1.5.0)
864
-
865
- #### Run Command Implementation
866
-
867
- The `run` command allows executing local commands with remote environment variables from the deployed application.
868
-
869
- **Usage:**
870
- ```bash
871
- saac run <command> # Run command with remote env vars
872
- saac run npm start # Start app with production env
873
- saac run --sync npm run build # Force refresh env vars (skip cache)
874
- saac run -q npm test # Quiet mode (suppress warnings)
875
- ```
876
-
877
- **Implementation Details:**
878
- - Fetches env vars from `GET /applications/:uuid/env/export`
879
- - Uses 5-minute in-memory cache (TTL: 300 seconds)
880
- - Creates temp file at `/tmp/saac-env-{uuid}.sh` with 0600 permissions
881
- - Sources env file and executes command in subshell
882
- - Automatic cleanup on exit (SIGINT/SIGTERM handlers)
883
- - Rate limit handling (10 requests/minute per user)
884
-
885
- **Security Features:**
886
- - Temp files with 0600 permissions (owner read-write only)
887
- - Automatic cleanup on process exit
888
- - Warning message about exposed secrets
889
- - Cache reduces API calls (rate limit protection)
890
-
891
- **Error Handling:**
892
- - 429 Rate Limit: Shows retry time, mentions caching
893
- - 403 Forbidden: User doesn't own application
894
- - 404 Not Found: Application doesn't exist
895
- - Network failures: Graceful error messages
896
-
897
- **Location:** `src/commands/run.js`
898
-
899
- ---
900
-
901
- #### Shell Command Implementation (Project Aurora)
902
-
903
- The `shell` command provides a **TRUE remote shell** - you're actually inside the Docker container, not just a local shell with env vars loaded. This is powered by Project Aurora (WebSocket + Docker + tmux).
904
-
905
- **Key Difference from Phase 1:**
906
- - **OLD (Phase 1)**: Local shell with remote env vars → wrong filesystem, wrong tools, wrong paths
907
- - **NEW (Aurora)**: Remote shell inside container → TRUE remote experience like SSH/Railway
908
-
909
- **Usage:**
910
- ```bash
911
- saac shell # Connect to remote container shell
80
+ // In api.js createClient():
81
+ if (process.env.SAAC_API_KEY) headers['X-API-Key'] = ...; // 1st
82
+ else if (user.sessionToken) headers['X-Session-Token'] = ...; // 2nd
83
+ else if (user.apiKey) headers['X-API-Key'] = ...; // 3rd
912
84
  ```
913
85
 
914
- **Architecture:**
915
- - WebSocket client connects to backend server
916
- - Backend creates Docker container with tmux session
917
- - Commands sent via WebSocket, executed in container
918
- - Terminal output captured from tmux and streamed back
919
- - Session persists even after disconnect (up to 1 hour idle)
920
-
921
- **Implementation Details:**
922
- - Uses `ws` package for WebSocket client
923
- - Connects to: `wss://apps.startanaicompany.com/api/v1/shell/connect`
924
- - Authentication via session token or API key
925
- - Readline interface for local terminal interaction
926
- - Automatic reconnection on disconnect (up to 3 attempts)
927
- - Heartbeat ping/pong every 30 seconds
928
-
929
- **Session Lifecycle:**
930
- 1. CLI connects via WebSocket with auth token
931
- 2. Backend creates session record in database (status: 'creating')
932
- 3. Python daemon creates Docker container with tmux
933
- 4. Container ready, status updates to 'active'
934
- 5. User interacts with remote shell
935
- 6. On disconnect, session marked 'idle' (persists for 1 hour)
936
- 7. Idle timeout or manual termination cleans up container
86
+ ## Key Command Implementations
937
87
 
938
- **Features:**
939
- - TRUE remote shell (inside container, not local)
940
- - Persistent session (survives reconnections)
941
- - ✅ Interactive commands (vim, nano, etc work!)
942
- - ✅ Working directory persists between commands
943
- - ✅ Command history (up arrow works)
944
- - ✅ Real-time output streaming
945
- - ✅ Ctrl+C sends to remote shell
88
+ ### Create Command
89
+ **Required flags:** `-s subdomain`, `-r repository`, `--org organization_id`
90
+ **Optional:** OAuth replaces `-t git-token` if connected
946
91
 
947
- **Connection Handling:**
948
- - 30-second timeout for initial container creation
949
- - Auto-reconnect on network issues (3 attempts, exponential backoff)
950
- - Graceful cleanup on exit (Ctrl+D or "exit" command)
951
-
952
- **User Experience:**
953
92
  ```bash
954
- $ saac shell
955
-
956
- Remote Shell: my-app
957
-
958
- Connecting to container...
959
- This may take up to 30 seconds for container creation.
960
-
961
- ✓ Connected to remote container
962
- Container is being created, please wait...
963
- ✓ Container is ready!
964
- Type commands below. Press Ctrl+D or type "exit" to quit.
965
-
966
- root@container:/app# ls -la
967
- total 48
968
- drwxr-xr-x 6 root root 4096 Jan 28 20:00 .
969
- drwxr-xr-x 18 root root 4096 Jan 28 20:00 ..
970
- -rw-r--r-- 1 root root 220 Jan 28 20:00 package.json
971
- drwxr-xr-x 2 root root 4096 Jan 28 20:00 src
972
-
973
- root@container:/app# cd src && pwd
974
- /app/src
975
-
976
- root@container:/app/src# exit
977
-
978
- Disconnecting from remote shell...
93
+ saac create my-app -s myapp -r git@git... --org 525ff3e4-...
979
94
  ```
980
95
 
981
- **Error Handling:**
982
- - `Authentication failed` → Token expired, run `saac login`
983
- - `Access denied` → User doesn't own the application
984
- - `Connection timeout` → Server unavailable or container failed to start
985
- - `Max reconnection attempts` → Network issue, try again later
986
-
987
- **Backend Requirements:**
988
- - WebSocket server at `/api/v1/shell/connect` endpoint
989
- - PostgreSQL tables: `shell_sessions`, `shell_commands`, `shell_output`
990
- - Python daemon managing Docker containers with tmux
991
- - Session cleanup after 1 hour idle or 4 hours total
992
-
993
- **Location:** `src/commands/shell.js` (403 lines, complete rewrite for Aurora)
994
-
995
- **Note:** Backend infrastructure (WebSocket server + Python daemon) must be deployed before this command works. CLI is ready, waiting for backend Phase 3.5 deployment.
996
-
997
- **Location:** `src/commands/shell.js`
998
-
999
- ---
96
+ Waits for deployment (30-120s typical, 5min max). Returns `success: true/false`.
1000
97
 
1001
- ### Remote Execution Commands (NEW in 1.6.0)
98
+ ### Logs Command (v1.6.0)
99
+ **Deployment logs:** `saac logs --deployment [uuid]`
100
+ **Runtime logs:** `saac logs --follow --type access`
1002
101
 
1003
- #### Exec Command Implementation
102
+ SSE streaming implementation:
103
+ - Native Fetch API with `text/event-stream`
104
+ - Parses `data: {...}` lines
105
+ - Graceful Ctrl+C cleanup
106
+ - Formatted: `12:38:32 PM [service] message`
107
+ - Types: access (Traefik), runtime (container), build
1004
108
 
1005
- The `exec` command allows executing commands directly in the deployed application's container.
109
+ ### Git OAuth
110
+ **Commands:** `git connect/list/disconnect`
111
+ **Flow:** Browser OAuth → CLI polls every 2s → Connection saved (AES-256-GCM encrypted)
1006
112
 
1007
- **Usage:**
113
+ ### Remote Execution (v1.6.0)
1008
114
  ```bash
1009
- saac exec "npm run db:migrate" # Run database migrations
1010
- saac exec "npm run build" --timeout 120 # With custom timeout
1011
- saac exec "python manage.py collectstatic" --workdir /app/backend
1012
- saac exec --history # View execution history
1013
- saac exec --history --limit 50 # Show more history
115
+ saac exec "npm run migrate" # Execute command
116
+ saac exec --history # View history
1014
117
  ```
118
+ **Security:** Command allowlist, dangerous pattern detection, rate limiting (30/5min)
1015
119
 
1016
- **Implementation Details:**
1017
- - Sends command to `POST /applications/:uuid/exec`
1018
- - Backend validates command against allowlist
1019
- - Python daemon executes via Docker API (no shell interpretation!)
1020
- - Returns: execution_id, exit_code, stdout, stderr, duration
1021
- - Command runs in container as detected user (not root)
120
+ ## API Endpoints (v1.6.0)
1022
121
 
1023
- **Architecture:**
1024
122
  ```
1025
- CLI → Backend API → Database (exec_requests table) → Python Daemon → Docker Container
1026
- ```
1027
-
1028
- **Security Features:**
1029
- - ✅ **3-layer validation:** Backend allowlist + daemon validation + Docker array execution
1030
- - ✅ **Command allowlist:** npm, node, python, bash, etc. (dangerous commands blocked)
1031
- - **Dangerous pattern detection:** Blocks `rm -rf`, `| sh`, `$(...)`, etc.
1032
- - ✅ **Rate limiting:** 30 requests per 5 minutes per user
1033
- - ✅ **No shell interpretation:** Commands executed as arrays, not strings
1034
- - ✅ **Non-root execution:** Commands run as app user, never privileged
1035
- - ✅ **Audit logging:** All executions logged with user, command, exit code
1036
-
1037
- **Allowed Commands:**
1038
- - Node.js: `npm`, `node`, `npx`, `yarn`, `pnpm`
1039
- - Python: `python`, `python3`, `pip`, `poetry`
1040
- - Ruby: `bundle`, `rake`, `rails`
1041
- - Build tools: `make`, `cmake`, `go`, `cargo`
1042
- - Shell: `sh`, `bash`, `echo`, `cat`, `ls`, `pwd`, `env`
1043
- - Database: `psql`, `mysql`, `mongosh`
1044
-
1045
- **Error Handling:**
1046
- - 400 Validation Error: Command not in allowlist or dangerous pattern detected
1047
- - 408 Timeout: Command exceeded timeout limit (default: 30s, max: 300s)
1048
- - 429 Rate Limit: Too many exec requests (30 per 5 minutes)
1049
- - 503 Container Not Running: Application container is not active
1050
- - 500 Execution Failed: Container error or daemon issue
1051
-
1052
- **Example Output:**
1053
- ```bash
1054
- $ saac exec "echo 'Hello from container!'"
1055
-
1056
- Executing Command: myapp
1057
- ──────────────────────────
1058
-
1059
- Command: echo 'Hello from container!'
1060
- Working Directory: /app
1061
- Timeout: 30s
1062
-
1063
- ✓ Command executed
1064
-
1065
- ✓ Execution ID: eb41b197-e215-4f9e-87ac-80ce57e732a6
1066
-
1067
- Exit Code: 0
1068
- Duration: 3531ms
1069
- Started: 1/28/2026, 8:55:37 PM
1070
- Completed: 1/28/2026, 8:55:41 PM
1071
-
1072
- Standard Output:
1073
- ────────────────────────────────────────────────────────────
1074
- Hello from container!
1075
- ────────────────────────────────────────────────────────────
1076
- ```
1077
-
1078
- **History Command:**
1079
- ```bash
1080
- $ saac exec --history
1081
-
1082
- Execution History: myapp
1083
- ────────────────────────
1084
-
1085
- ╔══════════╤═══════════╤═════════════╤═══════════╤══════════╤═══════════════╗
1086
- ║ ID │ Command │ Status │ Exit Code │ Duration │ Started ║
1087
- ╠══════════╪═══════════╪═════════════╪═══════════╪══════════╪═══════════════╣
1088
- ║ eb41b197 │ echo '...'│ ✓ completed │ 0 │ 0s │ 1/28/26, 8:55 ║
1089
- ║ a3f2c891 │ npm run...│ ✗ completed │ 1 │ 5s │ 1/28/26, 8:50 ║
1090
- ╚══════════╧═══════════╧═════════════╧═══════════╧══════════╧═══════════════╝
123
+ POST /api/v1/users/register
124
+ POST /api/v1/auth/login
125
+ GET /api/v1/users/me
126
+ POST /api/v1/applications # --org required
127
+ PATCH /api/v1/applications/:uuid
128
+ POST /api/v1/applications/:uuid/deploy
129
+ GET /api/v1/applications/:uuid/logs # ?follow=true for SSE
130
+ GET /api/v1/applications/:uuid/env/export
131
+ POST /api/v1/applications/:uuid/exec
1091
132
 
1092
- Showing 2 of 5 executions
133
+ # OAuth (no /api/v1 prefix)
134
+ GET /oauth/authorize
135
+ GET /oauth/poll/:session_id
1093
136
  ```
1094
137
 
1095
- **Location:** `src/commands/exec.js`
138
+ ## Critical Bugs Fixed (v1.6.0)
1096
139
 
1097
- **Backend Implementation:**
1098
- - Database table: `exec_requests` (status: pending running completed/failed/timeout)
1099
- - Python daemon: `/root/exec-daemon/exec_daemon.py` (polls every 2 seconds)
1100
- - Systemd service: `exec-daemon.service` (auto-restart, logs to `/var/log/exec-daemon.log`)
140
+ 1. **whoami verified field:** Changed `verified` → `email_verified`
141
+ 2. **keys show removed:** API keys are write-once, never readable
142
+ 3. **delete message:** Use `app.name` not `result.application_name`
143
+ 4. **organization_id:** Added required `--org` flag to create command
1101
144
 
1102
- ---
145
+ ## Testing Methodology (v1.6.0)
1103
146
 
1104
- **Backend Requirements (Implemented):**
1105
- - `GET /api/v1/applications/:uuid/env/export` - Export endpoint (deployed 2026-01-28)
1106
- - Returns: `environment` (object), `export_script` (bash format), `dotenv_format` (dotenv format)
1107
- - Rate limit: 10 requests/minute per user
1108
- - Authentication: X-Session-Token or X-API-Key
1109
- - Response includes `expires_in: 300` (5-minute cache recommendation)
1110
- - `POST /api/v1/applications/:uuid/exec` - Execute command in container (deployed 2026-01-28)
1111
- - `GET /api/v1/applications/:uuid/exec/history` - View execution history (deployed 2026-01-28)
1112
- - Rate limit: 30 requests per 5 minutes per user
147
+ **Process:**
148
+ 1. Test ONE feature at a time
149
+ 2. HIVE message to backend for validation
150
+ 3. Backend checks database/containers
151
+ 4. Fix bugs immediately
152
+ 5. Move to next feature
1113
153
 
1114
- **Implementation Pattern for New Commands:**
1115
- 1. Require flags, no interactive prompts (exception: `init` uses inquirer for app selection)
1116
- 2. Show usage info if required flags missing
1117
- 3. Validate inputs before API calls
1118
- 4. Use spinners for async operations
1119
- 5. Handle errors with descriptive messages
154
+ **35+ tests completed:**
155
+ - Authentication, auto-login, API keys
156
+ - Git OAuth, application management
157
+ - Environment/domain, logs, streaming
158
+ - Real app testing (golden-68-rekrytering-9jsq1e)
1120
159
 
1121
- ### MailHog Integration
160
+ ## Publishing Checklist
1122
161
 
1123
- The system uses MailHog for email verification in development:
1124
- - URL: https://mailhog.goryan.io
1125
- - Users must manually retrieve verification codes
1126
- - Production would use real SMTP
162
+ 1. Test all new features with live backend
163
+ 2. Check syntax: `node -c src/**/*.js bin/*.js`
164
+ 3. Update version in package.json
165
+ 4. Update CLAUDE.md
166
+ 5. Git commit and push
167
+ 6. `npm publish --access public`
1127
168
 
1128
- ### Domain Configuration
169
+ ## Version 1.6.0 Release Notes
1129
170
 
1130
- Default domain suffix: `startanaicompany.com`
1131
- Applications are accessible at: `{subdomain}.startanaicompany.com`
171
+ **New Features:**
172
+ - SSE streaming for real-time logs (`--follow`)
173
+ - Log type filtering (`--type access/runtime/build`)
174
+ - Formatted log display with timestamps
175
+ - Traefik access log integration
1132
176
 
1133
- ### Git Repository Integration
177
+ **Bug Fixes:**
178
+ - Fixed whoami verified field
179
+ - Removed keys show command
180
+ - Fixed delete success message
181
+ - Added organization_id requirement
1134
182
 
1135
- The wrapper API expects Git repositories to be hosted on the StartAnAiCompany Gitea instance:
1136
- - Gitea URL: https://git.startanaicompany.com
1137
- - During registration, Gitea username can be auto-detected or manually provided
1138
- - Applications reference repositories in the format: `git@git.startanaicompany.com:user/repo.git`
183
+ **Breaking Changes:**
184
+ - `saac create` requires `--org` flag
185
+ - `saac keys show` removed
1139
186
 
1140
- ## Current Status - Version 1.6.0
187
+ ## Dependencies
1141
188
 
1142
- ### Completed Features
189
+ - axios, chalk, commander, conf, inquirer, ora, boxen, table, validator, dotenv, open, ws
1143
190
 
1144
- **Authentication & Sessions:**
1145
- - ✅ `saac register` - Register with email only (git_username auto-detected from email)
1146
- - ✅ `saac login` - Two methods: API key (fast) or OTP (recovery)
1147
- - `saac login -e email -k api-key` - Login with API key
1148
- - `saac login -e email` - Request OTP via email
1149
- - `saac login -e email --otp code` - Verify OTP and login
1150
- - ✅ `saac verify` - Email verification, shows FULL API key for user to save
1151
- - ✅ `saac logout` - Logout from current device
1152
- - ✅ `saac logout-all` - Revoke all sessions
1153
- - ✅ `saac sessions` - List all active sessions
1154
- - ✅ `saac whoami` - Show current user info
191
+ ## Status Values
1155
192
 
1156
- **API Key Management (NEW in 1.4.16):**
1157
- - `saac keys regenerate` - Generate new API key (invalidates old one)
1158
- - `saac keys show` - Show API key information (prefix, created, last used)
193
+ - `running:healthy/unknown` Green "Running ✓"
194
+ - `active` Green "Active ✓"
195
+ - `creating` Yellow "Creating..."
196
+ - `stopped` → Yellow "Stopped"
197
+ - `error` → Red "Error ✗"
198
+ - `suspended` → Yellow "Suspended ⚠"
1159
199
 
1160
- **Git OAuth (NEW in 1.4.0):**
1161
- - ✅ `saac git connect [host]` - OAuth flow for Git authentication
1162
- - ✅ `saac git list` - List connected Git accounts
1163
- - ✅ `saac git disconnect <host>` - Revoke Git connection
1164
- - ✅ OAuth integration in create command (prompts if not connected)
1165
-
1166
- **Application Management:**
1167
- - ✅ `saac create` - Create application with ALL advanced features
1168
- - ✅ `saac update` - Update application configuration (PATCH endpoint)
1169
- - ✅ `saac init` - Link existing application to current directory (interactive)
1170
- - ✅ `saac deploy` - Trigger deployment for current application
1171
- - ✅ `saac deployments` - List deployment history with table display
1172
- - ✅ `saac logs` - View deployment logs (build logs) or runtime logs (container logs)
1173
- - ✅ `saac list` - List all applications with table display
1174
- - ✅ `saac status` - Show login status, user info, and applications
1175
- - ✅ `saac delete` - Delete application with confirmation prompt
1176
- - ✅ `saac whoami` - Show current user information
1177
-
1178
- **Environment & Domain Management:**
1179
- - ✅ `saac env set/get/list` - Manage environment variables (fully implemented)
1180
- - ✅ `saac domain set/show` - Manage application domain (fully implemented)
1181
-
1182
- **Local Development (Phase 1):**
1183
- - ✅ `saac run <command>` - Execute local command with remote environment variables
1184
- - Features: 5-minute caching, secure temp files (0600), automatic cleanup, rate limit handling
1185
-
1186
- **Remote Shell (Project Aurora - Phase 3.5):**
1187
- - ✅ `saac shell` - TRUE remote shell inside Docker container via WebSocket
1188
- - Features: Persistent sessions (tmux), interactive (vim/nano work), real-time output, auto-reconnect
1189
- - Architecture: WebSocket client + PostgreSQL + Python daemon + Docker + tmux
1190
- - **Status:** CLI ready, waiting for backend deployment
1191
-
1192
- **Remote Execution (NEW in 1.6.0):**
1193
- - ✅ `saac exec <command>` - Execute commands in remote container
1194
- - ✅ `saac exec --history` - View execution history
1195
- - Features: Command allowlist, dangerous pattern detection, rate limiting (30/5min), audit logging
1196
-
1197
- **All Commands Implemented!** ✅ No incomplete commands remain
1198
-
1199
- ### Critical Learnings & Bug Fixes
1200
-
1201
- **Issue 1: Config Location**
1202
- - **Problem:** Conf package auto-appended `-nodejs` suffix to folder name
1203
- - **Solution:** Use explicit `cwd: path.join(os.homedir(), '.config', 'startanaicompany')` instead of `projectName`
1204
- - **Location:** `src/lib/config.js`
1205
-
1206
- **Issue 2: Status Command Showing "Logged in" Then "Expired"**
1207
- - **Problem:** Command checked local session first, displayed status, THEN verified with server
1208
- - **Solution:** Verify with server FIRST before displaying any status
1209
- - **Location:** `src/commands/status.js`
1210
-
1211
- **Issue 3: Application List Inconsistency**
1212
- - **Problem:** `/users/me` returned `application_count: 1` but `/applications` returned empty array
1213
- - **Root Cause:** Backend filtered by `status='active'` but new apps start with `status='creating'`
1214
- - **Solution:** Backend team fixed to filter by `status != 'deleted'`
1215
- - **Location:** Backend - `src/services/application.js:447`
1216
-
1217
- **Issue 4: Register Endpoint 404 Error**
1218
- - **Problem:** CLI was calling `POST /api/v1/register` but actual endpoint is `POST /api/v1/users/register`
1219
- - **Solution:** Changed endpoint path in api.js
1220
- - **Location:** `src/lib/api.js:64`
1221
-
1222
- **Issue 5: Deprecated Field Names**
1223
- - **Problem:** CLI still used `gitea_username` and `gitea_api_token`
1224
- - **Solution:** Renamed to `git_username` and `git_api_token` throughout codebase
1225
- - **Affected:** `register.js`, `api.js`, `bin/saac.js`
1226
-
1227
- **Issue 6: Truncated API Key Display**
1228
- - **Problem:** Showing truncated API key with "Save this key" message was confusing
1229
- - **Solution:** Show FULL API key in verify command (it's only shown once)
1230
- - **Location:** `src/commands/verify.js:72`
1231
-
1232
- **Issue 7: Git Username Auto-Detection**
1233
- - **Finding:** Server auto-populates `git_username` from email address (e.g., `kalle.johansson@goryan.io` → `kalle.johansson`)
1234
- - **Behavior:** This is backend behavior, CLI just displays what server returns
1235
- - **No action needed:** Working as designed
1236
-
1237
- ### API Endpoint Reference
1238
-
1239
- **Correct Endpoint Paths (v1.5.0):**
1240
- - `POST /api/v1/users/register` - Register (email only, git_username optional)
1241
- - `POST /api/v1/users/verify` - Verify email with code
1242
- - `POST /api/v1/auth/login` - Login with API key, get session token
1243
- - `POST /api/v1/auth/logout` - Logout current session
1244
- - `POST /api/v1/auth/logout-all` - Revoke all sessions
1245
- - `GET /api/v1/auth/sessions` - List all sessions
1246
- - `GET /api/v1/users/me` - Get user info
1247
- - `GET /api/v1/users/me/oauth` - List OAuth connections
1248
- - `DELETE /api/v1/users/me/oauth/:git_host` - Revoke OAuth connection
1249
- - `GET /oauth/authorize` - Initiate OAuth flow (no /api/v1 prefix!)
1250
- - `GET /oauth/poll/:session_id` - Poll OAuth status (no /api/v1 prefix!)
1251
- - `POST /api/v1/applications` - Create application
1252
- - `GET /api/v1/applications` - List applications
1253
- - `PATCH /api/v1/applications/:uuid` - Update application
1254
- - `POST /api/v1/applications/:uuid/deploy` - Deploy application
1255
- - `GET /api/v1/applications/:uuid/deployments` - Get deployment history (NEW in 1.4.17)
1256
- - `GET /api/v1/applications/:uuid/deployment-logs` - Get deployment logs (NEW in 1.4.17)
1257
- - `GET /api/v1/applications/:uuid/logs` - Get runtime logs (container logs)
1258
- - `GET /api/v1/applications/:uuid/env/export` - Export environment variables (NEW in 1.5.0)
1259
- - `DELETE /api/v1/applications/:uuid` - Delete application
1260
-
1261
- **Important:** OAuth endpoints (`/oauth/*`) do NOT have the `/api/v1` prefix!
1262
-
1263
- ### OAuth Implementation Details
1264
-
1265
- **Token Handling:**
1266
- - Session tokens start with `st_`
1267
- - API keys start with `cw_`
1268
- - OAuth helper functions detect token type and use correct header:
1269
- - `st_*` → `X-Session-Token`
1270
- - `cw_*` → `X-API-Key`
1271
-
1272
- **Polling Strategy:**
1273
- - Initial 60-second wait before polling starts (gives user time to complete OAuth)
1274
- - Poll every 2 seconds for up to 5 minutes
1275
- - Handles 401/404 errors during polling (expected while user authorizes)
1276
- - Only fails on non-auth errors (500, network errors)
1277
-
1278
- **OAuth URL Construction:**
1279
- - Remove `/api/v1` suffix from base URL: `baseUrl.replace('/api/v1', '')`
1280
- - OAuth endpoints: `${baseUrl}/oauth/authorize` and `${baseUrl}/oauth/poll/:id`
1281
-
1282
- ### Application Status Values
1283
-
1284
- Backend returns these status values (from Coolify):
1285
- - `creating` - Application being created (initial state)
1286
- - `active` - Fully created and operational
1287
- - `running:healthy` - Container running and healthy
1288
- - `running:unknown` - Container running, health status unknown
1289
- - `stopped` - Container stopped
1290
- - `error` - Creation or deployment failed
1291
- - `suspended` - Suspended by admin
1292
-
1293
- **CLI Display Logic:**
1294
- ```javascript
1295
- if (status.startsWith('running')) {
1296
- display = 'Running ✓' (green);
1297
- } else if (status.startsWith('stopped')) {
1298
- display = 'Stopped' (yellow);
1299
- } else {
1300
- switch (status) {
1301
- case 'active': display = 'Active ✓' (green);
1302
- case 'creating': display = 'Creating...' (yellow);
1303
- case 'error': display = 'Error ✗' (red);
1304
- case 'suspended': display = 'Suspended ⚠' (yellow);
1305
- }
1306
- }
1307
- ```
1308
-
1309
- ### User Registration & Authentication Flow
1310
-
1311
- **Complete Flow:**
1312
- 1. `saac register -e user@example.com` → Creates account, sends verification email
1313
- 2. Check MailHog at https://mailhog.goryan.io for verification code
1314
- 3. `saac verify 123456` → Verifies email, returns API key (shown in full, only once)
1315
- 4. `saac login -e user@example.com -k cw_...` → Exchanges API key for 1-year session token
1316
- 5. Session token saved to `~/.config/startanaicompany/config.json`
1317
- 6. All future commands use session token automatically
1318
-
1319
- **Note:** User MUST login after verification to get session token. Verification only provides API key.
1320
-
1321
- ### Sessions Management Commands
1322
-
1323
- **Sessions Command (`saac sessions`):**
1324
- - Lists all active sessions with creation date, last used time, IP address, and expiration
1325
- - Uses table format for clear display
1326
- - Shows total session count
1327
- - Located in `src/commands/sessions.js`
1328
-
1329
- **Logout All Command (`saac logout-all`):**
1330
- - Revokes ALL session tokens across all devices
1331
- - Requires confirmation prompt unless `--yes` flag provided
1332
- - Clears local config after successful revocation
1333
- - Shows count of sessions revoked
1334
- - Located in `src/commands/logoutAll.js`
1335
- - **Exception to non-interactive rule:** Uses inquirer for confirmation prompt (but can be skipped with `-y`)
1336
-
1337
- **Implementation Notes:**
1338
- - Both commands check authentication before proceeding
1339
- - Handle gracefully if session already expired (401 errors)
1340
- - Clear local config even if server call fails
1341
-
1342
- ### Testing Considerations
200
+ ## Important Patterns
1343
201
 
1344
- When implementing new commands:
1345
- 1. Test both with flags and interactive prompts
1346
- 2. Verify error handling for missing authentication
1347
- 3. Verify error handling for missing project config
1348
- 4. Test API error responses
1349
- 5. Ensure proper exit codes (0 for success, 1 for errors)
1350
- 6. Check that spinners succeed/fail appropriately
1351
- 7. **NEVER truncate sensitive data if user needs to save it** (API keys, session tokens)
1352
- 8. Show full values for one-time credentials
202
+ **ensureAuthenticated() usage:**
203
+ - Use in ALL non-auth commands (deploy, list, create, etc.)
204
+ - Do NOT use in: login, logout, register, verify
1353
205
 
1354
- ### Publishing Checklist
206
+ **Project config requirement:**
207
+ - All app-specific commands need `.saac/config.json`
208
+ - Created by: `saac init` or `saac create`
1355
209
 
1356
- Before publishing to npm:
1357
- 1. Verify all new commands work with live backend
1358
- 2. Test OAuth flow end-to-end
1359
- 3. Check syntax: `node -c src/**/*.js bin/*.js`
1360
- 4. Update version in package.json
1361
- 5. Update CLAUDE.md with changes
1362
- 6. Run: `npm publish --access public --otp=<code>`
210
+ **Token validation:**
211
+ - `isAuthenticated()` - Checks local session expiration
212
+ - `ensureAuthenticated()` - Checks session + auto-login via env vars
1363
213
 
1364
- ### Dependencies
214
+ **Never truncate sensitive data:**
215
+ - Show FULL API keys when displayed (they're only shown once)
216
+ - Same for session tokens during verification
1365
217
 
1366
- **Required packages:**
1367
- - `axios` - HTTP client for API calls
1368
- - `chalk` - Terminal colors
1369
- - `commander` - CLI framework
1370
- - `conf` - Configuration management
1371
- - `inquirer` - Interactive prompts
1372
- - `ora` - Spinners
1373
- - `boxen` - Boxed messages
1374
- - `table` - Table display
1375
- - `validator` - Email validation
1376
- - `dotenv` - Environment variables
1377
- - `open` - Open browser for OAuth (v8.4.2 for compatibility with chalk v4)
218
+ ## MailHog & Testing
1378
219
 
1379
- **Version:** 1.5.0 (current)
220
+ - MailHog URL: https://mailhog.goryan.io
221
+ - Default domain: `{subdomain}.startanaicompany.com`
222
+ - Git server: https://git.startanaicompany.com