diff --git a/integ-tests/import.test.ts b/integ-tests/import.test.ts new file mode 100644 index 000000000..f01676c79 --- /dev/null +++ b/integ-tests/import.test.ts @@ -0,0 +1,111 @@ +import { createTelemetryHelper, runCLI } from '../src/test-utils/index.js'; +import { randomUUID } from 'node:crypto'; +import { mkdir, rm } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; + +describe('import command', () => { + let testDir: string; + let projectDir: string; + const telemetry = createTelemetryHelper(); + + beforeAll(async () => { + testDir = join(tmpdir(), `agentcore-import-telemetry-${randomUUID()}`); + await mkdir(testDir, { recursive: true }); + + const projectName = 'ImportTelemetryProj'; + const result = await runCLI(['create', '--name', projectName, '--no-agent'], testDir); + if (result.exitCode !== 0) { + throw new Error(`Failed to create project: ${result.stdout} ${result.stderr}`); + } + projectDir = join(testDir, projectName); + }); + + afterEach(() => { + telemetry.clearEntries(); + }); + + afterAll(async () => { + telemetry.destroy(); + await rm(testDir, { recursive: true, force: true }); + }); + + describe('import --source', () => { + it('emits failure telemetry for nonexistent source file', async () => { + const result = await runCLI(['import', '--source', '/nonexistent/file.yaml'], projectDir, { + env: telemetry.env, + }); + expect(result.exitCode).toBe(1); + telemetry.assertMetricEmitted({ + command: 'import', + exit_reason: 'failure', + }); + }); + }); + + describe('import runtime', () => { + it('emits failure telemetry for invalid ARN', async () => { + const result = await runCLI(['import', 'runtime', '--arn', 'invalid-arn'], projectDir, { + env: telemetry.env, + }); + expect(result.exitCode).toBe(1); + telemetry.assertMetricEmitted({ + command: 'import.runtime', + exit_reason: 'failure', + }); + }); + }); + + describe('import memory', () => { + it('emits failure telemetry for invalid ARN', async () => { + const result = await runCLI(['import', 'memory', '--arn', 'invalid-arn'], projectDir, { + env: telemetry.env, + }); + expect(result.exitCode).toBe(1); + telemetry.assertMetricEmitted({ + command: 'import.memory', + exit_reason: 'failure', + }); + }); + }); + + describe('import evaluator', () => { + it('emits failure telemetry for invalid ARN', async () => { + const result = await runCLI(['import', 'evaluator', '--arn', 'invalid-arn'], projectDir, { + env: telemetry.env, + }); + expect(result.exitCode).toBe(1); + telemetry.assertMetricEmitted({ + command: 'import.evaluator', + exit_reason: 'failure', + }); + }); + }); + + describe('import gateway', () => { + it('emits failure telemetry for invalid ARN', async () => { + const result = await runCLI(['import', 'gateway', '--arn', 'invalid-arn'], projectDir, { + env: telemetry.env, + }); + expect(result.exitCode).toBe(1); + telemetry.assertMetricEmitted({ + command: 'import.gateway', + exit_reason: 'failure', + }); + }); + }); + + describe('import online-eval', () => { + it('emits failure telemetry for invalid ARN', async () => { + const result = await runCLI(['import', 'online-eval', '--arn', 'invalid-arn'], projectDir, { + env: telemetry.env, + }); + expect(result.exitCode).toBe(1); + telemetry.assertMetricEmitted({ + command: 'import.online-eval', + exit_reason: 'failure', + }); + }); + }); +}); diff --git a/src/cli/commands/import/__tests__/import-gateway-flow.test.ts b/src/cli/commands/import/__tests__/import-gateway-flow.test.ts index 34803e8eb..153ead77a 100644 --- a/src/cli/commands/import/__tests__/import-gateway-flow.test.ts +++ b/src/cli/commands/import/__tests__/import-gateway-flow.test.ts @@ -63,6 +63,13 @@ vi.mock('../../../../lib', () => ({ this.name = 'NoProjectError'; } }, + ValidationError: class ValidationError extends Error { + constructor(msg: string) { + super(msg); + this.name = 'ValidationError'; + } + }, + toError: (err: unknown) => (err instanceof Error ? err : new Error(String(err))), findConfigRoot: (...args: unknown[]) => mockFindConfigRoot(...args), })); diff --git a/src/cli/commands/import/actions.ts b/src/cli/commands/import/actions.ts index bceefa711..990fe006f 100644 --- a/src/cli/commands/import/actions.ts +++ b/src/cli/commands/import/actions.ts @@ -200,7 +200,7 @@ export async function handleImport(options: ImportOptions): Promise { return; } - // Validate source file exists - if (!fs.existsSync(cliOptions.source)) { - console.error(`\x1b[31m[error]${reset} Source file not found: ${cliOptions.source}`); - process.exit(1); - } - const warnings: string[] = []; - const result = await handleImport({ - source: cliOptions.source, - target: cliOptions.target, - yes: cliOptions.yes, - onProgress: (message: string) => { - // Collect warnings for end-of-output display - if (message.includes('Warning') || message.includes('\x1b[33m')) { - warnings.push(message); - return; - } - - // Skipped items shown dimmed - if (message.startsWith('Skipping')) { - console.log(`${dim}[skip]${reset} ${message}`); - return; - } + const result = await withCommandRunTelemetry('import', {}, async () => { + if (!fs.existsSync(cliOptions.source!)) { + return { success: false as const, error: new ValidationError(`Source file not found: ${cliOptions.source}`) }; + } - // Normal progress steps shown as [done] - console.log(`${green}[done]${reset} ${message}`); - }, + const importResult = await handleImport({ + source: cliOptions.source!, + target: cliOptions.target, + yes: cliOptions.yes, + onProgress: (message: string) => { + // Collect warnings for end-of-output display + if (message.includes('Warning') || message.includes('\x1b[33m')) { + warnings.push(message); + return; + } + + // Skipped items shown dimmed + if (message.startsWith('Skipping')) { + console.log(`${dim}[skip]${reset} ${message}`); + return; + } + + // Normal progress steps shown as [done] + console.log(`${green}[done]${reset} ${message}`); + }, + }); + + return importResult; }); if (result.success) { diff --git a/src/cli/commands/import/import-evaluator.ts b/src/cli/commands/import/import-evaluator.ts index 914a50dfa..be3450767 100644 --- a/src/cli/commands/import/import-evaluator.ts +++ b/src/cli/commands/import/import-evaluator.ts @@ -6,6 +6,7 @@ import { listAllEvaluators, listAllOnlineEvaluationConfigs, } from '../../aws/agentcore-control'; +import { withCommandRunTelemetry } from '../../telemetry/cli-command-run.js'; import { ANSI } from './constants'; import { failResult, parseAndValidateArn } from './import-utils'; import { executeResourceImport } from './resource-import'; @@ -143,7 +144,7 @@ export function registerImportEvaluator(importCmd: Command): void { .option('--name ', 'Local name for the imported evaluator') .option('-y, --yes', 'Auto-confirm prompts') .action(async (cliOptions: ImportResourceOptions) => { - const result = await handleImportEvaluator(cliOptions); + const result = await withCommandRunTelemetry('import.evaluator', {}, () => handleImportEvaluator(cliOptions)); if (result.success) { console.log(''); diff --git a/src/cli/commands/import/import-gateway.ts b/src/cli/commands/import/import-gateway.ts index 5a7822210..6f9ec53ab 100644 --- a/src/cli/commands/import/import-gateway.ts +++ b/src/cli/commands/import/import-gateway.ts @@ -19,6 +19,7 @@ import { listAllGateways, } from '../../aws/agentcore-control'; import { isAccessDeniedError } from '../../errors'; +import { withCommandRunTelemetry } from '../../telemetry/cli-command-run.js'; import { ANSI } from './constants'; import { executeCdkImportPipeline } from './import-pipeline'; import { @@ -667,7 +668,7 @@ export function registerImportGateway(importCmd: Command): void { .description('Import an existing AgentCore Gateway (with targets) from your AWS account') .option('--arn ', 'Gateway ARN to import') .action(async (cliOptions: ImportResourceOptions) => { - const result = await handleImportGateway(cliOptions); + const result = await withCommandRunTelemetry('import.gateway', {}, () => handleImportGateway(cliOptions)); if (result.success) { console.log(''); diff --git a/src/cli/commands/import/import-memory.ts b/src/cli/commands/import/import-memory.ts index 2558d941a..73aaf20a6 100644 --- a/src/cli/commands/import/import-memory.ts +++ b/src/cli/commands/import/import-memory.ts @@ -1,6 +1,7 @@ import type { Memory } from '../../../schema'; import type { MemoryDetail, MemorySummary } from '../../aws/agentcore-control'; import { getMemoryDetail, listAllMemories } from '../../aws/agentcore-control'; +import { withCommandRunTelemetry } from '../../telemetry/cli-command-run.js'; import { ANSI } from './constants'; import { parseAndValidateArn } from './import-utils'; import { executeResourceImport } from './resource-import'; @@ -114,7 +115,7 @@ export function registerImportMemory(importCmd: Command): void { .option('--name ', 'Local name for the imported memory') .option('-y, --yes', 'Auto-confirm prompts') .action(async (cliOptions: ImportResourceOptions) => { - const result = await handleImportMemory(cliOptions); + const result = await withCommandRunTelemetry('import.memory', {}, () => handleImportMemory(cliOptions)); if (result.success) { console.log(''); diff --git a/src/cli/commands/import/import-online-eval.ts b/src/cli/commands/import/import-online-eval.ts index b43fa174a..d48a8bb52 100644 --- a/src/cli/commands/import/import-online-eval.ts +++ b/src/cli/commands/import/import-online-eval.ts @@ -6,6 +6,7 @@ import { listAllOnlineEvaluationConfigs, } from '../../aws/agentcore-control'; import { arnPrefix } from '../../aws/partition'; +import { withCommandRunTelemetry } from '../../telemetry/cli-command-run.js'; import { ANSI } from './constants'; import { failResult, findResourceInDeployedState, parseAndValidateArn } from './import-utils'; import { executeResourceImport } from './resource-import'; @@ -200,7 +201,7 @@ export function registerImportOnlineEval(importCmd: Command): void { .option('--name ', 'Local name for the imported online eval config') .option('-y, --yes', 'Auto-confirm prompts') .action(async (cliOptions: ImportResourceOptions) => { - const result = await handleImportOnlineEval(cliOptions); + const result = await withCommandRunTelemetry('import.online-eval', {}, () => handleImportOnlineEval(cliOptions)); if (result.success) { console.log(''); diff --git a/src/cli/commands/import/import-runtime.ts b/src/cli/commands/import/import-runtime.ts index ff6ba0640..ce1c607a0 100644 --- a/src/cli/commands/import/import-runtime.ts +++ b/src/cli/commands/import/import-runtime.ts @@ -1,6 +1,7 @@ import type { AgentEnvSpec } from '../../../schema'; import type { AgentRuntimeDetail, AgentRuntimeSummary } from '../../aws/agentcore-control'; import { getAgentRuntimeDetail, listAllAgentRuntimes } from '../../aws/agentcore-control'; +import { withCommandRunTelemetry } from '../../telemetry/cli-command-run.js'; import { ANSI } from './constants'; import { copyAgentSource, failResult, parseAndValidateArn } from './import-utils'; import { executeResourceImport } from './resource-import'; @@ -211,7 +212,7 @@ export function registerImportRuntime(importCmd: Command): void { .option('--name ', 'Local name for the imported runtime') .option('-y, --yes', 'Auto-confirm prompts') .action(async (cliOptions: RuntimeImportOptions) => { - const result = await handleImportRuntime(cliOptions); + const result = await withCommandRunTelemetry('import.runtime', {}, () => handleImportRuntime(cliOptions)); if (result.success) { console.log(''); diff --git a/src/cli/commands/import/import-utils.ts b/src/cli/commands/import/import-utils.ts index e29ada5ee..1e84a294c 100644 --- a/src/cli/commands/import/import-utils.ts +++ b/src/cli/commands/import/import-utils.ts @@ -1,4 +1,4 @@ -import { APP_DIR, ConfigIO, findConfigRoot } from '../../../lib'; +import { APP_DIR, ConfigIO, NoProjectError, ValidationError, findConfigRoot } from '../../../lib'; import type { AwsDeploymentTarget } from '../../../schema'; import { detectAccount, validateAwsCredentials } from '../../aws/account'; import { ExecLogger } from '../../logging'; @@ -67,7 +67,7 @@ export function failResult( logger.finalize(false); return { success: false, - error: new Error(error), + error: new ValidationError(error), resourceType, resourceName, logPath: logger.getRelativeLogPath(), @@ -91,7 +91,7 @@ export interface ProjectContext { export async function resolveProjectContext(): Promise { const configRoot = findConfigRoot(process.cwd()); if (!configRoot) { - throw new Error( + throw new NoProjectError( 'No agentcore project found in the current directory.\nRun `agentcore create ` first, then run import from inside the project.' ); } @@ -134,7 +134,7 @@ export async function resolveImportTarget(options: ResolveTargetOptions): Promis arn ) ) { - throw new Error( + throw new ValidationError( `Not a valid ARN: "${arn}".\nExpected format: arn::bedrock-agentcore:::/` ); } @@ -147,7 +147,7 @@ export async function resolveImportTarget(options: ResolveTargetOptions): Promis const arnRegion = arnRegionMatch?.[1]; const envRegion = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION; if (arnRegion && envRegion && envRegion !== arnRegion) { - throw new Error( + throw new ValidationError( `Region mismatch: AWS_REGION is "${envRegion}" but the ARN is in "${arnRegion}". ` + `Either re-run with AWS_REGION=${arnRegion} or pass an ARN from ${envRegion}.` ); @@ -158,14 +158,14 @@ export async function resolveImportTarget(options: ResolveTargetOptions): Promis if (targets.length === 0) { if (!arn) { - throw new Error( + throw new ValidationError( 'No deployment targets found in project.\nRun `agentcore deploy` first to set up a target, or use --arn so a target can be created automatically.' ); } const arnMatch = /^arn:[^:]+:bedrock-agentcore:([^:]+):([^:]+):/.exec(arn); if (!arnMatch) { - throw new Error( + throw new ValidationError( 'No deployment targets found in project and could not parse region/account from ARN.\nRun `agentcore deploy` first to set up a target, then re-run import.' ); } @@ -189,13 +189,13 @@ export async function resolveImportTarget(options: ResolveTargetOptions): Promis target = targets.find(t => t.name === targetName); if (!target) { const names = targets.map(t => ` - ${t.name} (${t.region}, ${t.account})`).join('\n'); - throw new Error(`Target "${targetName}" not found. Available targets:\n${names}`); + throw new ValidationError(`Target "${targetName}" not found. Available targets:\n${names}`); } } else if (targets.length === 1) { target = targets[0]!; } else { const names = targets.map(t => ` - ${t.name} (${t.region}, ${t.account})`).join('\n'); - throw new Error(`Multiple deployment targets found. Specify one with --target:\n${names}`); + throw new ValidationError(`Multiple deployment targets found. Specify one with --target:\n${names}`); } onProgress?.(`Using target: ${target.name} (${target.region}, ${target.account})`); @@ -207,7 +207,7 @@ export async function resolveImportTarget(options: ResolveTargetOptions): Promis // Validate credentials match the target account const callerAccount = await detectAccount(); if (callerAccount && target.account && callerAccount !== target.account) { - throw new Error( + throw new ValidationError( `Your AWS credentials are for account ${callerAccount}, but the target "${target.name}" is configured for account ${target.account}.\nEnsure your credentials match the deployment target.` ); } @@ -261,7 +261,7 @@ export function parseAndValidateArn( const match = ARN_PATTERN.exec(arn); const expectedArnType = RESOURCE_TYPE_CONFIG[expectedResourceType].arnType; if (!match) { - throw new Error( + throw new ValidationError( `Invalid ARN format: "${arn}". Expected format: arn::bedrock-agentcore:::${expectedArnType}/` ); } @@ -269,17 +269,17 @@ export function parseAndValidateArn( const [, region, account, resourceType, resourceId] = match; if (resourceType !== expectedArnType) { - throw new Error(`ARN resource type "${resourceType}" does not match expected type "${expectedArnType}".`); + throw new ValidationError(`ARN resource type "${resourceType}" does not match expected type "${expectedArnType}".`); } if (region !== target.region) { - throw new Error( + throw new ValidationError( `ARN region "${region}" does not match target region "${target.region}". Use --target to select a different deployment target.` ); } if (account !== target.account) { - throw new Error( + throw new ValidationError( `ARN account "${account}" does not match target account "${target.account}". Ensure the ARN belongs to the correct account.` ); } @@ -498,7 +498,7 @@ export async function copyAgentSource(options: CopyAgentSourceOptions): Promise< } } } else { - throw new Error(`Source path does not exist: ${sourcePath}`); + throw new ValidationError(`Source path does not exist: ${sourcePath}`); } // Container agents install dependencies inside the Docker image diff --git a/src/cli/telemetry/schemas/__tests__/command-run.test.ts b/src/cli/telemetry/schemas/__tests__/command-run.test.ts index ddefb4259..561899d7e 100644 --- a/src/cli/telemetry/schemas/__tests__/command-run.test.ts +++ b/src/cli/telemetry/schemas/__tests__/command-run.test.ts @@ -121,6 +121,15 @@ describe('COMMAND_SCHEMAS', () => { expect(COMMAND_SCHEMAS['telemetry.disable'].parse({})).toEqual({}); }); + it('import subcommand schemas accept empty object', () => { + expect(COMMAND_SCHEMAS.import.parse({})).toEqual({}); + expect(COMMAND_SCHEMAS['import.runtime'].parse({})).toEqual({}); + expect(COMMAND_SCHEMAS['import.memory'].parse({})).toEqual({}); + expect(COMMAND_SCHEMAS['import.evaluator'].parse({})).toEqual({}); + expect(COMMAND_SCHEMAS['import.online-eval'].parse({})).toEqual({}); + expect(COMMAND_SCHEMAS['import.gateway'].parse({})).toEqual({}); + }); + it('accepts valid dev invoke attrs', () => { const attrs = { dev_action: 'invoke', diff --git a/src/cli/telemetry/schemas/command-run.ts b/src/cli/telemetry/schemas/command-run.ts index 19d25b5bb..47e339b1a 100644 --- a/src/cli/telemetry/schemas/command-run.ts +++ b/src/cli/telemetry/schemas/command-run.ts @@ -173,6 +173,9 @@ export const COMMAND_SCHEMAS = { import: NoAttrs, 'import.runtime': NoAttrs, 'import.memory': NoAttrs, + 'import.evaluator': NoAttrs, + 'import.online-eval': NoAttrs, + 'import.gateway': NoAttrs, package: NoAttrs, validate: NoAttrs, 'help.modes': NoAttrs, diff --git a/src/cli/tui/screens/import/ImportFlow.tsx b/src/cli/tui/screens/import/ImportFlow.tsx index 0a0843f7e..83ac49eb5 100644 --- a/src/cli/tui/screens/import/ImportFlow.tsx +++ b/src/cli/tui/screens/import/ImportFlow.tsx @@ -12,8 +12,9 @@ import { HELP_TEXT } from '../../constants'; import { ArnInputScreen } from './ArnInputScreen'; import { CodePathScreen } from './CodePathScreen'; import { ImportProgressScreen } from './ImportProgressScreen'; -import { ImportSelectScreen, type ImportType } from './ImportSelectScreen'; +import { ImportSelectScreen } from './ImportSelectScreen'; import { YamlPathScreen } from './YamlPathScreen'; +import type { ImportType } from './types'; import { Box, Text } from 'ink'; import React, { useState } from 'react'; diff --git a/src/cli/tui/screens/import/ImportProgressScreen.tsx b/src/cli/tui/screens/import/ImportProgressScreen.tsx index a0f0229cf..4b511a9b5 100644 --- a/src/cli/tui/screens/import/ImportProgressScreen.tsx +++ b/src/cli/tui/screens/import/ImportProgressScreen.tsx @@ -1,11 +1,13 @@ import type { ImportResourceResult, ImportResult } from '../../../commands/import/types'; import { IMPORTABLE_RESOURCES } from '../../../commands/import/types'; +import { withCommandRunTelemetry } from '../../../telemetry/cli-command-run.js'; import { Panel } from '../../components/Panel'; import { Screen } from '../../components/Screen'; import { type Step, StepProgress } from '../../components/StepProgress'; import { HELP_TEXT } from '../../constants'; -import type { ImportType } from './ImportSelectScreen'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import type { ImportType } from './types.js'; +import { toTelemetryCommand } from './utils.js'; +import { useCallback, useEffect, useRef, useState } from 'react'; interface ImportProgressScreenProps { importType: ImportType; @@ -41,44 +43,38 @@ export function ImportProgressScreen({ started.current = true; const run = async () => { - if ((IMPORTABLE_RESOURCES as readonly string[]).includes(importType)) { - const handler = - importType === 'runtime' - ? (await import('../../../commands/import/import-runtime')).handleImportRuntime - : importType === 'memory' - ? (await import('../../../commands/import/import-memory')).handleImportMemory - : importType === 'evaluator' - ? (await import('../../../commands/import/import-evaluator')).handleImportEvaluator - : importType === 'gateway' - ? (await import('../../../commands/import/import-gateway')).handleImportGateway - : (await import('../../../commands/import/import-online-eval')).handleImportOnlineEval; + const telemetryResult = await withCommandRunTelemetry( + toTelemetryCommand(importType), + {}, + async (): Promise => { + if ((IMPORTABLE_RESOURCES as readonly string[]).includes(importType)) { + const handler = + importType === 'runtime' + ? (await import('../../../commands/import/import-runtime')).handleImportRuntime + : importType === 'memory' + ? (await import('../../../commands/import/import-memory')).handleImportMemory + : importType === 'evaluator' + ? (await import('../../../commands/import/import-evaluator')).handleImportEvaluator + : importType === 'gateway' + ? (await import('../../../commands/import/import-gateway')).handleImportGateway + : (await import('../../../commands/import/import-online-eval')).handleImportOnlineEval; - const result = await handler({ arn, code, onProgress }); - if (result.success) { - setSteps(prev => prev.map(s => (s.status === 'running' ? { ...s, status: 'success' } : s))); - onSuccess(result); - } else { - setSteps(prev => - prev.map(s => (s.status === 'running' ? { ...s, status: 'error', error: result.error.message } : s)) - ); - onError(result.error.message ?? 'Import failed'); + return handler({ arn, code, onProgress }); + } else { + const { handleImport } = await import('../../../commands/import/actions'); + return handleImport({ source: yamlPath!, onProgress }); + } } + ); + + if (telemetryResult.success) { + setSteps(prev => prev.map(s => (s.status === 'running' ? { ...s, status: 'success' } : s))); + onSuccess(telemetryResult); } else { - // Starter toolkit - const { handleImport } = await import('../../../commands/import/actions'); - const result = await handleImport({ - source: yamlPath!, - onProgress, - }); - if (result.success) { - setSteps(prev => prev.map(s => (s.status === 'running' ? { ...s, status: 'success' } : s))); - onSuccess(result); - } else { - setSteps(prev => - prev.map(s => (s.status === 'running' ? { ...s, status: 'error', error: result.error.message } : s)) - ); - onError(result.error.message ?? 'Import failed'); - } + setSteps(prev => + prev.map(s => (s.status === 'running' ? { ...s, status: 'error', error: telemetryResult.error.message } : s)) + ); + onError(telemetryResult.error.message ?? 'Import failed'); } }; diff --git a/src/cli/tui/screens/import/ImportSelectScreen.tsx b/src/cli/tui/screens/import/ImportSelectScreen.tsx index 9058ebb1e..4ba60ce85 100644 --- a/src/cli/tui/screens/import/ImportSelectScreen.tsx +++ b/src/cli/tui/screens/import/ImportSelectScreen.tsx @@ -1,7 +1,6 @@ import type { SelectableItem } from '../../components/SelectList'; import { SelectScreen } from '../../components/SelectScreen'; - -export type ImportType = 'runtime' | 'memory' | 'evaluator' | 'online-eval' | 'gateway' | 'starter-toolkit'; +import type { ImportType } from './types'; interface ImportSelectItem extends SelectableItem { id: ImportType; diff --git a/src/cli/tui/screens/import/types.ts b/src/cli/tui/screens/import/types.ts new file mode 100644 index 000000000..2ccf25154 --- /dev/null +++ b/src/cli/tui/screens/import/types.ts @@ -0,0 +1 @@ +export type ImportType = 'runtime' | 'memory' | 'evaluator' | 'online-eval' | 'gateway' | 'starter-toolkit'; diff --git a/src/cli/tui/screens/import/utils.ts b/src/cli/tui/screens/import/utils.ts new file mode 100644 index 000000000..a5980ee2b --- /dev/null +++ b/src/cli/tui/screens/import/utils.ts @@ -0,0 +1,7 @@ +import type { Command } from '../../../telemetry/schemas'; +import type { ImportType } from './types'; + +export function toTelemetryCommand(importType: ImportType): Command { + if (importType === 'starter-toolkit') return 'import'; + return `import.${importType}`; +}