From 1bda493d4190b0937d4895612a293c93b9ff26cf Mon Sep 17 00:00:00 2001 From: Eric Barroca Date: Sun, 5 Apr 2026 21:43:09 +0900 Subject: [PATCH] feat: add regional profile handling to vertesia cli --- packages/cli/README.md | 46 +++++++++++++++++++++++++++ packages/cli/src/index.ts | 4 +-- packages/cli/src/profiles/commands.ts | 12 +++---- packages/cli/src/profiles/index.ts | 45 +++++++++++++++----------- 4 files changed, 80 insertions(+), 27 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 821f34348..ca5472667 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -25,6 +25,52 @@ npm -g install @vertesia/cli vertesia help ``` +## Profiles + +The CLI uses configuration profiles to store the selected Vertesia account, project, endpoints, and auth token. + +Common profile commands: + +```bash +vertesia profiles show +vertesia profiles use +vertesia profiles add --target --region +vertesia auth token +``` + +Supported profile targets: + +- `local` +- `dev-*` +- `preview` +- `prod` +- custom `https://.../cli` URL + +Supported regions: + +- `dev1` +- `us1` +- `eu1` +- `jp1` + +Default region behavior: + +- `local` and `dev-*` default to `dev1` +- `preview` and `prod` default to `us1` + +Examples: + +```bash +# Dev branch profile +vertesia profiles add my-dev --target dev-feat-foo + +# Regional preview profile +vertesia profiles add my-preview-eu --target preview --region eu1 + +# Regional production profile +vertesia profiles add my-prod-jp --target prod --region jp1 +``` + ## Documentation See https://docs.vertesiahq.com/cli diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index d966bf10a..a1d2f2a60 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -149,8 +149,8 @@ profilesRoot.command('use [name]') }); profilesRoot.command('add [name]') .alias('create') - .option("-t, --target ", "The target environment for the profile. Possible values are: local, dev-main, dev-preview, preview, prod or a custom URL.") - .option("-r, --region ", `Deployment region: ${AVAILABLE_REGIONS.join(', ')}. Defaults to ${DEFAULT_REGION}. Only applies to preview and prod targets.`) + .option("-t, --target ", "The target environment for the profile. Possible values are: local, dev-*, preview, prod or a custom URL.") + .option("-r, --region ", `Deployment region: ${AVAILABLE_REGIONS.join(', ')}. Defaults to target-appropriate region (dev1 for local/dev-*, ${DEFAULT_REGION} otherwise).`) .option("-k, --apikey ", "The API key to use for the profile") .option("-p, --project ", "The project ID to use for the profile") .option("-a, --account ", "The account ID to use for the profile") diff --git a/packages/cli/src/profiles/commands.ts b/packages/cli/src/profiles/commands.ts index 737519f12..74a7d09d7 100644 --- a/packages/cli/src/profiles/commands.ts +++ b/packages/cli/src/profiles/commands.ts @@ -1,7 +1,7 @@ import colors from 'ansi-colors'; import enquirer from "enquirer"; import jwt from 'jsonwebtoken'; -import { AVAILABLE_REGIONS, DEFAULT_REGION, Region, config, getConfigUrl, getServerUrls, shouldRefreshProfileToken } from "./index.js"; +import { AVAILABLE_REGIONS, ConfigUrlRef, Region, config, getConfigUrl, getDefaultRegion, getServerUrls, shouldRefreshProfileToken } from "./index.js"; import { ConfigResult } from './server/index.js'; const { prompt } = enquirer; @@ -151,16 +151,16 @@ export async function createProfile(name?: string, options: CreateProfileOptions target = customResponse.url.trim(); } - // Prompt for region when target requires it (preview/prod only) - let region: Region = (options.region as Region) ?? DEFAULT_REGION; - const needsRegionPrompt = !options.region && (target === 'preview' || target === 'prod'); + // Default region depends on the selected target: dev targets use dev1, stable targets use us1. + let region: Region = (options.region as Region) ?? getDefaultRegion(target as ConfigUrlRef); + const needsRegionPrompt = !options.region && target !== 'custom'; if (needsRegionPrompt) { const regionResponse: any = await prompt({ type: 'select', name: 'region', message: 'Deployment region', choices: AVAILABLE_REGIONS, - initial: AVAILABLE_REGIONS[0], + initial: AVAILABLE_REGIONS.indexOf(region), } as any); region = regionResponse.region as Region; } @@ -241,4 +241,4 @@ async function _doRefreshToken(profileName: string, onResult?: OnResultCallback) if (r.refresh) { config.updateProfile(profileName).start(onResult); } -} \ No newline at end of file +} diff --git a/packages/cli/src/profiles/index.ts b/packages/cli/src/profiles/index.ts index f4b51002c..b50b3029c 100644 --- a/packages/cli/src/profiles/index.ts +++ b/packages/cli/src/profiles/index.ts @@ -15,24 +15,34 @@ export function getConfigFile(path?: string) { } } -export type Region = 'us1' | 'eu1' | 'jp1'; +export type Region = 'dev1' | 'us1' | 'eu1' | 'jp1'; export const DEFAULT_REGION: Region = 'us1'; -export const AVAILABLE_REGIONS: Region[] = ['us1', 'eu1', 'jp1']; +export const AVAILABLE_REGIONS: Region[] = ['dev1', 'us1', 'eu1', 'jp1']; export type ConfigUrlRef = "local" | "dev-main" | "dev-preview" | "preview" | "prod" | string; +export function isNamedDevTarget(value: string): boolean { + return /^dev-[a-zA-Z0-9-]+$/.test(value); +} + +export function getDefaultRegion(value: ConfigUrlRef): Region { + if (value === 'local' || isNamedDevTarget(value)) { + return 'dev1'; + } + return DEFAULT_REGION; +} + export function getConfigUrl(value: ConfigUrlRef, region: Region = DEFAULT_REGION): string { switch (value) { case "local": return "https://localhost:5173/cli"; - case "dev-main": - return "https://dev-main.ui.dev1.vertesia.io/cli"; - case "dev-preview": - return "https://dev-preview.ui.dev1.vertesia.io/cli"; case "preview": return `https://preview.cloud.${region}.vertesia.io/cli`; case "prod": return `https://cloud.${region}.vertesia.io/cli`; default: + if (isNamedDevTarget(value)) { + return `https://${value}.ui.${region}.vertesia.io/cli`; + } if (value.startsWith("http://") || value.startsWith("https://")) { return value; } else { @@ -47,16 +57,6 @@ export function getServerUrls(value: ConfigUrlRef, region: Region = DEFAULT_REGI studio_server_url: "http://localhost:8091", zeno_server_url: "http://localhost:8092", }; - case "dev-main": - return { - studio_server_url: "https://studio-server-dev-main.api.dev1.vertesia.io", - zeno_server_url: "https://zeno-server-dev-main.api.dev1.vertesia.io", - }; - case "dev-preview": - return { - studio_server_url: "https://studio-server-dev-preview.api.dev1.vertesia.io", - zeno_server_url: "https://zeno-server-dev-preview.api.dev1.vertesia.io", - }; case "preview": return { studio_server_url: `https://api-preview.${region}.vertesia.io`, @@ -68,13 +68,19 @@ export function getServerUrls(value: ConfigUrlRef, region: Region = DEFAULT_REGI zeno_server_url: `https://api.${region}.vertesia.io`, }; default: + if (isNamedDevTarget(value)) { + return { + studio_server_url: `https://studio-server-${value}.api.${region}.vertesia.io`, + zeno_server_url: `https://zeno-server-${value}.api.${region}.vertesia.io`, + }; + } throw new Error("Unable to detect server urls from custom target."); } } export function getCloudTypeFromConfigUrl(url: string) { if (url.startsWith("https://localhost")) { return "staging"; - } else if (url.includes(".ui.dev1.vertesia.io")) { + } else if (/\.ui\.[a-z0-9-]+\.vertesia\.io/.test(url)) { return "staging"; } else if (url.startsWith("https://preview.")) { return "preview"; @@ -239,8 +245,9 @@ export class Config { } createProfile(name: string, target: ConfigUrlRef, region: Region = DEFAULT_REGION) { - const config_url = getConfigUrl(target, region); - return new ConfigureProfile(this, { name, config_url, region }, true); + const resolvedRegion = region ?? getDefaultRegion(target); + const config_url = getConfigUrl(target, resolvedRegion); + return new ConfigureProfile(this, { name, config_url, region: resolvedRegion }, true); } updateProfile(name: string) {