suspend vm from cli

This commit is contained in:
Sarina Li
2025-11-24 01:00:21 -05:00
parent 606ba6fb11
commit f59f3aeb99
3 changed files with 54 additions and 1 deletions

View File

@@ -17,7 +17,9 @@ export async function runCli() {
' cua sb <command> Create and manage cloud sandboxes\n' +
' list View all your sandboxes\n' +
' create Provision a new sandbox\n' +
' start/stop Control sandbox state\n' +
' start Start or resume a sandbox\n' +
' stop Stop a sandbox (preserves disk)\n' +
' suspend Suspend a sandbox (preserves memory)\n' +
' vnc Open remote desktop\n' +
'\n' +
'Documentation: https://docs.cua.ai/libraries/cua-cli/commands'

View File

@@ -191,6 +191,41 @@ const restartHandler = async (argv: Record<string, unknown>) => {
process.exit(1);
};
const suspendHandler = async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
const res = await http(`/v1/vms/${encodeURIComponent(name)}/suspend`, {
token,
method: 'POST',
});
if (res.status === 202) {
const body = (await res.json().catch(() => ({}))) as {
status?: string;
};
console.log(body.status ?? 'suspending');
return;
}
if (res.status === 404) {
console.error('Sandbox not found');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
if (res.status === 400 || res.status === 500) {
const body = (await res.json().catch(() => ({}))) as { error?: string };
console.error(
body.error ??
"Suspend not supported for this VM. Use 'cua sb stop' instead."
);
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
};
const openHandler = async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive();
const name = String((argv as any).name);
@@ -296,6 +331,13 @@ export function registerSandboxCommands(y: Argv) {
y.positional('name', { type: 'string', describe: 'Sandbox name' }),
restartHandler
)
.command(
'suspend <name>',
'Suspend a sandbox, preserving memory state (use start to resume)',
(y) =>
y.positional('name', { type: 'string', describe: 'Sandbox name' }),
suspendHandler
)
.command(
['vnc <name>', 'open <name>'],
'Open remote desktop (VNC) connection in your browser',
@@ -378,6 +420,13 @@ export function registerSandboxCommands(y: Argv) {
y.positional('name', { type: 'string', describe: 'Sandbox name' }),
handler: restartHandler,
} as any)
.command({
command: 'suspend <name>',
describe: false as any, // Hide from help
builder: (y: Argv) =>
y.positional('name', { type: 'string', describe: 'Sandbox name' }),
handler: suspendHandler,
} as any)
.command({
command: ['vnc <name>', 'open <name>'],
describe: false as any, // Hide from help

View File

@@ -16,6 +16,8 @@ export type SandboxStatus =
| 'pending'
| 'running'
| 'stopped'
| 'suspended'
| 'suspending'
| 'terminated'
| 'failed';
export type SandboxItem = {