Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
7670c48
feat: add process and task APIs, enhance AgentsApi with process support
ebarroca Apr 15, 2026
e79b524
feat: add process validation functionality and export from index
ebarroca Apr 15, 2026
9c4d767
feat: enhance process validation and task handling with new interface…
ebarroca Apr 17, 2026
e8e4e54
feat(common): add user_message to ProcessRunConfig
ebarroca Apr 17, 2026
5b7f6a1
feat: enhance AppsApi and InteractionsApi with new system tool functi…
ebarroca Apr 19, 2026
3c93b18
feat: rename 'related_tools' to 'tools' across documentation and code…
ebarroca Apr 19, 2026
c03ea02
feat: enhance AgentsApi with new run recording methods and validation…
ebarroca Apr 19, 2026
16f0c28
feat: add process schema definition and validation tests; enhance Mon…
ebarroca Apr 19, 2026
461f54c
feat: remove optional xstate_snapshot from ProcessState interface
ebarroca Apr 19, 2026
9cfb1a5
feat: add PromptRenderResponse interface and update render method to …
ebarroca Apr 19, 2026
c92759f
feat: update endpoint for fetching system tools package to '/studio-t…
ebarroca Apr 20, 2026
17394c1
feat(cli): enhance client creation with explicit environment variable…
ebarroca Apr 21, 2026
0057d44
feat: update documentation for listChildren and getChildDetails metho…
ebarroca Apr 21, 2026
0a4685c
feat: implement keyring support for profile authentication; add strea…
ebarroca Apr 22, 2026
3786fb6
chore: update minimum release age and dependencies
ebarroca Apr 22, 2026
b44d9db
Merge branch 'main' of https://github.com/vertesia/composableai into …
ebarroca Apr 23, 2026
e272871
oauth in cli
ebarroca Apr 24, 2026
dad4beb
refactor: remove deprecated worker commands and related files
ebarroca Apr 24, 2026
47395a1
feat: add support for development deployment targets in config URL an…
ebarroca Apr 24, 2026
496b287
feat: implement worker commands and docker credential management
ebarroca Apr 24, 2026
38d4924
refactor: streamline content reading in createOrUpdateNpmRegistry fun…
ebarroca Apr 24, 2026
2ba90e7
feat: add idToken support in auth bundle and related interfaces
ebarroca Apr 25, 2026
4383ecd
feat: enhance project and authentication management with new commands…
ebarroca Apr 25, 2026
7d61656
feat: enhance OAuth client integration with discovery and error handling
ebarroca Apr 25, 2026
50286cc
feat: add node runtime detection and enhance initial headers with enc…
ebarroca Apr 25, 2026
e9c4d9f
feat: rename and enhance server runtime detection for improved header…
ebarroca Apr 25, 2026
fe2a927
feat: enhance OAuth profile to include config_url and add device auth…
ebarroca Apr 25, 2026
8fa3440
feat: use device flow for cli oauth
ebarroca Apr 25, 2026
012872f
feat: add OAuthAccess principal type and update access token payload …
ebarroca Apr 25, 2026
2bcf3d9
Merge remote-tracking branch 'origin/main' into feat-process-engine
ebarroca Apr 26, 2026
dc218f0
fix: preserve process runs in agent list response
ebarroca Apr 26, 2026
e2cbed5
chore: update subproject commit reference in llumiverse
ebarroca Apr 27, 2026
c50a413
Merge remote-tracking branch 'origin/main' into feat-process-engine
ebarroca Apr 27, 2026
f81c370
refactor: remove iterative generation activities and related types
ebarroca Apr 27, 2026
a1d420b
refactor: remove deprecated worker commands and related files
ebarroca Apr 28, 2026
e5875b5
refactor: simplify delay function and improve abort handling
ebarroca Apr 28, 2026
e5b0e14
Merge branch 'main' of https://github.com/vertesia/composableai into …
ebarroca Apr 28, 2026
8ead932
feat: enhance package publishing scripts with workspace filtering
ebarroca Apr 28, 2026
ac6c8cc
chore: remove deprecated create-worker package and related files
ebarroca Apr 28, 2026
700bb8a
chore: remove create-worker package and its dependencies from lockfile
ebarroca Apr 28, 2026
531d06c
feat: add process management features including listing, publishing, …
ebarroca May 1, 2026
1fbd6ec
Merge main into feat-process-engine
ebarroca May 1, 2026
a113f54
feat: app dashboards capability + plugin template chrome upgrade
ebarroca May 3, 2026
4b40605
feat: update theme customization and remove theme token selection fro…
ebarroca May 3, 2026
061dca0
docs(plugin-template): note TS18003 gotcha when deleting all widgets
ebarroca May 3, 2026
abda228
feat(plugin-template): conditional widgets typecheck via scripts/type…
ebarroca May 3, 2026
32a1aaa
Add appgen dev auth support
ebarroca May 3, 2026
72107b3
Stabilize appgen dev preview
ebarroca May 3, 2026
a1f222b
fix: avoid regex slash trimming in app endpoints
ebarroca May 3, 2026
56f6edd
feat(plugin-template): enhance plugin structure with new dependencies…
ebarroca May 4, 2026
0af9197
feat: add support for fetching composable token from Vertesia JWT and…
ebarroca May 5, 2026
63fb38f
fix(cli): exit cleanly on Ctrl-C in legacy auth refresh prompt
ebarroca May 5, 2026
3853e30
feat: implement app version management with new API endpoints and dat…
ebarroca May 6, 2026
cbccd01
feat(plugin-template): update routing for plugin UI and admin tools i…
ebarroca May 6, 2026
b587ea0
improve skills
michaelva May 7, 2026
f05431b
pull main
ebarroca May 7, 2026
8bd430d
fix: update tsconfig files to ignore deprecations and improve workflo…
ebarroca May 7, 2026
ba0b526
Merge remote-tracking branch 'origin/main' into feat-app-template-more
michaelva May 7, 2026
4db99b9
improve claude resources
michaelva May 8, 2026
e9d86f2
add agents resources
michaelva May 8, 2026
1934193
add corepack variable
michaelva May 8, 2026
f4cd30f
add i18n
michaelva May 8, 2026
18ff125
add env
michaelva May 8, 2026
ac959fa
remove obsolete templates
michaelva May 8, 2026
6cdbaef
handle regions
michaelva May 8, 2026
d0637ec
studio is not public
michaelva May 8, 2026
03e190d
improve package manager handling
michaelva May 8, 2026
5ffe2f1
fix build warning
michaelva May 8, 2026
cf06907
use proper structure for the UI
michaelva May 8, 2026
90d2ed7
table improvement
michaelva May 8, 2026
d599ef8
fix eslint
michaelva May 8, 2026
8730903
reusable preview panels
michaelva May 8, 2026
31050b3
fix console error
michaelva May 8, 2026
02ead79
improve skill and components
michaelva May 8, 2026
9f56f68
add conversation list
michaelva May 8, 2026
5bd7330
merge feat-app-template-more
michaelva May 8, 2026
8ef3143
fix merge
michaelva May 8, 2026
49eac94
add getAppInstallationPackage method and refactor RecentAgentRun type…
ebarroca May 9, 2026
fe9a000
update reference URLs in llms.txt to point to the new design document…
ebarroca May 10, 2026
4fe19c1
feat: Git credential helper for app repos + AppPackageScope + plugin …
ebarroca May 12, 2026
897efad
chore: update subproject commit reference in llumiverse
ebarroca May 12, 2026
626a74e
chore: update subproject commit reference in llumiverse
ebarroca May 12, 2026
090151c
refactor: remove hardcoded known Git base URLs and enhance alias clea…
ebarroca May 12, 2026
2e29cc8
feat: enhance Git credential helper to handle missing profiles with d…
ebarroca May 13, 2026
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
1 change: 1 addition & 0 deletions packages/build-tools/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"ignoreDeprecations": "6.0",
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
Expand Down
20 changes: 18 additions & 2 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
useProfile,
type CreateProfileOptions,
} from './profiles/commands.js';
import { configureGitAuth, serveGitCredential } from './profiles/git.js';
import { AVAILABLE_REGIONS, DEFAULT_REGION, getConfigFile } from './profiles/index.js';
import { listProjects, useProject } from './projects/index.js';
import runInteraction from './run/index.js';
Expand Down Expand Up @@ -58,7 +59,7 @@ const authRoot = program.command("auth")

authRoot.command("login [profile]")
.description("Authenticate a profile, creating it when it does not exist")
.option("-t, --target <env>", "The target environment for a new profile. Possible values are: local, dev-main, dev-preview, preview, prod or a custom URL.")
.option("-t, --target <env>", "The target environment for a new profile. Possible values are: local, preview, prod, or a custom URL.")
.option("-r, --region <region>", `Deployment region for a new profile: ${AVAILABLE_REGIONS.join(', ')}. Defaults to ${DEFAULT_REGION}. Only applies to preview and prod targets.`)
.option("-p, --project <project>", "Authenticate for the given project ID")
.option("-a, --account <account>", "The account ID to use when creating a profile")
Expand Down Expand Up @@ -89,6 +90,21 @@ authRoot.command("refresh")
.option("-p, --project <project>", "Refresh the current profile token for the given project ID")
.action((options: { project?: string }) => updateCurrentProfile(undefined, undefined, options))

const authGit = authRoot.command("git")
.description("Configure Git to authenticate to Vertesia app repositories with the active profile token.")
.option("-p, --profile <profile>", "Profile to use for Git credentials. Defaults to the active profile.")
.option("--url <url>", "Git server base URL, for example https://git.dev1.vertesia.io.")
.option("--no-alias", "Do not configure the vertesia:<app>.git short alias.")
.action((options: { profile?: string; url?: string; alias?: boolean }) => configureGitAuth(options));

authGit.command("credential [action]")
.description("Git credential helper entrypoint. Called by git; users should run `vertesia auth git` instead.")
.action(function (this: import("commander").Command, action: string | undefined) {
// --profile is declared on the parent `auth git`, so read via globals.
const profile = this.optsWithGlobals().profile as string | undefined;
return serveGitCredential(action, { profile });
});

program.command("envs [envId]")
.description("List the environments you have access to")
.action((envId: string | undefined, options: Record<string, any>) => {
Expand Down Expand Up @@ -169,7 +185,7 @@ profilesRoot.command('use [name]')
});
profilesRoot.command('add [name]')
.alias('create')
.option("-t, --target <env>", "The target environment for the profile. Possible values are: local, dev-main, dev-preview, preview, prod or a custom URL.")
.option("-t, --target <env>", "The target environment for the profile. Possible values are: local, preview, prod, or a custom URL.")
.option("-r, --region <region>", `Deployment region: ${AVAILABLE_REGIONS.join(', ')}. Defaults to ${DEFAULT_REGION}. Only applies to preview and prod targets.`)
.option("-k, --apikey <key>", "The API key or auth token to use for the profile")
.option("-p, --project <project>", "The project ID to use for the profile")
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/profiles/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ export async function createProfile(name?: string, options: CreateProfileOptions
});
}
if (!options.target) {
// only show dev environments in dev mode
const choices = config.isDevMode ? ['local', 'dev-main', 'dev-preview', 'preview', 'prod', 'custom'] : ['preview', 'prod'];
// Branch/dev deployments are custom URLs so profile config is explicit.
const choices = config.isDevMode ? ['local', 'preview', 'prod', 'custom'] : ['preview', 'prod', 'custom'];
questions.push({
type: 'select',
name: 'target',
Expand Down
212 changes: 212 additions & 0 deletions packages/cli/src/profiles/git.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { execFileSync } from 'node:child_process';
import process from 'node:process';
import { ensureProfileAccessToken } from './auth.js';
import { config, type Profile } from './index.js';

interface GitAuthOptions {
profile?: string;
url?: string;
alias?: boolean;
}

interface GitCredentialInput {
protocol?: string;
host?: string;
path?: string;
}

export async function configureGitAuth(options: GitAuthOptions = {}) {
const profile = getRequestedProfile(options.profile);
const gitBaseUrl = normalizeGitBaseUrl(options.url || gitServerUrlForProfile(profile));
const helper = `!vertesia auth git credential --profile ${shellQuote(profile.name)}`;

gitConfig('--global', `credential.${gitBaseUrl}.helper`, helper);
gitConfig('--global', `credential.${gitBaseUrl}.useHttpPath`, 'true');

if (options.alias !== false) {
removeKnownVertesiaAliases();
gitConfig('--global', `url.${gitBaseUrl}/.insteadOf`, 'vertesia:');
}

console.log(`Configured Git authentication for ${gitBaseUrl}`);
console.log(`Clone with: git clone ${gitBaseUrl}/<app-slug>.git`);
if (options.alias !== false) {
console.log('Short alias: git clone vertesia:<app-slug>.git');
}
}

export async function serveGitCredential(action: string | undefined, options: Pick<GitAuthOptions, 'profile'> = {}) {
if (!action || action === 'get') {
const envToken = process.env.VERTESIA_AUTH_TOKEN || process.env.VERTESIA_TOKEN;
if (envToken) {
writeCredential(envToken);
return;
}

const input = await readCredentialInput();
const profile = pickProfileForCredential(input, options.profile);
if (!profile) {
if (options.profile) {
throw new Error(
`Vertesia profile '${options.profile}' was not found. Run \`vertesia auth git\` from an active profile.`,
);
}
throw new Error(
`No Vertesia profile matches git host ${input.host || '<unknown>'}. Run \`vertesia auth git\`.`,
);
}

const token = await ensureProfileAccessToken(profile);
if (!token) {
throw new Error(`Profile ${profile.name} has no usable auth token. Run \`vertesia auth refresh\`.`);
}
writeCredential(token);
return;
}

// Git may call helpers with store/erase. Tokens are managed by Vertesia CLI
// profile auth, so there is nothing useful to persist from Git.
}

function getRequestedProfile(profileName?: string): Profile {
if (profileName) {
const profile = config.getProfile(profileName);
if (!profile) throw new Error(`Profile ${profileName} not found.`);
return profile;
}
if (!config.current) {
throw new Error(
'No Vertesia profile is selected. Run `vertesia auth login` or `vertesia profiles use <name>`.',
);
}
return config.current;
}

function pickProfileForCredential(input: GitCredentialInput, profileName?: string): Profile | undefined {
if (profileName) {
return config.getProfile(profileName);
}
const host = input.host?.toLowerCase();
if (!host) return config.current;
if (config.current && gitHostForProfile(config.current) === host) {
return config.current;
}
return config.profiles.find(profile => gitHostForProfile(profile) === host);
}

function gitHostForProfile(profile: Profile): string | undefined {
try {
return new URL(gitServerUrlForProfile(profile)).hostname.toLowerCase();
} catch {
return undefined;
}
}

function gitServerUrlForProfile(profile: Profile): string {
const override = process.env.VERTESIA_GIT_SERVER_URL
|| process.env.APP_GIT_SERVER_URL
|| process.env.APPGEN_GIT_SERVER_URL;
if (override) return normalizeGitBaseUrl(override);

const sourceUrl = profile.studio_server_url || profile.zeno_server_url || profile.config_url;
try {
const url = new URL(sourceUrl);
const host = url.hostname.toLowerCase();
if (host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local')) {
return 'https://git.dev1.vertesia.io';
}
if (host === 'api.vertesia.io' || host === 'api-preview.vertesia.io') {
return 'https://git.vertesia.io';
}
if (host.startsWith('api-preview.')) {
return `https://${host.replace(/^api-preview\./, 'git.')}`;
}
if (host.startsWith('api.')) {
return `https://${host.replace(/^api\./, 'git.')}`;
}

const parts = host.split('.');
const apiIndex = parts.indexOf('api');
const region = apiIndex >= 0 ? parts[apiIndex + 1] : undefined;
if (region && parts[apiIndex + 2] === 'vertesia') {
return `https://git.${region}.vertesia.io`;
}
} catch {
// Fall through to profile region/default below.
}

if (profile.region) return `https://git.${profile.region}.vertesia.io`;
return 'https://git.dev1.vertesia.io';
}

function normalizeGitBaseUrl(value: string): string {
const normalized = value.replace(/\/+$/, '');
if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
throw new Error(`Git server URL must be an http(s) URL: ${value}`);
}
return normalized;
}

function gitConfig(...args: string[]) {
execFileSync('git', ['config', ...args], { stdio: 'pipe' });
}

function removeKnownVertesiaAliases() {
let raw: string;
try {
raw = execFileSync('git', ['config', '--global', '--get-regexp', '^url\\..*\\.insteadOf$'], {
stdio: ['ignore', 'pipe', 'ignore'],
encoding: 'utf8',
});
} catch {
// No matching keys, or no global config — nothing to clean up.
return;
}
for (const line of raw.split('\n')) {
const space = line.indexOf(' ');
if (space < 0) continue;
const key = line.slice(0, space);
const value = line.slice(space + 1);
if (value !== 'vertesia:') continue;
try {
execFileSync('git', ['config', '--global', '--unset-all', key, '^vertesia:$'], { stdio: 'ignore' });
} catch {
// Best-effort; ignore if the key disappears between read and unset.
}
}
}

function shellQuote(value: string): string {
return `'${value.replace(/'/g, `'\\''`)}'`;
}

async function readCredentialInput(): Promise<GitCredentialInput> {
const text = await readStdin();
const input: Record<string, string> = {};
for (const line of text.split(/\r?\n/)) {
if (!line) break;
const separator = line.indexOf('=');
if (separator < 0) continue;
input[line.slice(0, separator)] = line.slice(separator + 1);
}
return input;
}

function readStdin(): Promise<string> {
return new Promise((resolve, reject) => {
let text = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => {
text += chunk;
});
process.stdin.on('end', () => resolve(text));
process.stdin.on('error', reject);
if (process.stdin.isTTY) {
resolve('');
}
});
}

function writeCredential(token: string) {
process.stdout.write(`username=vertesia\npassword=${token}\n`);
}
31 changes: 2 additions & 29 deletions packages/cli/src/profiles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,11 @@ export type Region = 'us1' | 'eu1' | 'jp1';
export const DEFAULT_REGION: Region = 'us1';
export const AVAILABLE_REGIONS: Region[] = ['us1', 'eu1', 'jp1'];

export type ConfigUrlRef = "local" | "dev-main" | "dev-preview" | "preview" | "prod" | string;
export type ConfigUrlRef = "local" | "preview" | "prod" | string;
export function getConfigUrl(value: ConfigUrlRef, region: Region = DEFAULT_REGION): string {
if (isDevDeploymentTarget(value)) {
return `https://${value}.ui.dev1.vertesia.io/cli`;
}
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":
Expand All @@ -46,28 +39,12 @@ export function getConfigUrl(value: ConfigUrlRef, region: Region = DEFAULT_REGIO
}
}
export function getServerUrls(value: ConfigUrlRef, region: Region = DEFAULT_REGION): { studio_server_url: string; zeno_server_url: string } {
if (isDevDeploymentTarget(value)) {
return {
studio_server_url: `https://studio-server-${value}.api.dev1.vertesia.io`,
zeno_server_url: `https://zeno-server-${value}.api.dev1.vertesia.io`,
};
}
switch (value) {
case "local":
return {
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`,
Expand All @@ -83,12 +60,8 @@ export function getServerUrls(value: ConfigUrlRef, region: Region = DEFAULT_REGI
}
}

function isDevDeploymentTarget(value: string): boolean {
return value.startsWith('dev-');
}

export function getCloudTypeFromConfigUrl(url: string) {
if (url.startsWith("https://localhost")) {
if (url.startsWith("http://localhost") || url.startsWith("https://localhost")) {
return "staging";
} else if (url.includes(".ui.dev1.vertesia.io")) {
return "staging";
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/profiles/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,17 @@ export async function startConfigSession(
console.error("Invalid token");
process.exit(1);
}
} else {
// Empty answer — enquirer 2.x resolves with `result: ''` on Ctrl-C
// rather than rejecting. Without this branch the HTTP server stays
// open and the process hangs.
cleanup();
console.log("\nAuthentication cancelled.");
process.exit(130);
}
} catch (err: unknown) {
// This could be thrown if the prompt is interrupted
cleanup();
if (signal?.aborted) {
return;
}
Expand Down
Loading