Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 0 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@
# Primary test API key (required for all e2e tests)
ELEVENLABS_API_KEY=your_test_account_api_key_here

# Secondary test API key (optional - only needed for multi-environment e2e tests)
# Multi-environment tests verify the CLI can handle multiple environments (prod, staging, dev, etc.)
# If not provided, multi-environment tests will be skipped
ELEVENLABS_TEST_API_KEY=your_second_test_account_api_key_here

# For local development and testing:
# 1. Copy this file to .env (DO NOT COMMIT .env)
# cp .env.example .env
Expand Down
38 changes: 6 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,6 @@ elevenlabs auth residency global # or 'us'
elevenlabs auth logout
```

### Multi-Environment Management

The ElevenLabs CLI supports managing agents, tools, and tests across multiple isolated environments (e.g., dev, staging, prod).

#### Login to Multiple Environments

```bash
# Login with your API key
elevenlabs auth login
```

#### Check Authentication Status

```bash
elevenlabs auth whoami
# Shows your authentication status
```

#### Logout

```bash
elevenlabs auth logout
```

## Quick Start

```bash
Expand All @@ -122,15 +98,13 @@ elevenlabs agents add "Support Bot" --template customer-service
elevenlabs agents push
```

> **Note**: This example uses the default 'prod' environment. For multi-environment workflows, see [Multi-Environment Management](#multi-environment-management) and [Multi-Environment Workflows](#multi-environment-workflows).

## Directory Structure

```
your_project/
├── agents.json # Central configuration with env field per agent
├── tools.json # Tool configurations with env field per tool
├── tests.json # Test configurations with env field per test
├── agents.json # Central configuration
├── tools.json # Tool configurations
├── tests.json # Test configurations
├── agent_configs/ # Agent configuration files
├── tool_configs/ # Tool configurations
└── test_configs/ # Test configurations
Expand Down Expand Up @@ -337,13 +311,13 @@ elevenlabs agents delete agent_123456789
## Workflow Examples

```bash
# List all agents across all environments
# List all agents
elevenlabs agents list

# Push all agents to their respective environments
# Push all agents
elevenlabs agents push

# Pull agents from all configured environments
# Pull agents
elevenlabs agents pull
```

Expand Down
20 changes: 11 additions & 9 deletions src/__tests__/cli.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ describe("CLI End-to-End Tests", () => {

// Login
const apiKey = process.env.ELEVENLABS_API_KEY!;
await runCli(["login", "--no-ui"], {
await runCli(["auth", "login", "--no-ui"], {
cwd: pushPullTempDir,
input: `${apiKey}\n`,
includeApiKey: true,
Expand Down Expand Up @@ -510,7 +510,7 @@ describe("CLI End-to-End Tests", () => {

// Delete all agents at once
try {
await runCli(["delete", "--all", "--no-ui"], {
await runCli(["agents", "delete", "--all", "--no-ui"], {
cwd: pushPullTempDir,
includeApiKey: true,
input: "y\n", // Answer the "Are you sure?" prompt
Expand Down Expand Up @@ -591,7 +591,7 @@ describe("CLI End-to-End Tests", () => {
console.log(`✓ Verified agent '${agentName}' is the only agent after pull`);

// Clean up: delete the agent
await runCli(["delete", createdAgent.id, "--no-ui"], {
await runCli(["agents", "delete", createdAgent.id, "--no-ui"], {
cwd: pushPullTempDir,
includeApiKey: true,
});
Expand Down Expand Up @@ -717,6 +717,7 @@ describe("CLI End-to-End Tests", () => {

for (const agentName of agentNames) {
const addResult = await runCli([
"agents",
"add",
agentName,
"--template",
Expand Down Expand Up @@ -1322,7 +1323,7 @@ describe("CLI End-to-End Tests", () => {

// Login
const apiKey = process.env.ELEVENLABS_API_KEY!;
await runCli(["login", "--no-ui"], {
await runCli(["auth", "login", "--no-ui"], {
cwd: pushPullTempDir,
input: `${apiKey}\n`,
includeApiKey: true,
Expand Down Expand Up @@ -1351,7 +1352,7 @@ describe("CLI End-to-End Tests", () => {

// Delete all tests at once
try {
await runCli(["delete-test", "--all", "--no-ui"], {
await runCli(["tests", "delete", "--all", "--no-ui"], {
cwd: pushPullTempDir,
includeApiKey: true,
input: "y\n", // Answer the "Are you sure?" prompt
Expand Down Expand Up @@ -1420,7 +1421,7 @@ describe("CLI End-to-End Tests", () => {
console.log(`✓ Verified test '${testName}' is the only test after pull`);

// Clean up: delete the test
await runCli(["delete-test", createdTest.id, "--no-ui"], {
await runCli(["tests", "delete", createdTest.id, "--no-ui"], {
cwd: pushPullTempDir,
includeApiKey: true,
});
Expand Down Expand Up @@ -1547,7 +1548,8 @@ describe("CLI End-to-End Tests", () => {

for (const testName of testNames) {
const addResult = await runCli([
"add-test",
"tests",
"add",
testName,
"--template",
"basic-llm",
Expand Down Expand Up @@ -1943,7 +1945,7 @@ describe("CLI End-to-End Tests", () => {

// Login
const apiKey = process.env.ELEVENLABS_API_KEY!;
await runCli(["login", "--no-ui"], {
await runCli(["auth", "login", "--no-ui"], {
cwd: pushPullTempDir,
input: `${apiKey}\n`,
includeApiKey: true,
Expand Down Expand Up @@ -1972,7 +1974,7 @@ describe("CLI End-to-End Tests", () => {

// Delete all tools at once
try {
await runCli(["delete-tool", "--all", "--no-ui"], {
await runCli(["tools", "delete", "--all", "--no-ui"], {
cwd: pushPullTempDir,
includeApiKey: true,
input: "y\n", // Answer the "Are you sure?" prompt
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/residency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ describe("Residency-specific API Client", () => {
mockedOs.homedir.mockReturnValue(tempDir);

await expect(getElevenLabsClient()).rejects.toThrow(
"No API key found for environment 'prod'. Use 'elevenlabs auth login' to authenticate or set ELEVENLABS_API_KEY environment variable."
"No API key found. Use 'elevenlabs auth login' to authenticate or set ELEVENLABS_API_KEY environment variable."
);
});
});
5 changes: 2 additions & 3 deletions src/agents/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,9 @@ export function createAddCommand(): Command {
}

// Create agent in ElevenLabs first to get ID
const environment = 'prod';
console.log(`Creating agent '${name}' in ElevenLabs (environment: ${environment})...`);
console.log(`Creating agent '${name}' in ElevenLabs...`);

const client = await getElevenLabsClient(environment);
const client = await getElevenLabsClient();

// Extract config components
const conversationConfig = agentConfig.conversation_config || {};
Expand Down
6 changes: 2 additions & 4 deletions src/agents/commands/delete-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@ export async function deleteAgent(agentId: string): Promise<void> {
const agentDef = agentsConfig.agents[agentIndex];
const agentName = await getAgentName(agentDef.config);
const configPath = agentDef.config;
const environment = 'prod';

console.log(`Deleting agent '${agentName}' (ID: ${agentId})...`);

// Delete from ElevenLabs (globally)
console.log('Deleting from ElevenLabs...');
const client = await getElevenLabsClient(environment);
const client = await getElevenLabsClient();

try {
await deleteAgentApi(client, agentId);
Expand Down Expand Up @@ -109,13 +108,12 @@ export async function deleteAllAgents(ui: boolean = true): Promise<void> {
for (const agentDef of agentsToDelete) {
try {
const agentName = await getAgentName(agentDef.config);
const environment = 'prod';
console.log(`Deleting '${agentName}' (${agentDef.id})...`);

// Delete from ElevenLabs
if (agentDef.id) {
try {
const client = await getElevenLabsClient(environment);
const client = await getElevenLabsClient();
await deleteAgentApi(client, agentDef.id);
console.log(` ✓ Deleted from ElevenLabs`);
} catch (error) {
Expand Down
12 changes: 5 additions & 7 deletions src/agents/commands/pull-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import fs from 'fs-extra';
import { readConfig, writeConfig, generateUniqueFilename } from '../../shared/utils.js';
import { getElevenLabsClient, listAgentsApi, getAgentApi } from '../../shared/elevenlabs-api.js';
import { AgentConfig } from '../templates.js';
import { listEnvironments } from '../../shared/config.js';
import { promptForConfirmation } from './utils.js';

const AGENTS_CONFIG_FILE = "agents.json";
Expand All @@ -27,15 +26,14 @@ interface PullOptions {

export async function pullAgents(options: PullOptions): Promise<void> {
const agentsConfigPath = path.resolve(AGENTS_CONFIG_FILE);
const environment = 'prod';

console.log(`Pulling from environment: ${environment}`);
console.log(`Pulling agents from ElevenLabs...`);

await pullAgentsFromEnvironment(options, environment, agentsConfigPath);
await pullAgentsFromEnvironment(options, agentsConfigPath);
}

async function pullAgentsFromEnvironment(options: PullOptions, environment: string, agentsConfigPath: string): Promise<void> {
const client = await getElevenLabsClient(environment);
async function pullAgentsFromEnvironment(options: PullOptions, agentsConfigPath: string): Promise<void> {
const client = await getElevenLabsClient();

// Load existing config
let agentsConfig: AgentsConfig;
Expand Down Expand Up @@ -211,7 +209,7 @@ async function pullAgentsFromEnvironment(options: PullOptions, environment: stri
};

agentsConfig.agents.push(newAgent);
console.log(` ✓ Added '${agent.name}' (config: ${configPath}) [${environment}]`);
console.log(` ✓ Added '${agent.name}' (config: ${configPath})`);
}

itemsProcessed++;
Expand Down
4 changes: 1 addition & 3 deletions src/agents/commands/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ export function createPullCommand(): Command {
try {
if (options.ui !== false) {
// Use Ink UI for pull
const environments = ['prod'];
const { waitUntilExit } = render(
React.createElement(PullView, {
agent: options.agent,
outputDir: options.outputDir,
dryRun: options.dryRun,
update: options.update,
all: options.all,
environments
all: options.all
})
);
await waitUntilExit();
Expand Down
17 changes: 8 additions & 9 deletions src/agents/commands/push-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ export async function pushAgents(dryRun: boolean = false): Promise<void> {
const agentsConfig = await readConfig<AgentsConfig>(agentsConfigPath);

const agentsToProcess = agentsConfig.agents;
const environment = 'prod';

console.log(`Pushing ${agentsToProcess.length} agent(s) to environment: ${environment}`);
console.log(`Pushing ${agentsToProcess.length} agent(s) to ElevenLabs...`);

let changesMade = false;

Expand Down Expand Up @@ -60,20 +59,20 @@ export async function pushAgents(dryRun: boolean = false): Promise<void> {
const agentId = agentDef.id;

// Always push (force override)
console.log(`${agentDefName} [${environment}]: Will push (force override)`);
console.log(`${agentDefName}: Will push (force override)`);

if (dryRun) {
console.log(`[DRY RUN] Would update agent: ${agentDefName} [${environment}]`);
console.log(`[DRY RUN] Would update agent: ${agentDefName}`);
continue;
}

// Initialize ElevenLabs client for this agent's environment
// Initialize ElevenLabs client
let client;
try {
client = await getElevenLabsClient(environment);
client = await getElevenLabsClient();
} catch (error) {
console.log(`Error: ${error}`);
console.log(`Skipping agent ${agentDefName} - environment '${environment}' not configured`);
console.log(`Skipping agent ${agentDefName} - not configured`);
continue;
}

Expand All @@ -95,7 +94,7 @@ export async function pushAgents(dryRun: boolean = false): Promise<void> {
platformSettings,
tags
);
console.log(`Created agent ${agentDefName} (ID: ${newAgentId}) [${environment}]`);
console.log(`Created agent ${agentDefName} (ID: ${newAgentId})`);

// Store agent ID in index file
agentDef.id = newAgentId;
Expand All @@ -110,7 +109,7 @@ export async function pushAgents(dryRun: boolean = false): Promise<void> {
platformSettings,
tags
);
console.log(`Updated agent ${agentDefName} (ID: ${agentId}) [${environment}]`);
console.log(`Updated agent ${agentDefName} (ID: ${agentId})`);
}

changesMade = true;
Expand Down
7 changes: 2 additions & 5 deletions src/agents/commands/test-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,11 @@ export async function runAgentTests(agentId: string): Promise<void> {

const testIds = attachedTests.map(test => test.test_id);

// Get agent environment
const environment = 'prod';

console.log(`Running ${testIds.length} test(s) for agent '${agentName}' [${environment}]...`);
console.log(`Running ${testIds.length} test(s) for agent '${agentName}'...`);
console.log('');

// Run tests without UI
const client = await getElevenLabsClient(environment);
const client = await getElevenLabsClient();

try {
// Import the API functions we need
Expand Down
2 changes: 1 addition & 1 deletion src/agents/ui/AddAgentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const AddAgentView: React.FC<AddAgentViewProps> = ({

// Step 2: Upload to ElevenLabs first to get ID
setStatusMessage('Creating agent in ElevenLabs...');
const client = await getElevenLabsClient('prod');
const client = await getElevenLabsClient();
const conversationConfig = agentConfig.conversation_config || {};
const platformSettings = agentConfig.platform_settings;
const tags = agentConfig.tags || [];
Expand Down
Loading
Loading