Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ rand = "0.8"
sha2 = "0.10"
tiny_http = "0.12"
comfy-table = "7"
inquire = "0.9.4"
indicatif = "0.17"
nix = { version = "0.29", features = ["fs"] }
flate2 = "1"
Expand Down
79 changes: 77 additions & 2 deletions skills/hotdata-cli/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: hotdata-cli
description: Use this skill when the user wants to run hotdata CLI commands, query the HotData API, list workspaces, list connections, list tables, manage datasets, execute SQL queries, or interact with the hotdata service. Activate when the user says "run hotdata", "query hotdata", "list workspaces", "list connections", "list tables", "list datasets", "create a dataset", "upload a dataset", "execute a query", or asks you to use the hotdata CLI.
description: Use this skill when the user wants to run hotdata CLI commands, query the HotData API, list workspaces, list connections, create connections, list tables, manage datasets, execute SQL queries, or interact with the hotdata service. Activate when the user says "run hotdata", "query hotdata", "list workspaces", "list connections", "create a connection", "list tables", "list datasets", "create a dataset", "upload a dataset", "execute a query", or asks you to use the hotdata CLI.
version: 0.1.3
---

Expand Down Expand Up @@ -36,7 +36,66 @@ Returns workspaces with `public_id`, `name`, `active`, `favorite`, `provision_st
```
hotdata connections list [--workspace-id <workspace_id>] [--format table|json|yaml]
```
Routes via API gateway using `X-Workspace-Id` header.
Returns `id`, `name`, `source_type` for each connection in the workspace.

### Create a Connection

#### Step 1 — Discover available connection types
```
hotdata connections create list [--workspace-id <workspace_id>] [--format table|json|yaml]
```
Returns all available connection types with `name` and `label`.

#### Step 2 — Inspect the schema for a specific type
```
hotdata connections create list <name> [--workspace-id <workspace_id>] [--format json]
```
Returns `config` and `auth` JSON Schema objects describing all required and optional fields for that connection type. Use `--format json` to get the full schema detail.

- `config` — connection configuration fields (host, port, database, etc.). May be `null` for services that need no configuration.
- `auth` — authentication fields (password, token, credentials, etc.). May be `null` for services that need no authentication. May be a `oneOf` with multiple authentication method options.

#### Step 3 — Create the connection
```
hotdata connections create \
--name "my-connection" \
--type <source_type> \
--config '<json object>' \
[--workspace-id <workspace_id>]
```

The `--config` JSON object must contain all **required** fields from `config` plus the **auth fields** merged in at the top level. Auth fields are not nested — they sit alongside config fields in the same object.

Example for PostgreSQL (required: `host`, `port`, `user`, `database` + auth field `password`):
```
hotdata connections create \
--name "my-postgres" \
--type postgres \
--config '{"host":"db.example.com","port":5432,"user":"myuser","database":"mydb","password":"..."}'
```

**Security: never expose credentials in plain text.** Passwords, tokens, API keys, and any field with `"format": "password"` in the schema must never be hardcoded as literal strings in CLI commands. Always use one of these safe approaches:

- Read from an environment variable:
```
--config "{\"host\":\"db.example.com\",\"port\":5432,\"user\":\"myuser\",\"database\":\"mydb\",\"password\":\"$DB_PASSWORD\"}"
```
- Read a credential from a file and inject it:
```
--config "{\"token\":\"$(cat ~/.secrets/my-token)\"}"
```

**Field-building rules from the schema:**

- Include all fields listed in `config.required` — these are mandatory.
- Include optional config fields only if the user provides values for them.
- For `auth` with a single method (no `oneOf`): include all `auth.required` fields in the config object.
- For `auth` with `oneOf`: pick one authentication method and include only its required fields.
- Fields with `"format": "password"` are credentials — apply the security rules above.
- Fields with `"type": "integer"` must be JSON numbers, not strings (e.g. `"port": 5432` not `"port": "5432"`).
- Fields with `"type": "boolean"` must be JSON booleans (e.g. `"use_tls": true`).
- Fields with `"type": "array"` must be JSON arrays (e.g. `"spreadsheet_ids": ["abc", "def"]`).
- Nested `oneOf` fields must be a JSON object including a `"type"` discriminator field matching the chosen variant's `const` value.

### List Tables and Columns
```
Expand Down Expand Up @@ -136,3 +195,19 @@ hotdata init # Create ~/.hotdata/config.yml
```
hotdata query "SELECT 1"
```

## Workflow: Creating a Connection

1. List available connection types:
```
hotdata connections create list
```
2. Inspect the schema for the desired type:
```
hotdata connections create list <type_name> --format json
```
3. Collect required config and auth field values from the user or environment. **Never hardcode credentials — use env vars or files.**
4. Create the connection:
```
hotdata connections create --name "my-connection" --type <type_name> --config '<json>'
```
57 changes: 30 additions & 27 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub enum Commands {
id: Option<String>,

/// Workspace ID (defaults to first workspace from login)
#[arg(long)]
#[arg(long, global = true)]
workspace_id: Option<String>,

/// Output format (used with dataset ID)
Expand Down Expand Up @@ -63,6 +63,10 @@ pub enum Commands {

/// Manage workspace connections
Connections {
/// Workspace ID (defaults to first workspace from login)
#[arg(long, global = true)]
workspace_id: Option<String>,

#[command(subcommand)]
command: ConnectionsCommands,
},
Expand Down Expand Up @@ -285,25 +289,33 @@ pub enum WorkspaceCommands {
},
}

#[derive(Subcommand)]
pub enum ConnectionsCreateCommands {
/// List available connection types, or get details for a specific type
List {
/// Connection type name (e.g. postgres, mysql); omit to list all
name: Option<String>,

/// Output format
#[arg(long, default_value = "table", value_parser = ["table", "json", "yaml"])]
format: String,
},
}

#[derive(Subcommand)]
pub enum ConnectionsCommands {
/// Interactively create a new connection
New,

/// List all connections for a workspace
List {
/// Workspace ID (defaults to first workspace from login)
#[arg(long)]
workspace_id: Option<String>,

/// Output format
#[arg(long, default_value = "table", value_parser = ["table", "json", "yaml"])]
format: String,
},

/// Get details for a specific connection
Get {
/// Workspace ID (defaults to first workspace from login)
#[arg(long)]
workspace_id: Option<String>,

/// Connection ID
connection_id: String,

Expand All @@ -312,35 +324,30 @@ pub enum ConnectionsCommands {
format: String,
},

/// Create a new connection in a workspace
/// Create a new connection, or list/inspect available connection types
Create {
/// Workspace ID (defaults to first workspace from login)
#[arg(long)]
workspace_id: Option<String>,
#[command(subcommand)]
command: Option<ConnectionsCreateCommands>,

/// Connection name
#[arg(long)]
name: String,
name: Option<String>,

/// Connection type
/// Connection source type (e.g. postgres, mysql, snowflake)
#[arg(long = "type")]
conn_type: String,
source_type: Option<String>,

/// Connection config as JSON string
/// Connection config as a JSON object
#[arg(long)]
config: String,
config: Option<String>,

/// Output format
#[arg(long, default_value = "yaml", value_parser = ["table", "json", "yaml"])]
#[arg(long, default_value = "table", value_parser = ["table", "json", "yaml"])]
format: String,
},

/// Update a connection in a workspace
Update {
/// Workspace ID (defaults to first workspace from login)
#[arg(long)]
workspace_id: Option<String>,

/// Connection ID
connection_id: String,

Expand All @@ -363,10 +370,6 @@ pub enum ConnectionsCommands {

/// Delete a connection from a workspace
Delete {
/// Workspace ID (defaults to first workspace from login)
#[arg(long)]
workspace_id: Option<String>,

/// Connection ID
connection_id: String,
},
Expand Down
Loading
Loading