Skip to content

Commit 12b0e12

Browse files
committed
refactor: move telemetry into .add() methods so both CLI and TUI paths get coverage
1 parent 29a9614 commit 12b0e12

11 files changed

Lines changed: 555 additions & 502 deletions

src/cli/primitives/AgentPrimitive.tsx

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {
3434
import { executeImportAgent } from '../operations/agent/import';
3535
import { setupPythonProject } from '../operations/python';
3636
import type { RemovalPreview, RemovalResult, SchemaChange } from '../operations/remove/types';
37-
import { cliCommandRun } from '../telemetry/cli-command-run.js';
37+
import { cliCommandRun, withAddTelemetry } from '../telemetry/cli-command-run.js';
3838
import {
3939
AgentType,
4040
AuthorizerType,
@@ -114,37 +114,51 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
114114
}
115115

116116
async add(options: AddAgentOptions): Promise<AddResult<{ agentName: string; agentPath?: string }>> {
117-
try {
118-
const configBaseDir = findConfigRoot();
119-
if (!configBaseDir) {
120-
return { success: false, error: new NoProjectError().message };
121-
}
117+
const attrs = {
118+
language: standardize(Language, options.language),
119+
framework: standardize(Framework, options.framework),
120+
model_provider: standardize(ModelProviderEnum, options.modelProvider),
121+
agent_type: standardize(AgentType, options.type),
122+
build: standardize(Build, options.buildType),
123+
protocol: standardize(Protocol, options.protocol ?? 'HTTP'),
124+
network_mode: standardize(NetworkModeEnum, options.networkMode ?? 'PUBLIC'),
125+
authorizer_type: standardize(AuthorizerType, options.authorizerType ?? 'NONE'),
126+
memory: standardize(Memory, options.memory ?? 'none'),
127+
};
122128

123-
const configIO = new ConfigIO({ baseDir: configBaseDir });
129+
return withAddTelemetry('add.agent', attrs, async () => {
130+
try {
131+
const configBaseDir = findConfigRoot();
132+
if (!configBaseDir) {
133+
return { success: false, error: new NoProjectError().message };
134+
}
124135

125-
if (!configIO.configExists('project')) {
126-
return { success: false, error: new NoProjectError().message };
127-
}
136+
const configIO = new ConfigIO({ baseDir: configBaseDir });
128137

129-
const project = await configIO.readProjectSpec();
130-
const existingAgent = project.runtimes.find(agent => agent.name === options.name);
131-
if (existingAgent) {
132-
return {
133-
success: false,
134-
error: `Agent "${options.name}" already exists. To update its configuration, edit agentcore/agentcore.json directly.`,
135-
};
136-
}
138+
if (!configIO.configExists('project')) {
139+
return { success: false, error: new NoProjectError().message };
140+
}
137141

138-
if (options.type === 'import') {
139-
return await this.handleImportPath(options, configBaseDir);
140-
} else if (options.type === 'byo') {
141-
return await this.handleByoPath(options, configIO, configBaseDir);
142-
} else {
143-
return await this.handleCreatePath(options, configBaseDir);
142+
const project = await configIO.readProjectSpec();
143+
const existingAgent = project.runtimes.find(agent => agent.name === options.name);
144+
if (existingAgent) {
145+
return {
146+
success: false,
147+
error: `Agent "${options.name}" already exists. To update its configuration, edit agentcore/agentcore.json directly.`,
148+
};
149+
}
150+
151+
if (options.type === 'import') {
152+
return await this.handleImportPath(options, configBaseDir);
153+
} else if (options.type === 'byo') {
154+
return await this.handleByoPath(options, configIO, configBaseDir);
155+
} else {
156+
return await this.handleCreatePath(options, configBaseDir);
157+
}
158+
} catch (err) {
159+
return { success: false, error: getErrorMessage(err) };
144160
}
145-
} catch (err) {
146-
return { success: false, error: getErrorMessage(err) };
147-
}
161+
});
148162
}
149163

150164
async remove(agentName: string): Promise<RemovalResult> {
@@ -277,7 +291,7 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
277291

278292
// Any flag triggers non-interactive CLI mode
279293
if (cliOptions.name || cliOptions.framework || cliOptions.json) {
280-
await cliCommandRun('add.agent', !!cliOptions.json, async () => {
294+
await cliCommandRun(!!cliOptions.json, async () => {
281295
const validation = validateAddAgentOptions(cliOptions);
282296
if (!validation.valid) {
283297
throw new Error(validation.error);
@@ -340,18 +354,6 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
340354
console.log(`\x1b[33mNote: ${VPC_ENDPOINT_WARNING}\x1b[0m`);
341355
}
342356
}
343-
344-
return {
345-
language: standardize(Language, cliOptions.language!),
346-
framework: standardize(Framework, cliOptions.framework!),
347-
model_provider: standardize(ModelProviderEnum, cliOptions.modelProvider!),
348-
agent_type: standardize(AgentType, cliOptions.type ?? 'create'),
349-
build: standardize(Build, cliOptions.build ?? 'CodeZip'),
350-
protocol: standardize(Protocol, cliOptions.protocol ?? 'HTTP'),
351-
network_mode: standardize(NetworkModeEnum, cliOptions.networkMode ?? 'PUBLIC'),
352-
authorizer_type: standardize(AuthorizerType, cliOptions.authorizerType ?? 'NONE'),
353-
memory: standardize(Memory, cliOptions.memory ?? 'none'),
354-
};
355357
});
356358
} else {
357359
try {

src/cli/primitives/CredentialPrimitive.tsx

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { CredentialSchema } from '../../schema';
44
import { validateAddCredentialOptions } from '../commands/add/validate';
55
import { getErrorMessage } from '../errors';
66
import type { RemovalPreview, RemovalResult, SchemaChange } from '../operations/remove/types';
7-
import { cliCommandRun } from '../telemetry/cli-command-run.js';
8-
import { CredentialType, standardize } from '../telemetry/schemas/common-shapes.js';
7+
import { cliCommandRun, withAddTelemetry } from '../telemetry/cli-command-run.js';
98
import { requireTTY } from '../tui/guards/tty';
109
import { BasePrimitive } from './BasePrimitive';
1110
import { computeDefaultCredentialEnvVarName } from './credential-utils';
@@ -73,12 +72,18 @@ export class CredentialPrimitive extends BasePrimitive<AddCredentialOptions, Rem
7372
protected override readonly article: string = 'a';
7473

7574
async add(options: AddCredentialOptions): Promise<AddResult<{ credentialName: string }>> {
76-
try {
77-
const credential = await this.createCredential(options);
78-
return { success: true, credentialName: credential.name };
79-
} catch (err) {
80-
return { success: false, error: getErrorMessage(err) };
81-
}
75+
return withAddTelemetry(
76+
'add.credential',
77+
{ credential_type: options.authorizerType === 'OAuthCredentialProvider' ? 'oauth' : 'api-key' },
78+
async () => {
79+
try {
80+
const credential = await this.createCredential(options);
81+
return { success: true, credentialName: credential.name };
82+
} catch (err) {
83+
return { success: false, error: getErrorMessage(err) };
84+
}
85+
}
86+
);
8287
}
8388

8489
async remove(credentialName: string, options?: { force?: boolean }): Promise<RemovalResult> {
@@ -291,7 +296,7 @@ export class CredentialPrimitive extends BasePrimitive<AddCredentialOptions, Rem
291296
cliOptions.scopes
292297
) {
293298
// CLI mode
294-
await cliCommandRun('add.credential', !!cliOptions.json, async () => {
299+
await cliCommandRun(!!cliOptions.json, async () => {
295300
const validation = validateAddCredentialOptions({
296301
name: cliOptions.name,
297302
type: cliOptions.type as 'api-key' | 'oauth' | undefined,
@@ -336,10 +341,6 @@ export class CredentialPrimitive extends BasePrimitive<AddCredentialOptions, Rem
336341
} else {
337342
console.log(`Added credential '${result.credentialName}'`);
338343
}
339-
340-
return {
341-
credential_type: standardize(CredentialType, cliOptions.type ?? 'api-key'),
342-
};
343344
});
344345
} else {
345346
try {

src/cli/primitives/EvaluatorPrimitive.ts

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { EvaluationLevel, Evaluator, EvaluatorConfig } from '../../schema';
33
import { EvaluationLevelSchema, EvaluatorSchema } from '../../schema';
44
import { getErrorMessage } from '../errors';
55
import type { RemovalPreview, RemovalResult, SchemaChange } from '../operations/remove/types';
6-
import { cliCommandRun } from '../telemetry/cli-command-run.js';
6+
import { cliCommandRun, withAddTelemetry } from '../telemetry/cli-command-run.js';
77
import { EvaluatorType, Level, standardize } from '../telemetry/schemas/common-shapes.js';
88
import { renderCodeBasedEvaluatorTemplate } from '../templates/EvaluatorRenderer';
99
import { requireTTY } from '../tui/guards/tty';
@@ -42,23 +42,34 @@ export class EvaluatorPrimitive extends BasePrimitive<AddEvaluatorOptions, Remov
4242
readonly primitiveSchema = EvaluatorSchema;
4343

4444
async add(options: AddEvaluatorOptions): Promise<AddResult<{ evaluatorName: string; codePath?: string }>> {
45-
try {
46-
const evaluator = await this.createEvaluator(options);
45+
const evalType = options.config.codeBased ? 'code-based' : 'llm-as-a-judge';
4746

48-
// Scaffold code for managed code-based evaluators
49-
if (options.config.codeBased?.managed) {
50-
const configRoot = findConfigRoot()!;
51-
const projectRoot = dirname(configRoot);
52-
const codeLocation = options.config.codeBased.managed.codeLocation;
53-
const targetDir = join(projectRoot, codeLocation);
54-
await renderCodeBasedEvaluatorTemplate(options.name, targetDir);
55-
return { success: true, evaluatorName: evaluator.name, codePath: codeLocation };
56-
}
47+
return withAddTelemetry<'add.evaluator', { evaluatorName: string; codePath?: string }>(
48+
'add.evaluator',
49+
{
50+
evaluator_type: standardize(EvaluatorType, evalType),
51+
level: standardize(Level, options.level),
52+
},
53+
async () => {
54+
try {
55+
const evaluator = await this.createEvaluator(options);
56+
57+
// Scaffold code for managed code-based evaluators
58+
if (options.config.codeBased?.managed) {
59+
const configRoot = findConfigRoot()!;
60+
const projectRoot = dirname(configRoot);
61+
const codeLocation = options.config.codeBased.managed.codeLocation;
62+
const targetDir = join(projectRoot, codeLocation);
63+
await renderCodeBasedEvaluatorTemplate(options.name, targetDir);
64+
return { success: true, evaluatorName: evaluator.name, codePath: codeLocation };
65+
}
5766

58-
return { success: true, evaluatorName: evaluator.name };
59-
} catch (err) {
60-
return { success: false, error: getErrorMessage(err) };
61-
}
67+
return { success: true, evaluatorName: evaluator.name };
68+
} catch (err) {
69+
return { success: false, error: getErrorMessage(err) };
70+
}
71+
}
72+
);
6273
}
6374

6475
async remove(evaluatorName: string): Promise<RemovalResult> {
@@ -204,7 +215,7 @@ export class EvaluatorPrimitive extends BasePrimitive<AddEvaluatorOptions, Remov
204215
}
205216

206217
if (cliOptions.name || cliOptions.json) {
207-
await cliCommandRun('add.evaluator', !!cliOptions.json, async () => {
218+
await cliCommandRun(!!cliOptions.json, async () => {
208219
const fail = (error: string): never => {
209220
throw new Error(error);
210221
};
@@ -313,11 +324,6 @@ export class EvaluatorPrimitive extends BasePrimitive<AddEvaluatorOptions, Remov
313324
console.log(`Added evaluator '${result.evaluatorName}'`);
314325
}
315326
}
316-
317-
return {
318-
evaluator_type: standardize(EvaluatorType, evalType),
319-
level: standardize(Level, levelResult.data!),
320-
};
321327
});
322328
} else {
323329
try {

src/cli/primitives/GatewayPrimitive.ts

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type { AddGatewayOptions as CLIAddGatewayOptions } from '../commands/add/
1212
import { validateAddGatewayOptions } from '../commands/add/validate';
1313
import { getErrorMessage } from '../errors';
1414
import type { RemovalPreview, RemovalResult, SchemaChange } from '../operations/remove/types';
15-
import { cliCommandRun } from '../telemetry/cli-command-run.js';
15+
import { cliCommandRun, withAddTelemetry } from '../telemetry/cli-command-run.js';
1616
import { AuthorizerType, PolicyEngineMode, standardize } from '../telemetry/schemas/common-shapes.js';
1717
import { requireTTY } from '../tui/guards/tty';
1818
import type { AddGatewayConfig } from '../tui/screens/mcp/types';
@@ -62,13 +62,28 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
6262
readonly primitiveSchema = AgentCoreGatewaySchema;
6363

6464
async add(options: AddGatewayOptions): Promise<AddResult<{ gatewayName: string }>> {
65-
try {
66-
const config = this.buildGatewayConfig(options);
67-
const result = await this.createGatewayFromWizard(config);
68-
return { success: true, gatewayName: result.name };
69-
} catch (err) {
70-
return { success: false, error: getErrorMessage(err) };
71-
}
65+
const attrs = {
66+
authorizer_type: standardize(AuthorizerType, options.authorizerType ?? 'NONE'),
67+
has_policy_engine: !!options.policyEngine,
68+
policy_engine_mode: standardize(PolicyEngineMode, options.policyEngineMode ?? 'log_only'),
69+
semantic_search: options.enableSemanticSearch !== false,
70+
runtime_count: options.runtimes
71+
? options.runtimes
72+
.split(',')
73+
.map(s => s.trim())
74+
.filter(Boolean).length
75+
: 0,
76+
};
77+
78+
return withAddTelemetry('add.gateway', attrs, async () => {
79+
try {
80+
const config = this.buildGatewayConfig(options);
81+
const result = await this.createGatewayFromWizard(config);
82+
return { success: true, gatewayName: result.name };
83+
} catch (err) {
84+
return { success: false, error: getErrorMessage(err) };
85+
}
86+
});
7287
}
7388

7489
async remove(gatewayName: string): Promise<RemovalResult> {
@@ -188,7 +203,7 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
188203
console.error('No agentcore project found. Run `agentcore create` first.');
189204
process.exit(1);
190205
}
191-
await cliCommandRun('add.gateway', !!cliOptions.json, async () => {
206+
await cliCommandRun(!!cliOptions.json, async () => {
192207
const validation = validateAddGatewayOptions(cliOptions);
193208
if (!validation.valid) {
194209
throw new Error(validation.error);
@@ -226,20 +241,6 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
226241
} else {
227242
console.log(`Added gateway '${result.gatewayName}'`);
228243
}
229-
230-
const runtimeCount = cliOptions.runtimes
231-
? cliOptions.runtimes
232-
.split(',')
233-
.map(s => s.trim())
234-
.filter(Boolean).length
235-
: 0;
236-
return {
237-
authorizer_type: standardize(AuthorizerType, cliOptions.authorizerType ?? 'NONE'),
238-
has_policy_engine: !!cliOptions.policyEngine,
239-
policy_engine_mode: standardize(PolicyEngineMode, cliOptions.policyEngineMode ?? 'log_only'),
240-
semantic_search: cliOptions.semanticSearch !== false,
241-
runtime_count: runtimeCount,
242-
};
243244
});
244245
});
245246

0 commit comments

Comments
 (0)