Merge pull request #579 from sarinali/feat/new-usecase-connections-extraction

Post Event Contact Usecase
This commit is contained in:
Sarina Li
2025-11-16 18:55:09 -05:00
committed by GitHub
5 changed files with 1805 additions and 281 deletions
+12 -12
View File
@@ -4,9 +4,9 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
version: version:
description: 'Version to publish (default: from package.json)' description: "Version to publish (default: from package.json)"
required: false required: false
default: '' default: ""
jobs: jobs:
build-and-publish: build-and-publish:
@@ -14,25 +14,25 @@ jobs:
id-token: write id-token: write
contents: write contents: write
packages: write packages: write
strategy: strategy:
matrix: matrix:
include: include:
- target: bun-linux-x64 - target: bun-linux-x64
ext: '' ext: ""
binary_name: cua-linux-x64 binary_name: cua-linux-x64
- target: bun-darwin-x64 - target: bun-darwin-x64
ext: '' ext: ""
binary_name: cua-darwin-x64 binary_name: cua-darwin-x64
- target: bun-darwin-arm64 - target: bun-darwin-arm64
ext: '' ext: ""
binary_name: cua-darwin-arm64 binary_name: cua-darwin-arm64
- target: bun-windows-x64 - target: bun-windows-x64
ext: '.exe' ext: ".exe"
binary_name: cua-windows-x64 binary_name: cua-windows-x64
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -140,7 +140,7 @@ jobs:
```bash ```bash
# For Linux/macOS # For Linux/macOS
curl -fsSL https://cua.ai/cli/install.sh | sh curl -fsSL https://cua.ai/cli/install.sh | sh
# For Windows (PowerShell) # For Windows (PowerShell)
irm https://cua.ai/cli/install.ps1 | iex irm https://cua.ai/cli/install.ps1 | iex
``` ```
@@ -149,7 +149,7 @@ jobs:
```bash ```bash
# Using bun # Using bun
bun add -g @trycua/cli bun add -g @trycua/cli
# Or using npm # Or using npm
npm install -g @trycua/cli npm install -g @trycua/cli
``` ```
@@ -209,4 +209,4 @@ jobs:
asset_name: cua-windows-x64.exe asset_name: cua-windows-x64.exe
asset_content_type: application/octet-stream asset_content_type: application/octet-stream
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"title": "Cookbook", "title": "Cookbook",
"description": "Real-world examples of building with Cua", "description": "Real-world examples of building with Cua",
"pages": ["form-filling"] "pages": ["form-filling", "post-event-contact-export"]
} }
File diff suppressed because it is too large Load Diff
+259 -259
View File
@@ -11,11 +11,12 @@ export function registerVmCommands(y: Argv) {
.command( .command(
['list', 'ls', 'ps'], ['list', 'ls', 'ps'],
'List sandboxes', 'List sandboxes',
(y) => y.option('show-passwords', { (y) =>
type: 'boolean', y.option('show-passwords', {
default: false, type: 'boolean',
describe: 'Show sandbox passwords in output' default: false,
}), describe: 'Show sandbox passwords in output',
}),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const res = await http('/v1/vms', { token }); const res = await http('/v1/vms', { token });
@@ -34,284 +35,283 @@ export function registerVmCommands(y: Argv) {
) )
.command( .command(
'create', 'create',
'Create a new sandbox', 'Create a new sandbox',
(y) => (y) =>
y y
.option('os', { .option('os', {
type: 'string', type: 'string',
choices: ['linux', 'windows', 'macos'], choices: ['linux', 'windows', 'macos'],
demandOption: true, demandOption: true,
describe: 'Operating system', describe: 'Operating system',
}) })
.option('configuration', { .option('configuration', {
type: 'string', type: 'string',
choices: ['small', 'medium', 'large'], choices: ['small', 'medium', 'large'],
demandOption: true, demandOption: true,
describe: 'Sandbox size configuration', describe: 'Sandbox size configuration',
}) })
.option('region', { .option('region', {
type: 'string', type: 'string',
choices: [ choices: [
'north-america', 'north-america',
'europe', 'europe',
'asia-pacific', 'asia-pacific',
'south-america', 'south-america',
], ],
demandOption: true, demandOption: true,
describe: 'Sandbox region', describe: 'Sandbox region',
}), }),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const { os, configuration, region } = argv as { const { os, configuration, region } = argv as {
os: string; os: string;
configuration: string; configuration: string;
region: string; region: string;
}; };
const res = await http('/v1/vms', { const res = await http('/v1/vms', {
token, token,
method: 'POST', method: 'POST',
body: { os, configuration, region }, body: { os, configuration, region },
}); });
if (res.status === 401) { if (res.status === 401) {
clearApiKey(); clearApiKey();
console.error("Unauthorized. Try 'cua login' again."); console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
if (res.status === 400) {
console.error('Invalid request or unsupported configuration');
process.exit(1);
}
if (res.status === 500) {
console.error('Internal server error');
process.exit(1);
}
if (res.status === 200) {
// Sandbox ready immediately
const data = (await res.json()) as {
status: string;
name: string;
password: string;
host: string;
};
console.log(`Sandbox created and ready: ${data.name}`);
console.log(`Password: ${data.password}`);
console.log(`Host: ${data.host}`);
return;
}
if (res.status === 202) {
// Sandbox provisioning started
const data = (await res.json()) as {
status: string;
name: string;
job_id: string;
};
console.log(`Sandbox provisioning started: ${data.name}`);
console.log(`Job ID: ${data.job_id}`);
console.log("Use 'cua list' to monitor provisioning progress");
return;
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1); process.exit(1);
} }
)
if (res.status === 400) {
console.error('Invalid request or unsupported configuration');
process.exit(1);
}
if (res.status === 500) {
console.error('Internal server error');
process.exit(1);
}
if (res.status === 200) {
// Sandbox ready immediately
const data = (await res.json()) as {
status: string;
name: string;
password: string;
host: string;
};
console.log(`Sandbox created and ready: ${data.name}`);
console.log(`Password: ${data.password}`);
console.log(`Host: ${data.host}`);
return;
}
if (res.status === 202) {
// Sandbox provisioning started
const data = (await res.json()) as {
status: string;
name: string;
job_id: string;
};
console.log(`Sandbox provisioning started: ${data.name}`);
console.log(`Job ID: ${data.job_id}`);
console.log("Use 'cua list' to monitor provisioning progress");
return;
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command( .command(
'delete <name>', 'delete <name>',
'Delete a sandbox', 'Delete a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }), (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const name = String((argv as any).name); const name = String((argv as any).name);
const res = await http(`/v1/vms/${encodeURIComponent(name)}`, { const res = await http(`/v1/vms/${encodeURIComponent(name)}`, {
token, token,
method: 'DELETE', method: 'DELETE',
}); });
if (res.status === 202) { if (res.status === 202) {
const body = (await res.json().catch(() => ({}))) as { const body = (await res.json().catch(() => ({}))) as {
status?: string; status?: string;
}; };
console.log(`Sandbox deletion initiated: ${body.status ?? 'deleting'}`); console.log(
return; `Sandbox deletion initiated: ${body.status ?? 'deleting'}`
} );
return;
}
if (res.status === 404) { if (res.status === 404) {
console.error('Sandbox 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 login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1); process.exit(1);
} }
)
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command( .command(
'start <name>', 'start <name>',
'Start a sandbox', 'Start a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }), (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const name = String((argv as any).name); const name = String((argv as any).name);
const res = await http(`/v1/vms/${encodeURIComponent(name)}/start`, { const res = await http(`/v1/vms/${encodeURIComponent(name)}/start`, {
token, token,
method: 'POST', method: 'POST',
}); });
if (res.status === 204) { if (res.status === 204) {
console.log('Start accepted'); console.log('Start accepted');
return; return;
} }
if (res.status === 404) { if (res.status === 404) {
console.error('Sandbox not found'); console.error('Sandbox not found');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1); process.exit(1);
} }
) if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command( .command(
'stop <name>', 'stop <name>',
'Stop a sandbox', 'Stop a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }), (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const name = String((argv as any).name); const name = String((argv as any).name);
const res = await http(`/v1/vms/${encodeURIComponent(name)}/stop`, { const res = await http(`/v1/vms/${encodeURIComponent(name)}/stop`, {
token, token,
method: 'POST', method: 'POST',
}); });
if (res.status === 202) { if (res.status === 202) {
const body = (await res.json().catch(() => ({}))) as { const body = (await res.json().catch(() => ({}))) as {
status?: string; status?: string;
}; };
console.log(body.status ?? 'stopping'); console.log(body.status ?? 'stopping');
return; return;
} }
if (res.status === 404) { if (res.status === 404) {
console.error('Sandbox not found'); console.error('Sandbox not found');
process.exit(1);
}
if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1); process.exit(1);
} }
) if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command( .command(
'restart <name>', 'restart <name>',
'Restart a sandbox', 'Restart a sandbox',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }), (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const name = String((argv as any).name); const name = String((argv as any).name);
const res = await http( const res = await http(`/v1/vms/${encodeURIComponent(name)}/restart`, {
`/v1/vms/${encodeURIComponent(name)}/restart`, token,
{ method: 'POST',
token, });
method: 'POST', if (res.status === 202) {
} const body = (await res.json().catch(() => ({}))) as {
); status?: string;
if (res.status === 202) { };
const body = (await res.json().catch(() => ({}))) as { console.log(body.status ?? 'restarting');
status?: string; return;
}; }
console.log(body.status ?? 'restarting'); if (res.status === 404) {
return; console.error('Sandbox not found');
}
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);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1); process.exit(1);
} }
) if (res.status === 401) {
clearApiKey();
console.error("Unauthorized. Try 'cua login' again.");
process.exit(1);
}
console.error(`Unexpected status: ${res.status}`);
process.exit(1);
}
)
.command( .command(
'open <name>', 'open <name>',
'Open NoVNC for a sandbox in your browser', 'Open NoVNC for a sandbox in your browser',
(y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }), (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
async (argv: Record<string, unknown>) => { async (argv: Record<string, unknown>) => {
const token = await ensureApiKeyInteractive(); const token = await ensureApiKeyInteractive();
const name = String((argv as any).name); const name = String((argv as any).name);
const listRes = await http('/v1/vms', { token }); const listRes = await http('/v1/vms', { token });
if (listRes.status === 401) { if (listRes.status === 401) {
clearApiKey(); clearApiKey();
console.error("Unauthorized. Try 'cua login' again."); console.error("Unauthorized. Try 'cua login' again.");
process.exit(1); process.exit(1);
}
if (!listRes.ok) {
console.error(`Request failed: ${listRes.status}`);
process.exit(1);
}
const vms = (await listRes.json()) as VmItem[];
const vm = vms.find((v) => v.name === name);
if (!vm) {
console.error('Sandbox not found');
process.exit(1);
}
const host =
vm.host && vm.host.length
? vm.host
: `${vm.name}.containers.cloud.trycua.com`;
const url = `https://${host}/vnc.html?autoconnect=true&password=${encodeURIComponent(vm.password)}`;
console.log(`Opening NoVNC: ${url}`);
await openInBrowser(url);
} }
) if (!listRes.ok) {
// .command( console.error(`Request failed: ${listRes.status}`);
// 'chat <name>', process.exit(1);
// 'Open CUA playground for a sandbox', }
// (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }), const vms = (await listRes.json()) as VmItem[];
// async (argv: Record<string, unknown>) => { const vm = vms.find((v) => v.name === name);
// const token = await ensureApiKeyInteractive(); if (!vm) {
// const name = String((argv as any).name); console.error('Sandbox not found');
// const listRes = await http('/v1/vms', { token }); process.exit(1);
// if (listRes.status === 401) { }
// clearApiKey(); const host =
// console.error("Unauthorized. Try 'cua login' again."); vm.host && vm.host.length
// process.exit(1); ? vm.host
// } : `${vm.name}.containers.cloud.trycua.com`;
// if (!listRes.ok) { const url = `https://${host}/vnc.html?autoconnect=true&password=${encodeURIComponent(vm.password)}`;
// console.error(`Request failed: ${listRes.status}`); console.log(`Opening NoVNC: ${url}`);
// process.exit(1); await openInBrowser(url);
// } }
// const vms = (await listRes.json()) as VmItem[]; );
// const vm = vms.find((v) => v.name === name); // .command(
// if (!vm) { // 'chat <name>',
// console.error('Sandbox not found'); // 'Open CUA playground for a sandbox',
// process.exit(1); // (y) => y.positional('name', { type: 'string', describe: 'Sandbox name' }),
// } // async (argv: Record<string, unknown>) => {
// const host = // const token = await ensureApiKeyInteractive();
// vm.host && vm.host.length // const name = String((argv as any).name);
// ? vm.host // const listRes = await http('/v1/vms', { token });
// : `${vm.name}.containers.cloud.trycua.com`; // if (listRes.status === 401) {
// const base = WEBSITE_URL.replace(/\/$/, ''); // clearApiKey();
// const url = `${base}/dashboard/playground?host=${encodeURIComponent(host)}&id=${encodeURIComponent(vm.name)}&name=${encodeURIComponent(vm.name)}&vnc_password=${encodeURIComponent(vm.password)}&fullscreen=true`; // console.error("Unauthorized. Try 'cua login' again.");
// console.log(`Opening Playground: ${url}`); // process.exit(1);
// await openInBrowser(url); // }
// } // if (!listRes.ok) {
// ); // console.error(`Request failed: ${listRes.status}`);
// process.exit(1);
// }
// const vms = (await listRes.json()) as VmItem[];
// const vm = vms.find((v) => v.name === name);
// if (!vm) {
// console.error('Sandbox not found');
// process.exit(1);
// }
// const host =
// vm.host && vm.host.length
// ? vm.host
// : `${vm.name}.containers.cloud.trycua.com`;
// const base = WEBSITE_URL.replace(/\/$/, '');
// const url = `${base}/dashboard/playground?host=${encodeURIComponent(host)}&id=${encodeURIComponent(vm.name)}&name=${encodeURIComponent(vm.name)}&vnc_password=${encodeURIComponent(vm.password)}&fullscreen=true`;
// console.log(`Opening Playground: ${url}`);
// await openInBrowser(url);
// }
// );
} }
+10 -9
View File
@@ -26,28 +26,29 @@ export type VmItem = {
}; };
export function printVmList(items: VmItem[], showPasswords: boolean = false) { export function printVmList(items: VmItem[], showPasswords: boolean = false) {
const headers = showPasswords const headers = showPasswords
? ['NAME', 'STATUS', 'PASSWORD', 'HOST'] ? ['NAME', 'STATUS', 'PASSWORD', 'HOST']
: ['NAME', 'STATUS', 'HOST']; : ['NAME', 'STATUS', 'HOST'];
const rows: string[][] = [ const rows: string[][] = [
headers, headers,
...items.map((v) => showPasswords ...items.map((v) =>
? [v.name, String(v.status), v.password, v.host || ''] showPasswords
: [v.name, String(v.status), v.host || ''] ? [v.name, String(v.status), v.password, v.host || '']
: [v.name, String(v.status), v.host || '']
), ),
]; ];
const numCols = headers.length; const numCols = headers.length;
const widths: number[] = new Array(numCols).fill(0); const widths: number[] = new Array(numCols).fill(0);
for (const r of rows) for (const r of rows)
for (let i = 0; i < numCols; i++) for (let i = 0; i < numCols; i++)
widths[i] = Math.max(widths[i] ?? 0, (r[i] ?? '').length); widths[i] = Math.max(widths[i] ?? 0, (r[i] ?? '').length);
for (const r of rows) for (const r of rows)
console.log(r.map((c, i) => (c ?? '').padEnd(widths[i] ?? 0)).join(' ')); console.log(r.map((c, i) => (c ?? '').padEnd(widths[i] ?? 0)).join(' '));
if (items.length === 0) console.log('No VMs found'); if (items.length === 0) console.log('No VMs found');
} }