Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/handlers/gateway/connector/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { gatewayIdSchema } from "../../invoke/request";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createGetGatewayConnectorHandler = (core: Core) =>
name: "get",
description: "get a connector-backed Gateway Target",
flags: [
flag("gateway-id", "the ID of the Gateway", z.string().min(1)),
flag("gateway-id", "the ID of the Gateway", gatewayIdSchema.min(1)),
flag("id", "the ID of the connector-backed Gateway Target", z.string().min(1)),
],
handle: async (ctx, flags) => {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/gateway/connector/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { gatewayIdSchema } from "../../invoke/request";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createListGatewayConnectorsHandler = (core: Core) =>
name: "list",
description: "list connectors configured for an AgentCore Gateway",
flags: [
flag("gateway-id", "the ID of the Gateway", z.string().min(1)),
flag("gateway-id", "the ID of the Gateway", gatewayIdSchema.min(1)),
flag("next-token", "pagination token returned by a previous request", z.string().optional()),
flag("max-results", "maximum number of items to return", z.number().optional()),
],
Expand Down
17 changes: 17 additions & 0 deletions src/handlers/gateway/gateway.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,23 @@ describe("gateway validation", () => {
},
);

test.each([
["Gateway get", ["gateway", "get", "--id"]],
["Target list", ["gateway", "target", "list", "--gateway-id"]],
["Rule list", ["gateway", "rule", "list", "--gateway-id"]],
["Connector list", ["gateway", "connector", "list", "--gateway-id"]],
] as const)("rejects a Gateway ARN for %s before calling Core", async (_name, args) => {
const core = new TestCoreClient();
const arn = `arn:aws:bedrock-agentcore:${REGION}:123456789012:gateway/${GATEWAY_ID}`;

await expectError(
run([...args, arn], core),
/must be a Gateway ID, not an ARN/,
InputValidationError,
);
expect(core.gateway.calls).toEqual([]);
});

test("rejects a non-numeric max-results value before calling Core", async () => {
const core = new TestCoreClient();

Expand Down
4 changes: 2 additions & 2 deletions src/handlers/gateway/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import z from "zod";
import { gatewayIdSchema } from "../invoke/request";
import { createHandler, flag } from "../../../router";
import { JsonRendererKey } from "../../../tui";
import type { Core } from "../../types";
Expand All @@ -8,7 +8,7 @@ export const createGetGatewayHandler = (core: Core) =>
createHandler({
name: "get",
description: "get an AgentCore Gateway",
flags: [flag("id", "the ID of the Gateway", z.string().min(1))],
flags: [flag("id", "the ID of the Gateway", gatewayIdSchema.min(1))],
handle: async (ctx, flags) => {
ctx
.require(JsonRendererKey)
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/gateway/rule/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { gatewayIdSchema } from "../../invoke/request";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createGetGatewayRuleHandler = (core: Core) =>
name: "get",
description: "get a Gateway Rule",
flags: [
flag("gateway-id", "the ID of the Gateway", z.string().min(1)),
flag("gateway-id", "the ID of the Gateway", gatewayIdSchema.min(1)),
flag("rule-id", "the ID of the Gateway Rule", z.string().min(1)),
],
handle: async (ctx, flags) => {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/gateway/rule/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { gatewayIdSchema } from "../../invoke/request";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createListGatewayRulesHandler = (core: Core) =>
name: "list",
description: "list Rules for an AgentCore Gateway",
flags: [
flag("gateway-id", "the ID of the Gateway", z.string().min(1)),
flag("gateway-id", "the ID of the Gateway", gatewayIdSchema.min(1)),
flag("next-token", "pagination token returned by a previous request", z.string().optional()),
flag("max-results", "maximum number of items to return", z.number().optional()),
],
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/gateway/target/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { gatewayIdSchema } from "../../invoke/request";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createGetGatewayTargetHandler = (core: Core) =>
name: "get",
description: "get a Gateway Target",
flags: [
flag("gateway-id", "the ID of the Gateway", z.string().min(1)),
flag("gateway-id", "the ID of the Gateway", gatewayIdSchema.min(1)),
flag("target-id", "the ID of the Gateway Target", z.string().min(1)),
],
handle: async (ctx, flags) => {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/gateway/target/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { gatewayIdSchema } from "../../invoke/request";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createListGatewayTargetsHandler = (core: Core) =>
name: "list",
description: "list Targets for an AgentCore Gateway",
flags: [
flag("gateway-id", "the ID of the Gateway", z.string().min(1)),
flag("gateway-id", "the ID of the Gateway", gatewayIdSchema.min(1)),
flag("next-token", "pagination token returned by a previous request", z.string().optional()),
flag("max-results", "maximum number of items to return", z.number().optional()),
],
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/delete/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../router";
import type { Core } from "../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createDeleteHarnessHandler = (core: Core) =>
name: "delete",
description: "delete a harness",
flags: [
flag("id", "the ID of the harness to delete", z.string().min(1).max(48)),
flag("id", "the ID of the harness to delete", harnessIdSchema),
flag(
"delete-managed-memory",
"whether to also delete the managed Memory (default true; pass false to keep it)",
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/endpoint/create/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createCreateEndpointHandler = (core: Core) =>
name: "create",
description: "create a harness endpoint",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("name", "the name of the endpoint", z.string().min(1)),
flag(
"target-version",
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/endpoint/delete/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createDeleteEndpointHandler = (core: Core) =>
name: "delete",
description: "delete a harness endpoint",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the endpoint name (qualifier)", z.string().min(1)),
],
handle: async (ctx, flags) => {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/endpoint/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createGetEndpointHandler = (core: Core) =>
name: "get",
description: "get a harness endpoint",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the endpoint name (qualifier)", z.string().min(1)),
],
handle: async (ctx, flags) => {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/endpoint/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createListEndpointsHandler = (core: Core) =>
name: "list",
description: "list a harness's endpoints",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("next-token", "next token to use on paginated", z.string().optional()),
flag("max-results", "max number of items to return", z.number().optional()),
],
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/endpoint/update/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createUpdateEndpointHandler = (core: Core) =>
name: "update",
description: "update a harness endpoint",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the endpoint name (qualifier)", z.string().min(1)),
flag("target-version", "the harness version the endpoint points to", z.string().optional()),
],
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/exec/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../idSchema";
import z from "zod";
import { createHandler, flag, PathKey } from "../../../router";
import type { AppIO } from "../../../io";
Expand All @@ -13,7 +14,7 @@ export const createExecHarnessHandler = (core: Core, io: AppIO) =>
name: "exec",
description: "run a shell command in a harness",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("command", "the shell command to run", z.string().optional()),
flag(
"session-id",
Expand Down
4 changes: 2 additions & 2 deletions src/handlers/harness/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import z from "zod";
import { harnessIdSchema } from "../idSchema";
import { createHandler, flag } from "../../../router";
import type { Core } from "../../types.tsx";
import { coreOptsFromCtx } from "../../utils.tsx";
Expand All @@ -8,7 +8,7 @@ export const createGetHarnessHandler = (core: Core) =>
createHandler({
name: "get",
description: "get a harness",
flags: [flag("id", "the ID of the harness", z.string().min(1).max(48))],
flags: [flag("id", "the ID of the harness", harnessIdSchema)],
handle: async (ctx, flags) => {
const harness = await core.harness.getHarness(flags["id"], coreOptsFromCtx(ctx));
ctx.require(JsonRendererKey).renderJson(harness);
Expand Down
18 changes: 18 additions & 0 deletions src/handlers/harness/idSchema.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, test } from "bun:test";
import { harnessIdSchema } from "./idSchema";

describe("harnessIdSchema", () => {
test("accepts the longest ID the service issues (40-char name + 10-char suffix)", () => {
const id = `${"a".repeat(40)}-${"B".repeat(10)}`;
expect(id).toHaveLength(51);
expect(harnessIdSchema.safeParse(id).success).toBe(true);
});

test("rejects an ARN with an ARN-specific message", () => {
const result = harnessIdSchema.safeParse(
"arn:aws:bedrock-agentcore:us-west-2:123456789012:harness/MyHarness-abcdefghij",
);
expect(result.success).toBe(false);
expect(result.error?.issues[0]?.message).toBe("must be a harness ID, not an ARN");
});
});
8 changes: 8 additions & 0 deletions src/handlers/harness/idSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import z from "zod";

// The service's harnessId is a <=40-char name plus "-" and a 10-char suffix: 51 max.
export const harnessIdSchema = z
.string()
.min(1)
.refine((value) => !value.startsWith("arn:"), "must be a harness ID, not an ARN")
.max(51);
3 changes: 2 additions & 1 deletion src/handlers/harness/invoke/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../idSchema";
import z from "zod";
import { createHandler, flag, PathKey } from "../../../router";
import type { AppIO } from "../../../io";
Expand All @@ -11,7 +12,7 @@ import { invokeHarnessTurn } from "./operation.ts";
import { HarnessInvokeLaunchContextKey } from "./launchContext";

export const invokeHarnessFlags = [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("prompt", "the message to send to the harness", z.string().optional()),
flag(
"user-id",
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/logs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../idSchema";
import z from "zod";
import { DEFAULT_ENDPOINT_QUALIFIER, runtimeLogGroup } from "../../../core/observability/index";
import type { AppIO } from "../../../io";
Expand All @@ -7,7 +8,7 @@ import type { Core } from "../../types";
import { coreOptsFromCtx } from "../../utils";

const harnessFlags = [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the harness endpoint qualifier", z.string().min(1).optional()),
] as const;

Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/shell/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../idSchema";
import z from "zod";
import { InputValidationError } from "../../../errors";
import type { AppIO } from "../../../io";
Expand All @@ -14,7 +15,7 @@ export const createHarnessShellHandler = (core: Core, io: AppIO) =>
name: "shell",
description: "open an interactive shell in a harness",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the harness endpoint qualifier", z.string().min(1).optional()),
flag("session-id", "the Runtime session ID to use", z.string().min(33).max(256).optional()),
flag("bearer-token", "the CUSTOM_JWT bearer token", z.string().optional(), {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/traces/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { DEFAULT_ENDPOINT_QUALIFIER, runtimeLogGroup } from "../../../../core/observability/index";
import type { AppIO } from "../../../../io";
Expand All @@ -8,7 +9,7 @@ import type { Core } from "../../../types";
import { coreOptsFromCtx } from "../../../utils";

const harnessFlags = [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the harness endpoint qualifier", z.string().min(1).optional()),
] as const;

Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/traces/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { DEFAULT_ENDPOINT_QUALIFIER, runtimeLogGroup } from "../../../../core/observability/index";
import type { AppIO } from "../../../../io";
Expand All @@ -7,7 +8,7 @@ import type { Core } from "../../../types";
import { coreOptsFromCtx } from "../../../utils";

const harnessFlags = [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("qualifier", "the harness endpoint qualifier", z.string().min(1).optional()),
] as const;

Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/update/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../idSchema";
import z from "zod";
import type {
AuthorizerConfiguration,
Expand Down Expand Up @@ -29,7 +30,7 @@ export const createUpdateHarnessHandler = (core: Core) =>
name: "update",
description: "update a harness (creates a new version)",
flags: [
flag("id", "the ID of the harness to update", z.string().min(1).max(48)),
flag("id", "the ID of the harness to update", harnessIdSchema),
flag("execution-role-arn", "IAM role the harness assumes", z.string().optional(), {
help: parameterHelp.executionRoleArn,
}),
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/version/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createGetVersionHandler = (core: Core) =>
name: "get",
description: "get a specific version of a harness",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("version", "the harness version to get", z.string().min(1)),
],
handle: async (ctx, flags) => {
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/harness/version/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { harnessIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import type { Core } from "../../../types.tsx";
Expand All @@ -9,7 +10,7 @@ export const createListVersionsHandler = (core: Core) =>
name: "list",
description: "list a harness's versions",
flags: [
flag("id", "the ID of the harness", z.string().min(1).max(48)),
flag("id", "the ID of the harness", harnessIdSchema),
flag("next-token", "next token to use on paginated", z.string().optional()),
flag("max-results", "max number of items to return", z.number().optional()),
],
Expand Down
3 changes: 2 additions & 1 deletion src/handlers/memory/actor/list/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { memoryIdSchema } from "../../idSchema";
import z from "zod";
import { createHandler, flag } from "../../../../router";
import { JsonRendererKey } from "../../../../tui";
Expand All @@ -9,7 +10,7 @@ export const createListMemoryActorsHandler = (core: Core) =>
name: "list",
description: "list actors in an AgentCore Memory",
flags: [
flag("id", "the ID of the Memory", z.string().min(1)),
flag("id", "the ID of the Memory", memoryIdSchema),
flag("max-results", "maximum number of actors to return", z.number().optional()),
flag("next-token", "pagination token returned by a previous request", z.string().optional()),
],
Expand Down
Loading
Loading