From 5b8b41d777b14396f49e5abf7e42b3520bb290d4 Mon Sep 17 00:00:00 2001 From: Boris Starkov Date: Wed, 22 Oct 2025 18:10:13 +0100 Subject: [PATCH 1/7] make codebase modular --- jest.config.js | 2 +- package.json | 2 - .../add-commands-filename.integration.test.ts | 10 +- src/__tests__/casing.test.ts | 2 +- src/__tests__/config.test.ts | 6 +- .../filename-generation.integration.test.ts | 2 +- src/__tests__/residency.test.ts | 10 +- src/__tests__/utils.test.ts | 2 +- src/{ => agents}/__tests__/widget.test.ts | 2 +- src/agents/commands/add.ts | 128 + src/agents/commands/delete-impl.ts | 166 + src/agents/commands/delete.ts | 40 + src/agents/commands/index.ts | 29 + src/agents/commands/init.ts | 129 + src/agents/commands/list.ts | 28 + src/agents/commands/pull-impl.ts | 262 ++ src/agents/commands/pull.ts | 50 + src/agents/commands/push-impl.ts | 139 + src/agents/commands/push.ts | 79 + src/agents/commands/status-impl.ts | 71 + src/agents/commands/status.ts | 31 + src/agents/commands/templates.ts | 40 + src/agents/commands/test-impl.ts | 126 + src/agents/commands/test.ts | 27 + src/agents/commands/utils.ts | 70 + src/agents/commands/widget-impl.ts | 53 + src/agents/commands/widget.ts | 16 + src/{ => agents}/templates.ts | 0 src/{ui/views => agents/ui}/AddAgentView.tsx | 14 +- src/{ui/views => agents/ui}/InitView.tsx | 10 +- .../views => agents/ui}/ListAgentsView.tsx | 18 +- src/{ui/views => agents/ui}/PullView.tsx | 10 +- src/{ui/views => agents/ui}/PushView.tsx | 8 +- .../views => agents/ui}/StatusDashboard.tsx | 8 +- src/{ui/views => agents/ui}/StatusView.tsx | 10 +- src/{ui/views => agents/ui}/TestView.tsx | 12 +- src/auth/commands/index.ts | 17 + src/auth/commands/login.ts | 76 + src/auth/commands/logout.ts | 38 + src/auth/commands/residency.ts | 54 + src/auth/commands/whoami.ts | 56 + src/{ui/views => auth/ui}/LoginView.tsx | 8 +- src/{ui/views => auth/ui}/LogoutView.tsx | 10 +- src/{ui/views => auth/ui}/ResidencyView.tsx | 8 +- src/{ui/views => auth/ui}/WhoamiView.tsx | 12 +- src/cli.ts | 3199 +---------------- src/components/commands/add.ts | 44 + src/components/commands/index.ts | 11 + src/index.ts | 16 - src/{ => shared}/auth.ts | 0 src/{ => shared}/config.ts | 0 src/{ => shared}/elevenlabs-api.ts | 2 +- src/{ => shared}/tools.ts | 0 src/{ => shared}/utils.ts | 0 src/{ => tests}/__tests__/test-api.test.ts | 2 +- .../test-commands.integration.test.ts | 8 +- .../__tests__/test-templates.test.ts | 2 +- src/tests/commands/add.ts | 33 + src/tests/commands/delete.ts | 38 + src/tests/commands/impl.ts | 601 ++++ src/tests/commands/index.ts | 17 + src/tests/commands/pull.ts | 31 + src/tests/commands/push.ts | 18 + src/{test-templates.ts => tests/templates.ts} | 0 src/{ui/views => tests/ui}/AddTestView.tsx | 14 +- .../__tests__/push-tools.integration.test.ts | 12 +- src/{ => tools}/__tests__/tools.test.ts | 8 +- src/tools/commands/add.ts | 24 + src/tools/commands/delete.ts | 38 + src/tools/commands/impl.ts | 683 ++++ src/tools/commands/index.ts | 17 + src/tools/commands/pull.ts | 50 + src/tools/commands/push.ts | 27 + src/{ui/views => tools/ui}/PullToolsView.tsx | 16 +- src/{ui/views => tools/ui}/PushToolsView.tsx | 8 +- src/ui/views/HelpView.tsx | 199 +- 76 files changed, 3591 insertions(+), 3418 deletions(-) rename src/{ => agents}/__tests__/widget.test.ts (98%) create mode 100644 src/agents/commands/add.ts create mode 100644 src/agents/commands/delete-impl.ts create mode 100644 src/agents/commands/delete.ts create mode 100644 src/agents/commands/index.ts create mode 100644 src/agents/commands/init.ts create mode 100644 src/agents/commands/list.ts create mode 100644 src/agents/commands/pull-impl.ts create mode 100644 src/agents/commands/pull.ts create mode 100644 src/agents/commands/push-impl.ts create mode 100644 src/agents/commands/push.ts create mode 100644 src/agents/commands/status-impl.ts create mode 100644 src/agents/commands/status.ts create mode 100644 src/agents/commands/templates.ts create mode 100644 src/agents/commands/test-impl.ts create mode 100644 src/agents/commands/test.ts create mode 100644 src/agents/commands/utils.ts create mode 100644 src/agents/commands/widget-impl.ts create mode 100644 src/agents/commands/widget.ts rename src/{ => agents}/templates.ts (100%) rename src/{ui/views => agents/ui}/AddAgentView.tsx (94%) rename src/{ui/views => agents/ui}/InitView.tsx (97%) rename src/{ui/views => agents/ui}/ListAgentsView.tsx (90%) rename src/{ui/views => agents/ui}/PullView.tsx (98%) rename src/{ui/views => agents/ui}/PushView.tsx (97%) rename src/{ui/views => agents/ui}/StatusDashboard.tsx (96%) rename src/{ui/views => agents/ui}/StatusView.tsx (96%) rename src/{ui/views => agents/ui}/TestView.tsx (95%) create mode 100644 src/auth/commands/index.ts create mode 100644 src/auth/commands/login.ts create mode 100644 src/auth/commands/logout.ts create mode 100644 src/auth/commands/residency.ts create mode 100644 src/auth/commands/whoami.ts rename src/{ui/views => auth/ui}/LoginView.tsx (95%) rename src/{ui/views => auth/ui}/LogoutView.tsx (93%) rename src/{ui/views => auth/ui}/ResidencyView.tsx (94%) rename src/{ui/views => auth/ui}/WhoamiView.tsx (93%) create mode 100644 src/components/commands/add.ts create mode 100644 src/components/commands/index.ts delete mode 100644 src/index.ts rename src/{ => shared}/auth.ts (100%) rename src/{ => shared}/config.ts (100%) rename src/{ => shared}/elevenlabs-api.ts (99%) rename src/{ => shared}/tools.ts (100%) rename src/{ => shared}/utils.ts (100%) rename src/{ => tests}/__tests__/test-api.test.ts (99%) rename src/{ => tests}/__tests__/test-commands.integration.test.ts (97%) rename src/{ => tests}/__tests__/test-templates.test.ts (99%) create mode 100644 src/tests/commands/add.ts create mode 100644 src/tests/commands/delete.ts create mode 100644 src/tests/commands/impl.ts create mode 100644 src/tests/commands/index.ts create mode 100644 src/tests/commands/pull.ts create mode 100644 src/tests/commands/push.ts rename src/{test-templates.ts => tests/templates.ts} (100%) rename src/{ui/views => tests/ui}/AddTestView.tsx (97%) rename src/{ => tools}/__tests__/push-tools.integration.test.ts (96%) rename src/{ => tools}/__tests__/tools.test.ts (99%) create mode 100644 src/tools/commands/add.ts create mode 100644 src/tools/commands/delete.ts create mode 100644 src/tools/commands/impl.ts create mode 100644 src/tools/commands/index.ts create mode 100644 src/tools/commands/pull.ts create mode 100644 src/tools/commands/push.ts rename src/{ui/views => tools/ui}/PullToolsView.tsx (97%) rename src/{ui/views => tools/ui}/PushToolsView.tsx (97%) diff --git a/jest.config.js b/jest.config.js index b09dfab..24a8f53 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,7 +4,7 @@ export default { extensionsToTreatAsEsm: ['.ts'], testEnvironment: '/jest-environment.cjs', roots: ['/src'], - testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], + testMatch: ['**/__tests__/**/*.ts', '**/*.spec.ts', '**/*.test.ts'], collectCoverageFrom: [ 'src/**/*.ts', '!src/**/*.d.ts', diff --git a/package.json b/package.json index 1da4b10..6e3bc01 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,6 @@ "bugs": { "url": "https://github.com/elevenlabs/cli/issues" }, - "main": "dist/index.js", - "types": "dist/index.d.ts", "bin": { "elevenlabs": "./bin/elevenlabs" }, diff --git a/src/__tests__/add-commands-filename.integration.test.ts b/src/__tests__/add-commands-filename.integration.test.ts index 0ba5d09..4f610ac 100644 --- a/src/__tests__/add-commands-filename.integration.test.ts +++ b/src/__tests__/add-commands-filename.integration.test.ts @@ -24,7 +24,7 @@ const mockCreateToolApi = jest.fn(); const mockCreateTestApi = jest.fn(); const mockGetElevenLabsClient = jest.fn(); -jest.mock("../elevenlabs-api", () => ({ +jest.mock("../shared/elevenlabs-api", () => ({ getElevenLabsClient: mockGetElevenLabsClient, createAgentApi: mockCreateAgentApi, createToolApi: mockCreateToolApi, @@ -32,10 +32,10 @@ jest.mock("../elevenlabs-api", () => ({ })); // Import after mocking -import { readConfig, writeConfig, generateUniqueFilename, toCamelCaseKeys } from "../utils"; -import { getTemplateByName } from "../templates"; -import { createAgentApi } from "../elevenlabs-api"; -import { getBasicLLMTestTemplate } from "../test-templates"; +import { readConfig, writeConfig, generateUniqueFilename, toCamelCaseKeys } from "../shared/utils"; +import { getTemplateByName } from "../agents/templates"; +import { createAgentApi } from "../shared/elevenlabs-api"; +import { getBasicLLMTestTemplate } from "../tests/templates"; describe("Add Commands - Name-based Filenames", () => { let tempDir: string; diff --git a/src/__tests__/casing.test.ts b/src/__tests__/casing.test.ts index 2c87317..0e3b00d 100644 --- a/src/__tests__/casing.test.ts +++ b/src/__tests__/casing.test.ts @@ -1,4 +1,4 @@ -import { createAgentApi, updateAgentApi, getAgentApi } from "../elevenlabs-api"; +import { createAgentApi, updateAgentApi, getAgentApi } from "../shared/elevenlabs-api"; import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js"; describe("Key casing normalization", () => { diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index eb8bd2b..fedd9f1 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -14,7 +14,7 @@ import { isLoggedIn, getResidency, setResidency, -} from "../config"; +} from "../shared/config"; import { describe, it, @@ -31,7 +31,7 @@ jest.mock("os", () => ({ })); // Mock auth module for better isolation -jest.mock("../auth", () => { +jest.mock("../shared/auth", () => { let storedApiKey: string | undefined; const resetStoredApiKey = () => { storedApiKey = undefined; @@ -74,7 +74,7 @@ describe("Config Management", () => { // Mock os.homedir to return our temp directory mockedOs.homedir.mockReturnValue(tempDir); // Reset mock state - const authMock = jest.mocked(await import("../auth.js")); + const authMock = jest.mocked(await import("../shared/auth.js")); if ( "__resetStoredApiKey" in authMock && typeof authMock.__resetStoredApiKey === "function" diff --git a/src/__tests__/filename-generation.integration.test.ts b/src/__tests__/filename-generation.integration.test.ts index b6ea67a..59ffb37 100644 --- a/src/__tests__/filename-generation.integration.test.ts +++ b/src/__tests__/filename-generation.integration.test.ts @@ -1,4 +1,4 @@ -import { generateUniqueFilename } from "../utils"; +import { generateUniqueFilename } from "../shared/utils"; import fs from "fs-extra"; import path from "path"; import os from "os"; diff --git a/src/__tests__/residency.test.ts b/src/__tests__/residency.test.ts index 09ee813..63ad01f 100644 --- a/src/__tests__/residency.test.ts +++ b/src/__tests__/residency.test.ts @@ -5,8 +5,8 @@ import * as fs from "fs-extra"; import * as path from "path"; import * as os from "os"; -import { getElevenLabsClient } from "../elevenlabs-api"; -import { setResidency, setApiKey } from "../config"; +import { getElevenLabsClient } from "../shared/elevenlabs-api"; +import { setResidency, setApiKey } from "../shared/config"; import { describe, it, @@ -34,7 +34,7 @@ jest.mock("os", () => ({ })); // Mock auth module for better isolation -jest.mock("../auth", () => { +jest.mock("../shared/auth", () => { let storedApiKey: string | undefined; return { storeApiKey: jest.fn().mockImplementation((key: unknown) => { @@ -136,7 +136,7 @@ describe("Residency-specific API Client", () => { delete process.env.ELEVENLABS_API_KEY; // Clear the stored API key from the mocked auth module - const { removeApiKey } = await import("../config"); + const { removeApiKey } = await import("../shared/config"); await removeApiKey(); // Clear the temp directory to remove any stored config @@ -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 login --env prod' to authenticate or set ELEVENLABS_API_KEY environment variable." + "No API key found for environment 'prod'. Use 'elevenlabs auth login --env prod' to authenticate or set ELEVENLABS_API_KEY environment variable." ); }); }); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index afc5ede..05979b0 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,4 +1,4 @@ -import { calculateConfigHash, toCamelCaseKeys, generateUniqueFilename } from "../utils"; +import { calculateConfigHash, toCamelCaseKeys, generateUniqueFilename } from "../shared/utils"; import fs from "fs-extra"; import path from "path"; import os from "os"; diff --git a/src/__tests__/widget.test.ts b/src/agents/__tests__/widget.test.ts similarity index 98% rename from src/__tests__/widget.test.ts rename to src/agents/__tests__/widget.test.ts index 5bc1d7b..5dee215 100644 --- a/src/__tests__/widget.test.ts +++ b/src/agents/__tests__/widget.test.ts @@ -2,7 +2,7 @@ * Tests for widget generation logic with residency support */ -import { getResidency, setResidency } from "../config"; +import { getResidency, setResidency } from "../../shared/config"; import { describe, it, diff --git a/src/agents/commands/add.ts b/src/agents/commands/add.ts new file mode 100644 index 0000000..48e56b3 --- /dev/null +++ b/src/agents/commands/add.ts @@ -0,0 +1,128 @@ +import { Command } from 'commander'; +import { render } from 'ink'; +import React from 'react'; +import path from 'path'; +import fs from 'fs-extra'; +import AddAgentView from '../ui/AddAgentView.js'; +import { readConfig, writeConfig, generateUniqueFilename } from '../../shared/utils.js'; +import { getTemplateByName, AgentConfig } from '../templates.js'; +import { getElevenLabsClient, createAgentApi } from '../../shared/elevenlabs-api.js'; + +const AGENTS_CONFIG_FILE = "agents.json"; + +interface AgentsConfig { + agents: Array<{ + config: string; + id?: string; + env?: string; + }>; +} + +interface AgentDefinition { + config: string; + id?: string; + env?: string; +} + +interface AddOptions { + configPath?: string; + template: string; + env: string; +} + +export function createAddCommand(): Command { + return new Command('add') + .description('Add a new agent - creates config, uploads to ElevenLabs, and saves ID') + .argument('', 'Name of the agent to create') + .option('--config-path ', 'Custom config path (optional)') + .option('--template