@startanaicompany/cli 1.6.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +19 -1
- package/CLAUDE.md +143 -1300
- package/README.md +263 -2
- package/bin/saac.js +32 -2
- package/package.json +1 -1
- package/src/commands/db.js +322 -0
- package/src/commands/deploy.js +194 -30
- package/src/commands/exec.js +77 -16
- package/src/lib/api.js +66 -2
package/CLAUDE.md
CHANGED
|
@@ -1,196 +1,59 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
|
-
This file provides guidance to Claude Code
|
|
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
|
|
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
|
-
|
|
9
|
+
**Current Version:** 1.6.0
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
33
|
-
|
|
34
|
-
The CLI maintains two separate configuration files:
|
|
21
|
+
### Configuration System
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
55
|
-
-
|
|
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
|
-
-
|
|
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
|
-
|
|
73
|
-
- `logger.success()`
|
|
74
|
-
- `logger.
|
|
75
|
-
- `logger.
|
|
76
|
-
- `logger.
|
|
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
|
-
|
|
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.
|
|
98
|
-
2.
|
|
99
|
-
3. Check
|
|
100
|
-
4.
|
|
101
|
-
5.
|
|
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
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
## Development Notes
|
|
66
|
+
## Authentication Flow
|
|
206
67
|
|
|
207
|
-
### Session
|
|
68
|
+
### Session Tokens (Primary Method)
|
|
69
|
+
- 1-year expiration
|
|
70
|
+
- Stored in global config
|
|
71
|
+
- Header: `X-Session-Token`
|
|
208
72
|
|
|
209
|
-
|
|
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
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
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
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
98
|
+
### Logs Command (v1.6.0)
|
|
99
|
+
**Deployment logs:** `saac logs --deployment [uuid]`
|
|
100
|
+
**Runtime logs:** `saac logs --follow --type access`
|
|
1002
101
|
|
|
1003
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
### Remote Execution (v1.6.0)
|
|
1008
114
|
```bash
|
|
1009
|
-
saac exec "npm run
|
|
1010
|
-
saac exec
|
|
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
|
-
|
|
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
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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
|
-
|
|
133
|
+
# OAuth (no /api/v1 prefix)
|
|
134
|
+
GET /oauth/authorize
|
|
135
|
+
GET /oauth/poll/:session_id
|
|
1093
136
|
```
|
|
1094
137
|
|
|
1095
|
-
|
|
138
|
+
## Critical Bugs Fixed (v1.6.0)
|
|
1096
139
|
|
|
1097
|
-
**
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
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
|
-
**
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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
|
-
**
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
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
|
-
|
|
160
|
+
## Publishing Checklist
|
|
1122
161
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
-
|
|
169
|
+
## Version 1.6.0 Release Notes
|
|
1129
170
|
|
|
1130
|
-
|
|
1131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1136
|
-
-
|
|
1137
|
-
-
|
|
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
|
-
##
|
|
187
|
+
## Dependencies
|
|
1141
188
|
|
|
1142
|
-
|
|
189
|
+
- axios, chalk, commander, conf, inquirer, ora, boxen, table, validator, dotenv, open, ws
|
|
1143
190
|
|
|
1144
|
-
|
|
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
|
-
|
|
1157
|
-
-
|
|
1158
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
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
|
-
|
|
206
|
+
**Project config requirement:**
|
|
207
|
+
- All app-specific commands need `.saac/config.json`
|
|
208
|
+
- Created by: `saac init` or `saac create`
|
|
1355
209
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
220
|
+
- MailHog URL: https://mailhog.goryan.io
|
|
221
|
+
- Default domain: `{subdomain}.startanaicompany.com`
|
|
222
|
+
- Git server: https://git.startanaicompany.com
|