"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentEnvSpecSchema = exports.RuntimeEndpointSchema = exports.RuntimeEndpointNameSchema = exports.LifecycleConfigurationSchema = exports.LIFECYCLE_TIMEOUT_MAX = exports.LIFECYCLE_TIMEOUT_MIN = exports.FilesystemConfigurationSchema = exports.SessionStorageSchema = exports.RequestHeaderAllowlistSchema = exports.MAX_HEADER_ALLOWLIST_SIZE = exports.HEADER_NAME_PATTERN = exports.HEADER_ALLOWLIST_PREFIX = exports.NetworkConfigSchema = exports.InstrumentationSchema = exports.EnvVarSchema = exports.EntrypointSchema = exports.BuildTypeSchema = exports.AgentTypeSchema = exports.AccessSchema = exports.GatewayNameSchema = exports.EnvVarNameSchema = exports.AgentNameSchema = void 0;
exports.checkAllowlistHeader = checkAllowlistHeader;
/**
 * Agent Schema v2 - Clean, simplified model
 *
 * @module agent-env
 */
const constants_1 = require("../constants");
const auth_1 = require("./auth");
const tags_1 = require("./primitives/tags");
const zod_1 = require("zod");
// ============================================================================
// Name Schemas
// ============================================================================
// https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateAgentRuntime.html
exports.AgentNameSchema = zod_1.z
    .string()
    .min(1, 'Name is required')
    .max(48)
    .regex(/^[a-zA-Z][a-zA-Z0-9_]{0,47}$/, 'Must begin with a letter and contain only alphanumeric characters and underscores (max 48 chars)');
exports.EnvVarNameSchema = zod_1.z
    .string()
    .min(1)
    .max(255)
    .regex(/^[A-Za-z_][A-Za-z0-9_]*$/, 'Must start with a letter or underscore, contain only letters, digits, and underscores');
// https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateGateway.html
exports.GatewayNameSchema = zod_1.z
    .string()
    .min(1)
    .max(100)
    .regex(
// eslint-disable-next-line security/detect-unsafe-regex -- input bounded to 100 chars by .max(100) above
/^[0-9a-zA-Z](?:[0-9a-zA-Z-]*[0-9a-zA-Z])?$/, 'Gateway name must be alphanumeric with optional hyphens (max 100 chars)');
// ============================================================================
// Common Types
// ============================================================================
/** Access level for resource sharing */
exports.AccessSchema = zod_1.z.enum(['read', 'readwrite']);
// ============================================================================
// Agent Schema
// ============================================================================
exports.AgentTypeSchema = zod_1.z.literal('AgentCoreRuntime');
exports.BuildTypeSchema = zod_1.z.enum(['CodeZip', 'Container']);
// Use RuntimeVersionSchema from constants (supports both Python and Node/TypeScript)
// Not re-exported here to avoid duplicate export conflicts
/**
 * Entrypoint schema - supports both Python (.py) and TypeScript (.ts/.js) files.
 * Python: main.py or main.py:handler
 * TypeScript: main.ts, main.js, or index.ts
 */
exports.EntrypointSchema = zod_1.z
    .string()
    .min(1)
    .regex(
// eslint-disable-next-line security/detect-unsafe-regex -- character class quantifiers don't cause backtracking
/^[a-zA-Z0-9_][a-zA-Z0-9_/.-]*\.(py|ts|js)(:[a-zA-Z_][a-zA-Z0-9_]*)?$/, 'Must be a Python (.py) or TypeScript (.ts/.js) file path with optional handler (e.g., "main.py:handler" or "index.ts")');
const DirectoryPathSchema = zod_1.z.string().min(1);
exports.EnvVarSchema = zod_1.z.object({
    name: exports.EnvVarNameSchema,
    value: zod_1.z.string(),
});
/**
 * Instrumentation configuration for runtime observability.
 */
exports.InstrumentationSchema = zod_1.z.object({
    /**
     * Enable OpenTelemetry instrumentation using aws-opentelemetry-distro.
     * When enabled, the runtime entrypoint is wrapped with opentelemetry-instrument.
     * Defaults to true for new runtimes.
     */
    enableOtel: zod_1.z.boolean().default(true),
});
/**
 * Network configuration for VPC mode.
 * Required when networkMode is 'VPC'.
 */
exports.NetworkConfigSchema = zod_1.z.object({
    subnets: zod_1.z
        .array(zod_1.z.string().regex(/^subnet-[0-9a-zA-Z]{8,17}$/))
        .min(1)
        .max(16),
    securityGroups: zod_1.z
        .array(zod_1.z.string().regex(/^sg-[0-9a-zA-Z]{8,17}$/))
        .min(1)
        .max(16),
});
/**
 * Allowed request headers for the runtime.
 * Per https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-header-allowlist.html
 * any valid HTTP header name (alphanumeric, hyphens, underscores) may be allow-listed,
 * provided it is not structurally reserved (x-amz-*, x-amzn-* except Runtime-Custom-*).
 * Maximum 20 headers.
 */
exports.HEADER_ALLOWLIST_PREFIX = 'X-Amzn-Bedrock-AgentCore-Runtime-Custom-';
exports.HEADER_NAME_PATTERN = /^[A-Za-z0-9\-_]+$/;
exports.MAX_HEADER_ALLOWLIST_SIZE = 20;
/**
 * Validate a single allowlist header name. Returns null if valid, or a specific
 * error message describing which rule the input violated.
 *
 * Note: 'x-amz-' and 'x-amzn-' are disjoint prefixes (position 5 differs: '-' vs 'n'),
 * so the two checks below are independent.
 */
function checkAllowlistHeader(val) {
    if (!exports.HEADER_NAME_PATTERN.test(val)) {
        return `Header name "${val}" must contain only alphanumeric characters, hyphens, and underscores.`;
    }
    const lower = val.toLowerCase();
    if (lower.startsWith('x-amz-')) {
        return `Header "${val}" is not allowed. Headers starting with "x-amz-" are reserved for AWS request signing.`;
    }
    if (lower.startsWith('x-amzn-') && !lower.startsWith('x-amzn-bedrock-agentcore-runtime-custom-')) {
        return `Header "${val}" is not allowed. Headers starting with "x-amzn-" are reserved, except for "X-Amzn-Bedrock-AgentCore-Runtime-Custom-*".`;
    }
    return null;
}
exports.RequestHeaderAllowlistSchema = zod_1.z
    .array(zod_1.z.string().superRefine((val, ctx) => {
    const error = checkAllowlistHeader(val);
    if (error) {
        ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: error });
    }
}))
    .max(exports.MAX_HEADER_ALLOWLIST_SIZE, `Maximum ${exports.MAX_HEADER_ALLOWLIST_SIZE} headers allowed`);
/**
 * Session storage configuration for filesystem persistence.
 * Files written to mountPath persist across session stop/resume cycles.
 */
exports.SessionStorageSchema = zod_1.z.object({
    /** Absolute mount path under /mnt with exactly one subdirectory level (e.g. /mnt/data). */
    mountPath: zod_1.z
        .string()
        .regex(/^\/mnt\/[^/]+$/, 'Must be a path under /mnt with exactly one subdirectory (e.g. /mnt/data)'),
});
exports.FilesystemConfigurationSchema = zod_1.z.object({
    sessionStorage: exports.SessionStorageSchema,
});
/** Minimum allowed value for lifecycle timeout fields (seconds). */
exports.LIFECYCLE_TIMEOUT_MIN = 60;
/** Maximum allowed value for lifecycle timeout fields (seconds). */
exports.LIFECYCLE_TIMEOUT_MAX = 28800;
/**
 * Lifecycle configuration for runtime sessions.
 * Controls idle timeout and max lifetime of runtime instances.
 */
exports.LifecycleConfigurationSchema = zod_1.z
    .object({
    /** Idle session timeout in seconds. API default: 900s. */
    idleRuntimeSessionTimeout: zod_1.z.number().int().min(exports.LIFECYCLE_TIMEOUT_MIN).max(exports.LIFECYCLE_TIMEOUT_MAX).optional(),
    /** Max instance lifetime in seconds. API default: 28800s. */
    maxLifetime: zod_1.z.number().int().min(exports.LIFECYCLE_TIMEOUT_MIN).max(exports.LIFECYCLE_TIMEOUT_MAX).optional(),
})
    .superRefine((data, ctx) => {
    if (data.idleRuntimeSessionTimeout !== undefined && data.maxLifetime !== undefined) {
        if (data.idleRuntimeSessionTimeout > data.maxLifetime) {
            ctx.addIssue({
                code: zod_1.z.ZodIssueCode.custom,
                message: 'idleRuntimeSessionTimeout must be <= maxLifetime',
                path: ['idleRuntimeSessionTimeout'],
            });
        }
    }
});
// ============================================================================
// Runtime Endpoint Schema
// ============================================================================
/**
 * Endpoint name follows the AgentCore API regex for endpoint aliases.
 */
exports.RuntimeEndpointNameSchema = zod_1.z
    .string()
    .min(1, 'Endpoint name is required')
    .max(48)
    .regex(/^[a-zA-Z][a-zA-Z0-9_]{0,47}$/, 'Must begin with a letter and contain only alphanumeric characters and underscores (max 48 chars)');
exports.RuntimeEndpointSchema = zod_1.z.object({
    /** Version number this endpoint points to. Must be >= 1. */
    version: zod_1.z.number().int().min(1),
    /** Optional human-readable description of this endpoint. */
    description: zod_1.z.string().max(200).optional(),
});
/**
 * AgentEnvSpec - represents an AgentCore Runtime.
 * This is a top-level resource in the schema.
 */
exports.AgentEnvSpecSchema = zod_1.z
    .object({
    name: exports.AgentNameSchema,
    /** Optional description for the runtime. */
    description: zod_1.z.string().max(200).optional(),
    build: exports.BuildTypeSchema,
    entrypoint: exports.EntrypointSchema,
    codeLocation: DirectoryPathSchema,
    /** Custom Dockerfile name for Container builds. Must be a filename, not a path. Default: 'Dockerfile' */
    dockerfile: zod_1.z
        .string()
        .min(1)
        .max(255)
        .regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/, 'Must be a filename (no path separators or traversal)')
        .optional(),
    runtimeVersion: constants_1.RuntimeVersionSchema.optional(),
    /** Environment variables to set on the runtime */
    envVars: zod_1.z.array(exports.EnvVarSchema).optional(),
    /** Network mode for the runtime. Defaults to PUBLIC. */
    networkMode: constants_1.NetworkModeSchema.optional(),
    /** Network configuration for VPC mode. Required when networkMode is 'VPC'. */
    networkConfig: exports.NetworkConfigSchema.optional(),
    /** Instrumentation settings for observability. Defaults to OTel enabled. */
    instrumentation: exports.InstrumentationSchema.optional(),
    /** Protocol for the runtime (HTTP, MCP, A2A, AGUI). */
    protocol: constants_1.ProtocolModeSchema.optional(),
    /** Allowed request headers forwarded to the runtime at invocation time. */
    requestHeaderAllowlist: exports.RequestHeaderAllowlistSchema.optional(),
    /** ARN of an existing IAM execution role to use instead of creating a new one. */
    executionRoleArn: zod_1.z.string().optional(),
    /** Authorizer type for inbound requests. Defaults to AWS_IAM. */
    authorizerType: auth_1.RuntimeAuthorizerTypeSchema.optional(),
    /** Authorizer configuration. Required when authorizerType is CUSTOM_JWT. */
    authorizerConfiguration: auth_1.AuthorizerConfigSchema.optional(),
    tags: tags_1.TagsSchema.optional(),
    /** Lifecycle configuration for runtime sessions. */
    lifecycleConfiguration: exports.LifecycleConfigurationSchema.optional(),
    /** Filesystem configurations for session-scoped persistent storage. */
    filesystemConfigurations: zod_1.z.array(exports.FilesystemConfigurationSchema).optional(),
    /** Named endpoints (version aliases) for this runtime. Keys are endpoint names. */
    endpoints: zod_1.z.record(exports.RuntimeEndpointNameSchema, exports.RuntimeEndpointSchema).optional(),
})
    .superRefine((data, ctx) => {
    if (data.networkMode === 'VPC' && !data.networkConfig) {
        ctx.addIssue({
            code: zod_1.z.ZodIssueCode.custom,
            message: 'networkConfig is required when networkMode is VPC',
            path: ['networkConfig'],
        });
    }
    if (data.networkMode !== 'VPC' && data.networkConfig) {
        ctx.addIssue({
            code: zod_1.z.ZodIssueCode.custom,
            message: 'networkConfig is only allowed when networkMode is VPC',
            path: ['networkConfig'],
        });
    }
    if (data.authorizerType === 'CUSTOM_JWT' && !data.authorizerConfiguration?.customJwtAuthorizer) {
        ctx.addIssue({
            code: zod_1.z.ZodIssueCode.custom,
            message: 'authorizerConfiguration with customJwtAuthorizer is required when authorizerType is CUSTOM_JWT',
            path: ['authorizerConfiguration'],
        });
    }
    if (data.authorizerType !== 'CUSTOM_JWT' && data.authorizerConfiguration) {
        ctx.addIssue({
            code: zod_1.z.ZodIssueCode.custom,
            message: 'authorizerConfiguration is only allowed when authorizerType is CUSTOM_JWT',
            path: ['authorizerConfiguration'],
        });
    }
    // If adding more Container-specific fields, consider consolidating into a containerConfig object (see networkConfig pattern)
    if (data.build !== 'Container' && data.dockerfile) {
        ctx.addIssue({
            code: zod_1.z.ZodIssueCode.custom,
            message: 'dockerfile is only allowed for Container builds',
            path: ['dockerfile'],
        });
    }
});
//# sourceMappingURL=agent-env.js.map