flatten CLI, rename VM to sandbox

This commit is contained in:
Dillon DuPont
2025-11-14 16:23:40 -05:00
parent 1040ed8a9e
commit f7f00e7e5d
7 changed files with 257 additions and 235 deletions

View File

@@ -332,7 +332,7 @@ Learn more about agents in [Agent Loops](/agent-sdk/agent-loops) and available m
## CLI Quickstart
Get started quickly with the CUA CLI - the easiest way to manage cloud VMs and run AI agents.
Get started quickly with the CUA CLI - the easiest way to manage cloud sandboxes and run AI agents.
<Steps>
<Step>
@@ -386,10 +386,10 @@ Login to your CUA account:
```bash
# Interactive browser login (recommended)
cua auth login
cua login
# Or provide your API key directly
cua auth login --api-key sk-your-api-key-here
cua login --api-key sk-your-api-key-here
```
If you don't have a CUA account yet, sign up at [cua.ai/signin](https://cua.ai/signin).
@@ -404,10 +404,10 @@ Create a cloud sandbox where your AI agents will run:
```bash
# Create a Linux sandbox (recommended for most use cases)
cua vm create --os linux --configuration small --region north-america
cua create --os linux --configuration small --region north-america
# Or create a Windows sandbox
cua vm create --os windows --configuration small --region north-america
cua create --os windows --configuration small --region north-america
```
@@ -430,7 +430,7 @@ You can now interact with your sandbox in multiple ways:
#### Option 1: Open the AI Playground (Recommended)
```bash
cua vm chat my-vm-abc123
cua chat my-vm-abc123
```
This opens the full CUA playground in your browser where you can chat with AI agents that control your sandbox.
@@ -438,7 +438,7 @@ This opens the full CUA playground in your browser where you can chat with AI ag
#### Option 2: Access VNC Desktop
```bash
cua vm vnc my-vm-abc123
cua open my-vm-abc123
```
This opens a remote desktop connection to your sandbox.
@@ -447,14 +447,14 @@ This opens a remote desktop connection to your sandbox.
```bash
# List all your sandboxes
cua vm list
cua list
# Start/stop sandboxes as needed
cua vm stop my-vm-abc123
cua vm start my-vm-abc123
cua stop my-vm-abc123
cua start my-vm-abc123
# Delete sandboxes when done
cua vm delete my-vm-abc123
cua delete my-vm-abc123
```
</Step>
@@ -463,7 +463,7 @@ cua vm delete my-vm-abc123
### Try Some AI Tasks
Once you have the playground open (`cua vm chat`), try asking the AI to:
Once you have the playground open (`cua chat`), try asking the AI to:
- "Take a screenshot and tell me what's on the screen"
- "Open Firefox and navigate to github.com"

View File

@@ -8,23 +8,23 @@ import { Callout } from 'fumadocs-ui/components/callout';
## Overview
The CUA CLI provides two main command groups:
The CUA CLI provides commands for authentication and sandbox management:
- **`cua auth`** - Authentication and API key management
- **`cua vm`** - Virtual machine lifecycle management
- **Authentication** - `cua login`, `cua env`, `cua logout`
- **Sandbox Management** - `cua list`, `cua create`, `cua start`, `cua stop`, `cua restart`, `cua delete`, `cua open`, `cua chat`
## Authentication Commands
### `cua auth login`
### `cua login`
Authenticate with your CUA account using browser-based OAuth flow.
```bash
# Interactive browser login
cua auth login
cua login
# Direct API key login
cua auth login --api-key sk-your-api-key-here
cua login --api-key sk-your-api-key-here
```
**Options:**
@@ -32,22 +32,22 @@ cua auth login --api-key sk-your-api-key-here
**Example:**
```bash
$ cua auth login
$ cua login
Opening browser for CLI auth...
API key saved
```
### `cua auth pull`
### `cua env`
Create or update a `.env` file in the current directory with your CUA API key.
```bash
cua auth pull
cua env
```
**Example:**
```bash
$ cua auth pull
$ cua env
Wrote /path/to/your/project/.env
```
@@ -56,172 +56,184 @@ The generated `.env` file will contain:
CUA_API_KEY=sk-your-api-key-here
```
### `cua auth logout`
### `cua logout`
Remove the stored API key from your system.
```bash
cua auth logout
cua logout
```
**Example:**
```bash
$ cua auth logout
$ cua logout
Logged out
```
## Virtual Machine Commands
## Sandbox Commands
### `cua vm list`
### `cua list`
List all your virtual machines with their current status.
List all your sandboxes with their current status. Passwords are hidden by default for security.
```bash
cua vm list
# List sandboxes (passwords hidden)
cua list
# Show passwords explicitly
cua list --show-passwords
# Alternative aliases
cua ls
cua ps
```
**Example Output:**
**Example Output (default, passwords hidden):**
```
┌─────────────────┬──────────┬────────┬─────────────────┬──────────────────────────────────────┐
│ Name │ Status │ OS │ Configuration │ Host │
├─────────────────┼──────────┼────────┼─────────────────┼──────────────────────────────────────┤
│ my-dev-vm │ running │ linux │ small │ my-dev-vm.containers.cloud.trycua.com │
│ test-windows │ stopped │ windows│ medium │ test-windows.containers.cloud.trycua.com │
└─────────────────┴──────────┴────────┴─────────────────┴──────────────────────────────────────┘
NAME STATUS HOST
my-dev-sandbox running my-dev-sandbox.containers.cloud.trycua.com
test-windows stopped test-windows.containers.cloud.trycua.com
```
### `cua vm create`
**Example Output (with --show-passwords):**
```
NAME STATUS PASSWORD HOST
my-dev-sandbox running secure-pass-123 my-dev-sandbox.containers.cloud.trycua.com
test-windows stopped another-pass-456 test-windows.containers.cloud.trycua.com
```
Create a new virtual machine.
### `cua create`
Create a new sandbox.
```bash
cua vm create --os <OS> --configuration <SIZE> --region <REGION>
cua create --os <OS> --configuration <SIZE> --region <REGION>
```
**Required Options:**
- `--os` - Operating system: `linux`, `windows`, `macos`
- `--configuration` - VM size: `small`, `medium`, `large`
- `--configuration` - Sandbox size: `small`, `medium`, `large`
- `--region` - Region: `north-america`, `europe`, `asia-pacific`, `south-america`
**Examples:**
```bash
# Create a small Linux VM in North America
cua vm create --os linux --configuration small --region north-america
# Create a small Linux sandbox in North America
cua create --os linux --configuration small --region north-america
# Create a medium Windows VM in Europe
cua vm create --os windows --configuration medium --region europe
# Create a medium Windows sandbox in Europe
cua create --os windows --configuration medium --region europe
# Create a large macOS VM in Asia Pacific
cua vm create --os macos --configuration large --region asia-pacific
# Create a large macOS sandbox in Asia Pacific
cua create --os macos --configuration large --region asia-pacific
```
**Response Types:**
**Immediate (Status 200):**
```bash
VM created and ready: my-new-vm-abc123
Sandbox created and ready: my-new-sandbox-abc123
Password: secure-password-here
Host: my-new-vm-abc123.containers.cloud.trycua.com
Host: my-new-sandbox-abc123.containers.cloud.trycua.com
```
**Provisioning (Status 202):**
```bash
VM provisioning started: my-new-vm-abc123
Sandbox provisioning started: my-new-sandbox-abc123
Job ID: job-xyz789
Use 'cua vm list' to monitor provisioning progress
Use 'cua list' to monitor provisioning progress
```
### `cua vm start`
### `cua start`
Start a stopped virtual machine.
Start a stopped sandbox.
```bash
cua vm start <name>
cua start <name>
```
**Example:**
```bash
$ cua vm start my-dev-vm
$ cua start my-dev-sandbox
Start accepted
```
### `cua vm stop`
### `cua stop`
Stop a running virtual machine.
Stop a running sandbox.
```bash
cua vm stop <name>
cua stop <name>
```
**Example:**
```bash
$ cua vm stop my-dev-vm
$ cua stop my-dev-sandbox
stopping
```
### `cua vm restart`
### `cua restart`
Restart a virtual machine.
Restart a sandbox.
```bash
cua vm restart <name>
cua restart <name>
```
**Example:**
```bash
$ cua vm restart my-dev-vm
$ cua restart my-dev-sandbox
restarting
```
### `cua vm delete`
### `cua delete`
Delete a virtual machine permanently.
Delete a sandbox permanently.
```bash
cua vm delete <name>
cua delete <name>
```
**Example:**
```bash
$ cua vm delete old-test-vm
VM deletion initiated: deleting
$ cua delete old-test-sandbox
Sandbox deletion initiated: deleting
```
<Callout type="warn">
This action is irreversible. All data on the VM will be permanently lost.
This action is irreversible. All data on the sandbox will be permanently lost.
</Callout>
### `cua vm vnc`
### `cua open`
Open the VNC interface for a VM in your browser.
Open the VNC interface for a sandbox in your browser.
```bash
cua vm vnc <name>
cua open <name>
```
**Example:**
```bash
$ cua vm vnc my-dev-vm
Opening NoVNC: https://my-dev-vm.containers.cloud.trycua.com/vnc.html?autoconnect=true&password=...
$ cua open my-dev-sandbox
Opening NoVNC: https://my-dev-sandbox.containers.cloud.trycua.com/vnc.html?autoconnect=true&password=...
```
This command automatically opens your default browser to the VNC interface with the correct password pre-filled.
### `cua vm chat`
### `cua chat`
Open the CUA Dashboard Playground for a VM in your browser.
Open the CUA playground for a sandbox in your browser.
```bash
cua vm chat <name>
cua chat <name>
```
**Example:**
```bash
$ cua vm chat my-dev-vm
$ cua chat my-dev-sandbox
Opening Playground: https://cua.ai/dashboard/playground?host=...
```
This opens the full CUA playground interface where you can interact with your VM using AI agents.
This opens the full CUA playground interface where you can interact with your sandbox using AI agents.
## Global Options
@@ -231,9 +243,9 @@ Get help for any command:
```bash
cua --help
cua auth --help
cua vm --help
cua vm create --help
cua login --help
cua create --help
cua list --help
```
### Environment Variables
@@ -258,32 +270,32 @@ The CLI provides clear error messages for common issues:
### Authentication Errors
```bash
$ cua vm list
Unauthorized. Try 'cua auth login' again.
$ cua list
Unauthorized. Try 'cua login' again.
```
### VM Not Found
### Sandbox Not Found
```bash
$ cua vm start nonexistent-vm
VM not found
$ cua start nonexistent-sandbox
Sandbox not found
```
### Invalid Configuration
```bash
$ cua vm create --os invalid --configuration small --region north-america
$ cua create --os invalid --configuration small --region north-america
Invalid request or unsupported configuration
```
## Tips and Best Practices
### 1. Use Descriptive VM Names
### 1. Use Descriptive Sandbox Names
```bash
# Good
cua vm create --os linux --configuration small --region north-america
cua create --os linux --configuration small --region north-america
# Then rename or use meaningful names in the dashboard
# Better workflow
cua vm list # Check the generated name
cua list # Check the generated name
# Use that name consistently
```
@@ -291,26 +303,26 @@ cua vm list # Check the generated name
```bash
# Set up your project with API key
cd my-project
cua auth pull
cua env
# Now your project has CUA_API_KEY in .env
```
### 3. Quick VM Access
### 3. Quick Sandbox Access
```bash
# Create aliases for frequently used VMs
alias dev-vm="cua vm chat my-development-vm"
alias prod-vm="cua vm vnc my-production-vm"
# Create aliases for frequently used sandboxes
alias dev-sandbox="cua chat my-development-sandbox"
alias prod-sandbox="cua open my-production-sandbox"
```
### 4. Monitoring Provisioning
```bash
# For VMs that need provisioning time
cua vm create --os windows --configuration large --region europe
# VM provisioning started: my-vm-abc123
# For sandboxes that need provisioning time
cua create --os windows --configuration large --region europe
# Sandbox provisioning started: my-sandbox-abc123
# Job ID: job-xyz789
# Check status periodically
watch -n 5 cua vm list
watch -n 5 cua list
```
## Next Steps

View File

@@ -1,16 +1,16 @@
---
title: Cua CLI
description: Command-line interface for managing Cua cloud VMs and authentication
description: Command-line interface for managing Cua cloud sandboxes and authentication
---
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
The Cua CLI is a command-line tool that provides an intuitive interface for managing your Cua cloud virtual machines and authentication. It offers a streamlined workflow for creating, managing, and connecting to cloud sandboxes.
The Cua CLI is a command-line tool that provides an intuitive interface for managing your Cua cloud sandboxes and authentication. It offers a streamlined workflow for creating, managing, and connecting to cloud sandboxes.
## Key Features
- **Authentication Management**: Secure login with browser-based OAuth flow
- **VM Lifecycle**: Create, start, stop, restart, and delete cloud VMs
- **Sandbox Lifecycle**: Create, start, stop, restart, and delete cloud sandboxes
- **Quick Access**: Direct links to VNC and playground interfaces
- **Cross-Platform**: Works on macOS, Linux, and Windows
- **Environment Integration**: Automatic `.env` file generation
@@ -22,32 +22,32 @@ The Cua CLI is a command-line tool that provides an intuitive interface for mana
curl -LsSf https://cua.ai/cli/install.sh | sh
# Login to your CUA account
cua auth login
cua login
# Create a new Linux VM
cua vm create --os linux --configuration small --region north-america
# Create a new Linux sandbox
cua create --os linux --configuration small --region north-america
# List your VMs
cua vm list
# List your sandboxes
cua list
# Open the playground for your VM
cua vm chat my-vm-name
# Open the playground for your sandbox
cua chat my-sandbox-name
```
## Use Cases
### Development Workflow
- Quickly spin up cloud sandboxes for testing
- Manage multiple VMs across different regions
- Manage multiple sandboxes across different regions
- Integrate with CI/CD pipelines
### Team Collaboration
- Share VM configurations and access
- Share sandbox configurations and access
- Standardize development environments
- Quick onboarding for new team members
### Automation
- Script VM provisioning and management
- Script sandbox provisioning and management
- Integrate with deployment workflows
- Automate environment setup

View File

@@ -59,10 +59,10 @@ After installation, you'll need to authenticate with your CUA account:
```bash
# Login with browser-based OAuth flow
cua auth login
cua login
# Or provide your API key directly
cua auth login --api-key sk-your-api-key-here
cua login --api-key sk-your-api-key-here
```
## Updating

View File

@@ -4,47 +4,44 @@ import { writeEnvFile } from '../util';
import type { Argv } from 'yargs';
export function registerAuthCommands(y: Argv) {
return y.command('auth', 'Auth commands', (ya) =>
ya
.command(
'login',
'Open browser to authorize and store API key',
(y) =>
y.option('api-key', {
type: 'string',
describe: 'API key to store directly',
}),
async (argv: Record<string, unknown>) => {
if (argv['api-key']) {
setApiKey(String(argv['api-key']));
console.log('API key saved');
return;
}
console.log('Opening browser for CLI auth...');
const token = await loginViaBrowser();
setApiKey(token);
return y
.command(
'login',
'Open browser to authorize and store API key',
(y) =>
y.option('api-key', {
type: 'string',
describe: 'API key to store directly',
}),
async (argv: Record<string, unknown>) => {
if (argv['api-key']) {
setApiKey(String(argv['api-key']));
console.log('API key saved');
return;
}
)
.command(
'pull',
'Create or update .env with CUA_API_KEY (login if needed)',
() => {},
async (_argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const out = await writeEnvFile(process.cwd(), token);
console.log(`Wrote ${out}`);
}
)
.command(
'logout',
'Remove stored API key',
() => {},
async (_argv: Record<string, unknown>) => {
clearApiKey();
console.log('Logged out');
}
)
.demandCommand(1, 'Specify an auth subcommand')
);
console.log('Opening browser for CLI auth...');
const token = await loginViaBrowser();
setApiKey(token);
console.log('API key saved');
}
)
.command(
'env',
'Create or update .env with CUA_API_KEY (login if needed)',
() => {},
async (_argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const out = await writeEnvFile(process.cwd(), token);
console.log(`Wrote ${out}`);
}
)
.command(
'logout',
'Remove stored API key',
() => {},
async (_argv: Record<string, unknown>) => {
clearApiKey();
console.log('Logged out');
}
);
}

View File

@@ -1,37 +1,40 @@
import type { Argv } from 'yargs';
import { ensureApiKeyInteractive } from '../auth';
import { http } from '../http';
import { printVmList, openInBrowser } from '../util';
import { WEBSITE_URL } from '../config';
import type { VmItem } from '../util';
import { http } from '../http';
import { clearApiKey } from '../storage';
import type { VmItem } from '../util';
import { openInBrowser, printVmList } from '../util';
export function registerVmCommands(y: Argv) {
return y.command('vm', 'VM commands', (yv) =>
yv
.command(
'list',
'List VMs',
() => {},
async (_argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const res = await http('/v1/vms', { token });
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
process.exit(1);
}
if (!res.ok) {
console.error(`Request failed: ${res.status}`);
process.exit(1);
}
const data = (await res.json()) as VmItem[];
printVmList(data);
return y
.command(
['list', 'ls', 'ps'],
'List sandboxes',
(y) => y.option('show-passwords', {
type: 'boolean',
default: false,
describe: 'Show sandbox passwords in output'
}),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const res = await http('/v1/vms', { token });
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
)
.command(
'create',
'Create a new VM',
if (!res.ok) {
console.error(`Request failed: ${res.status}`);
process.exit(1);
}
const data = (await res.json()) as VmItem[];
printVmList(data, Boolean(argv['show-passwords']));
}
)
.command(
'create',
'Create a new sandbox',
(y) =>
y
.option('os', {
@@ -44,7 +47,7 @@ export function registerVmCommands(y: Argv) {
type: 'string',
choices: ['small', 'medium', 'large'],
demandOption: true,
describe: 'VM size configuration',
describe: 'Sandbox size configuration',
})
.option('region', {
type: 'string',
@@ -55,7 +58,7 @@ export function registerVmCommands(y: Argv) {
'south-america',
],
demandOption: true,
describe: 'VM region',
describe: 'Sandbox region',
}),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
@@ -73,7 +76,7 @@ export function registerVmCommands(y: Argv) {
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
@@ -88,29 +91,29 @@ export function registerVmCommands(y: Argv) {
}
if (res.status === 200) {
// VM ready immediately
// Sandbox ready immediately
const data = (await res.json()) as {
status: string;
name: string;
password: string;
host: string;
};
console.log(`VM created and ready: ${data.name}`);
console.log(`Sandbox created and ready: ${data.name}`);
console.log(`Password: ${data.password}`);
console.log(`Host: ${data.host}`);
return;
}
if (res.status === 202) {
// VM provisioning started
// Sandbox provisioning started
const data = (await res.json()) as {
status: string;
name: string;
job_id: string;
};
console.log(`VM provisioning started: ${data.name}`);
console.log(`Sandbox provisioning started: ${data.name}`);
console.log(`Job ID: ${data.job_id}`);
console.log("Use 'cua vm list' to monitor provisioning progress");
console.log("Use 'cua list' to monitor provisioning progress");
return;
}
@@ -118,10 +121,10 @@ export function registerVmCommands(y: Argv) {
process.exit(1);
}
)
.command(
'delete <name>',
'Delete a VM',
(y) => y.positional('name', { type: 'string', describe: 'VM name' }),
.command(
'delete <name>',
'Delete a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
@@ -134,18 +137,18 @@ export function registerVmCommands(y: Argv) {
const body = (await res.json().catch(() => ({}))) as {
status?: string;
};
console.log(`VM deletion initiated: ${body.status ?? 'deleting'}`);
console.log(`Sandbox deletion initiated: ${body.status ?? 'deleting'}`);
return;
}
if (res.status === 404) {
console.error('VM not found or not owned by you');
console.error('Sandbox not found or not owned by you');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
@@ -153,10 +156,10 @@ export function registerVmCommands(y: Argv) {
process.exit(1);
}
)
.command(
'start <name>',
'Start a VM',
(y) => y.positional('name', { type: 'string', describe: 'VM name' }),
.command(
'start <name>',
'Start a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
@@ -169,22 +172,22 @@ export function registerVmCommands(y: Argv) {
return;
}
if (res.status === 404) {
console.error('VM not found');
console.error('Sandbox not found');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command(
'stop <name>',
'Stop a VM',
(y) => y.positional('name', { type: 'string', describe: 'VM name' }),
.command(
'stop <name>',
'Stop a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
@@ -200,22 +203,22 @@ export function registerVmCommands(y: Argv) {
return;
}
if (res.status === 404) {
console.error('VM not found');
console.error('Sandbox not found');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command(
'restart <name>',
'Restart a VM',
(y) => y.positional('name', { type: 'string', describe: 'VM name' }),
.command(
'restart <name>',
'Restart a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
@@ -234,29 +237,29 @@ export function registerVmCommands(y: Argv) {
return;
}
if (res.status === 404) {
console.error('VM not found');
console.error('Sandbox not found');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command(
'vnc <name>',
'Open NoVNC for a VM in your browser',
(y) => y.positional('name', { type: 'string', describe: 'VM name' }),
.command(
'open <name>',
'Open NoVNC for a sandbox in your browser',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
const listRes = await http('/v1/vms', { token });
if (listRes.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
if (!listRes.ok) {
@@ -266,7 +269,7 @@ export function registerVmCommands(y: Argv) {
const vms = (await listRes.json()) as VmItem[];
const vm = vms.find((v) => v.name === name);
if (!vm) {
console.error('VM not found');
console.error('Sandbox not found');
process.exit(1);
}
const host =
@@ -278,17 +281,17 @@ export function registerVmCommands(y: Argv) {
await openInBrowser(url);
}
)
.command(
'chat <name>',
'Open CUA dashboard playground for a VM',
(y) => y.positional('name', { type: 'string', describe: 'VM name' }),
.command(
'chat <name>',
'Open CUA playground for a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
const listRes = await http('/v1/vms', { token });
if (listRes.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua auth login' again.");
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
if (!listRes.ok) {
@@ -298,7 +301,7 @@ export function registerVmCommands(y: Argv) {
const vms = (await listRes.json()) as VmItem[];
const vm = vms.find((v) => v.name === name);
if (!vm) {
console.error('VM not found');
console.error('Sandbox not found');
process.exit(1);
}
const host =
@@ -310,7 +313,5 @@ export function registerVmCommands(y: Argv) {
console.log(`Opening Playground: ${url}`);
await openInBrowser(url);
}
)
.demandCommand(1, 'Specify a vm subcommand')
);
);
}

View File

@@ -25,17 +25,29 @@ export type VmItem = {
host?: string;
};
export function printVmList(items: VmItem[]) {
export function printVmList(items: VmItem[], showPasswords: boolean = false) {
const headers = showPasswords
? ['NAME', 'STATUS', 'PASSWORD', 'HOST']
: ['NAME', 'STATUS', 'HOST'];
const rows: string[][] = [
['NAME', 'STATUS', 'PASSWORD', 'HOST'],
...items.map((v) => [v.name, String(v.status), v.password, v.host || '']),
headers,
...items.map((v) => showPasswords
? [v.name, String(v.status), v.password, v.host || '']
: [v.name, String(v.status), v.host || '']
),
];
const widths: number[] = [0, 0, 0, 0];
const numCols = headers.length;
const widths: number[] = new Array(numCols).fill(0);
for (const r of rows)
for (let i = 0; i < 4; i++)
for (let i = 0; i < numCols; i++)
widths[i] = Math.max(widths[i] ?? 0, (r[i] ?? '').length);
for (const r of rows)
console.log(r.map((c, i) => (c ?? '').padEnd(widths[i] ?? 0)).join(' '));
if (items.length === 0) console.log('No VMs found');
}