carto-cli 0.1.0-rc.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/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 20.19
@@ -0,0 +1,497 @@
1
+ # CARTO CLI - Engineering Architecture Document
2
+
3
+ **Version:** 0.1.0
4
+ **Last Updated:** 2025-11-06
5
+
6
+ ## 1. Project Overview
7
+
8
+ CARTO CLI is a command-line interface for managing CARTO Geospatial Cloud Native platform resources. Built for system administrators and power users to manage maps, workflows, connections, users, and other resources via the CARTO API.
9
+
10
+ **Key Goals:**
11
+ - Provide programmatic access to CARTO platform resources
12
+ - Enable automation and scripting of administrative tasks
13
+ - Support multi-tenant/multi-profile authentication
14
+ - Work as a standalone executable with no runtime dependencies
15
+
16
+ ## 2. Core Architecture Principles
17
+
18
+ ### Minimal Dependencies
19
+ - **Zero external runtime dependencies**: Bundles into a single executable file
20
+ - **Native Node.js modules only**: Uses built-in `https`, `http`, `fs`, `path`, `os`
21
+ - **Strategic exceptions**: Only 1 runtime dependency:
22
+ - `js-yaml`: YAML parsing for tenant config fetching
23
+ - **Optional user-installed dependencies**:
24
+ - `duckdb`: SQL querying of activity data (installed separately by users who need it)
25
+
26
+ ### Single Executable Distribution
27
+ - TypeScript compiled to JavaScript (`dist/`)
28
+ - Bundled with `@vercel/ncc` into single file (`bundle/carto`)
29
+ - Optional: Platform-specific binaries via `pkg` (macOS, Linux, Windows)
30
+
31
+ ### Custom HTTP Implementation
32
+ - No dependencies on `axios`, `node-fetch`, or similar HTTP libraries
33
+ - Direct usage of Node.js native `http`/`https` modules for full control
34
+ - Custom streaming support for NDJSON responses (AI features)
35
+
36
+ ## 3. Technical Stack
37
+
38
+ ### Language & Runtime
39
+ - **Language**: TypeScript 5.9+
40
+ - **Target**: CommonJS modules
41
+ - **Runtime**: Node.js 18+
42
+ - **Bundler**: Vercel ncc (0.38+)
43
+
44
+ ### Dependencies Rationale
45
+
46
+ **Production:**
47
+ - `js-yaml` (4.1.0): Parse YAML config from tenant domains
48
+
49
+ **Optional (User-Installed):**
50
+ - `duckdb` (1.1.3+): SQL querying for `carto activity query` command
51
+ - Not included in package.json
52
+ - Users install separately: `npm install duckdb`
53
+ - Dynamic import with helpful error if missing
54
+ - Only needed for activity data SQL queries (~1% of users)
55
+
56
+ **Development:**
57
+ - `typescript`: Type safety and modern JavaScript features
58
+ - `@vercel/ncc`: Bundle TypeScript output into single file
59
+ - `pkg`: Create platform-specific native executables
60
+
61
+ **Why no axios/fetch?**
62
+ - Smaller bundle size
63
+ - Full control over request/response handling
64
+ - Native streaming support for NDJSON
65
+ - No external attack surface
66
+
67
+ ## 4. Project Structure
68
+
69
+ ```
70
+ src/
71
+ ├── index.ts # CLI entry point, command routing
72
+ ├── api.ts # ApiClient class, HTTP abstraction layer
73
+ ├── http.ts # Native Node.js HTTP/HTTPS wrapper
74
+ ├── config.ts # Configuration & credential management
75
+ ├── auth-oauth.ts # OAuth authentication & token validation
76
+ ├── colors.ts # ANSI color codes for terminal output
77
+ ├── logo.ts # ASCII CARTO logo
78
+ ├── help.ts # Help text and documentation
79
+ └── commands/
80
+ ├── auth.ts # Personal authentication (OAuth flow)
81
+ ├── credentials.ts # Application credentials (API tokens, OAuth clients)
82
+ ├── maps.ts # Maps CRUD operations
83
+ ├── workflows.ts # Workflows CRUD operations
84
+ ├── connections.ts # Connections CRUD operations
85
+ ├── imports.ts # Data import operations
86
+ ├── users.ts # User management operations
87
+ └── admin.ts # Superadmin operations
88
+ ```
89
+
90
+ **Module Responsibilities:**
91
+
92
+ - **`index.ts`**: Command-line argument parsing, routing to command handlers
93
+ - **`api.ts`**: Centralized HTTP client with authentication, multi-endpoint support
94
+ - **`http.ts`**: Low-level HTTP request wrapper (request, streaming)
95
+ - **`config.ts`**: Profile management, credential storage, tenant config fetching
96
+ - **`auth-oauth.ts`**: JWT token parsing, expiration validation, OAuth helpers
97
+ - **`commands/*`**: Feature-specific command implementations
98
+
99
+ ## 5. Key Design Decisions
100
+
101
+ ### 5.1 Custom HTTP Client (`http.ts`)
102
+
103
+ **Decision**: Implement custom HTTP wrapper instead of using libraries.
104
+
105
+ **Rationale:**
106
+ - Reduce bundle size (critical for single-file distribution)
107
+ - Full control over request/response lifecycle
108
+ - Native NDJSON streaming support for AI features
109
+ - Eliminate third-party dependency risks
110
+
111
+ **Implementation:**
112
+ - `request()`: Standard JSON request/response
113
+ - `streamRequest()`: NDJSON streaming with chunk callbacks
114
+ - Automatic protocol detection (http/https)
115
+ - Promise-based API
116
+
117
+ ### 5.2 Multi-Endpoint API Architecture (`api.ts`)
118
+
119
+ **Decision**: Single ApiClient class supporting multiple CARTO API endpoints.
120
+
121
+ **Endpoints:**
122
+ - `baseUrl`: Main API (v3 endpoints) - `https://{tenant}.api.carto.com`
123
+ - `workspaceUrl`: Workspace API - `https://workspace-{tenant}.app.carto.com`
124
+ - `accountsUrl`: Accounts API - `https://accounts.app.carto.com`
125
+ - `aiApiUrl`: AI Features API - `https://ai-api-{tenant}.api.carto.com`
126
+ - `litellmUrl`: LiteLLM Proxy - `https://litellm-{tenant}.api.carto.com`
127
+
128
+ **URL Resolution:**
129
+ - Dynamic: Fetch from `https://{tenant_domain}/config.yaml` at runtime
130
+ - Static: LiteLLM URL constructed from tenant ID (not in config.yaml yet)
131
+ - Cached: In-memory cache per process to avoid repeated fetches
132
+
133
+ **Benefits:**
134
+ - Single client instance manages all endpoints
135
+ - Unified authentication across all APIs
136
+ - Consistent error handling and debugging
137
+
138
+ ### 5.3 Authentication & Profile Management
139
+
140
+ **Token Precedence** (highest to lowest):
141
+ 1. `--token` CLI flag
142
+ 2. `CARTO_API_TOKEN` environment variable
143
+ 3. Profile credentials file (`~/.carto_credentials.json`)
144
+ 4. Legacy config file (`~/.carto/config.json`)
145
+
146
+ **Profile System:**
147
+ ```json
148
+ {
149
+ "current_profile": "production",
150
+ "profiles": {
151
+ "production": {
152
+ "token": "eyJhbG...",
153
+ "tenant_id": "gcp-us-east1",
154
+ "tenant_domain": "myorg.app.carto.com",
155
+ "organization_id": "ac_xxxxx",
156
+ "organization_name": "MyOrg",
157
+ "user_email": "user@example.com",
158
+ "auth_environment": "production"
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ **Design Decisions:**
165
+ - Multi-profile support: Switch between environments/organizations
166
+ - Suggested profile names: `{tenant}/{org}/{email}` or `{env}/{tenant}/{org}/{email}`
167
+ - Profile switching via `CARTO_PROFILE` env var or `--profile` flag
168
+ - OAuth-based authentication (browser flow) with JWT tokens
169
+ - Token expiration validation and warnings (< 10% lifetime remaining)
170
+
171
+ **Token Storage:**
172
+ - **IMPORTANT**: Tokens stored WITHOUT `Bearer` prefix in credentials file
173
+ - `Bearer` prefix added only when creating `Authorization` header
174
+ - Prevents duplicate prefix bugs
175
+
176
+ ### 5.4 Configuration Management
177
+
178
+ **Dynamic Tenant Configuration:**
179
+ - Fetch API URLs from `https://{tenant_domain}/config.yaml`
180
+ - Enables multi-tenant support without hardcoding URLs
181
+ - Per-tenant caching to reduce network calls
182
+
183
+ **YAML Config Structure:**
184
+ ```yaml
185
+ apis:
186
+ accountsUrl: https://accounts.app.carto.com
187
+ workspaceUrl: https://workspace-{tenant}.app.carto.com
188
+ baseUrl: https://{tenant}.api.carto.com
189
+ aiApi: https://ai-api-{tenant}.api.carto.com
190
+ ```
191
+
192
+ **Migration Support:**
193
+ - Automatic migration from old credentials format to new multi-profile format
194
+ - Preserves backward compatibility with legacy config file
195
+
196
+ ### 5.5 Build & Distribution Pipeline
197
+
198
+ **Three-Stage Build:**
199
+
200
+ 1. **TypeScript Compilation** (`npm run build`)
201
+ - Input: `src/*.ts`
202
+ - Output: `dist/*.js` (CommonJS)
203
+ - Tool: `tsc`
204
+
205
+ 2. **Bundling** (`npm run bundle`)
206
+ - Input: `dist/index.js`
207
+ - Output: `bundle/carto` (single executable file)
208
+ - Tool: `@vercel/ncc`
209
+ - Includes: All dependencies, Node.js modules
210
+
211
+ 3. **Platform Executables** (`npm run pkg`)
212
+ - Input: `bundle/carto`
213
+ - Output: `bin/carto-{platform}`
214
+ - Tool: `pkg`
215
+ - Platforms: macOS (x64), Linux (x64), Windows (x64)
216
+
217
+ **Distribution Strategy:**
218
+ - Primary: Single bundled JavaScript file (`bundle/carto`)
219
+ - Optional: Native executables for platforms without Node.js
220
+
221
+ ### 5.6 Request Tracking Header
222
+
223
+ **Header Convention**: `clientId: carto-cli/{version}`
224
+
225
+ **Rationale:**
226
+ - Platform-wide convention (used across CARTO services)
227
+ - Enables usage analytics and tracking
228
+ - Version tracking for API compatibility monitoring
229
+ - Support and debugging assistance
230
+
231
+ **Implementation:**
232
+ - Added to all API requests automatically
233
+ - Version sourced from `package.json` dynamically
234
+ - Single source of truth for version number
235
+
236
+ ## 6. Code Conventions
237
+
238
+ ### Async/Await Pattern
239
+ ```typescript
240
+ // All I/O operations use async/await
241
+ async function listMaps(): Promise<void> {
242
+ const client = await ApiClient.create();
243
+ const maps = await client.getWorkspace('/workspace-api/maps');
244
+ // ...
245
+ }
246
+ ```
247
+
248
+ ### Error Handling
249
+ ```typescript
250
+ // Consistent try/catch with clear error messages
251
+ try {
252
+ const result = await apiCall();
253
+ } catch (error: any) {
254
+ if (jsonOutput) {
255
+ console.log(JSON.stringify({ error: error.message }, null, 2));
256
+ } else {
257
+ console.error(`${red('✗')} Error: ${error.message}`);
258
+ }
259
+ process.exit(1);
260
+ }
261
+ ```
262
+
263
+ ### Output Formatting
264
+
265
+ **Dual Output Mode:**
266
+ - Human-readable: Formatted tables, colors, icons (✓, ✗, →)
267
+ - JSON mode: `--json` flag for machine parsing
268
+
269
+ ```typescript
270
+ if (jsonOutput) {
271
+ console.log(JSON.stringify(data, null, 2));
272
+ } else {
273
+ console.log(`${green('✓')} Success: ${message}`);
274
+ }
275
+ ```
276
+
277
+ **Color Usage:**
278
+ - Success: Green (`✓`)
279
+ - Error: Red (`✗`)
280
+ - Info: Cyan
281
+ - Warning: Dim
282
+ - Highlights: Bold
283
+
284
+ ### Pagination Pattern
285
+
286
+ **Consistent Pagination:**
287
+ - `--page <number>`: Page number (1-indexed)
288
+ - `--page-size <number>`: Items per page
289
+ - `--all`: Fetch all pages automatically
290
+
291
+ ```typescript
292
+ async getAllPaginated(
293
+ basePath: string,
294
+ baseParams: Record<string, string> = {},
295
+ pageParamName: string = 'page',
296
+ pageSizeParamName: string = 'page_size',
297
+ defaultPageSize: number = 100
298
+ ): Promise<any>
299
+ ```
300
+
301
+ **User Feedback:**
302
+ - Single page: No message
303
+ - Multiple pages: "Page X of Y (use --all to fetch all pages)"
304
+ - With `--all`: "Fetched all N items"
305
+
306
+ ### Command Function Signature
307
+ ```typescript
308
+ async function commandHandler(
309
+ args: string[],
310
+ token?: string,
311
+ baseUrl?: string,
312
+ jsonOutput: boolean = false,
313
+ debug: boolean = false,
314
+ profile?: string
315
+ ): Promise<void>
316
+ ```
317
+
318
+ **Parameters:**
319
+ - `args`: Command-specific arguments
320
+ - `token`: Optional auth token override
321
+ - `baseUrl`: Optional API base URL override
322
+ - `jsonOutput`: Enable JSON output mode
323
+ - `debug`: Enable HTTP request debugging
324
+ - `profile`: Profile name for multi-tenant support
325
+
326
+ ## 7. Testing Strategy
327
+
328
+ ### Manual Testing Approach
329
+
330
+ **No automated unit tests.** Testing is performed via comprehensive manual test scenarios documented in `TEST_GUIDE.md`.
331
+
332
+ **Rationale:**
333
+ - CLI tools require integration testing with real APIs
334
+ - Command-line interactions difficult to unit test effectively
335
+ - Manual testing provides better coverage of user workflows
336
+ - Faster development iteration without test maintenance overhead
337
+
338
+ **Test Categories:**
339
+ 1. **Smoke Tests**: Version, help, auth status (no API calls)
340
+ 2. **Command Tests**: Each command with all parameter combinations
341
+ 3. **Integration Tests**: Real API calls with valid credentials
342
+ 4. **Error Handling**: Invalid inputs, missing parameters, API errors
343
+ 5. **Profile Tests**: Multi-profile authentication and switching
344
+ 6. **Pagination Tests**: Single page, multiple pages, --all flag
345
+ 7. **Output Tests**: Human-readable vs JSON format
346
+
347
+ **Test Execution:**
348
+ ```bash
349
+ # Quick smoke test (always run before commit)
350
+ npm run build && npm run bundle && \
351
+ ./bundle/carto --version && \
352
+ ./bundle/carto --help && \
353
+ ./bundle/carto auth status
354
+ ```
355
+
356
+ **Test Documentation:**
357
+ - Comprehensive test scenarios in `TEST_GUIDE.md`
358
+ - Expected outputs for each command
359
+ - Error case validation
360
+ - Test checklist for pre-commit verification
361
+
362
+ ### Quality Assurance
363
+ - **Pre-commit**: Smoke tests mandatory
364
+ - **Pre-PR**: Full test suite execution
365
+ - **Manual review**: User-facing changes tested with real workflows
366
+
367
+ ## 8. Security Considerations
368
+
369
+ ### Credential Storage
370
+ - Tokens stored in `~/.carto_credentials.json` (file permissions: 600)
371
+ - Sensitive data (tokens) masked in debug output
372
+ - No credentials logged or printed accidentally
373
+
374
+ ### Token Handling
375
+ - Never log full tokens (mask to first 20 chars)
376
+ - Environment variables take precedence (avoid file storage)
377
+ - Token expiration validation prevents usage of expired credentials
378
+
379
+ ### Debug Mode Safety
380
+ ```typescript
381
+ // Mask Authorization header in debug output
382
+ const displayValue = key === 'Authorization' && value.startsWith('Bearer ')
383
+ ? `Bearer ${value.substring(7, 27)}...`
384
+ : value;
385
+ ```
386
+
387
+ ## 9. API Integration
388
+
389
+ ### Resource Endpoints
390
+
391
+ **V3 API** (`https://{tenant}.api.carto.com/v3`):
392
+ - `/v3/tokens`: API Access Tokens
393
+ - `/v3/connections`: Data connections
394
+ - `/v3/imports`: Data imports
395
+
396
+ **Workspace API** (`https://workspace-{tenant}.app.carto.com`):
397
+ - `/workspace-api/maps`: Maps management
398
+ - `/workspace-api/workflows`: Workflows management
399
+
400
+ **Accounts API** (`https://accounts.app.carto.com`):
401
+ - `/oauth-clients`: OAuth client management (SPA, M2M)
402
+ - `/accounts/users`: User management
403
+ - `/accounts/invite`: User invitations
404
+ - `/accounts/resources`: Superadmin resource operations
405
+
406
+ **AI API** (`https://ai-api-{tenant}.api.carto.com`):
407
+ - `/feature/{featureName}`: AI features (NDJSON streaming)
408
+
409
+ **LiteLLM Proxy** (`https://litellm-{tenant}.api.carto.com`):
410
+ - `/v1/chat/completions`: OpenAI-compatible chat
411
+ - `/v1/models`: List available models
412
+
413
+ ### Authentication Flow
414
+
415
+ **OAuth Browser Flow:**
416
+ 1. CLI starts local HTTP server (port 8080)
417
+ 2. Opens browser to CARTO Auth0 login page
418
+ 3. User authenticates in browser
419
+ 4. Auth0 redirects to `http://localhost:8080/callback?code=...`
420
+ 5. CLI exchanges code for JWT token
421
+ 6. Token and user info saved to credentials file
422
+ 7. Server shuts down, CLI exits
423
+
424
+ **Token Validation:**
425
+ - Parse JWT to extract expiration (`exp` claim)
426
+ - Warn when < 10% of lifetime remaining
427
+ - Error and suggest re-authentication when expired
428
+ - Display user-friendly time remaining (e.g., "2 hours 30 minutes")
429
+
430
+ ## 10. Future Considerations
431
+
432
+ ### Potential Enhancements
433
+ - **Automated Testing**: Consider integration test framework for critical paths
434
+ - **LiteLLM URL**: Add to `config.yaml` instead of constructing from tenant ID
435
+ - **Extended DuckDB Usage**: Additional data analysis and transformation commands
436
+ - **Shell Completions**: Generate bash/zsh completion scripts
437
+ - **Configuration Profiles**: Support for additional per-profile settings
438
+ - **Batch Operations**: Parallel execution for bulk resource operations
439
+
440
+ ### Known Limitations
441
+ - No progress bars for long-running operations (imports show polling status)
442
+ - File upload limited to 1GB (GCS signed URL constraint)
443
+ - LiteLLM URL not in config.yaml (hardcoded construction)
444
+ - No retry logic for transient API failures
445
+ - Debug mode shows full request/response (can be verbose)
446
+ - DuckDB requires separate installation for SQL query features
447
+
448
+ ## 11. Development Workflow
449
+
450
+ ### Adding New Commands
451
+
452
+ 1. Create command module in `src/commands/`
453
+ 2. Export async function with standard signature
454
+ 3. Import and route in `src/index.ts`
455
+ 4. Add help text in `src/help.ts`
456
+ 5. Update `README.md` with command documentation
457
+ 6. Add test scenarios to `TEST_GUIDE.md`
458
+ 7. Rebuild: `npm run build && npm run bundle`
459
+ 8. Test with smoke tests and feature-specific tests
460
+
461
+ ### Adding New API Endpoints
462
+
463
+ 1. Add method to `ApiClient` class in `src/api.ts`
464
+ 2. Use appropriate endpoint (get, post, patch, delete, getWorkspace, etc.)
465
+ 3. Handle both JSON and formatted output modes
466
+ 4. Follow error handling pattern
467
+ 5. Update documentation and test guide
468
+
469
+ ### Version Updates
470
+
471
+ **Single source of truth**: `package.json`
472
+
473
+ Version automatically used in:
474
+ - `--version` flag output
475
+ - `clientId` request header
476
+ - Help text display
477
+
478
+ Update process:
479
+ ```bash
480
+ # 1. Update version in package.json
481
+ npm version patch|minor|major
482
+
483
+ # 2. Rebuild
484
+ npm run build && npm run bundle
485
+
486
+ # 3. Verify version
487
+ ./bundle/carto --version
488
+ ```
489
+
490
+ ## 12. References
491
+
492
+ - **README.md**: User-facing documentation and command reference
493
+ - **TEST_GUIDE.md**: Comprehensive manual testing scenarios
494
+ - **CLAUDE.md**: Project guidance for AI assistants
495
+ - **carto-api.postman_collection.json**: API reference and examples
496
+ - **package.json**: Dependencies and build scripts
497
+ - **tsconfig.json**: TypeScript compiler configuration
package/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2025-12-31
11
+
12
+ ### Added
13
+ - Initial release
14
+ - Authentication commands (login, logout, status, whoami)
15
+ - M2M OAuth authentication for CI/CD (`auth login --m2m`)
16
+ - Maps commands (list, get, create, update, delete, copy)
17
+ - Workflows commands (list, delete)
18
+ - Connections commands (list, get, create, update, delete)
19
+ - Credentials commands (tokens, SPA, M2M OAuth clients)
20
+ - Users commands (list, get, invite, invitations)
21
+ - Imports commands (create with file/URL)
22
+ - Admin commands (list, batch-delete, transfer)
23
+ - Multi-profile support
24
+ - JSON output mode
25
+ - Unit and integration test suite
26
+ - E2E tests against live CARTO API
27
+ - CI/CD pipeline for NPM publishing and GitHub releases
28
+ - Nightly test workflow with Slack notifications
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025 CARTO
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.