diff --git a/src/handlers/harness/create/index.tsx b/src/handlers/harness/create/index.tsx index 1eaab0591..602e3195a 100644 --- a/src/handlers/harness/create/index.tsx +++ b/src/handlers/harness/create/index.tsx @@ -16,77 +16,95 @@ import { JsonRendererKey } from "../../../tui"; import { InputValidationError } from "../../../errors"; import { parameterHelp } from "../parameterHelp.tsx"; +const CONFIGURATION = "Configuration:"; +const AGENT = "Agent:"; +const COMPUTE = "Compute environment:"; +const ACCESS = "Access:"; +const LIMITS = "Limits:"; + export const createCreateHarnessHandler = (core: Core) => createHandler({ name: "create", description: "create a harness", flags: [ - flag("name", "the name of the harness", z.string().optional(), { help: parameterHelp.name }), + flag("name", "the name of the harness", z.string().optional(), { + group: CONFIGURATION, + }), flag( "execution-role-arn", "IAM role the harness assumes; a default role is created when omitted", z.string().optional(), - { help: parameterHelp.executionRoleArn }, + { group: CONFIGURATION }, ), + flag("tags", "tags to apply (JSON object of key/value strings)", z.string().optional(), { + help: parameterHelp.tags, + group: CONFIGURATION, + }), flag("system-prompt", "the agent's system prompt", z.string().optional(), { - help: parameterHelp.systemPrompt, + group: AGENT, }), flag("model", "model configuration (JSON HarnessModelConfiguration)", z.string().optional(), { help: parameterHelp.model, + group: AGENT, }), flag("tools", "tools available to the agent (JSON HarnessTool[])", z.string().optional(), { help: parameterHelp.tools, + group: AGENT, }), flag("skills", "skills available to the agent (JSON HarnessSkill[])", z.string().optional(), { help: parameterHelp.skills, + group: AGENT, }), flag( "allowed-tools", "tool allowlist patterns (e.g. * or @serverName/toolName)", z.array(z.string()).optional(), - { help: parameterHelp.allowedTools }, + { help: parameterHelp.allowedTools, group: AGENT }, ), flag( "memory", "memory configuration (JSON HarnessMemoryConfiguration)", z.string().optional(), - { help: parameterHelp.memory }, + { help: parameterHelp.memory, group: AGENT }, ), flag( "truncation", "context truncation configuration (JSON HarnessTruncationConfiguration)", z.string().optional(), - { help: parameterHelp.truncation }, + { help: parameterHelp.truncation, group: AGENT }, ), flag( "environment", "compute environment configuration (JSON HarnessEnvironmentProviderRequest)", z.string().optional(), - { help: parameterHelp.environment }, + { help: parameterHelp.environment, group: COMPUTE }, ), flag( "environment-artifact", "environment artifact, e.g. a container image (JSON HarnessEnvironmentArtifact)", z.string().optional(), - { help: parameterHelp.environmentArtifact }, + { help: parameterHelp.environmentArtifact, group: COMPUTE }, ), flag( "environment-variables", "environment variables (JSON object of key/value strings)", z.string().optional(), - { help: parameterHelp.environmentVariables }, + { help: parameterHelp.environmentVariables, group: COMPUTE }, ), flag( "authorizer-configuration", "inbound authorizer configuration (JSON AuthorizerConfiguration)", z.string().optional(), - { help: parameterHelp.authorizerConfiguration }, + { help: parameterHelp.authorizerConfiguration, group: ACCESS }, ), - flag("max-iterations", "max agent loop iterations per invocation", z.number().optional()), - flag("max-tokens", "max total output tokens per invocation", z.number().optional()), - flag("timeout-seconds", "max duration in seconds per invocation", z.number().optional()), - flag("tags", "tags to apply (JSON object of key/value strings)", z.string().optional(), { - help: parameterHelp.tags, + flag("max-iterations", "max agent loop iterations per invocation", z.number().optional(), { + group: LIMITS, + }), + flag("max-tokens", "max total output tokens per invocation", z.number().optional(), { + group: LIMITS, + }), + flag("timeout-seconds", "max duration in seconds per invocation", z.number().optional(), { + group: LIMITS, }), ], handle: async (ctx, flags) => { diff --git a/src/handlers/harness/logs/index.tsx b/src/handlers/harness/logs/index.tsx index 634244964..d116e2e25 100644 --- a/src/handlers/harness/logs/index.tsx +++ b/src/handlers/harness/logs/index.tsx @@ -6,9 +6,13 @@ import { createLogsHandler } from "../../observability/logs"; import type { Core } from "../../types"; import { coreOptsFromCtx } from "../../utils"; +const LOG_SOURCE = "Log source:"; + const harnessFlags = [ - flag("id", "the ID of the harness", z.string().min(1).max(48)), - flag("qualifier", "the harness endpoint qualifier", z.string().min(1).optional()), + flag("id", "the ID of the harness", z.string().min(1).max(48), { group: LOG_SOURCE }), + flag("qualifier", "the harness endpoint qualifier", z.string().min(1).optional(), { + group: LOG_SOURCE, + }), ] as const; export const createHarnessLogsHandler = (core: Core, io: AppIO) => diff --git a/src/handlers/harness/parameterHelp.tsx b/src/handlers/harness/parameterHelp.tsx index 4e78c5561..f6b6d6072 100644 --- a/src/handlers/harness/parameterHelp.tsx +++ b/src/handlers/harness/parameterHelp.tsx @@ -9,32 +9,6 @@ // enclosing object, not on the command line. export const parameterHelp = { - name: `(string) -The name of the harness. Must start with a letter and contain only -alphanumeric characters and underscores. - -Pattern: [a-zA-Z][a-zA-Z0-9_]{0,39}`, - - executionRoleArn: `(string) -The ARN of the IAM role the harness assumes when running. The role must -trust bedrock-agentcore.amazonaws.com and have permissions for the services -the agent needs (Bedrock model invocation, CloudWatch Logs, built-in tools, -memory, ...). - -When omitted, the CLI provisions a default per-harness role named -AgentCoreHarness- with the baseline policy and uses it. - -Example: - --execution-role-arn arn:aws:iam::123456789012:role/MyHarnessRole`, - - systemPrompt: `(string) -The system prompt that defines the agent's behavior and instructions. The -CLI wraps the string into the API's content-block list ([{"text": ...}]) -for you. - -Example: - --system-prompt 'You are a concise research assistant.'`, - model: `(JSON: tagged union object) The model configuration for the harness. Supports Amazon Bedrock, OpenAI, Google Gemini, and LiteLLM providers. Exactly one of the following top-level diff --git a/src/handlers/harness/update/index.tsx b/src/handlers/harness/update/index.tsx index 8bc20fc3f..cf69c5568 100644 --- a/src/handlers/harness/update/index.tsx +++ b/src/handlers/harness/update/index.tsx @@ -24,87 +24,106 @@ function updated(value: T | undefined, clear: boolean): { optionalValue?: T } return value !== undefined ? { optionalValue: value } : undefined; } +const AGENT = "Agent:"; +const COMPUTE = "Compute environment:"; +const ACCESS = "Access:"; +const LIMITS = "Limits:"; + export const createUpdateHarnessHandler = (core: Core) => createHandler({ name: "update", description: "update a harness (creates a new version)", flags: [ - flag("id", "the ID of the harness to update", z.string().max(48).optional()), + flag("id", "the ID of the harness to update", z.string().max(48).optional(), { + group: "Target:", + }), flag("execution-role-arn", "IAM role the harness assumes", z.string().optional(), { - help: parameterHelp.executionRoleArn, + group: "Configuration:", }), flag("system-prompt", "the agent's system prompt", z.string().optional(), { - help: parameterHelp.systemPrompt, + group: AGENT, }), flag("model", "model configuration (JSON HarnessModelConfiguration)", z.string().optional(), { help: parameterHelp.model, + group: AGENT, }), flag("tools", "tools available to the agent (JSON HarnessTool[])", z.string().optional(), { help: parameterHelp.tools, + group: AGENT, }), flag("skills", "skills available to the agent (JSON HarnessSkill[])", z.string().optional(), { help: parameterHelp.skills, + group: AGENT, }), flag( "allowed-tools", "tool allowlist patterns (e.g. * or @serverName/toolName)", z.array(z.string()).optional(), - { help: parameterHelp.allowedTools }, + { help: parameterHelp.allowedTools, group: AGENT }, ), flag( "memory", "memory configuration (JSON HarnessMemoryConfiguration)", z.string().optional(), - { help: parameterHelp.memory }, + { help: parameterHelp.memory, group: AGENT }, ), flag( "clear-memory", "clear the memory configuration (pass true)", z.enum(["true", "false"]).optional(), + { group: AGENT }, ), flag( "truncation", "context truncation configuration (JSON HarnessTruncationConfiguration)", z.string().optional(), - { help: parameterHelp.truncation }, + { help: parameterHelp.truncation, group: AGENT }, ), flag( "environment", "compute environment configuration (JSON HarnessEnvironmentProviderRequest)", z.string().optional(), - { help: parameterHelp.environment }, + { help: parameterHelp.environment, group: COMPUTE }, ), flag( "environment-artifact", "environment artifact, e.g. a container image (JSON HarnessEnvironmentArtifact)", z.string().optional(), - { help: parameterHelp.environmentArtifact }, + { help: parameterHelp.environmentArtifact, group: COMPUTE }, ), flag( "clear-environment-artifact", "clear the environment artifact (pass true)", z.enum(["true", "false"]).optional(), + { group: COMPUTE }, ), flag( "environment-variables", "environment variables (JSON object; replaces all existing)", z.string().optional(), - { help: parameterHelp.environmentVariables }, + { help: parameterHelp.environmentVariables, group: COMPUTE }, ), flag( "authorizer-configuration", "inbound authorizer configuration (JSON AuthorizerConfiguration)", z.string().optional(), - { help: parameterHelp.authorizerConfiguration }, + { help: parameterHelp.authorizerConfiguration, group: ACCESS }, ), flag( "clear-authorizer-configuration", "clear the authorizer configuration (pass true)", z.enum(["true", "false"]).optional(), + { group: ACCESS }, ), - flag("max-iterations", "max agent loop iterations per invocation", z.number().optional()), - flag("max-tokens", "max total output tokens per invocation", z.number().optional()), - flag("timeout-seconds", "max duration in seconds per invocation", z.number().optional()), + flag("max-iterations", "max agent loop iterations per invocation", z.number().optional(), { + group: LIMITS, + }), + flag("max-tokens", "max total output tokens per invocation", z.number().optional(), { + group: LIMITS, + }), + flag("timeout-seconds", "max duration in seconds per invocation", z.number().optional(), { + group: LIMITS, + }), ], handle: async (ctx, flags) => { // Required at runtime but declared optional so that a bare diff --git a/src/handlers/observability/logs.ts b/src/handlers/observability/logs.ts index 4a149012a..569b54e93 100644 --- a/src/handlers/observability/logs.ts +++ b/src/handlers/observability/logs.ts @@ -23,24 +23,32 @@ const levelSchema = z ) .optional(); +const TIME_WINDOW = "Time window:"; +const FILTERING = "Filtering:"; + const logFlags = [ flag( "since", 'search window start: "5m", "1h", ISO 8601, epoch ms, or "now"', z.string().min(1).optional(), + { group: TIME_WINDOW }, ), flag( "until", 'search window end: "5m", "1h", ISO 8601, epoch ms, or "now"', z.string().min(1).optional(), + { group: TIME_WINDOW }, ), - flag("tail", "tail new log records", z.boolean().default(false)), - flag("level", `filter by log level (${LOG_LEVELS.join(", ")})`, levelSchema), - flag("query", "CloudWatch Logs filter pattern", z.string().optional()), + flag("tail", "tail new log records", z.boolean().default(false), { group: TIME_WINDOW }), + flag("level", `filter by log level (${LOG_LEVELS.join(", ")})`, levelSchema, { + group: FILTERING, + }), + flag("query", "CloudWatch Logs filter pattern", z.string().optional(), { group: FILTERING }), flag( "limit", "maximum number of log records to return in search mode", z.number().int().positive().optional(), + { group: FILTERING }, ), ] as const; diff --git a/src/handlers/runtime/invoke/index.tsx b/src/handlers/runtime/invoke/index.tsx index 750bdcf5c..090de1b3b 100644 --- a/src/handlers/runtime/invoke/index.tsx +++ b/src/handlers/runtime/invoke/index.tsx @@ -17,39 +17,92 @@ import { writeRuntimeInvokeResponse } from "./response"; import { RuntimeInvokeLaunchContextKey } from "./launchContext"; import { invokeRuntimeTarget } from "./operation"; +const TARGET = "Target:"; +const PAYLOAD = "Payload:"; +const SESSION = "Session:"; +const MCP = "MCP:"; +const TRACING = "Tracing:"; + export const createInvokeRuntimeHandler = (core: Core, io: AppIO) => createHandler({ name: "invoke", description: "invoke a Runtime", flags: [ - flag("id", "the ID of the Runtime", runtimeIdSchema.optional()), - flag("payload", "the inline payload to send", z.string().optional(), { - sensitive: true, + flag("id", "the ID of the Runtime", runtimeIdSchema.optional(), { group: TARGET }), + flag("qualifier", "the Runtime endpoint qualifier", z.string().optional(), { + group: TARGET, }), - flag("qualifier", "the Runtime endpoint qualifier", z.string().optional()), - flag("content-type", "the payload content type", z.string().optional()), - flag("accept", "the accepted response content type", z.string().optional()), - flag("session-id", "the Runtime session ID", z.string().optional()), - flag("user-id", 'the Runtime user ID (default "default")', z.string().optional()), - flag("header", "an ordered application header", z.array(z.string()).optional(), { + flag("payload", "the inline payload to send", z.string().optional(), { sensitive: true, + group: PAYLOAD, }), - flag("bearer-token", "the CUSTOM_JWT bearer token", z.string().optional(), { - sensitive: true, + flag("content-type", "the payload content type", z.string().optional(), { group: PAYLOAD }), + flag("accept", "the accepted response content type", z.string().optional(), { + group: PAYLOAD, }), - flag("mcp-session-id", "the MCP session ID", z.string().optional()), - flag("mcp-protocol-version", "the MCP protocol version", z.string().optional()), - flag("mcp-method", "the MCP method", z.string().optional()), - flag("mcp-name", "the MCP tool, resource, or prompt name", z.string().optional()), - flag("trace-id", "the X-Ray trace ID", z.string().optional()), - flag("trace-parent", "the W3C trace parent", z.string().optional()), - flag("trace-state", "the W3C trace state", z.string().optional()), - flag("baggage", "the W3C baggage", z.string().optional()), flag( "output-file", "the response output file", z.string().min(1, "requires a nonempty path").optional(), + { group: PAYLOAD }, ), + flag("session-id", "the Runtime session ID", z.string().optional(), { group: SESSION }), + flag("user-id", 'the Runtime user ID (default "default")', z.string().optional(), { + group: SESSION, + }), + flag("bearer-token", "the CUSTOM_JWT bearer token", z.string().optional(), { + sensitive: true, + group: "Authentication:", + }), + flag("header", "an ordered application header", z.array(z.string()).optional(), { + sensitive: true, + group: "Application headers:", + }), + flag("mcp-session-id", "the MCP session ID", z.string().optional(), { group: MCP }), + flag("mcp-protocol-version", "the MCP protocol version", z.string().optional(), { + group: MCP, + }), + flag("mcp-method", "the MCP method", z.string().optional(), { group: MCP }), + flag("mcp-name", "the MCP tool, resource, or prompt name", z.string().optional(), { + group: MCP, + }), + flag("trace-id", "the X-Ray trace ID", z.string().optional(), { + group: TRACING, + help: `(string) +The AWS X-Ray trace ID to associate this invocation with, sent as the +X-Amzn-Trace-Id header. Format: 1-<8 hex digits>-<24 hex digits>. + +Example: + --trace-id 1-5759e988-bd862e3fe1be46a994272793`, + }), + flag("trace-parent", "the W3C trace parent", z.string().optional(), { + group: TRACING, + help: `(string) +The W3C Trace Context traceparent header identifying the parent span. +Format: ---. + +Example: + --trace-parent 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01`, + }), + flag("trace-state", "the W3C trace state", z.string().optional(), { + group: TRACING, + help: `(string) +The W3C Trace Context tracestate header carrying vendor-specific trace +data. Format: a comma-separated list of key=value pairs. + +Example: + --trace-state vendor1=opaqueValue1,vendor2=opaqueValue2`, + }), + flag("baggage", "the W3C baggage", z.string().optional(), { + group: TRACING, + help: `(string) +The W3C Baggage header carrying application-defined key=value context +propagated across the request. Format: a comma-separated list of key=value +pairs. + +Example: + --baggage userId=alice,sessionId=abc123`, + }), ], handle: async (ctx, flags) => { if (flags.id === undefined) { diff --git a/src/handlers/runtime/logs/index.tsx b/src/handlers/runtime/logs/index.tsx index bea96272f..8e141b74c 100644 --- a/src/handlers/runtime/logs/index.tsx +++ b/src/handlers/runtime/logs/index.tsx @@ -7,9 +7,13 @@ import type { Core } from "../../types"; import { coreOptsFromCtx } from "../../utils"; import { runtimeIdSchema } from "../invoke/request"; +const LOG_SOURCE = "Log source:"; + const runtimeFlags = [ - flag("id", "the ID of the Runtime", runtimeIdSchema), - flag("qualifier", "the Runtime endpoint qualifier", z.string().min(1).optional()), + flag("id", "the ID of the Runtime", runtimeIdSchema, { group: LOG_SOURCE }), + flag("qualifier", "the Runtime endpoint qualifier", z.string().min(1).optional(), { + group: LOG_SOURCE, + }), ] as const; /**