@startanaicompany/cli 1.4.16 → 1.4.18
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 +495 -36
- package/bin/saac.js +58 -4
- package/package.json +1 -1
- package/src/commands/create.js +27 -6
- package/src/commands/delete.js +191 -4
- package/src/commands/deploy.js +41 -8
- package/src/commands/deployments.js +130 -0
- package/src/commands/domain.js +204 -3
- package/src/commands/env.js +264 -3
- package/src/commands/exec.js +277 -0
- package/src/commands/keys.js +0 -0
- package/src/commands/logs.js +232 -4
- package/src/commands/run.js +170 -0
- package/src/commands/shell.js +166 -0
- package/src/commands/whoami.js +90 -4
- package/src/lib/api.js +63 -4
- package/src/lib/errorDisplay.js +170 -0
package/CLAUDE.md
CHANGED
|
@@ -275,6 +275,71 @@ saac create web -s web -r git@git... -t abc123 \
|
|
|
275
275
|
- Saves project config to `.saac/config.json` after successful creation
|
|
276
276
|
- Displays next steps and useful commands
|
|
277
277
|
|
|
278
|
+
**Deployment Behavior (NEW):**
|
|
279
|
+
|
|
280
|
+
The `create` command now **waits for the initial deployment to complete** (up to 5 minutes) before returning.
|
|
281
|
+
|
|
282
|
+
**Response Time:**
|
|
283
|
+
- Typical: 30-120 seconds
|
|
284
|
+
- Maximum: 5 minutes (timeout)
|
|
285
|
+
|
|
286
|
+
**Success Response:**
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"success": true,
|
|
290
|
+
"coolify_app_uuid": "...",
|
|
291
|
+
"app_name": "my-app",
|
|
292
|
+
"domain": "https://myapp.startanaicompany.com",
|
|
293
|
+
"deployment_status": "finished",
|
|
294
|
+
"deployment_uuid": "...",
|
|
295
|
+
"git_branch": "master"
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Failure Response (HTTP 200 with success: false):**
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"success": false,
|
|
303
|
+
"coolify_app_uuid": "...",
|
|
304
|
+
"app_name": "my-app",
|
|
305
|
+
"deployment_status": "failed",
|
|
306
|
+
"message": "Port 8080 is already in use. Remove host port bindings...",
|
|
307
|
+
"errors": [
|
|
308
|
+
{
|
|
309
|
+
"type": "PORT_CONFLICT",
|
|
310
|
+
"message": "Port 8080 is already in use...",
|
|
311
|
+
"detail": "Bind for 0.0.0.0:8080 failed..."
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"relevant_logs": [...],
|
|
315
|
+
"last_logs": [...]
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Error Types:**
|
|
320
|
+
- `PORT_CONFLICT` - Host port binding conflict in docker-compose.yml
|
|
321
|
+
- `BUILD_FAILED` - Build process returned non-zero exit code
|
|
322
|
+
- `TIMEOUT` - Deployment didn't complete in 5 minutes
|
|
323
|
+
- `UNKNOWN` - Generic deployment failure
|
|
324
|
+
|
|
325
|
+
**Important Notes:**
|
|
326
|
+
1. **Application is created even if deployment fails** - the UUID is saved to `.saac/config.json`
|
|
327
|
+
2. Failed deployments return HTTP 200 (not 4xx) with `success: false`
|
|
328
|
+
3. CLI must check the `success` field, not just HTTP status code
|
|
329
|
+
4. Detailed error information is displayed with actionable advice
|
|
330
|
+
5. User can fix the issue and run `saac deploy` to retry
|
|
331
|
+
|
|
332
|
+
**Error Display:**
|
|
333
|
+
The CLI displays comprehensive error information:
|
|
334
|
+
- Error summary message
|
|
335
|
+
- Structured error details with types
|
|
336
|
+
- Relevant error logs (filtered)
|
|
337
|
+
- Last log lines for context
|
|
338
|
+
- Actionable advice based on error type:
|
|
339
|
+
- `PORT_CONFLICT`: Remove host port bindings from docker-compose.yml
|
|
340
|
+
- `BUILD_FAILED`: Check Dockerfile, run `docker build .` locally
|
|
341
|
+
- `TIMEOUT`: Check `saac status` and `saac logs`, may still be running
|
|
342
|
+
|
|
278
343
|
### Update Command Implementation
|
|
279
344
|
|
|
280
345
|
The `update` command allows modifying application configuration after deployment using `PATCH /api/v1/applications/:uuid`.
|
|
@@ -373,7 +438,7 @@ saac git disconnect git.startanaicompany.com
|
|
|
373
438
|
|
|
374
439
|
### Deploy Command Implementation
|
|
375
440
|
|
|
376
|
-
The `deploy` command triggers deployment for
|
|
441
|
+
The `deploy` command triggers deployment and **waits for completion** (up to 5 minutes).
|
|
377
442
|
|
|
378
443
|
**Usage:**
|
|
379
444
|
```bash
|
|
@@ -385,13 +450,57 @@ saac deploy --force
|
|
|
385
450
|
1. Validates authentication (session token not expired)
|
|
386
451
|
2. Checks for project config (`.saac/config.json`)
|
|
387
452
|
3. Makes POST request to `/api/v1/applications/:uuid/deploy`
|
|
388
|
-
4.
|
|
389
|
-
5.
|
|
453
|
+
4. **Waits for deployment to complete** (up to 5 minutes)
|
|
454
|
+
5. Displays deployment status with detailed error information on failure
|
|
390
455
|
|
|
391
|
-
**Response
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
|
|
456
|
+
**Response Time:**
|
|
457
|
+
- Typical: 30-120 seconds
|
|
458
|
+
- Maximum: 5 minutes (timeout)
|
|
459
|
+
|
|
460
|
+
**Success Response:**
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"success": true,
|
|
464
|
+
"status": "finished",
|
|
465
|
+
"deployment_uuid": "...",
|
|
466
|
+
"git_branch": "master",
|
|
467
|
+
"domain": "https://myapp.startanaicompany.com",
|
|
468
|
+
"traefik_status": "queued"
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Failure Response:**
|
|
473
|
+
```json
|
|
474
|
+
{
|
|
475
|
+
"success": false,
|
|
476
|
+
"status": "failed",
|
|
477
|
+
"message": "Build failed with exit code 1",
|
|
478
|
+
"errors": [
|
|
479
|
+
{
|
|
480
|
+
"type": "BUILD_FAILED",
|
|
481
|
+
"message": "Build failed with exit code 1",
|
|
482
|
+
"detail": "npm ERR! code ELIFECYCLE..."
|
|
483
|
+
}
|
|
484
|
+
],
|
|
485
|
+
"relevant_logs": [...],
|
|
486
|
+
"last_logs": [...]
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Error Types:**
|
|
491
|
+
- `PORT_CONFLICT` - Host port binding conflict
|
|
492
|
+
- `BUILD_FAILED` - Build process failed
|
|
493
|
+
- `TIMEOUT` - Deployment didn't complete in 5 minutes
|
|
494
|
+
- `UNKNOWN` - Generic failure
|
|
495
|
+
|
|
496
|
+
**Error Display:**
|
|
497
|
+
The CLI displays:
|
|
498
|
+
1. Error summary message
|
|
499
|
+
2. Structured error details with types
|
|
500
|
+
3. Relevant logs (filtered error logs)
|
|
501
|
+
4. Last 5 log lines for context
|
|
502
|
+
5. Actionable advice based on error type
|
|
503
|
+
6. Suggestion to view full logs with `saac logs --follow`
|
|
395
504
|
|
|
396
505
|
**Note:** The `--force` flag is defined in the CLI but not currently used by the API.
|
|
397
506
|
|
|
@@ -542,16 +651,361 @@ Uses the same status display logic as the status command (see "Application Statu
|
|
|
542
651
|
- Domains fallback to `{subdomain}.startanaicompany.com` if not set
|
|
543
652
|
- Branch defaults to 'master' if not specified
|
|
544
653
|
|
|
545
|
-
###
|
|
654
|
+
### Deployments Command Implementation
|
|
655
|
+
|
|
656
|
+
The `deployments` command displays deployment history for the current application in a formatted table.
|
|
657
|
+
|
|
658
|
+
**Usage:**
|
|
659
|
+
```bash
|
|
660
|
+
saac deployments
|
|
661
|
+
saac deploys # Alias
|
|
662
|
+
|
|
663
|
+
# With pagination
|
|
664
|
+
saac deployments --limit 10
|
|
665
|
+
saac deployments --limit 20 --offset 20
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**How It Works:**
|
|
669
|
+
1. Validates authentication (session token not expired)
|
|
670
|
+
2. Checks for project config (`.saac/config.json`)
|
|
671
|
+
3. Fetches deployment history from `/api/v1/applications/:uuid/deployments`
|
|
672
|
+
4. Displays table with columns: UUID, Status, Branch, Commit, Duration, Trigger, Date
|
|
673
|
+
5. Shows pagination info if more deployments available
|
|
674
|
+
|
|
675
|
+
**Options:**
|
|
676
|
+
- `-l, --limit <number>` - Number of deployments to show (default: 20)
|
|
677
|
+
- `-o, --offset <number>` - Offset for pagination (default: 0)
|
|
678
|
+
|
|
679
|
+
**Status Display:**
|
|
680
|
+
- ✅ `finished` - Displayed in green
|
|
681
|
+
- ✗ `failed` - Displayed in red
|
|
682
|
+
- ⏳ `running`, `queued` - Displayed in yellow
|
|
683
|
+
- `unknown` - Displayed in gray
|
|
684
|
+
|
|
685
|
+
**Table Formatting:**
|
|
686
|
+
- UUID truncated to 26 characters for readability
|
|
687
|
+
- Commit SHA truncated to 7 characters
|
|
688
|
+
- Duration shown in seconds
|
|
689
|
+
- Date formatted with `toLocaleString()`
|
|
690
|
+
|
|
691
|
+
**Response Fields:**
|
|
692
|
+
```json
|
|
693
|
+
{
|
|
694
|
+
"deployments": [
|
|
695
|
+
{
|
|
696
|
+
"deployment_uuid": "...",
|
|
697
|
+
"status": "finished",
|
|
698
|
+
"git_branch": "master",
|
|
699
|
+
"git_commit": "abc1234",
|
|
700
|
+
"duration_seconds": 45,
|
|
701
|
+
"triggered_by": "api",
|
|
702
|
+
"started_at": "2024-01-20T12:00:00Z"
|
|
703
|
+
}
|
|
704
|
+
],
|
|
705
|
+
"total": 100
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
**Next Steps:**
|
|
710
|
+
After viewing deployment history, use:
|
|
711
|
+
- `saac logs --deployment` - View latest deployment logs
|
|
712
|
+
- `saac logs --deployment <uuid>` - View specific deployment logs
|
|
713
|
+
|
|
714
|
+
### Logs Command Implementation
|
|
715
|
+
|
|
716
|
+
The `logs` command displays application logs with support for both deployment logs (build logs) and runtime logs (container logs).
|
|
717
|
+
|
|
718
|
+
**Usage:**
|
|
719
|
+
```bash
|
|
720
|
+
# View latest deployment logs (build logs)
|
|
721
|
+
saac logs --deployment
|
|
722
|
+
saac logs -d
|
|
723
|
+
|
|
724
|
+
# View specific deployment logs
|
|
725
|
+
saac logs --deployment abc123-def456-...
|
|
726
|
+
saac logs abc123-def456-... --deployment
|
|
727
|
+
|
|
728
|
+
# View deployment logs in raw format
|
|
729
|
+
saac logs --deployment --raw
|
|
730
|
+
|
|
731
|
+
# View runtime logs (container logs)
|
|
732
|
+
saac logs
|
|
733
|
+
saac logs --tail 200
|
|
734
|
+
saac logs --follow
|
|
735
|
+
saac logs --since 1h
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
**Two Modes:**
|
|
739
|
+
|
|
740
|
+
**1. Deployment Logs Mode (Build Logs)**
|
|
741
|
+
- Enabled with `--deployment` flag
|
|
742
|
+
- Shows logs from the build/deployment process
|
|
743
|
+
- Includes build output, errors, and deployment status
|
|
744
|
+
- Supports both parsed (colorized) and raw formats
|
|
745
|
+
|
|
746
|
+
**Options:**
|
|
747
|
+
- `--deployment [uuid]` - View deployment logs (omit UUID for latest)
|
|
748
|
+
- `--raw` - Show raw log output (no parsing or colorization)
|
|
749
|
+
- `--include-hidden` - Include hidden log lines
|
|
750
|
+
|
|
751
|
+
**Display:**
|
|
752
|
+
- Header with deployment UUID, status, commit, duration
|
|
753
|
+
- Parsed logs with stderr highlighted in red
|
|
754
|
+
- Error summary if deployment failed
|
|
755
|
+
- Structured error information with types and details
|
|
756
|
+
|
|
757
|
+
**2. Runtime Logs Mode (Container Logs)**
|
|
758
|
+
- Default mode when no `--deployment` flag
|
|
759
|
+
- Shows logs from the running container
|
|
760
|
+
- Real-time application output
|
|
761
|
+
|
|
762
|
+
**Options:**
|
|
763
|
+
- `-t, --tail <lines>` - Number of lines to show (default: 100)
|
|
764
|
+
- `-f, --follow` - Follow log output (not yet implemented)
|
|
765
|
+
- `--since <time>` - Show logs since timestamp
|
|
766
|
+
|
|
767
|
+
**Error Handling:**
|
|
768
|
+
- 404 error → No deployments found, suggests `saac deploy`
|
|
769
|
+
- 501 error → Runtime logs not implemented, suggests deployment logs
|
|
770
|
+
|
|
771
|
+
**Response Fields (Deployment Logs):**
|
|
772
|
+
```json
|
|
773
|
+
{
|
|
774
|
+
"deployment_uuid": "...",
|
|
775
|
+
"status": "finished",
|
|
776
|
+
"commit": "abc1234",
|
|
777
|
+
"commit_message": "Fix bug",
|
|
778
|
+
"started_at": "2024-01-20T12:00:00Z",
|
|
779
|
+
"finished_at": "2024-01-20T12:02:00Z",
|
|
780
|
+
"duration_seconds": 120,
|
|
781
|
+
"log_count": 150,
|
|
782
|
+
"logs": [
|
|
783
|
+
{
|
|
784
|
+
"type": "stdout",
|
|
785
|
+
"output": "Installing dependencies..."
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
"type": "stderr",
|
|
789
|
+
"output": "npm WARN deprecated package@1.0.0"
|
|
790
|
+
}
|
|
791
|
+
],
|
|
792
|
+
"raw_logs": "...",
|
|
793
|
+
"errors": [
|
|
794
|
+
{
|
|
795
|
+
"type": "BUILD_FAILED",
|
|
796
|
+
"message": "Build failed with exit code 1",
|
|
797
|
+
"detail": "npm ERR! code ELIFECYCLE"
|
|
798
|
+
}
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
```
|
|
546
802
|
|
|
547
|
-
|
|
548
|
-
- `src/commands/env.js` - Not implemented (stub only)
|
|
549
|
-
- `src/commands/domain.js` - Not implemented (stub only)
|
|
550
|
-
- `src/commands/logs.js` - Not implemented (stub only)
|
|
551
|
-
- `src/commands/delete.js` - Not implemented (stub only)
|
|
552
|
-
- `src/commands/whoami.js` - Not implemented (stub only)
|
|
803
|
+
### Local Development Commands (NEW in 1.5.0)
|
|
553
804
|
|
|
554
|
-
|
|
805
|
+
#### Run Command Implementation
|
|
806
|
+
|
|
807
|
+
The `run` command allows executing local commands with remote environment variables from the deployed application.
|
|
808
|
+
|
|
809
|
+
**Usage:**
|
|
810
|
+
```bash
|
|
811
|
+
saac run <command> # Run command with remote env vars
|
|
812
|
+
saac run npm start # Start app with production env
|
|
813
|
+
saac run --sync npm run build # Force refresh env vars (skip cache)
|
|
814
|
+
saac run -q npm test # Quiet mode (suppress warnings)
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
**Implementation Details:**
|
|
818
|
+
- Fetches env vars from `GET /applications/:uuid/env/export`
|
|
819
|
+
- Uses 5-minute in-memory cache (TTL: 300 seconds)
|
|
820
|
+
- Creates temp file at `/tmp/saac-env-{uuid}.sh` with 0600 permissions
|
|
821
|
+
- Sources env file and executes command in subshell
|
|
822
|
+
- Automatic cleanup on exit (SIGINT/SIGTERM handlers)
|
|
823
|
+
- Rate limit handling (10 requests/minute per user)
|
|
824
|
+
|
|
825
|
+
**Security Features:**
|
|
826
|
+
- Temp files with 0600 permissions (owner read-write only)
|
|
827
|
+
- Automatic cleanup on process exit
|
|
828
|
+
- Warning message about exposed secrets
|
|
829
|
+
- Cache reduces API calls (rate limit protection)
|
|
830
|
+
|
|
831
|
+
**Error Handling:**
|
|
832
|
+
- 429 Rate Limit: Shows retry time, mentions caching
|
|
833
|
+
- 403 Forbidden: User doesn't own application
|
|
834
|
+
- 404 Not Found: Application doesn't exist
|
|
835
|
+
- Network failures: Graceful error messages
|
|
836
|
+
|
|
837
|
+
**Location:** `src/commands/run.js`
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
#### Shell Command Implementation
|
|
842
|
+
|
|
843
|
+
The `shell` command opens an interactive shell with remote environment variables loaded.
|
|
844
|
+
|
|
845
|
+
**Usage:**
|
|
846
|
+
```bash
|
|
847
|
+
saac shell # Open shell (uses $SHELL or /bin/bash)
|
|
848
|
+
saac shell --cmd zsh # Use specific shell
|
|
849
|
+
saac shell --sync # Force refresh env vars (skip cache)
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Implementation Details:**
|
|
853
|
+
- Fetches env vars from `GET /applications/:uuid/env/export`
|
|
854
|
+
- Uses 5-minute in-memory cache (same as `run`)
|
|
855
|
+
- Creates temp file for env vars at `/tmp/saac-env-{uuid}.sh`
|
|
856
|
+
- Special handling for bash and zsh (custom RC files)
|
|
857
|
+
- Sources environment before shell starts
|
|
858
|
+
- Automatic cleanup when shell exits
|
|
859
|
+
|
|
860
|
+
**Shell-Specific Behavior:**
|
|
861
|
+
- **bash:** Uses `--rcfile` with temporary RC that sources env + user's `.bashrc`
|
|
862
|
+
- **zsh:** Uses `ZDOTDIR` with temporary `.zshrc` that sources env + user's `.zshrc`
|
|
863
|
+
- **other:** Fallback to bash with source then exec
|
|
864
|
+
|
|
865
|
+
**Security Features:**
|
|
866
|
+
- Same as `run` command (0600 permissions, cleanup, warnings)
|
|
867
|
+
- Sets environment variables: `SAAC_ENV_LOADED=1`, `SAAC_APP_NAME`, `SAAC_APP_UUID`
|
|
868
|
+
|
|
869
|
+
**User Experience:**
|
|
870
|
+
```bash
|
|
871
|
+
$ saac shell
|
|
872
|
+
✓ Environment variables retrieved
|
|
873
|
+
|
|
874
|
+
🚀 Opening shell with 6 environment variables loaded
|
|
875
|
+
|
|
876
|
+
Application: my-app
|
|
877
|
+
Shell: bash
|
|
878
|
+
Variables: 6
|
|
879
|
+
|
|
880
|
+
⚠️ Secrets are exposed on local machine
|
|
881
|
+
Temporary file: /tmp/saac-env-abc123.sh (will be deleted on exit)
|
|
882
|
+
|
|
883
|
+
Type "exit" or press Ctrl+D to close the shell
|
|
884
|
+
────────────────────────────────────────────────────────────────
|
|
885
|
+
|
|
886
|
+
bash-5.0$ echo $NODE_ENV
|
|
887
|
+
production
|
|
888
|
+
bash-5.0$ exit
|
|
889
|
+
|
|
890
|
+
✓ Shell closed, environment variables cleared
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
**Location:** `src/commands/shell.js`
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
897
|
+
### Remote Execution Commands (NEW in 1.6.0)
|
|
898
|
+
|
|
899
|
+
#### Exec Command Implementation
|
|
900
|
+
|
|
901
|
+
The `exec` command allows executing commands directly in the deployed application's container.
|
|
902
|
+
|
|
903
|
+
**Usage:**
|
|
904
|
+
```bash
|
|
905
|
+
saac exec "npm run db:migrate" # Run database migrations
|
|
906
|
+
saac exec "npm run build" --timeout 120 # With custom timeout
|
|
907
|
+
saac exec "python manage.py collectstatic" --workdir /app/backend
|
|
908
|
+
saac exec --history # View execution history
|
|
909
|
+
saac exec --history --limit 50 # Show more history
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
**Implementation Details:**
|
|
913
|
+
- Sends command to `POST /applications/:uuid/exec`
|
|
914
|
+
- Backend validates command against allowlist
|
|
915
|
+
- Python daemon executes via Docker API (no shell interpretation!)
|
|
916
|
+
- Returns: execution_id, exit_code, stdout, stderr, duration
|
|
917
|
+
- Command runs in container as detected user (not root)
|
|
918
|
+
|
|
919
|
+
**Architecture:**
|
|
920
|
+
```
|
|
921
|
+
CLI → Backend API → Database (exec_requests table) → Python Daemon → Docker Container
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
**Security Features:**
|
|
925
|
+
- ✅ **3-layer validation:** Backend allowlist + daemon validation + Docker array execution
|
|
926
|
+
- ✅ **Command allowlist:** npm, node, python, bash, etc. (dangerous commands blocked)
|
|
927
|
+
- ✅ **Dangerous pattern detection:** Blocks `rm -rf`, `| sh`, `$(...)`, etc.
|
|
928
|
+
- ✅ **Rate limiting:** 30 requests per 5 minutes per user
|
|
929
|
+
- ✅ **No shell interpretation:** Commands executed as arrays, not strings
|
|
930
|
+
- ✅ **Non-root execution:** Commands run as app user, never privileged
|
|
931
|
+
- ✅ **Audit logging:** All executions logged with user, command, exit code
|
|
932
|
+
|
|
933
|
+
**Allowed Commands:**
|
|
934
|
+
- Node.js: `npm`, `node`, `npx`, `yarn`, `pnpm`
|
|
935
|
+
- Python: `python`, `python3`, `pip`, `poetry`
|
|
936
|
+
- Ruby: `bundle`, `rake`, `rails`
|
|
937
|
+
- Build tools: `make`, `cmake`, `go`, `cargo`
|
|
938
|
+
- Shell: `sh`, `bash`, `echo`, `cat`, `ls`, `pwd`, `env`
|
|
939
|
+
- Database: `psql`, `mysql`, `mongosh`
|
|
940
|
+
|
|
941
|
+
**Error Handling:**
|
|
942
|
+
- 400 Validation Error: Command not in allowlist or dangerous pattern detected
|
|
943
|
+
- 408 Timeout: Command exceeded timeout limit (default: 30s, max: 300s)
|
|
944
|
+
- 429 Rate Limit: Too many exec requests (30 per 5 minutes)
|
|
945
|
+
- 503 Container Not Running: Application container is not active
|
|
946
|
+
- 500 Execution Failed: Container error or daemon issue
|
|
947
|
+
|
|
948
|
+
**Example Output:**
|
|
949
|
+
```bash
|
|
950
|
+
$ saac exec "echo 'Hello from container!'"
|
|
951
|
+
|
|
952
|
+
Executing Command: myapp
|
|
953
|
+
──────────────────────────
|
|
954
|
+
|
|
955
|
+
Command: echo 'Hello from container!'
|
|
956
|
+
Working Directory: /app
|
|
957
|
+
Timeout: 30s
|
|
958
|
+
|
|
959
|
+
✓ Command executed
|
|
960
|
+
|
|
961
|
+
✓ Execution ID: eb41b197-e215-4f9e-87ac-80ce57e732a6
|
|
962
|
+
|
|
963
|
+
Exit Code: 0
|
|
964
|
+
Duration: 3531ms
|
|
965
|
+
Started: 1/28/2026, 8:55:37 PM
|
|
966
|
+
Completed: 1/28/2026, 8:55:41 PM
|
|
967
|
+
|
|
968
|
+
Standard Output:
|
|
969
|
+
────────────────────────────────────────────────────────────
|
|
970
|
+
Hello from container!
|
|
971
|
+
────────────────────────────────────────────────────────────
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
**History Command:**
|
|
975
|
+
```bash
|
|
976
|
+
$ saac exec --history
|
|
977
|
+
|
|
978
|
+
Execution History: myapp
|
|
979
|
+
────────────────────────
|
|
980
|
+
|
|
981
|
+
╔══════════╤═══════════╤═════════════╤═══════════╤══════════╤═══════════════╗
|
|
982
|
+
║ ID │ Command │ Status │ Exit Code │ Duration │ Started ║
|
|
983
|
+
╠══════════╪═══════════╪═════════════╪═══════════╪══════════╪═══════════════╣
|
|
984
|
+
║ eb41b197 │ echo '...'│ ✓ completed │ 0 │ 0s │ 1/28/26, 8:55 ║
|
|
985
|
+
║ a3f2c891 │ npm run...│ ✗ completed │ 1 │ 5s │ 1/28/26, 8:50 ║
|
|
986
|
+
╚══════════╧═══════════╧═════════════╧═══════════╧══════════╧═══════════════╝
|
|
987
|
+
|
|
988
|
+
Showing 2 of 5 executions
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
**Location:** `src/commands/exec.js`
|
|
992
|
+
|
|
993
|
+
**Backend Implementation:**
|
|
994
|
+
- Database table: `exec_requests` (status: pending → running → completed/failed/timeout)
|
|
995
|
+
- Python daemon: `/root/exec-daemon/exec_daemon.py` (polls every 2 seconds)
|
|
996
|
+
- Systemd service: `exec-daemon.service` (auto-restart, logs to `/var/log/exec-daemon.log`)
|
|
997
|
+
|
|
998
|
+
---
|
|
999
|
+
|
|
1000
|
+
**Backend Requirements (Implemented):**
|
|
1001
|
+
- `GET /api/v1/applications/:uuid/env/export` - Export endpoint (deployed 2026-01-28)
|
|
1002
|
+
- Returns: `environment` (object), `export_script` (bash format), `dotenv_format` (dotenv format)
|
|
1003
|
+
- Rate limit: 10 requests/minute per user
|
|
1004
|
+
- Authentication: X-Session-Token or X-API-Key
|
|
1005
|
+
- Response includes `expires_in: 300` (5-minute cache recommendation)
|
|
1006
|
+
- `POST /api/v1/applications/:uuid/exec` - Execute command in container (deployed 2026-01-28)
|
|
1007
|
+
- `GET /api/v1/applications/:uuid/exec/history` - View execution history (deployed 2026-01-28)
|
|
1008
|
+
- Rate limit: 30 requests per 5 minutes per user
|
|
555
1009
|
|
|
556
1010
|
**Implementation Pattern for New Commands:**
|
|
557
1011
|
1. Require flags, no interactive prompts (exception: `init` uses inquirer for app selection)
|
|
@@ -560,15 +1014,6 @@ Several commands still need implementation:
|
|
|
560
1014
|
4. Use spinners for async operations
|
|
561
1015
|
5. Handle errors with descriptive messages
|
|
562
1016
|
|
|
563
|
-
**Example Module Structure for env.js:**
|
|
564
|
-
```javascript
|
|
565
|
-
async function set(vars) { /* implementation */ }
|
|
566
|
-
async function get(key) { /* implementation */ }
|
|
567
|
-
async function list() { /* implementation */ }
|
|
568
|
-
|
|
569
|
-
module.exports = { set, get, list };
|
|
570
|
-
```
|
|
571
|
-
|
|
572
1017
|
### MailHog Integration
|
|
573
1018
|
|
|
574
1019
|
The system uses MailHog for email verification in development:
|
|
@@ -588,7 +1033,7 @@ The wrapper API expects Git repositories to be hosted on the StartAnAiCompany Gi
|
|
|
588
1033
|
- During registration, Gitea username can be auto-detected or manually provided
|
|
589
1034
|
- Applications reference repositories in the format: `git@git.startanaicompany.com:user/repo.git`
|
|
590
1035
|
|
|
591
|
-
## Current Status - Version 1.
|
|
1036
|
+
## Current Status - Version 1.6.0
|
|
592
1037
|
|
|
593
1038
|
### Completed Features
|
|
594
1039
|
|
|
@@ -619,17 +1064,28 @@ The wrapper API expects Git repositories to be hosted on the StartAnAiCompany Gi
|
|
|
619
1064
|
- ✅ `saac update` - Update application configuration (PATCH endpoint)
|
|
620
1065
|
- ✅ `saac init` - Link existing application to current directory (interactive)
|
|
621
1066
|
- ✅ `saac deploy` - Trigger deployment for current application
|
|
1067
|
+
- ✅ `saac deployments` - List deployment history with table display
|
|
1068
|
+
- ✅ `saac logs` - View deployment logs (build logs) or runtime logs (container logs)
|
|
622
1069
|
- ✅ `saac list` - List all applications with table display
|
|
623
1070
|
- ✅ `saac status` - Show login status, user info, and applications
|
|
1071
|
+
- ✅ `saac delete` - Delete application with confirmation prompt
|
|
1072
|
+
- ✅ `saac whoami` - Show current user information
|
|
1073
|
+
|
|
1074
|
+
**Environment & Domain Management:**
|
|
1075
|
+
- ✅ `saac env set/get/list` - Manage environment variables (fully implemented)
|
|
1076
|
+
- ✅ `saac domain set/show` - Manage application domain (fully implemented)
|
|
1077
|
+
|
|
1078
|
+
**Local Development (NEW in 1.5.0):**
|
|
1079
|
+
- ✅ `saac run <command>` - Execute local command with remote environment variables
|
|
1080
|
+
- ✅ `saac shell` - Open interactive shell with remote environment variables
|
|
1081
|
+
- Features: 5-minute caching, secure temp files (0600), automatic cleanup, rate limit handling
|
|
1082
|
+
|
|
1083
|
+
**Remote Execution (NEW in 1.6.0):**
|
|
1084
|
+
- ✅ `saac exec <command>` - Execute commands in remote container
|
|
1085
|
+
- ✅ `saac exec --history` - View execution history
|
|
1086
|
+
- Features: Command allowlist, dangerous pattern detection, rate limiting (30/5min), audit logging
|
|
624
1087
|
|
|
625
|
-
**
|
|
626
|
-
- ⏳ `saac logs` - Not implemented (stub only)
|
|
627
|
-
- ⏳ `saac env` - Not implemented (stub only, needs to export object with set/get/list methods)
|
|
628
|
-
- ⏳ `saac domain` - Not implemented (stub only, needs to export object with set/show methods)
|
|
629
|
-
- ⏳ `saac delete` - Not implemented (stub only)
|
|
630
|
-
- ⏳ `saac whoami` - Not implemented (stub only)
|
|
631
|
-
- ✅ `saac deploy` - Fully implemented
|
|
632
|
-
- ✅ `saac list` - Fully implemented
|
|
1088
|
+
**All Commands Implemented!** ✅ No incomplete commands remain
|
|
633
1089
|
|
|
634
1090
|
### Critical Learnings & Bug Fixes
|
|
635
1091
|
|
|
@@ -671,7 +1127,7 @@ The wrapper API expects Git repositories to be hosted on the StartAnAiCompany Gi
|
|
|
671
1127
|
|
|
672
1128
|
### API Endpoint Reference
|
|
673
1129
|
|
|
674
|
-
**Correct Endpoint Paths (v1.
|
|
1130
|
+
**Correct Endpoint Paths (v1.5.0):**
|
|
675
1131
|
- `POST /api/v1/users/register` - Register (email only, git_username optional)
|
|
676
1132
|
- `POST /api/v1/users/verify` - Verify email with code
|
|
677
1133
|
- `POST /api/v1/auth/login` - Login with API key, get session token
|
|
@@ -687,7 +1143,10 @@ The wrapper API expects Git repositories to be hosted on the StartAnAiCompany Gi
|
|
|
687
1143
|
- `GET /api/v1/applications` - List applications
|
|
688
1144
|
- `PATCH /api/v1/applications/:uuid` - Update application
|
|
689
1145
|
- `POST /api/v1/applications/:uuid/deploy` - Deploy application
|
|
690
|
-
- `GET /api/v1/applications/:uuid/
|
|
1146
|
+
- `GET /api/v1/applications/:uuid/deployments` - Get deployment history (NEW in 1.4.17)
|
|
1147
|
+
- `GET /api/v1/applications/:uuid/deployment-logs` - Get deployment logs (NEW in 1.4.17)
|
|
1148
|
+
- `GET /api/v1/applications/:uuid/logs` - Get runtime logs (container logs)
|
|
1149
|
+
- `GET /api/v1/applications/:uuid/env/export` - Export environment variables (NEW in 1.5.0)
|
|
691
1150
|
- `DELETE /api/v1/applications/:uuid` - Delete application
|
|
692
1151
|
|
|
693
1152
|
**Important:** OAuth endpoints (`/oauth/*`) do NOT have the `/api/v1` prefix!
|
|
@@ -808,4 +1267,4 @@ Before publishing to npm:
|
|
|
808
1267
|
- `dotenv` - Environment variables
|
|
809
1268
|
- `open` - Open browser for OAuth (v8.4.2 for compatibility with chalk v4)
|
|
810
1269
|
|
|
811
|
-
**Version:** 1.
|
|
1270
|
+
**Version:** 1.5.0 (current)
|
package/bin/saac.js
CHANGED
|
@@ -22,6 +22,7 @@ const init = require('../src/commands/init');
|
|
|
22
22
|
const create = require('../src/commands/create');
|
|
23
23
|
const update = require('../src/commands/update');
|
|
24
24
|
const deploy = require('../src/commands/deploy');
|
|
25
|
+
const deployments = require('../src/commands/deployments');
|
|
25
26
|
const logs = require('../src/commands/logs');
|
|
26
27
|
const env = require('../src/commands/env');
|
|
27
28
|
const domain = require('../src/commands/domain');
|
|
@@ -30,6 +31,9 @@ const list = require('../src/commands/list');
|
|
|
30
31
|
const status = require('../src/commands/status');
|
|
31
32
|
const whoami = require('../src/commands/whoami');
|
|
32
33
|
const manual = require('../src/commands/manual');
|
|
34
|
+
const run = require('../src/commands/run');
|
|
35
|
+
const shell = require('../src/commands/shell');
|
|
36
|
+
const execCmd = require('../src/commands/exec');
|
|
33
37
|
|
|
34
38
|
// Configure CLI
|
|
35
39
|
program
|
|
@@ -200,12 +204,62 @@ program
|
|
|
200
204
|
.action(deploy);
|
|
201
205
|
|
|
202
206
|
program
|
|
203
|
-
.command('
|
|
204
|
-
.
|
|
205
|
-
.
|
|
206
|
-
.option('-
|
|
207
|
+
.command('deployments')
|
|
208
|
+
.alias('deploys')
|
|
209
|
+
.description('List deployment history')
|
|
210
|
+
.option('-l, --limit <number>', 'Number of deployments to show', '20')
|
|
211
|
+
.option('-o, --offset <number>', 'Offset for pagination', '0')
|
|
212
|
+
.action(deployments);
|
|
213
|
+
|
|
214
|
+
program
|
|
215
|
+
.command('logs [deploymentUuid]')
|
|
216
|
+
.description('View application logs (runtime or deployment logs)')
|
|
217
|
+
.option('-d, --deployment [uuid]', 'View deployment logs (build logs)')
|
|
218
|
+
.option('--raw', 'Show raw log output (deployment logs only)')
|
|
219
|
+
.option('--include-hidden', 'Include hidden log lines (deployment logs only)')
|
|
220
|
+
.option('-t, --tail <lines>', 'Number of lines to show (runtime logs only)', '100')
|
|
221
|
+
.option('-f, --follow', 'Follow log output (runtime logs only)')
|
|
222
|
+
.option('--since <time>', 'Show logs since timestamp (runtime logs only)')
|
|
207
223
|
.action(logs);
|
|
208
224
|
|
|
225
|
+
// Local development commands
|
|
226
|
+
program
|
|
227
|
+
.command('run <command>')
|
|
228
|
+
.description('Run local command with remote environment variables')
|
|
229
|
+
.option('--sync', 'Force refresh environment variables (skip cache)')
|
|
230
|
+
.option('-q, --quiet', 'Quiet mode (suppress warnings)')
|
|
231
|
+
.action(run);
|
|
232
|
+
|
|
233
|
+
program
|
|
234
|
+
.command('shell')
|
|
235
|
+
.description('Open interactive shell with remote environment variables')
|
|
236
|
+
.option('--cmd <shell>', 'Shell to use (default: $SHELL or /bin/bash)')
|
|
237
|
+
.option('--sync', 'Force refresh environment variables (skip cache)')
|
|
238
|
+
.action(shell);
|
|
239
|
+
|
|
240
|
+
// Remote execution commands
|
|
241
|
+
program
|
|
242
|
+
.command('exec [command]')
|
|
243
|
+
.description('Execute command in remote container')
|
|
244
|
+
.option('--workdir <path>', 'Working directory (default: /app)')
|
|
245
|
+
.option('--timeout <seconds>', 'Timeout in seconds (default: 30, max: 300)', '30')
|
|
246
|
+
.option('--history', 'View execution history')
|
|
247
|
+
.option('--limit <number>', 'Limit for history (default: 20, max: 100)', '20')
|
|
248
|
+
.option('--offset <number>', 'Offset for history pagination (default: 0)', '0')
|
|
249
|
+
.action((command, options) => {
|
|
250
|
+
if (options.history) {
|
|
251
|
+
execCmd.history(options);
|
|
252
|
+
} else if (command) {
|
|
253
|
+
execCmd.exec(command, options);
|
|
254
|
+
} else {
|
|
255
|
+
console.error('Error: command is required (unless using --history)');
|
|
256
|
+
console.log('\nUsage:');
|
|
257
|
+
console.log(' saac exec <command> Execute command');
|
|
258
|
+
console.log(' saac exec --history View execution history');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
|
|
209
263
|
// Environment variable commands
|
|
210
264
|
const envCommand = program
|
|
211
265
|
.command('env')
|