@chanl/cli 2.0.4 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/agents.test.js +574 -0
- package/dist/__tests__/agents.test.js.map +1 -0
- package/dist/__tests__/auth.test.d.ts +2 -0
- package/dist/__tests__/auth.test.js +87 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/calls.test.d.ts +2 -0
- package/dist/__tests__/calls.test.js +115 -0
- package/dist/__tests__/calls.test.js.map +1 -0
- package/dist/__tests__/chat.test.d.ts +2 -0
- package/dist/__tests__/chat.test.js +357 -0
- package/dist/__tests__/chat.test.js.map +1 -0
- package/dist/__tests__/cli-base.test.d.ts +2 -0
- package/dist/__tests__/cli-base.test.js +87 -0
- package/dist/__tests__/cli-base.test.js.map +1 -0
- package/dist/__tests__/cli-missing.test.d.ts +2 -0
- package/dist/__tests__/cli-missing.test.js +546 -0
- package/dist/__tests__/cli-missing.test.js.map +1 -0
- package/dist/__tests__/cli-p0-scenarios.test.d.ts +2 -0
- package/dist/__tests__/cli-p0-scenarios.test.js +123 -0
- package/dist/__tests__/cli-p0-scenarios.test.js.map +1 -0
- package/dist/__tests__/helpers.d.ts +234 -0
- package/dist/__tests__/helpers.js +314 -0
- package/dist/__tests__/helpers.js.map +1 -0
- package/dist/__tests__/mcp.test.d.ts +2 -0
- package/dist/__tests__/mcp.test.js +470 -0
- package/dist/__tests__/mcp.test.js.map +1 -0
- package/dist/__tests__/prompts.test.d.ts +2 -0
- package/dist/__tests__/prompts.test.js +227 -0
- package/dist/__tests__/prompts.test.js.map +1 -0
- package/dist/__tests__/scenarios.test.d.ts +2 -0
- package/dist/__tests__/scenarios.test.js +270 -0
- package/dist/__tests__/scenarios.test.js.map +1 -0
- package/dist/__tests__/tools.test.d.ts +2 -0
- package/dist/__tests__/tools.test.js +331 -0
- package/dist/__tests__/tools.test.js.map +1 -0
- package/dist/__tests__/toolsets.test.d.ts +2 -0
- package/dist/__tests__/toolsets.test.js +196 -0
- package/dist/__tests__/toolsets.test.js.map +1 -0
- package/dist/__tests__/workspaces.test.d.ts +2 -0
- package/dist/__tests__/workspaces.test.js +155 -0
- package/dist/__tests__/workspaces.test.js.map +1 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents.js +194 -6
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/chat.js +2 -2
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.js +24 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/customers.d.ts +8 -0
- package/dist/commands/customers.js +289 -0
- package/dist/commands/customers.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/memory.js +134 -1
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/messaging.d.ts +12 -0
- package/dist/commands/messaging.js +208 -0
- package/dist/commands/messaging.js.map +1 -0
- package/dist/commands/prompts.js +148 -1
- package/dist/commands/prompts.js.map +1 -1
- package/dist/commands/scenarios.js +121 -0
- package/dist/commands/scenarios.js.map +1 -1
- package/dist/commands/scorecards.js +3 -3
- package/dist/commands/scorecards.js.map +1 -1
- package/dist/commands/whatsapp.d.ts +8 -0
- package/dist/commands/whatsapp.js +332 -0
- package/dist/commands/whatsapp.js.map +1 -0
- package/dist/utils/config-store.d.ts +8 -0
- package/dist/utils/config-store.js +18 -1
- package/dist/utils/config-store.js.map +1 -1
- package/dist/utils/sdk-factory.js +15 -3
- package/dist/utils/sdk-factory.js.map +1 -1
- package/package.json +4 -4
- package/dist/__tests__/cli.test.js +0 -2308
- package/dist/__tests__/cli.test.js.map +0 -1
- /package/dist/__tests__/{cli.test.d.ts → agents.test.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/cli-missing.test.ts"],"sourcesContent":["/**\n * CLI coverage for commands missing from cli.test.ts\n *\n * cli.test.ts (the 2773-line existing file) covers tools, toolsets, mcp,\n * chat, agents import/publish/push-mcp, scenarios run-all, prompts\n * list/get/delete, workspaces, and a few others. It leaves these\n * commands entirely untested:\n *\n * config, health, memory, knowledge, personas, scorecards\n *\n * This file adds coverage for those 6. Same SDK-mocking pattern as the\n * original file: vi.mock('@chanl/sdk') at module level, mockSdk built\n * per-test, createProgram() + parseAsync to exercise the CLI wiring.\n *\n * What each block covers:\n * config — list / get / set / delete / path / use (all local; no SDK)\n * health — status check (single SDK call through /health)\n * memory — list + get + search + delete (voice-bot-critical commands)\n * knowledge— list + get + search (KB management)\n * personas — list + get + create (scenarios test persona lifecycle)\n * scorecards — list + get (read-only surface)\n */\n\nimport {\n describe,\n it,\n expect,\n beforeEach,\n afterEach,\n vi,\n type Mock,\n} from 'vitest';\nimport { createProgram } from '../cli';\nimport { configStore } from '../utils/config-store';\nimport { ChanlSDK } from '@chanl/sdk';\n\n// Mock the SDK at the module level so every `new ChanlSDK(...)` in CLI\n// commands returns our controllable mockSdk object.\nvi.mock('@chanl/sdk', () => ({\n ChanlSDK: vi.fn(),\n}));\n\n// Mock the config store. Handlers read `getApiKey`, `getBaseUrl`,\n// `getWorkspaceId`, etc. from here in addition to mutating via set/has/\n// delete/getAll. We provide the full surface so config command tests\n// can drive store state.\nvi.mock('../utils/config-store', () => ({\n configStore: {\n // Read accessors (used by sdk-factory + individual commands)\n getApiKey: vi.fn(() => 'test-api-key'),\n getBaseUrl: vi.fn(() => 'https://api.chanl.ai'),\n getWorkspaceId: vi.fn(() => 'ws_123'),\n getJwtToken: vi.fn(() => null),\n getAuthMethod: vi.fn(() => null),\n getAppUrl: vi.fn(() => 'https://app.chanl.ai'),\n getDeployment: vi.fn(() => 'cloud'),\n getApiPrefix: vi.fn(() => '/api/v1'),\n getPath: vi.fn(() => '/home/user/.chanl/config.json'),\n // Mutators\n set: vi.fn(),\n get: vi.fn(),\n has: vi.fn(() => false),\n delete: vi.fn(),\n getAll: vi.fn(() => ({\n apiKey: 'test-api-key',\n baseUrl: 'https://api.chanl.ai',\n workspaceId: 'ws_123',\n })),\n setApiKey: vi.fn(),\n setBaseUrl: vi.fn(),\n setWorkspaceId: vi.fn(),\n removeApiKey: vi.fn(),\n clearAuth: vi.fn(),\n isAuthenticated: vi.fn(() => true),\n },\n}));\n\n// Capture console output across `parseAsync`.\nfunction captureOutput() {\n const logs: string[] = [];\n const errors: string[] = [];\n const origLog = console.log;\n const origErr = console.error;\n console.log = (...args: unknown[]) => {\n logs.push(args.map(String).join(' '));\n };\n console.error = (...args: unknown[]) => {\n errors.push(args.map(String).join(' '));\n };\n return {\n logs,\n errors,\n restore: () => {\n console.log = origLog;\n console.error = origErr;\n },\n };\n}\n\nasync function runCli(args: string[]): Promise<{\n exitCode: number;\n logs: string[];\n errors: string[];\n}> {\n const out = captureOutput();\n const program = createProgram();\n process.exitCode = undefined;\n try {\n await program.parseAsync(['node', 'chanl', ...args]);\n } catch {\n // commander throws on some errors — expected\n }\n out.restore();\n return {\n exitCode: process.exitCode ?? 0,\n logs: out.logs,\n errors: out.errors,\n };\n}\n\n// Shared mockSdk builder — every module we exercise needs to be present\n// on the mocked constructor return value so `sdk.memory.list()` and\n// friends resolve to a spy the test can drive.\nfunction makeMockSdk() {\n return {\n memory: {\n list: vi.fn(),\n get: vi.fn(),\n create: vi.fn(),\n update: vi.fn(),\n delete: vi.fn(),\n search: vi.fn(),\n extract: vi.fn(),\n bulkDelete: vi.fn(),\n },\n knowledge: {\n list: vi.fn(),\n get: vi.fn(),\n create: vi.fn(),\n update: vi.fn(),\n delete: vi.fn(),\n search: vi.fn(),\n getTaskStatus: vi.fn(),\n getChunks: vi.fn(),\n },\n personas: {\n list: vi.fn(),\n get: vi.fn(),\n create: vi.fn(),\n update: vi.fn(),\n delete: vi.fn(),\n },\n scorecard: {\n list: vi.fn(),\n get: vi.fn(),\n create: vi.fn(),\n update: vi.fn(),\n delete: vi.fn(),\n },\n health: {\n getHealth: vi.fn(),\n getDetailedHealth: vi.fn(),\n },\n };\n}\n\ntype MockSdk = ReturnType<typeof makeMockSdk>;\n\ndescribe('CLI (missing commands)', () => {\n let mockSdk: MockSdk;\n\n beforeEach(() => {\n vi.clearAllMocks();\n mockSdk = makeMockSdk();\n (ChanlSDK as unknown as Mock).mockImplementation(() => mockSdk);\n (configStore.getApiKey as Mock).mockReturnValue('test-api-key');\n (configStore.getWorkspaceId as Mock).mockReturnValue('ws_123');\n (configStore.isAuthenticated as Mock).mockReturnValue(true);\n });\n\n afterEach(() => {\n process.exitCode = undefined;\n });\n\n // ─────────────────────────────────────────────────────────────────\n // config — local store ops, no SDK\n // ─────────────────────────────────────────────────────────────────\n\n describe('config list', () => {\n it('reads all values from configStore.getAll', async () => {\n (configStore.getAll as Mock).mockReturnValueOnce({\n apiKey: 'ak_secret',\n baseUrl: 'https://api.chanl.ai',\n workspaceId: 'ws_1',\n });\n\n const { exitCode } = await runCli(['config', 'list']);\n\n expect(exitCode).toBe(0);\n expect(configStore.getAll).toHaveBeenCalled();\n });\n });\n\n describe('config get', () => {\n it('reads a single key via configStore.getApiKey', async () => {\n await runCli(['config', 'get', 'apiKey']);\n expect(configStore.getApiKey).toHaveBeenCalled();\n });\n\n it('exits 1 on an unknown key', async () => {\n const { exitCode, errors } = await runCli([\n 'config',\n 'get',\n 'nonexistentKey',\n ]);\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Unknown configuration key'))).toBe(\n true,\n );\n });\n });\n\n describe('config set', () => {\n it('writes via configStore.set with the provided key + value', async () => {\n const { exitCode } = await runCli([\n 'config',\n 'set',\n 'workspaceId',\n 'ws_new',\n ]);\n expect(exitCode).toBe(0);\n expect(configStore.set).toHaveBeenCalledWith('workspaceId', 'ws_new');\n });\n\n it('rejects invalid baseUrl values', async () => {\n const { exitCode, errors } = await runCli([\n 'config',\n 'set',\n 'baseUrl',\n 'not-a-url',\n ]);\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Invalid URL'))).toBe(true);\n });\n\n it('rejects invalid defaultFormat', async () => {\n const { exitCode, errors } = await runCli([\n 'config',\n 'set',\n 'defaultFormat',\n 'xml',\n ]);\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Invalid format'))).toBe(true);\n });\n\n it('rejects unknown config keys', async () => {\n const { exitCode, errors } = await runCli([\n 'config',\n 'set',\n 'bogus',\n 'anything',\n ]);\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Unknown configuration key'))).toBe(\n true,\n );\n });\n\n it('clears a stale `server` alias when setting baseUrl, so the change actually takes effect', async () => {\n // getBaseUrl() prefers `server` over `baseUrl` — if `server` is left\n // set, `config set baseUrl` would silently do nothing from the\n // caller's perspective. It must clear `server` too.\n (configStore.has as Mock).mockReturnValueOnce(true); // server is set\n\n const { exitCode, logs } = await runCli([\n 'config',\n 'set',\n 'baseUrl',\n 'http://localhost:3100',\n ]);\n\n expect(exitCode).toBe(0);\n expect(configStore.set).toHaveBeenCalledWith('baseUrl', 'http://localhost:3100');\n expect(configStore.delete).toHaveBeenCalledWith('server');\n expect(logs.join('\\n')).toContain(\"cleared 'server'\");\n });\n\n it('does not touch `server` when it was not set', async () => {\n (configStore.has as Mock).mockReturnValueOnce(false); // server not set\n\n const { exitCode } = await runCli([\n 'config',\n 'set',\n 'baseUrl',\n 'http://localhost:3100',\n ]);\n\n expect(exitCode).toBe(0);\n expect(configStore.delete).not.toHaveBeenCalled();\n });\n\n it('does not touch `server` when setting an unrelated key', async () => {\n const { exitCode } = await runCli(['config', 'set', 'workspaceId', 'ws_new']);\n\n expect(exitCode).toBe(0);\n expect(configStore.delete).not.toHaveBeenCalled();\n });\n });\n\n describe('config use', () => {\n it('switches baseUrl to the preset for a known environment', async () => {\n const { exitCode, logs } = await runCli(['config', 'use', 'production']);\n\n expect(exitCode).toBe(0);\n expect(configStore.set).toHaveBeenCalledWith('baseUrl', 'https://platform.chanl.ai');\n expect(logs.join('\\n')).toContain('platform.chanl.ai');\n });\n\n it('clears a stale `server` alias so the switch actually takes effect', async () => {\n (configStore.has as Mock).mockReturnValueOnce(true); // server is set\n\n const { exitCode, logs } = await runCli(['config', 'use', 'production']);\n\n expect(exitCode).toBe(0);\n expect(configStore.delete).toHaveBeenCalledWith('server');\n expect(logs.join('\\n')).toContain(\"cleared 'server'\");\n });\n\n it('rejects an unknown environment', async () => {\n const { exitCode, errors } = await runCli(['config', 'use', 'bogus-env']);\n\n expect(exitCode).toBe(1);\n expect(errors.some((e) => e.includes('Unknown environment'))).toBe(true);\n expect(configStore.set).not.toHaveBeenCalled();\n });\n });\n\n describe('config delete', () => {\n it('returns success silently when key was never set', async () => {\n (configStore.has as Mock).mockReturnValueOnce(false);\n\n const { exitCode } = await runCli(['config', 'delete', 'workspaceId']);\n\n expect(exitCode).toBe(0);\n // delete is NOT called when key isn't set\n expect(configStore.delete).not.toHaveBeenCalled();\n });\n\n it('calls configStore.delete when key exists', async () => {\n (configStore.has as Mock).mockReturnValueOnce(true);\n\n const { exitCode } = await runCli(['config', 'delete', 'workspaceId']);\n\n expect(exitCode).toBe(0);\n expect(configStore.delete).toHaveBeenCalledWith('workspaceId');\n });\n });\n\n // ─────────────────────────────────────────────────────────────────\n // health — single SDK call\n // ─────────────────────────────────────────────────────────────────\n\n describe('health', () => {\n it('calls sdk.health.getHealth() and prints status', async () => {\n mockSdk.health.getHealth.mockResolvedValueOnce({\n success: true,\n data: {\n status: 'ok',\n timestamp: new Date().toISOString(),\n version: '2.0.4',\n uptime: 3600,\n },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode, logs } = await runCli(['health']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.health.getHealth).toHaveBeenCalled();\n const output = logs.join('\\n');\n expect(output).toContain('healthy');\n });\n\n it('exits 1 on network error', async () => {\n mockSdk.health.getHealth.mockRejectedValueOnce(new Error('Connection refused'));\n\n const { exitCode, errors } = await runCli(['health']);\n\n expect(exitCode).toBe(1);\n expect(errors.join('\\n')).toContain('Connection refused');\n });\n\n it('outputs JSON with --json flag', async () => {\n mockSdk.health.getHealth.mockResolvedValueOnce({\n success: true,\n data: {\n status: 'ok',\n timestamp: '2026-04-09T00:00:00Z',\n version: '2.0.4',\n uptime: 100,\n },\n message: 'OK',\n timestamp: '2026-04-09T00:00:00Z',\n });\n\n const { exitCode, logs } = await runCli(['health', '--json']);\n\n expect(exitCode).toBe(0);\n // JSON output should parse\n const parsed = JSON.parse(logs.join(''));\n expect(parsed.status).toBe('ok');\n });\n });\n\n // ─────────────────────────────────────────────────────────────────\n // memory — list / get / search / delete (voice bot critical)\n // ─────────────────────────────────────────────────────────────────\n\n describe('memory list', () => {\n it('calls sdk.memory.list with default pagination', async () => {\n mockSdk.memory.list.mockResolvedValueOnce({\n success: true,\n data: { memories: [], total: 0 },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['memory', 'list']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.memory.list).toHaveBeenCalledWith(\n expect.objectContaining({ page: 1, limit: 20 }),\n );\n });\n\n it('forwards filters from flags', async () => {\n mockSdk.memory.list.mockResolvedValueOnce({\n success: true,\n data: { memories: [], total: 0 },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n await runCli([\n 'memory',\n 'list',\n '--entity-type',\n 'customer',\n '--entity-id',\n 'cust_1',\n '--agent-id',\n 'ag_1',\n '--source',\n 'manual',\n '--limit',\n '50',\n '--page',\n '2',\n ]);\n\n expect(mockSdk.memory.list).toHaveBeenCalledWith(\n expect.objectContaining({\n entityType: 'customer',\n entityId: 'cust_1',\n agentId: 'ag_1',\n source: 'manual',\n limit: 50,\n page: 2,\n }),\n );\n });\n\n it('prints \"No memories found\" on empty result', async () => {\n mockSdk.memory.list.mockResolvedValueOnce({\n success: true,\n data: { memories: [], total: 0 },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { logs } = await runCli(['memory', 'list']);\n\n expect(logs.join('\\n')).toContain('No memories found');\n });\n\n it('exits 1 on SDK error', async () => {\n mockSdk.memory.list.mockRejectedValueOnce(new Error('Forbidden'));\n const { exitCode, errors } = await runCli(['memory', 'list']);\n expect(exitCode).toBe(1);\n expect(errors.join('\\n')).toContain('Forbidden');\n });\n });\n\n describe('memory get', () => {\n it('fetches by ID', async () => {\n mockSdk.memory.get.mockResolvedValueOnce({\n success: true,\n data: {\n id: 'mem_1',\n entityType: 'customer',\n entityId: 'cust_1',\n content: 'Prefers email',\n isActive: true,\n createdAt: '2026-04-09T00:00:00Z',\n },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['memory', 'get', 'mem_1']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.memory.get).toHaveBeenCalledWith('mem_1');\n });\n });\n\n describe('memory search', () => {\n it('requires entity-type, entity-id, and query', async () => {\n mockSdk.memory.search.mockResolvedValueOnce({\n success: true,\n data: {\n memories: [\n {\n id: 'mem_1',\n content: 'prefers email',\n score: 0.92,\n entityType: 'customer',\n entityId: 'cust_1',\n createdAt: '2026-04-09T00:00:00Z',\n },\n ],\n },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli([\n 'memory',\n 'search',\n '--entity-type',\n 'customer',\n '--entity-id',\n 'cust_1',\n '--query',\n 'email preferences',\n '--limit',\n '5',\n ]);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.memory.search).toHaveBeenCalledWith(\n expect.objectContaining({\n entityType: 'customer',\n entityId: 'cust_1',\n query: 'email preferences',\n limit: 5,\n }),\n );\n });\n });\n\n // ─────────────────────────────────────────────────────────────────\n // knowledge — list / get / search\n // ─────────────────────────────────────────────────────────────────\n\n describe('knowledge list', () => {\n it('calls sdk.knowledge.list', async () => {\n mockSdk.knowledge.list.mockResolvedValueOnce({\n success: true,\n data: { items: [], total: 0 },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['knowledge', 'list']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.knowledge.list).toHaveBeenCalled();\n });\n });\n\n describe('knowledge search', () => {\n it('calls sdk.knowledge.search with query body', async () => {\n mockSdk.knowledge.search.mockResolvedValueOnce({\n success: true,\n data: { results: [] },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli([\n 'knowledge',\n 'search',\n 'how to return',\n ]);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.knowledge.search).toHaveBeenCalledWith(\n expect.objectContaining({ query: 'how to return' }),\n );\n });\n });\n\n // ─────────────────────────────────────────────────────────────────\n // personas — list / get\n // ─────────────────────────────────────────────────────────────────\n\n describe('personas list', () => {\n it('calls sdk.personas.list', async () => {\n mockSdk.personas.list.mockResolvedValueOnce({\n success: true,\n data: { personas: [], total: 0 },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['personas', 'list']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.personas.list).toHaveBeenCalled();\n });\n });\n\n describe('personas get', () => {\n it('calls sdk.personas.get with id', async () => {\n mockSdk.personas.get.mockResolvedValueOnce({\n success: true,\n data: {\n id: 'p_1',\n name: 'Frustrated Customer',\n gender: 'female',\n emotion: 'frustrated',\n language: 'english',\n accent: 'american',\n intentClarity: 'very clear',\n speechStyle: 'fast',\n backgroundNoise: false,\n allowInterruptions: true,\n backstory: 'Bad shipping',\n isActive: true,\n createdAt: '2026-04-09T00:00:00Z',\n updatedAt: '2026-04-09T00:00:00Z',\n },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['personas', 'get', 'p_1']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.personas.get).toHaveBeenCalledWith('p_1');\n });\n });\n\n // ─────────────────────────────────────────────────────────────────\n // scorecards — list / get\n // ─────────────────────────────────────────────────────────────────\n\n describe('scorecards list', () => {\n it('calls sdk.scorecard.list', async () => {\n mockSdk.scorecard.list.mockResolvedValueOnce({\n success: true,\n data: { scorecards: [], total: 0 },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['scorecards', 'list']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.scorecard.list).toHaveBeenCalled();\n });\n });\n\n describe('scorecards get', () => {\n it('calls sdk.scorecard.get with id', async () => {\n mockSdk.scorecard.get.mockResolvedValueOnce({\n success: true,\n data: {\n id: 'sc_1',\n name: 'Customer Quality',\n status: 'active',\n },\n message: 'OK',\n timestamp: new Date().toISOString(),\n });\n\n const { exitCode } = await runCli(['scorecards', 'get', 'sc_1']);\n\n expect(exitCode).toBe(0);\n expect(mockSdk.scorecard.get).toHaveBeenCalledWith('sc_1');\n });\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { configStore } from "../utils/config-store";
|
|
3
|
+
import {
|
|
4
|
+
runCli,
|
|
5
|
+
installFetchMock,
|
|
6
|
+
mockFetchResponse,
|
|
7
|
+
setupFetchConfigStore
|
|
8
|
+
} from "./helpers";
|
|
9
|
+
vi.mock("../utils/config-store", () => ({
|
|
10
|
+
configStore: {
|
|
11
|
+
getApiKey: vi.fn(),
|
|
12
|
+
getBaseUrl: vi.fn(),
|
|
13
|
+
getWorkspaceId: vi.fn(),
|
|
14
|
+
getJwtToken: vi.fn(),
|
|
15
|
+
getAuthMethod: vi.fn(() => null),
|
|
16
|
+
getAppUrl: vi.fn(() => "https://app.chanl.ai"),
|
|
17
|
+
getDeployment: vi.fn(),
|
|
18
|
+
getApiPrefix: vi.fn(),
|
|
19
|
+
setApiKey: vi.fn(),
|
|
20
|
+
setBaseUrl: vi.fn(),
|
|
21
|
+
setWorkspaceId: vi.fn(),
|
|
22
|
+
removeApiKey: vi.fn(),
|
|
23
|
+
clearAuth: vi.fn(),
|
|
24
|
+
delete: vi.fn(),
|
|
25
|
+
isAuthenticated: vi.fn(),
|
|
26
|
+
getPath: vi.fn(() => "/home/user/.chanl/config.json")
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
describe("CLI P0 scenario/agent commands", () => {
|
|
30
|
+
let mockFetch;
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
vi.clearAllMocks();
|
|
33
|
+
mockFetch = installFetchMock();
|
|
34
|
+
setupFetchConfigStore(configStore);
|
|
35
|
+
});
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
process.exitCode = void 0;
|
|
38
|
+
});
|
|
39
|
+
it("agents clone fetches source then creates copy", async () => {
|
|
40
|
+
const source = {
|
|
41
|
+
id: "agent_src",
|
|
42
|
+
name: "Source Bot",
|
|
43
|
+
platform: "custom",
|
|
44
|
+
type: "text",
|
|
45
|
+
configuration: { model: { provider: "openai", model: "gpt-4o" } }
|
|
46
|
+
};
|
|
47
|
+
const cloned = { ...source, id: "agent_copy", name: "Source Bot (copy)" };
|
|
48
|
+
mockFetchResponse(mockFetch, { success: true, data: source, message: "OK", timestamp: "" });
|
|
49
|
+
mockFetchResponse(mockFetch, { success: true, data: cloned, message: "OK", timestamp: "" });
|
|
50
|
+
const { logs, exitCode } = await runCli(["agents", "clone", "agent_src"]);
|
|
51
|
+
expect(exitCode).toBe(0);
|
|
52
|
+
expect(logs.join("\n")).toContain("agent_copy");
|
|
53
|
+
});
|
|
54
|
+
it("agents report shows test summary", async () => {
|
|
55
|
+
mockFetchResponse(mockFetch, {
|
|
56
|
+
success: true,
|
|
57
|
+
data: {
|
|
58
|
+
agentId: "agent_1",
|
|
59
|
+
totalRuns: 3,
|
|
60
|
+
passed: 2,
|
|
61
|
+
failed: 1,
|
|
62
|
+
avgScore: 85,
|
|
63
|
+
successRate: 66,
|
|
64
|
+
scenarios: [{
|
|
65
|
+
scenarioId: "scen_1",
|
|
66
|
+
scenarioName: "Greeting",
|
|
67
|
+
runs: 3,
|
|
68
|
+
avgScore: 85,
|
|
69
|
+
lastRun: "2026-03-09T10:00:00.000Z",
|
|
70
|
+
lastPassed: true
|
|
71
|
+
}],
|
|
72
|
+
trend: []
|
|
73
|
+
},
|
|
74
|
+
message: "OK",
|
|
75
|
+
timestamp: ""
|
|
76
|
+
});
|
|
77
|
+
const { logs, exitCode } = await runCli(["agents", "report", "agent_1"]);
|
|
78
|
+
expect(exitCode).toBe(0);
|
|
79
|
+
expect(logs.join("\n")).toContain("Total runs: 3");
|
|
80
|
+
});
|
|
81
|
+
it("scenarios cancel sends DELETE", async () => {
|
|
82
|
+
mockFetchResponse(mockFetch, { success: true, data: null, message: "OK", timestamp: "" });
|
|
83
|
+
const { logs, exitCode } = await runCli(["scenarios", "cancel", "exec_1"]);
|
|
84
|
+
expect(exitCode).toBe(0);
|
|
85
|
+
expect(logs.join("\n").toLowerCase()).toMatch(/cancel/);
|
|
86
|
+
const [url, init] = mockFetch.mock.calls[0];
|
|
87
|
+
expect(url).toContain("/executions/exec_1");
|
|
88
|
+
expect(init?.method).toBe("DELETE");
|
|
89
|
+
});
|
|
90
|
+
it("scenarios retry POSTs retry endpoint", async () => {
|
|
91
|
+
mockFetchResponse(mockFetch, {
|
|
92
|
+
success: true,
|
|
93
|
+
data: { id: "exec_retry", status: "queued" },
|
|
94
|
+
message: "OK",
|
|
95
|
+
timestamp: ""
|
|
96
|
+
});
|
|
97
|
+
const { exitCode } = await runCli(["scenarios", "retry", "exec_fail"]);
|
|
98
|
+
expect(exitCode).toBe(0);
|
|
99
|
+
expect(mockFetch.mock.calls[0][0]).toContain("/retry");
|
|
100
|
+
});
|
|
101
|
+
it("scenarios batch POSTs execute-batch", async () => {
|
|
102
|
+
mockFetchResponse(mockFetch, {
|
|
103
|
+
success: true,
|
|
104
|
+
data: { batchId: "batch_1", status: "running", progress: { total: 2, completed: 0 } },
|
|
105
|
+
message: "OK",
|
|
106
|
+
timestamp: ""
|
|
107
|
+
});
|
|
108
|
+
const { logs, exitCode } = await runCli([
|
|
109
|
+
"scenarios",
|
|
110
|
+
"batch",
|
|
111
|
+
"--agent",
|
|
112
|
+
"agent_1",
|
|
113
|
+
"--scenarios",
|
|
114
|
+
"scen_1,scen_2"
|
|
115
|
+
]);
|
|
116
|
+
expect(exitCode).toBe(0);
|
|
117
|
+
expect(logs.join("\n")).toContain("batch_1");
|
|
118
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
119
|
+
expect(init?.method).toBe("POST");
|
|
120
|
+
expect(init?.body).toContain("scen_1");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=cli-p0-scenarios.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/__tests__/cli-p0-scenarios.test.ts"],"sourcesContent":["/**\n * P0 CLI commands — agents clone/report, scenarios cancel/retry/cross-product/batch\n */\n\nimport { describe, it, expect, beforeEach, afterEach, vi, type Mock } from 'vitest';\nimport { configStore } from '../utils/config-store';\nimport {\n runCli,\n installFetchMock,\n mockFetchResponse,\n setupFetchConfigStore,\n type FetchConfigStoreMock,\n} from './helpers';\n\nvi.mock('../utils/config-store', () => ({\n configStore: {\n getApiKey: vi.fn(),\n getBaseUrl: vi.fn(),\n getWorkspaceId: vi.fn(),\n getJwtToken: vi.fn(),\n getAuthMethod: vi.fn(() => null),\n getAppUrl: vi.fn(() => 'https://app.chanl.ai'),\n getDeployment: vi.fn(),\n getApiPrefix: vi.fn(),\n setApiKey: vi.fn(),\n setBaseUrl: vi.fn(),\n setWorkspaceId: vi.fn(),\n removeApiKey: vi.fn(),\n clearAuth: vi.fn(),\n delete: vi.fn(),\n isAuthenticated: vi.fn(),\n getPath: vi.fn(() => '/home/user/.chanl/config.json'),\n },\n}));\n\ndescribe('CLI P0 scenario/agent commands', () => {\n let mockFetch: Mock;\n\n beforeEach(() => {\n vi.clearAllMocks();\n mockFetch = installFetchMock();\n setupFetchConfigStore(configStore as unknown as FetchConfigStoreMock);\n });\n\n afterEach(() => {\n process.exitCode = undefined;\n });\n\n it('agents clone fetches source then creates copy', async () => {\n const source = {\n id: 'agent_src',\n name: 'Source Bot',\n platform: 'custom',\n type: 'text',\n configuration: { model: { provider: 'openai', model: 'gpt-4o' } },\n };\n const cloned = { ...source, id: 'agent_copy', name: 'Source Bot (copy)' };\n\n mockFetchResponse(mockFetch, { success: true, data: source, message: 'OK', timestamp: '' });\n mockFetchResponse(mockFetch, { success: true, data: cloned, message: 'OK', timestamp: '' });\n\n const { logs, exitCode } = await runCli(['agents', 'clone', 'agent_src']);\n expect(exitCode).toBe(0);\n expect(logs.join('\\n')).toContain('agent_copy');\n });\n\n it('agents report shows test summary', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: {\n agentId: 'agent_1',\n totalRuns: 3,\n passed: 2,\n failed: 1,\n avgScore: 85,\n successRate: 66,\n scenarios: [{\n scenarioId: 'scen_1',\n scenarioName: 'Greeting',\n runs: 3,\n avgScore: 85,\n lastRun: '2026-03-09T10:00:00.000Z',\n lastPassed: true,\n }],\n trend: [],\n },\n message: 'OK',\n timestamp: '',\n });\n\n const { logs, exitCode } = await runCli(['agents', 'report', 'agent_1']);\n expect(exitCode).toBe(0);\n expect(logs.join('\\n')).toContain('Total runs: 3');\n });\n\n it('scenarios cancel sends DELETE', async () => {\n mockFetchResponse(mockFetch, { success: true, data: null, message: 'OK', timestamp: '' });\n\n const { logs, exitCode } = await runCli(['scenarios', 'cancel', 'exec_1']);\n expect(exitCode).toBe(0);\n expect(logs.join('\\n').toLowerCase()).toMatch(/cancel/);\n const [url, init] = mockFetch.mock.calls[0]!;\n expect(url).toContain('/executions/exec_1');\n expect(init?.method).toBe('DELETE');\n });\n\n it('scenarios retry POSTs retry endpoint', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: { id: 'exec_retry', status: 'queued' },\n message: 'OK',\n timestamp: '',\n });\n\n const { exitCode } = await runCli(['scenarios', 'retry', 'exec_fail']);\n expect(exitCode).toBe(0);\n expect(mockFetch.mock.calls[0]![0]).toContain('/retry');\n });\n\n it('scenarios batch POSTs execute-batch', async () => {\n mockFetchResponse(mockFetch, {\n success: true,\n data: { batchId: 'batch_1', status: 'running', progress: { total: 2, completed: 0 } },\n message: 'OK',\n timestamp: '',\n });\n\n const { logs, exitCode } = await runCli([\n 'scenarios', 'batch', '--agent', 'agent_1', '--scenarios', 'scen_1,scen_2',\n ]);\n expect(exitCode).toBe(0);\n expect(logs.join('\\n')).toContain('batch_1');\n const [, init] = mockFetch.mock.calls[0]!;\n expect(init?.method).toBe('POST');\n expect(init?.body).toContain('scen_1');\n });\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { Mock } from 'vitest';
|
|
2
|
+
import { ToolExecution, Tool, Toolset } from '@chanl/sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared CLI test helpers
|
|
6
|
+
*
|
|
7
|
+
* Two parallel mocking strategies are supported:
|
|
8
|
+
*
|
|
9
|
+
* 1. **SDK-level mocking** (legacy, most files) — `makeMockSdk()` +
|
|
10
|
+
* `vi.mock('@chanl/sdk', ...)` replaces the SDK entirely with spies.
|
|
11
|
+
* Fast and simple but the tests never run real SDK code, so they miss
|
|
12
|
+
* contract drift between CLI and SDK.
|
|
13
|
+
*
|
|
14
|
+
* 2. **Fetch-level mocking** (new, P0 files) — `installFetchMock()` +
|
|
15
|
+
* `mockFetchResponse()` stubs only `global.fetch`. The real `ChanlSDK`
|
|
16
|
+
* runs — real URL building, real auth header injection, real response
|
|
17
|
+
* unwrapping, real error mapping. Tests assert on BOTH CLI output AND
|
|
18
|
+
* the HTTP request the SDK built. This catches regressions where a
|
|
19
|
+
* CLI handler passes the wrong argument shape to the SDK or the SDK
|
|
20
|
+
* changes its endpoint path.
|
|
21
|
+
*
|
|
22
|
+
* NOTE: vi.mock('@chanl/sdk') and vi.mock('../utils/config-store') must be
|
|
23
|
+
* declared in EACH test file (vitest hoists vi.mock calls to the top of the
|
|
24
|
+
* file they're defined in — they do not carry across imports).
|
|
25
|
+
*
|
|
26
|
+
* For fetch-level tests, do NOT mock '@chanl/sdk' — let the real SDK run.
|
|
27
|
+
* Only mock '../utils/config-store' and install the fetch mock in beforeEach.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
declare function captureOutput(): {
|
|
31
|
+
logs: string[];
|
|
32
|
+
errors: string[];
|
|
33
|
+
restore: () => void;
|
|
34
|
+
};
|
|
35
|
+
declare function runCli(args: string[]): Promise<{
|
|
36
|
+
exitCode: number;
|
|
37
|
+
logs: string[];
|
|
38
|
+
errors: string[];
|
|
39
|
+
}>;
|
|
40
|
+
type MockSdk = {
|
|
41
|
+
tools: {
|
|
42
|
+
list: Mock;
|
|
43
|
+
get: Mock;
|
|
44
|
+
create: Mock;
|
|
45
|
+
update: Mock;
|
|
46
|
+
delete: Mock;
|
|
47
|
+
execute: Mock;
|
|
48
|
+
test: Mock;
|
|
49
|
+
getExecutions: Mock;
|
|
50
|
+
enable: Mock;
|
|
51
|
+
disable: Mock;
|
|
52
|
+
getCategories: Mock;
|
|
53
|
+
};
|
|
54
|
+
toolsets: {
|
|
55
|
+
list: Mock;
|
|
56
|
+
get: Mock;
|
|
57
|
+
create: Mock;
|
|
58
|
+
update: Mock;
|
|
59
|
+
delete: Mock;
|
|
60
|
+
enable: Mock;
|
|
61
|
+
disable: Mock;
|
|
62
|
+
manageTools: Mock;
|
|
63
|
+
};
|
|
64
|
+
workspace: {
|
|
65
|
+
getAll: Mock;
|
|
66
|
+
getById: Mock;
|
|
67
|
+
};
|
|
68
|
+
mcp: {
|
|
69
|
+
getConfig: Mock;
|
|
70
|
+
getStatus: Mock;
|
|
71
|
+
listTools: Mock;
|
|
72
|
+
callTool: Mock;
|
|
73
|
+
getTool: Mock;
|
|
74
|
+
};
|
|
75
|
+
chat: {
|
|
76
|
+
createSession: Mock;
|
|
77
|
+
sendMessage: Mock;
|
|
78
|
+
getMessages: Mock;
|
|
79
|
+
endSession: Mock;
|
|
80
|
+
deleteSession: Mock;
|
|
81
|
+
session: Mock;
|
|
82
|
+
};
|
|
83
|
+
calls: {
|
|
84
|
+
list: Mock;
|
|
85
|
+
get: Mock;
|
|
86
|
+
getTranscript: Mock;
|
|
87
|
+
getMetrics: Mock;
|
|
88
|
+
getAnalysis: Mock;
|
|
89
|
+
analyze: Mock;
|
|
90
|
+
import: Mock;
|
|
91
|
+
delete: Mock;
|
|
92
|
+
bulkDelete: Mock;
|
|
93
|
+
};
|
|
94
|
+
scenarios: {
|
|
95
|
+
list: Mock;
|
|
96
|
+
get: Mock;
|
|
97
|
+
run: Mock;
|
|
98
|
+
runAll: Mock;
|
|
99
|
+
getExecution: Mock;
|
|
100
|
+
getExecutions: Mock;
|
|
101
|
+
getSimulations: Mock;
|
|
102
|
+
};
|
|
103
|
+
agents: {
|
|
104
|
+
list: Mock;
|
|
105
|
+
get: Mock;
|
|
106
|
+
create: Mock;
|
|
107
|
+
update: Mock;
|
|
108
|
+
delete: Mock;
|
|
109
|
+
sync: Mock;
|
|
110
|
+
syncAll: Mock;
|
|
111
|
+
getStats: Mock;
|
|
112
|
+
importFromPlatform: Mock;
|
|
113
|
+
publish: Mock;
|
|
114
|
+
pushMcp: Mock;
|
|
115
|
+
};
|
|
116
|
+
prompts: {
|
|
117
|
+
list: Mock;
|
|
118
|
+
get: Mock;
|
|
119
|
+
create: Mock;
|
|
120
|
+
update: Mock;
|
|
121
|
+
delete: Mock;
|
|
122
|
+
getVersions: Mock;
|
|
123
|
+
revert: Mock;
|
|
124
|
+
resolve: Mock;
|
|
125
|
+
apply: Mock;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
declare function makeMockSdk(): MockSdk;
|
|
129
|
+
/**
|
|
130
|
+
* Prime the (already-mocked) configStore with default "logged in" values.
|
|
131
|
+
* Each test file must declare `vi.mock('../utils/config-store', ...)` at the
|
|
132
|
+
* top — this helper then sets the common authenticated defaults.
|
|
133
|
+
*/
|
|
134
|
+
declare function setupAuthenticatedConfigStore(configStore: {
|
|
135
|
+
getApiKey: Mock;
|
|
136
|
+
getWorkspaceId: Mock;
|
|
137
|
+
isAuthenticated: Mock;
|
|
138
|
+
}): void;
|
|
139
|
+
declare const mockTool: Tool;
|
|
140
|
+
declare const mockExecution: ToolExecution;
|
|
141
|
+
declare const mockMcpConfig: {
|
|
142
|
+
serverUrl: string;
|
|
143
|
+
environment: "production";
|
|
144
|
+
status: "active";
|
|
145
|
+
toolCount: number;
|
|
146
|
+
resourceCount: number;
|
|
147
|
+
promptCount: number;
|
|
148
|
+
agentConfigs: {
|
|
149
|
+
claude: {
|
|
150
|
+
mcpServers: {
|
|
151
|
+
chanl: {
|
|
152
|
+
url: string;
|
|
153
|
+
headers: {
|
|
154
|
+
'X-API-Key': string;
|
|
155
|
+
};
|
|
156
|
+
transport: string;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
cursor: {
|
|
161
|
+
mcpServers: {
|
|
162
|
+
chanl: {
|
|
163
|
+
url: string;
|
|
164
|
+
headers: {
|
|
165
|
+
'X-API-Key': string;
|
|
166
|
+
};
|
|
167
|
+
transport: string;
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
generic: {
|
|
172
|
+
url: string;
|
|
173
|
+
headers: {
|
|
174
|
+
'X-API-Key': string;
|
|
175
|
+
};
|
|
176
|
+
transport: string;
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
declare const mockMcpStatusHealthy: {
|
|
181
|
+
healthy: boolean;
|
|
182
|
+
message: string;
|
|
183
|
+
responseTimeMs: number;
|
|
184
|
+
};
|
|
185
|
+
declare const mockMcpStatusUnhealthy: {
|
|
186
|
+
healthy: boolean;
|
|
187
|
+
message: string;
|
|
188
|
+
error: string;
|
|
189
|
+
};
|
|
190
|
+
declare const mockToolset: Toolset;
|
|
191
|
+
/** Install a fresh fetch mock on globalThis. Returns the mock for chaining. */
|
|
192
|
+
declare function installFetchMock(): Mock;
|
|
193
|
+
/**
|
|
194
|
+
* Queue a successful JSON response on the fetch mock. Each call queues one
|
|
195
|
+
* response — tests that make N requests need N calls to this in order.
|
|
196
|
+
*/
|
|
197
|
+
declare function mockFetchResponse(mockFetch: Mock, body: unknown, status?: number): void;
|
|
198
|
+
/** Queue a JSON error response. SDK reads error.message / message from body. */
|
|
199
|
+
declare function mockFetchError(mockFetch: Mock, body: unknown, status: number): void;
|
|
200
|
+
/**
|
|
201
|
+
* Assert that the Nth fetch call matched the expected method/path and,
|
|
202
|
+
* optionally, body/headers.
|
|
203
|
+
*/
|
|
204
|
+
declare function expectFetchCalled(mockFetch: Mock, expected: {
|
|
205
|
+
method: string;
|
|
206
|
+
path: string;
|
|
207
|
+
body?: unknown;
|
|
208
|
+
headers?: Record<string, string>;
|
|
209
|
+
}, callIndex?: number): void;
|
|
210
|
+
/** Get the URL string of the Nth fetch call. */
|
|
211
|
+
declare function getFetchUrl(mockFetch: Mock, callIndex?: number): string;
|
|
212
|
+
/** Get the parsed JSON body of the Nth fetch call. */
|
|
213
|
+
declare function getFetchBody(mockFetch: Mock, callIndex?: number): unknown;
|
|
214
|
+
/**
|
|
215
|
+
* Type alias for the subset of configStore mock surface fetch-level tests
|
|
216
|
+
* need. Extends the basic "authenticated" shape with getBaseUrl, which is
|
|
217
|
+
* critical — the real SDK reads baseUrl from here to build full URLs.
|
|
218
|
+
*/
|
|
219
|
+
type FetchConfigStoreMock = {
|
|
220
|
+
getApiKey: Mock;
|
|
221
|
+
getBaseUrl: Mock;
|
|
222
|
+
getWorkspaceId: Mock;
|
|
223
|
+
getJwtToken: Mock;
|
|
224
|
+
getDeployment: Mock;
|
|
225
|
+
getApiPrefix: Mock;
|
|
226
|
+
isAuthenticated: Mock;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Prime a config store mock for fetch-level tests. Sets a test base URL so
|
|
230
|
+
* the real SDK builds URLs that can be string-matched in assertions.
|
|
231
|
+
*/
|
|
232
|
+
declare function setupFetchConfigStore(configStore: FetchConfigStoreMock): void;
|
|
233
|
+
|
|
234
|
+
export { type FetchConfigStoreMock, type MockSdk, captureOutput, expectFetchCalled, getFetchBody, getFetchUrl, installFetchMock, makeMockSdk, mockExecution, mockFetchError, mockFetchResponse, mockMcpConfig, mockMcpStatusHealthy, mockMcpStatusUnhealthy, mockTool, mockToolset, runCli, setupAuthenticatedConfigStore, setupFetchConfigStore };
|