@aws/nx-plugin 1.0.0-rc.27 → 1.0.0-rc.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/mcp-server/tools/general-guidance.js +1 -0
- package/src/mcp-server/tools/general-guidance.js.map +1 -1
- package/src/preset/generator.js +1 -1
- package/src/preset/generator.js.map +1 -1
- package/src/py/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +7 -18
- package/src/py/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +8 -19
- package/src/py/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +9 -24
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +3 -10
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +4 -0
- package/src/utils/agent-core-constructs/files/cdk/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.ts.template +12 -2
- package/src/utils/agent-core-constructs/files/cdk/core/agentcore-gateway/agentcore-gateway.ts.template +112 -0
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +115 -1
- package/src/utils/format.d.ts +1 -1
- package/src/utils/format.js +71 -4
- package/src/utils/format.js.map +1 -1
- package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +1 -0
- package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template +1 -0
package/package.json
CHANGED
|
@@ -49,6 +49,7 @@ ${PACKAGE_MANAGERS.map((pm)=>buildNxCommand('<options>', pm)).join(' - \n')}
|
|
|
49
49
|
- (Omit -D for production dependencies)
|
|
50
50
|
- When specifying project names as arguments to generators, prefer the _fully qualified_ project name, for example \`@workspace-name/project-name\`. Check the \`project.json\` file for the specific package to find its fully qualified name
|
|
51
51
|
- When no generator exists for a specific framework required, use the base \`ts#project\` and \`py#project\` generators and build on top.
|
|
52
|
+
- Leave the \`--infra\` option at its default value unless the user has explicitly instructed otherwise. Generators choose a sensible default type of infrastructure to deploy the project with, so only override \`--infra\` when the user has specified a particular requirement.
|
|
52
53
|
|
|
53
54
|
## Useful Commands
|
|
54
55
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/mcp-server/tools/general-guidance.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { NxGeneratorInfo } from '../../utils/generators';\nimport { IAC_PROVIDERS } from '../../utils/iac-providers';\nimport { buildNxCommand, fetchGuidePages } from '../generator-info';\nimport { PACKAGE_MANAGERS } from '../schema';\n\nexport const TOOL_SELECTION_GUIDE = `## Tool Selection Guide\n\n- Use the \\`general-guidance\\` tool for guidance and best practices for working with Nx and the Nx Plugin for AWS.\n- Use the \\`create-workspace-command\\` tool to discover how to create a workspace to start a new project.\n- Use the \\`list-generators\\` tool to discover the available generators and how to run them.\n- Use the \\`generator-guide\\` tool to retrieve detailed information about a specific generator.`;\n\n/**\n * Add a tool which provides general guidance for using Nx and the Nx Plugin for AWS\n */\nexport const addGeneralGuidanceTool = (\n server: McpServer,\n generators: NxGeneratorInfo[],\n) => {\n server.registerTool(\n 'general-guidance',\n {\n title: 'General Guidance',\n description:\n 'Tool for guidance and best practices for working with Nx and the Nx Plugin for AWS',\n },\n async () => ({\n content: [\n {\n type: 'text' as const,\n text: `# Nx Plugin for AWS Guidance\n\n${TOOL_SELECTION_GUIDE}\n\n## Getting Started\n\n- Choose a package manager first. You can choose between ${PACKAGE_MANAGERS.join(', ')}. It's recommended to use \"pnpm\" if the user has no preference\n- Choose an infrastructure as code (IaC) provider next. You can choose between ${IAC_PROVIDERS.join(', ')}. It's recommended to use CDK if the user has no preference\n- Next, you must create an Nx workspace. Use the \\`create-workspace-command\\` tool for more details, and provide it with your chosen package manager\n- After this, you can start scaffolding the main components of your application using generators. Use the \\`list-generators\\` tool to discover available generators, and the \\`generator-guide\\` tool for more detailed information about a specific generator\n\n## Nx Primer\n\n- Prefix nx commands with the appropriate prefix for your package manager, for example:\n${PACKAGE_MANAGERS.map((pm) => buildNxCommand('<options>', pm)).join(' - \\n')}\n- Each project in your workspace has a file named \\`project.json\\` which contains important project information such as its name, and defines the \"targets\" which can be run for that project, for example building or testing the project\n- Use the command \\`nx reset\\` to reset the Nx daemon when unexpected issues arise\n- After adding dependencies between TypeScript projects, use \\`nx sync\\` to ensure project references are set up correctly\n\n## General Instructions\n\n- Workspaces contain a single \\`package.json\\` file at the root which defines the dependencies for all projects. Therefore when installing dependencies, you must add these to the root workspace using the appropriate command for your package manager:\n - pnpm add -w -D <package>\n - yarn add -D <package>\n - npm install --legacy-peer-deps -D <package>\n - bun install -D <package>\n - (Omit -D for production dependencies)\n- When specifying project names as arguments to generators, prefer the _fully qualified_ project name, for example \\`@workspace-name/project-name\\`. Check the \\`project.json\\` file for the specific package to find its fully qualified name\n- When no generator exists for a specific framework required, use the base \\`ts#project\\` and \\`py#project\\` generators and build on top.\n\n## Useful Commands\n\n- Fix lint issues with \\`nx run-many --target lint --configuration=fix --all --output-style=stream\\`\n- Build all projects with \\`nx run-many --target build --all --output-style=stream\\`\n- Prefer importing the CDK constructs vended by generators in \\`packages/common/constructs\\` over writing your own\n\n## Best Practices\n\n- After running a generator, use the \\`nx show projects\\` command to check which projects have been added (if any)\n- Carefully examine the files that have been generated and always refer back to the generator guide when working in a generated project\n- Generate all projects into the \\`packages/\\` directory\n- After making changes to your projects, fix linting issues, then run a full build\n- When it's time to start testing a project, suggest to the user that infrastructure is deployed to AWS. For websites, if a runtime-config.json is needed, use the load:runtime-config target after a deployment to point a local website at a sandbox stack.\n\n## Batching Generators\n\nWhen scaffolding several projects in one go, chain generators to avoid a slow dependency install after every generator:\n\n- **Chain generators** with \\`&&\\` in a single command. Pass \\`--prefer-install-dependencies=false\\` on each generator except the last so dependencies install once at the end, for example:\n\n${PACKAGE_MANAGERS.map(\n (pm) => ` \\`\\`\\`bash\n ${buildNxCommand('g @aws/nx-plugin:ts#trpc-api --no-interactive --name=my-app-api --auth=IAM --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#react-website --no-interactive --name=my-app-website --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:connection --no-interactive --sourceProject=@my-app/my-app-website --targetProject=@my-app/my-app-api --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#infra --no-interactive --name=infra', pm)} && \\\\\n ${buildNxCommand('sync', pm)}\n \\`\\`\\``,\n).join('\\n')}\n\n- **\\`--prefer-install-dependencies=false\\`** asks a generator to defer its dependency install so the batch installs once at the end (the final generator above omits the flag and installs everything).\n- **\\`nx sync\\`** is required before building — generators modify TypeScript project references.\n\n## Detailed Guides\n\nPlease refer to the below documentation for important details regarding workspaces and working with TypeScript or Python projects.\n\n${await fetchGuidePages(['workspace', 'typescript-project', 'python-project'], generators)}\n\n `,\n },\n ],\n }),\n );\n};\n"],"names":["IAC_PROVIDERS","buildNxCommand","fetchGuidePages","PACKAGE_MANAGERS","TOOL_SELECTION_GUIDE","addGeneralGuidanceTool","server","generators","registerTool","title","description","content","type","text","join","map","pm"],"mappings":"AAAA;;;CAGC,GAGD,SAASA,aAAa,QAAQ,+BAA4B;AAC1D,SAASC,cAAc,EAAEC,eAAe,QAAQ,uBAAoB;AACpE,SAASC,gBAAgB,QAAQ,eAAY;AAE7C,OAAO,MAAMC,uBAAuB,CAAC;;;;;+FAK0D,CAAC,CAAC;AAEjG;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CACpCC,QACAC;IAEAD,OAAOE,YAAY,CACjB,oBACA;QACEC,OAAO;QACPC,aACE;IACJ,GACA,UAAa,CAAA;YACXC,SAAS;gBACP;oBACEC,MAAM;oBACNC,MAAM,CAAC;;AAEjB,EAAET,qBAAqB;;;;yDAIkC,EAAED,iBAAiBW,IAAI,CAAC,MAAM;+EACR,EAAEd,cAAcc,IAAI,CAAC,MAAM;;;;;;;AAO1G,EAAEX,iBAAiBY,GAAG,CAAC,CAACC,KAAOf,eAAe,aAAae,KAAKF,IAAI,CAAC,SAAS
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/mcp-server/tools/general-guidance.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { NxGeneratorInfo } from '../../utils/generators';\nimport { IAC_PROVIDERS } from '../../utils/iac-providers';\nimport { buildNxCommand, fetchGuidePages } from '../generator-info';\nimport { PACKAGE_MANAGERS } from '../schema';\n\nexport const TOOL_SELECTION_GUIDE = `## Tool Selection Guide\n\n- Use the \\`general-guidance\\` tool for guidance and best practices for working with Nx and the Nx Plugin for AWS.\n- Use the \\`create-workspace-command\\` tool to discover how to create a workspace to start a new project.\n- Use the \\`list-generators\\` tool to discover the available generators and how to run them.\n- Use the \\`generator-guide\\` tool to retrieve detailed information about a specific generator.`;\n\n/**\n * Add a tool which provides general guidance for using Nx and the Nx Plugin for AWS\n */\nexport const addGeneralGuidanceTool = (\n server: McpServer,\n generators: NxGeneratorInfo[],\n) => {\n server.registerTool(\n 'general-guidance',\n {\n title: 'General Guidance',\n description:\n 'Tool for guidance and best practices for working with Nx and the Nx Plugin for AWS',\n },\n async () => ({\n content: [\n {\n type: 'text' as const,\n text: `# Nx Plugin for AWS Guidance\n\n${TOOL_SELECTION_GUIDE}\n\n## Getting Started\n\n- Choose a package manager first. You can choose between ${PACKAGE_MANAGERS.join(', ')}. It's recommended to use \"pnpm\" if the user has no preference\n- Choose an infrastructure as code (IaC) provider next. You can choose between ${IAC_PROVIDERS.join(', ')}. It's recommended to use CDK if the user has no preference\n- Next, you must create an Nx workspace. Use the \\`create-workspace-command\\` tool for more details, and provide it with your chosen package manager\n- After this, you can start scaffolding the main components of your application using generators. Use the \\`list-generators\\` tool to discover available generators, and the \\`generator-guide\\` tool for more detailed information about a specific generator\n\n## Nx Primer\n\n- Prefix nx commands with the appropriate prefix for your package manager, for example:\n${PACKAGE_MANAGERS.map((pm) => buildNxCommand('<options>', pm)).join(' - \\n')}\n- Each project in your workspace has a file named \\`project.json\\` which contains important project information such as its name, and defines the \"targets\" which can be run for that project, for example building or testing the project\n- Use the command \\`nx reset\\` to reset the Nx daemon when unexpected issues arise\n- After adding dependencies between TypeScript projects, use \\`nx sync\\` to ensure project references are set up correctly\n\n## General Instructions\n\n- Workspaces contain a single \\`package.json\\` file at the root which defines the dependencies for all projects. Therefore when installing dependencies, you must add these to the root workspace using the appropriate command for your package manager:\n - pnpm add -w -D <package>\n - yarn add -D <package>\n - npm install --legacy-peer-deps -D <package>\n - bun install -D <package>\n - (Omit -D for production dependencies)\n- When specifying project names as arguments to generators, prefer the _fully qualified_ project name, for example \\`@workspace-name/project-name\\`. Check the \\`project.json\\` file for the specific package to find its fully qualified name\n- When no generator exists for a specific framework required, use the base \\`ts#project\\` and \\`py#project\\` generators and build on top.\n- Leave the \\`--infra\\` option at its default value unless the user has explicitly instructed otherwise. Generators choose a sensible default type of infrastructure to deploy the project with, so only override \\`--infra\\` when the user has specified a particular requirement.\n\n## Useful Commands\n\n- Fix lint issues with \\`nx run-many --target lint --configuration=fix --all --output-style=stream\\`\n- Build all projects with \\`nx run-many --target build --all --output-style=stream\\`\n- Prefer importing the CDK constructs vended by generators in \\`packages/common/constructs\\` over writing your own\n\n## Best Practices\n\n- After running a generator, use the \\`nx show projects\\` command to check which projects have been added (if any)\n- Carefully examine the files that have been generated and always refer back to the generator guide when working in a generated project\n- Generate all projects into the \\`packages/\\` directory\n- After making changes to your projects, fix linting issues, then run a full build\n- When it's time to start testing a project, suggest to the user that infrastructure is deployed to AWS. For websites, if a runtime-config.json is needed, use the load:runtime-config target after a deployment to point a local website at a sandbox stack.\n\n## Batching Generators\n\nWhen scaffolding several projects in one go, chain generators to avoid a slow dependency install after every generator:\n\n- **Chain generators** with \\`&&\\` in a single command. Pass \\`--prefer-install-dependencies=false\\` on each generator except the last so dependencies install once at the end, for example:\n\n${PACKAGE_MANAGERS.map(\n (pm) => ` \\`\\`\\`bash\n ${buildNxCommand('g @aws/nx-plugin:ts#trpc-api --no-interactive --name=my-app-api --auth=IAM --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#react-website --no-interactive --name=my-app-website --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:connection --no-interactive --sourceProject=@my-app/my-app-website --targetProject=@my-app/my-app-api --prefer-install-dependencies=false', pm)} && \\\\\n ${buildNxCommand('g @aws/nx-plugin:ts#infra --no-interactive --name=infra', pm)} && \\\\\n ${buildNxCommand('sync', pm)}\n \\`\\`\\``,\n).join('\\n')}\n\n- **\\`--prefer-install-dependencies=false\\`** asks a generator to defer its dependency install so the batch installs once at the end (the final generator above omits the flag and installs everything).\n- **\\`nx sync\\`** is required before building — generators modify TypeScript project references.\n\n## Detailed Guides\n\nPlease refer to the below documentation for important details regarding workspaces and working with TypeScript or Python projects.\n\n${await fetchGuidePages(['workspace', 'typescript-project', 'python-project'], generators)}\n\n `,\n },\n ],\n }),\n );\n};\n"],"names":["IAC_PROVIDERS","buildNxCommand","fetchGuidePages","PACKAGE_MANAGERS","TOOL_SELECTION_GUIDE","addGeneralGuidanceTool","server","generators","registerTool","title","description","content","type","text","join","map","pm"],"mappings":"AAAA;;;CAGC,GAGD,SAASA,aAAa,QAAQ,+BAA4B;AAC1D,SAASC,cAAc,EAAEC,eAAe,QAAQ,uBAAoB;AACpE,SAASC,gBAAgB,QAAQ,eAAY;AAE7C,OAAO,MAAMC,uBAAuB,CAAC;;;;;+FAK0D,CAAC,CAAC;AAEjG;;CAEC,GACD,OAAO,MAAMC,yBAAyB,CACpCC,QACAC;IAEAD,OAAOE,YAAY,CACjB,oBACA;QACEC,OAAO;QACPC,aACE;IACJ,GACA,UAAa,CAAA;YACXC,SAAS;gBACP;oBACEC,MAAM;oBACNC,MAAM,CAAC;;AAEjB,EAAET,qBAAqB;;;;yDAIkC,EAAED,iBAAiBW,IAAI,CAAC,MAAM;+EACR,EAAEd,cAAcc,IAAI,CAAC,MAAM;;;;;;;AAO1G,EAAEX,iBAAiBY,GAAG,CAAC,CAACC,KAAOf,eAAe,aAAae,KAAKF,IAAI,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqC9E,EAAEX,iBAAiBY,GAAG,CACpB,CAACC,KAAO,CAAC;EACT,EAAEf,eAAe,kHAAkHe,IAAI;IACrI,EAAEf,eAAe,gHAAgHe,IAAI;IACrI,EAAEf,eAAe,8JAA8Je,IAAI;IACnL,EAAEf,eAAe,2DAA2De,IAAI;IAChF,EAAEf,eAAe,QAAQe,IAAI;QACzB,CAAC,EACPF,IAAI,CAAC,MAAM;;;;;;;;;AASb,EAAE,MAAMZ,gBAAgB;wBAAC;wBAAa;wBAAsB;qBAAiB,EAAEK,YAAY;;IAEvF,CAAC;gBACG;aACD;QACH,CAAA;AAEJ,EAAE"}
|
package/src/preset/generator.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/ import { addDependenciesToPackageJson, detectPackageManager, generateFiles, joinPathFragments, OverwriteStrategy, readNxJson, updateJson, updateNxJson } from "@nx/devkit";
|
|
5
5
|
import { initGenerator } from "@nx/js";
|
|
6
6
|
import { execSync } from "child_process";
|
|
7
|
-
import
|
|
7
|
+
import enquirer from "enquirer";
|
|
8
8
|
import { readFileSync } from "fs";
|
|
9
9
|
import yaml from "js-yaml";
|
|
10
10
|
import { readModulePackageJson } from "nx/src/utils/package-json";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/preset/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n addDependenciesToPackageJson,\n detectPackageManager,\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n readNxJson,\n type Tree,\n updateJson,\n updateNxJson,\n} from '@nx/devkit';\nimport { initGenerator } from '@nx/js';\nimport { execSync } from 'child_process';\nimport * as enquirer from 'enquirer';\nimport { readFileSync } from 'fs';\nimport yaml from 'js-yaml';\nimport { readModulePackageJson } from 'nx/src/utils/package-json';\nimport GeneratorsJson from '../../generators.json' with { type: 'json' };\nimport { SYNC_GENERATOR_NAME as TS_SYNC_GENERATOR_NAME } from '../ts/sync/generator';\nimport {\n ensureAwsNxPluginConfig,\n updateAwsNxPluginConfig,\n} from '../utils/config/utils';\nimport { inferContainers } from '../utils/containers';\nimport { DEFAULT_BIOME_CONFIG, formatFilesInSubtree } from '../utils/format';\nimport { installDependencies } from '../utils/install';\nimport { configureMcpServers } from '../utils/mcp';\nimport { addGeneratorMetricsIfApplicable } from '../utils/metrics';\nimport { getNpmScope } from '../utils/npm-scope';\nimport { getGeneratorInfo, type NxGeneratorInfo } from '../utils/nx';\nimport { getPackageManagerDisplayCommands } from '../utils/pkg-manager';\nimport { withVersions } from '../utils/versions';\nimport type { PresetGeneratorSchema } from './schema';\n\nconst WORKSPACES = ['packages/*'];\nconst NX_TYPESCRIPT_SYNC_GENERATOR = '@nx/js:typescript-sync';\n\n// Built dependencies whose install scripts the generated workspace trusts.\n// `onlyBuiltDependencies` is the pnpm 10 key (silently ignored by pnpm 11);\n// pnpm 11 reads `allowBuilds` instead. Any dep NOT in this allowlist will\n// have its install scripts skipped with a warning — matching pnpm 10's\n// default behaviour. The user can opt-in later via `pnpm approve-builds`.\nconst PNPM_BUILT_DEPENDENCIES = ['@swc/core', 'esbuild', 'nx', 'sharp'];\n\nexport const PRESET_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\nconst setUpWorkspaces = (tree: Tree) => {\n if (detectPackageManager() === 'pnpm') {\n tree.write(\n 'pnpm-workspace.yaml',\n yaml.dump(\n {\n packages: WORKSPACES,\n allowBuilds: Object.fromEntries(\n PNPM_BUILT_DEPENDENCIES.map((dep) => [dep, true]),\n ),\n onlyBuiltDependencies: PNPM_BUILT_DEPENDENCIES,\n },\n { quotingType: \"'\" },\n ),\n );\n } else {\n updateJson(tree, 'package.json', (json) => {\n json.workspaces = WORKSPACES;\n return json;\n });\n }\n};\n\n/**\n * Determines if the current user is an Amazon employee based on their git configuration.\n *\n * This function checks the git user email configuration to identify Amazon employees\n * by looking for email addresses with Amazon domains (e.g., @amazon.com, @amazon.co.uk).\n *\n * @returns {boolean} True if the user's git email has an Amazon domain, false otherwise\n *\n * @example\n * // Returns true for Amazon employee emails\n * // git config user.email = \"john.doe@amazon.com\"\n * isAmazonian(); // true\n *\n * @example\n * // Returns false for non-Amazon emails\n * // git config user.email = \"user@example.com\"\n * isAmazonian(); // false\n *\n * @example\n * // Returns false when git config is not available or throws an error\n * isAmazonian(); // false\n */\nexport function isAmazonian(): boolean {\n try {\n // Execute git command to retrieve the user's configured email address\n const gitEmail = execSync('git config user.email', {\n encoding: 'utf8',\n }).trim();\n\n // Return false if no email is configured\n if (!gitEmail) {\n return false;\n }\n\n // Split email address to extract domain part\n const emailParts = gitEmail.split('@');\n if (emailParts.length < 2) {\n return false;\n }\n\n // Extract domain and normalize to lowercase for comparison\n const domain = emailParts[1].toLowerCase();\n\n // Check if domain starts with 'amazon.' (covers amazon.com, amazon.co.uk, etc.)\n return domain.startsWith('amazon.');\n } catch (error) {\n // Return false if git command fails or any other error occurs\n // This handles cases where git is not installed or configured\n return false;\n }\n}\n\nconst setUpGitSecrets = (tree: Tree) => {\n const gitSecretsDir = joinPathFragments(\n import.meta.dirname,\n 'git-secrets-files',\n 'git-secrets-dir',\n );\n const huskyDir = joinPathFragments(\n import.meta.dirname,\n 'git-secrets-files',\n 'husky-dir',\n );\n\n tree.write(\n '.git-secrets/git-secrets',\n readFileSync(joinPathFragments(gitSecretsDir, 'git-secrets'), 'utf-8'),\n );\n tree.write(\n '.husky/pre-commit',\n readFileSync(joinPathFragments(huskyDir, 'pre-commit'), 'utf-8'),\n );\n tree.write('.gitallowed', '\\\\.git-secrets/git-secrets:\\n');\n\n updateJson(tree, 'package.json', (json) => ({\n ...json,\n scripts: {\n ...json.scripts,\n prepare: 'husky',\n },\n }));\n\n addDependenciesToPackageJson(tree, {}, withVersions(['husky']));\n};\n\nexport const presetGenerator = async (\n tree: Tree,\n {\n iac,\n gitSecrets,\n mcp,\n containers,\n preferInstallDependencies,\n }: PresetGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const resolvedContainers =\n !containers || containers === 'infer' ? inferContainers() : containers;\n if (\n isAmazonian() &&\n !process.env.VITEST &&\n !process.env.CI &&\n process.env.NX_DRY_RUN !== 'true' &&\n process.env.NX_INTERACTIVE !== 'false'\n ) {\n const { engagementId } = await enquirer.prompt<{ engagementId?: string }>([\n {\n name: 'engagementId',\n message: 'Please enter your engagementId (if known)',\n type: 'input',\n initial: 'None',\n },\n ]);\n\n if (engagementId != 'None') {\n await ensureAwsNxPluginConfig(tree);\n await updateAwsNxPluginConfig(tree, { tags: [engagementId] });\n }\n }\n\n // Write IaC provider and container engine to plugin config\n await ensureAwsNxPluginConfig(tree);\n await updateAwsNxPluginConfig(tree, {\n iac: { provider: iac },\n containers: { engine: resolvedContainers },\n });\n\n await initGenerator(tree, {\n formatter: 'none',\n addTsPlugin: true,\n });\n\n tree.delete('apps/.gitkeep');\n tree.delete('libs/.gitkeep');\n tree.write('packages/.gitkeep', '');\n\n setUpWorkspaces(tree);\n\n const nxJson = readNxJson(tree);\n updateNxJson(tree, {\n ...nxJson,\n analytics: false,\n targetDefaults: {\n ...nxJson.targetDefaults,\n compile: {\n ...nxJson.targetDefaults?.compile,\n syncGenerators: [\n ...(nxJson.targetDefaults?.compile?.syncGenerators ?? []).filter(\n (g) =>\n ![TS_SYNC_GENERATOR_NAME, NX_TYPESCRIPT_SYNC_GENERATOR].includes(\n g,\n ),\n ),\n NX_TYPESCRIPT_SYNC_GENERATOR,\n TS_SYNC_GENERATOR_NAME,\n ],\n },\n },\n });\n\n updateJson(tree, 'package.json', (packageJson) => ({\n ...packageJson,\n type: 'module',\n scripts: {\n ...packageJson.scripts,\n dev: 'nx run-many --target dev',\n build: 'nx run-many --target build',\n lint: 'nx run-many --target lint --configuration=fix',\n test: 'nx run-many --target test --all',\n 'build:skip-lint': 'nx run-many --target build --configuration=skip-lint',\n 'build:all': 'nx run-many --target build --all',\n 'affected:all': 'nx affected --target build',\n },\n }));\n\n addDependenciesToPackageJson(\n tree,\n {},\n {\n '@nx/workspace': readModulePackageJson('@nx/js').packageJson.version,\n ...withVersions(['typescript', '@biomejs/biome']),\n },\n );\n\n // Write biome.json for formatting and linting\n if (!tree.exists('biome.json')) {\n tree.write('biome.json', JSON.stringify(DEFAULT_BIOME_CONFIG, null, 2));\n }\n\n generateFiles(\n tree, // the virtual file system\n joinPathFragments(import.meta.dirname, 'files'),\n '.',\n {\n projectName: getNpmScope(tree),\n generators: Object.entries(GeneratorsJson.generators)\n .filter(([_, v]) => !v['hidden'])\n .map(([k, v]) => ({ name: k, description: v.description })),\n ...(() => {\n const cmds = getPackageManagerDisplayCommands();\n return {\n pkgMgrCmd: cmds.exec,\n buildCmd: `${cmds.run} build`,\n lintCmd: `${cmds.run} lint`,\n };\n })(),\n },\n {\n overwriteStrategy: OverwriteStrategy.Overwrite,\n },\n );\n\n if (gitSecrets !== false) {\n setUpGitSecrets(tree);\n }\n\n if (mcp !== false) {\n configureMcpServers(tree);\n }\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\nexport default presetGenerator;\n"],"names":["addDependenciesToPackageJson","detectPackageManager","generateFiles","joinPathFragments","OverwriteStrategy","readNxJson","updateJson","updateNxJson","initGenerator","execSync","enquirer","readFileSync","yaml","readModulePackageJson","GeneratorsJson","type","SYNC_GENERATOR_NAME","TS_SYNC_GENERATOR_NAME","ensureAwsNxPluginConfig","updateAwsNxPluginConfig","inferContainers","DEFAULT_BIOME_CONFIG","formatFilesInSubtree","installDependencies","configureMcpServers","getNpmScope","getGeneratorInfo","getPackageManagerDisplayCommands","withVersions","WORKSPACES","NX_TYPESCRIPT_SYNC_GENERATOR","PNPM_BUILT_DEPENDENCIES","PRESET_GENERATOR_INFO","filename","setUpWorkspaces","tree","write","dump","packages","allowBuilds","Object","fromEntries","map","dep","onlyBuiltDependencies","quotingType","json","workspaces","isAmazonian","gitEmail","encoding","trim","emailParts","split","length","domain","toLowerCase","startsWith","error","setUpGitSecrets","gitSecretsDir","dirname","huskyDir","scripts","prepare","presetGenerator","iac","gitSecrets","mcp","containers","preferInstallDependencies","resolvedContainers","process","env","VITEST","CI","NX_DRY_RUN","NX_INTERACTIVE","engagementId","prompt","name","message","initial","tags","provider","engine","formatter","addTsPlugin","delete","nxJson","analytics","targetDefaults","compile","syncGenerators","filter","g","includes","packageJson","dev","build","lint","test","version","exists","JSON","stringify","projectName","generators","entries","_","v","k","description","cmds","pkgMgrCmd","exec","buildCmd","run","lintCmd","overwriteStrategy","Overwrite","languages"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/preset/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n addDependenciesToPackageJson,\n detectPackageManager,\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n readNxJson,\n type Tree,\n updateJson,\n updateNxJson,\n} from '@nx/devkit';\nimport { initGenerator } from '@nx/js';\nimport { execSync } from 'child_process';\nimport enquirer from 'enquirer';\nimport { readFileSync } from 'fs';\nimport yaml from 'js-yaml';\nimport { readModulePackageJson } from 'nx/src/utils/package-json';\nimport GeneratorsJson from '../../generators.json' with { type: 'json' };\nimport { SYNC_GENERATOR_NAME as TS_SYNC_GENERATOR_NAME } from '../ts/sync/generator';\nimport {\n ensureAwsNxPluginConfig,\n updateAwsNxPluginConfig,\n} from '../utils/config/utils';\nimport { inferContainers } from '../utils/containers';\nimport { DEFAULT_BIOME_CONFIG, formatFilesInSubtree } from '../utils/format';\nimport { installDependencies } from '../utils/install';\nimport { configureMcpServers } from '../utils/mcp';\nimport { addGeneratorMetricsIfApplicable } from '../utils/metrics';\nimport { getNpmScope } from '../utils/npm-scope';\nimport { getGeneratorInfo, type NxGeneratorInfo } from '../utils/nx';\nimport { getPackageManagerDisplayCommands } from '../utils/pkg-manager';\nimport { withVersions } from '../utils/versions';\nimport type { PresetGeneratorSchema } from './schema';\n\nconst WORKSPACES = ['packages/*'];\nconst NX_TYPESCRIPT_SYNC_GENERATOR = '@nx/js:typescript-sync';\n\n// Built dependencies whose install scripts the generated workspace trusts.\n// `onlyBuiltDependencies` is the pnpm 10 key (silently ignored by pnpm 11);\n// pnpm 11 reads `allowBuilds` instead. Any dep NOT in this allowlist will\n// have its install scripts skipped with a warning — matching pnpm 10's\n// default behaviour. The user can opt-in later via `pnpm approve-builds`.\nconst PNPM_BUILT_DEPENDENCIES = ['@swc/core', 'esbuild', 'nx', 'sharp'];\n\nexport const PRESET_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\nconst setUpWorkspaces = (tree: Tree) => {\n if (detectPackageManager() === 'pnpm') {\n tree.write(\n 'pnpm-workspace.yaml',\n yaml.dump(\n {\n packages: WORKSPACES,\n allowBuilds: Object.fromEntries(\n PNPM_BUILT_DEPENDENCIES.map((dep) => [dep, true]),\n ),\n onlyBuiltDependencies: PNPM_BUILT_DEPENDENCIES,\n },\n { quotingType: \"'\" },\n ),\n );\n } else {\n updateJson(tree, 'package.json', (json) => {\n json.workspaces = WORKSPACES;\n return json;\n });\n }\n};\n\n/**\n * Determines if the current user is an Amazon employee based on their git configuration.\n *\n * This function checks the git user email configuration to identify Amazon employees\n * by looking for email addresses with Amazon domains (e.g., @amazon.com, @amazon.co.uk).\n *\n * @returns {boolean} True if the user's git email has an Amazon domain, false otherwise\n *\n * @example\n * // Returns true for Amazon employee emails\n * // git config user.email = \"john.doe@amazon.com\"\n * isAmazonian(); // true\n *\n * @example\n * // Returns false for non-Amazon emails\n * // git config user.email = \"user@example.com\"\n * isAmazonian(); // false\n *\n * @example\n * // Returns false when git config is not available or throws an error\n * isAmazonian(); // false\n */\nexport function isAmazonian(): boolean {\n try {\n // Execute git command to retrieve the user's configured email address\n const gitEmail = execSync('git config user.email', {\n encoding: 'utf8',\n }).trim();\n\n // Return false if no email is configured\n if (!gitEmail) {\n return false;\n }\n\n // Split email address to extract domain part\n const emailParts = gitEmail.split('@');\n if (emailParts.length < 2) {\n return false;\n }\n\n // Extract domain and normalize to lowercase for comparison\n const domain = emailParts[1].toLowerCase();\n\n // Check if domain starts with 'amazon.' (covers amazon.com, amazon.co.uk, etc.)\n return domain.startsWith('amazon.');\n } catch (error) {\n // Return false if git command fails or any other error occurs\n // This handles cases where git is not installed or configured\n return false;\n }\n}\n\nconst setUpGitSecrets = (tree: Tree) => {\n const gitSecretsDir = joinPathFragments(\n import.meta.dirname,\n 'git-secrets-files',\n 'git-secrets-dir',\n );\n const huskyDir = joinPathFragments(\n import.meta.dirname,\n 'git-secrets-files',\n 'husky-dir',\n );\n\n tree.write(\n '.git-secrets/git-secrets',\n readFileSync(joinPathFragments(gitSecretsDir, 'git-secrets'), 'utf-8'),\n );\n tree.write(\n '.husky/pre-commit',\n readFileSync(joinPathFragments(huskyDir, 'pre-commit'), 'utf-8'),\n );\n tree.write('.gitallowed', '\\\\.git-secrets/git-secrets:\\n');\n\n updateJson(tree, 'package.json', (json) => ({\n ...json,\n scripts: {\n ...json.scripts,\n prepare: 'husky',\n },\n }));\n\n addDependenciesToPackageJson(tree, {}, withVersions(['husky']));\n};\n\nexport const presetGenerator = async (\n tree: Tree,\n {\n iac,\n gitSecrets,\n mcp,\n containers,\n preferInstallDependencies,\n }: PresetGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const resolvedContainers =\n !containers || containers === 'infer' ? inferContainers() : containers;\n if (\n isAmazonian() &&\n !process.env.VITEST &&\n !process.env.CI &&\n process.env.NX_DRY_RUN !== 'true' &&\n process.env.NX_INTERACTIVE !== 'false'\n ) {\n const { engagementId } = await enquirer.prompt<{ engagementId?: string }>([\n {\n name: 'engagementId',\n message: 'Please enter your engagementId (if known)',\n type: 'input',\n initial: 'None',\n },\n ]);\n\n if (engagementId != 'None') {\n await ensureAwsNxPluginConfig(tree);\n await updateAwsNxPluginConfig(tree, { tags: [engagementId] });\n }\n }\n\n // Write IaC provider and container engine to plugin config\n await ensureAwsNxPluginConfig(tree);\n await updateAwsNxPluginConfig(tree, {\n iac: { provider: iac },\n containers: { engine: resolvedContainers },\n });\n\n await initGenerator(tree, {\n formatter: 'none',\n addTsPlugin: true,\n });\n\n tree.delete('apps/.gitkeep');\n tree.delete('libs/.gitkeep');\n tree.write('packages/.gitkeep', '');\n\n setUpWorkspaces(tree);\n\n const nxJson = readNxJson(tree);\n updateNxJson(tree, {\n ...nxJson,\n analytics: false,\n targetDefaults: {\n ...nxJson.targetDefaults,\n compile: {\n ...nxJson.targetDefaults?.compile,\n syncGenerators: [\n ...(nxJson.targetDefaults?.compile?.syncGenerators ?? []).filter(\n (g) =>\n ![TS_SYNC_GENERATOR_NAME, NX_TYPESCRIPT_SYNC_GENERATOR].includes(\n g,\n ),\n ),\n NX_TYPESCRIPT_SYNC_GENERATOR,\n TS_SYNC_GENERATOR_NAME,\n ],\n },\n },\n });\n\n updateJson(tree, 'package.json', (packageJson) => ({\n ...packageJson,\n type: 'module',\n scripts: {\n ...packageJson.scripts,\n dev: 'nx run-many --target dev',\n build: 'nx run-many --target build',\n lint: 'nx run-many --target lint --configuration=fix',\n test: 'nx run-many --target test --all',\n 'build:skip-lint': 'nx run-many --target build --configuration=skip-lint',\n 'build:all': 'nx run-many --target build --all',\n 'affected:all': 'nx affected --target build',\n },\n }));\n\n addDependenciesToPackageJson(\n tree,\n {},\n {\n '@nx/workspace': readModulePackageJson('@nx/js').packageJson.version,\n ...withVersions(['typescript', '@biomejs/biome']),\n },\n );\n\n // Write biome.json for formatting and linting\n if (!tree.exists('biome.json')) {\n tree.write('biome.json', JSON.stringify(DEFAULT_BIOME_CONFIG, null, 2));\n }\n\n generateFiles(\n tree, // the virtual file system\n joinPathFragments(import.meta.dirname, 'files'),\n '.',\n {\n projectName: getNpmScope(tree),\n generators: Object.entries(GeneratorsJson.generators)\n .filter(([_, v]) => !v['hidden'])\n .map(([k, v]) => ({ name: k, description: v.description })),\n ...(() => {\n const cmds = getPackageManagerDisplayCommands();\n return {\n pkgMgrCmd: cmds.exec,\n buildCmd: `${cmds.run} build`,\n lintCmd: `${cmds.run} lint`,\n };\n })(),\n },\n {\n overwriteStrategy: OverwriteStrategy.Overwrite,\n },\n );\n\n if (gitSecrets !== false) {\n setUpGitSecrets(tree);\n }\n\n if (mcp !== false) {\n configureMcpServers(tree);\n }\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\nexport default presetGenerator;\n"],"names":["addDependenciesToPackageJson","detectPackageManager","generateFiles","joinPathFragments","OverwriteStrategy","readNxJson","updateJson","updateNxJson","initGenerator","execSync","enquirer","readFileSync","yaml","readModulePackageJson","GeneratorsJson","type","SYNC_GENERATOR_NAME","TS_SYNC_GENERATOR_NAME","ensureAwsNxPluginConfig","updateAwsNxPluginConfig","inferContainers","DEFAULT_BIOME_CONFIG","formatFilesInSubtree","installDependencies","configureMcpServers","getNpmScope","getGeneratorInfo","getPackageManagerDisplayCommands","withVersions","WORKSPACES","NX_TYPESCRIPT_SYNC_GENERATOR","PNPM_BUILT_DEPENDENCIES","PRESET_GENERATOR_INFO","filename","setUpWorkspaces","tree","write","dump","packages","allowBuilds","Object","fromEntries","map","dep","onlyBuiltDependencies","quotingType","json","workspaces","isAmazonian","gitEmail","encoding","trim","emailParts","split","length","domain","toLowerCase","startsWith","error","setUpGitSecrets","gitSecretsDir","dirname","huskyDir","scripts","prepare","presetGenerator","iac","gitSecrets","mcp","containers","preferInstallDependencies","resolvedContainers","process","env","VITEST","CI","NX_DRY_RUN","NX_INTERACTIVE","engagementId","prompt","name","message","initial","tags","provider","engine","formatter","addTsPlugin","delete","nxJson","analytics","targetDefaults","compile","syncGenerators","filter","g","includes","packageJson","dev","build","lint","test","version","exists","JSON","stringify","projectName","generators","entries","_","v","k","description","cmds","pkgMgrCmd","exec","buildCmd","run","lintCmd","overwriteStrategy","Overwrite","languages"],"mappings":"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"}
|
|
@@ -27,18 +27,12 @@ class AgentCoreA2aClientConfig:
|
|
|
27
27
|
"""SigV4-authenticated A2A client config for a Bedrock AgentCore runtime."""
|
|
28
28
|
region = region_from_arn(agent_runtime_arn)
|
|
29
29
|
credentials = boto3.Session(region_name=region).get_credentials()
|
|
30
|
-
return _config(
|
|
31
|
-
a2a_url_from_arn(agent_runtime_arn), sigv4_auth(credentials, region)
|
|
32
|
-
)
|
|
30
|
+
return _config(a2a_url_from_arn(agent_runtime_arn), sigv4_auth(credentials, region))
|
|
33
31
|
|
|
34
32
|
@staticmethod
|
|
35
|
-
def with_jwt_auth(
|
|
36
|
-
agent_runtime_arn: str, access_token_provider: Callable[[], str]
|
|
37
|
-
) -> tuple[str, ClientConfig]:
|
|
33
|
+
def with_jwt_auth(agent_runtime_arn: str, access_token_provider: Callable[[], str]) -> tuple[str, ClientConfig]:
|
|
38
34
|
"""Bearer-authenticated A2A client config for a Bedrock AgentCore runtime."""
|
|
39
|
-
return _config(
|
|
40
|
-
a2a_url_from_arn(agent_runtime_arn), jwt_auth(access_token_provider)
|
|
41
|
-
)
|
|
35
|
+
return _config(a2a_url_from_arn(agent_runtime_arn), jwt_auth(access_token_provider))
|
|
42
36
|
|
|
43
37
|
@staticmethod
|
|
44
38
|
def without_auth(url: str) -> tuple[str, ClientConfig]:
|
|
@@ -92,9 +86,7 @@ class AgentCoreA2aClientStrands:
|
|
|
92
86
|
) -> A2AAgent:
|
|
93
87
|
"""Bearer-authenticated client for a Bedrock AgentCore runtime."""
|
|
94
88
|
return _build(
|
|
95
|
-
AgentCoreA2aClientConfig.with_jwt_auth(
|
|
96
|
-
agent_runtime_arn, access_token_provider
|
|
97
|
-
),
|
|
89
|
+
AgentCoreA2aClientConfig.with_jwt_auth(agent_runtime_arn, access_token_provider),
|
|
98
90
|
name=name,
|
|
99
91
|
description=description,
|
|
100
92
|
)
|
|
@@ -119,6 +111,7 @@ exports[`py#agent#a2a-connection generator > should match snapshot for agent-con
|
|
|
119
111
|
"import os
|
|
120
112
|
|
|
121
113
|
from strands.agent.a2a_agent import A2AAgent
|
|
114
|
+
|
|
122
115
|
from test_agent_connection.core.agentcore_a2a_client_strands import (
|
|
123
116
|
AgentCoreA2aClientStrands,
|
|
124
117
|
)
|
|
@@ -137,9 +130,7 @@ class RemoteClientStrands:
|
|
|
137
130
|
config = get_agentcore_runtime_config()
|
|
138
131
|
agent_runtime_arn = config.get("agentRuntimes", {}).get("Remote")
|
|
139
132
|
if not agent_runtime_arn:
|
|
140
|
-
raise RuntimeError(
|
|
141
|
-
"No connected agent runtime named 'Remote' found in runtime configuration."
|
|
142
|
-
)
|
|
133
|
+
raise RuntimeError("No connected agent runtime named 'Remote' found in runtime configuration.")
|
|
143
134
|
return AgentCoreA2aClientStrands.with_iam_auth(agent_runtime_arn)
|
|
144
135
|
"
|
|
145
136
|
`;
|
|
@@ -175,9 +166,7 @@ class SessionHeaderAuth(httpx.Auth):
|
|
|
175
166
|
yield from self._inner.auth_flow(request)
|
|
176
167
|
|
|
177
168
|
|
|
178
|
-
def sigv4_auth(
|
|
179
|
-
credentials, region: str, service: str = "bedrock-agentcore"
|
|
180
|
-
) -> httpx.Auth:
|
|
169
|
+
def sigv4_auth(credentials, region: str, service: str = "bedrock-agentcore") -> httpx.Auth:
|
|
181
170
|
"""Session-forwarding SigV4 auth (per-request, body-aware)."""
|
|
182
171
|
return SessionHeaderAuth(SigV4HTTPXAuth(credentials, service, region))
|
|
183
172
|
|
|
@@ -15,9 +15,7 @@ class AgentCoreGatewayMCPClientStrands:
|
|
|
15
15
|
region: str | None = None,
|
|
16
16
|
) -> MCPClient:
|
|
17
17
|
"""Create a gateway MCP client authenticated with IAM SigV4."""
|
|
18
|
-
return MCPClient(
|
|
19
|
-
AgentCoreGatewayMCPTransport.with_iam_auth(gateway_url, region)
|
|
20
|
-
)
|
|
18
|
+
return MCPClient(AgentCoreGatewayMCPTransport.with_iam_auth(gateway_url, region))
|
|
21
19
|
|
|
22
20
|
@staticmethod
|
|
23
21
|
def without_auth(gateway_url: str) -> MCPClient:
|
|
@@ -47,9 +45,7 @@ class AgentCoreGatewayMCPTransport:
|
|
|
47
45
|
region: str | None = None,
|
|
48
46
|
) -> TransportFactory:
|
|
49
47
|
"""Create a gateway MCP transport authenticated with IAM SigV4."""
|
|
50
|
-
return sigv4_transport(
|
|
51
|
-
gateway_url, region or region_from_gateway_url(gateway_url)
|
|
52
|
-
)
|
|
48
|
+
return sigv4_transport(gateway_url, region or region_from_gateway_url(gateway_url))
|
|
53
49
|
|
|
54
50
|
@staticmethod
|
|
55
51
|
def without_auth(gateway_url: str) -> TransportFactory:
|
|
@@ -88,9 +84,7 @@ def sigv4_transport(url: str, region: str) -> TransportFactory:
|
|
|
88
84
|
return _factory(url, sigv4_auth(credentials, region))
|
|
89
85
|
|
|
90
86
|
|
|
91
|
-
def jwt_transport(
|
|
92
|
-
url: str, access_token_provider: Callable[[], str]
|
|
93
|
-
) -> TransportFactory:
|
|
87
|
+
def jwt_transport(url: str, access_token_provider: Callable[[], str]) -> TransportFactory:
|
|
94
88
|
"""Bearer-token transport factory for a resolved AgentCore endpoint."""
|
|
95
89
|
return _factory(url, jwt_auth(access_token_provider))
|
|
96
90
|
|
|
@@ -104,13 +98,14 @@ def no_auth_transport(url: str) -> TransportFactory:
|
|
|
104
98
|
exports[`py#agent#gateway-connection generator > should match snapshot for agent-connection core files > my_gateway_client_strands.py 1`] = `
|
|
105
99
|
"import os
|
|
106
100
|
|
|
101
|
+
from strands.tools.mcp.mcp_client import MCPClient
|
|
102
|
+
|
|
107
103
|
from proj_agent_connection.core.agentcore_gateway_mcp_client_strands import (
|
|
108
104
|
AgentCoreGatewayMCPClientStrands,
|
|
109
105
|
)
|
|
110
106
|
from proj_agent_connection.core.runtime_config import (
|
|
111
107
|
get_agentcore_runtime_config,
|
|
112
108
|
)
|
|
113
|
-
from strands.tools.mcp.mcp_client import MCPClient
|
|
114
109
|
|
|
115
110
|
|
|
116
111
|
class MyGatewayClientStrands:
|
|
@@ -126,15 +121,11 @@ class MyGatewayClientStrands:
|
|
|
126
121
|
@staticmethod
|
|
127
122
|
def create() -> MCPClient:
|
|
128
123
|
if os.environ.get("LOCAL_DEV") == "true":
|
|
129
|
-
return AgentCoreGatewayMCPClientStrands.without_auth(
|
|
130
|
-
gateway_url="http://localhost:8100/mcp"
|
|
131
|
-
)
|
|
124
|
+
return AgentCoreGatewayMCPClientStrands.without_auth(gateway_url="http://localhost:8100/mcp")
|
|
132
125
|
config = get_agentcore_runtime_config()
|
|
133
126
|
gateway_url = config.get("gateways", {}).get("MyGateway")
|
|
134
127
|
if not gateway_url:
|
|
135
|
-
raise RuntimeError(
|
|
136
|
-
"No connected gateway named 'MyGateway' found in runtime configuration."
|
|
137
|
-
)
|
|
128
|
+
raise RuntimeError("No connected gateway named 'MyGateway' found in runtime configuration.")
|
|
138
129
|
return AgentCoreGatewayMCPClientStrands.with_iam_auth(gateway_url=gateway_url)
|
|
139
130
|
"
|
|
140
131
|
`;
|
|
@@ -170,9 +161,7 @@ class SessionHeaderAuth(httpx.Auth):
|
|
|
170
161
|
yield from self._inner.auth_flow(request)
|
|
171
162
|
|
|
172
163
|
|
|
173
|
-
def sigv4_auth(
|
|
174
|
-
credentials, region: str, service: str = "bedrock-agentcore"
|
|
175
|
-
) -> httpx.Auth:
|
|
164
|
+
def sigv4_auth(credentials, region: str, service: str = "bedrock-agentcore") -> httpx.Auth:
|
|
176
165
|
"""Session-forwarding SigV4 auth (per-request, body-aware)."""
|
|
177
166
|
return SessionHeaderAuth(SigV4HTTPXAuth(credentials, service, region))
|
|
178
167
|
|
|
@@ -35,15 +35,9 @@ class AgentCoreMCPClientStrands:
|
|
|
35
35
|
return MCPClient(AgentCoreMCPTransport.with_iam_auth(agent_runtime_arn))
|
|
36
36
|
|
|
37
37
|
@staticmethod
|
|
38
|
-
def with_jwt_auth(
|
|
39
|
-
agent_runtime_arn: str, access_token_provider: Callable[[], str]
|
|
40
|
-
) -> MCPClient:
|
|
38
|
+
def with_jwt_auth(agent_runtime_arn: str, access_token_provider: Callable[[], str]) -> MCPClient:
|
|
41
39
|
"""Bearer-authenticated client for a Bedrock AgentCore runtime."""
|
|
42
|
-
return MCPClient(
|
|
43
|
-
AgentCoreMCPTransport.with_jwt_auth(
|
|
44
|
-
agent_runtime_arn, access_token_provider
|
|
45
|
-
)
|
|
46
|
-
)
|
|
40
|
+
return MCPClient(AgentCoreMCPTransport.with_jwt_auth(agent_runtime_arn, access_token_provider))
|
|
47
41
|
|
|
48
42
|
@staticmethod
|
|
49
43
|
def without_auth(url: str) -> MCPClient:
|
|
@@ -70,14 +64,10 @@ class AgentCoreMCPTransport:
|
|
|
70
64
|
@staticmethod
|
|
71
65
|
def with_iam_auth(agent_runtime_arn: str) -> TransportFactory:
|
|
72
66
|
"""SigV4-authenticated transport for a Bedrock AgentCore runtime."""
|
|
73
|
-
return sigv4_transport(
|
|
74
|
-
mcp_url_from_arn(agent_runtime_arn), region_from_arn(agent_runtime_arn)
|
|
75
|
-
)
|
|
67
|
+
return sigv4_transport(mcp_url_from_arn(agent_runtime_arn), region_from_arn(agent_runtime_arn))
|
|
76
68
|
|
|
77
69
|
@staticmethod
|
|
78
|
-
def with_jwt_auth(
|
|
79
|
-
agent_runtime_arn: str, access_token_provider: Callable[[], str]
|
|
80
|
-
) -> TransportFactory:
|
|
70
|
+
def with_jwt_auth(agent_runtime_arn: str, access_token_provider: Callable[[], str]) -> TransportFactory:
|
|
81
71
|
"""Bearer-authenticated transport for a Bedrock AgentCore runtime."""
|
|
82
72
|
return jwt_transport(mcp_url_from_arn(agent_runtime_arn), access_token_provider)
|
|
83
73
|
|
|
@@ -118,9 +108,7 @@ def sigv4_transport(url: str, region: str) -> TransportFactory:
|
|
|
118
108
|
return _factory(url, sigv4_auth(credentials, region))
|
|
119
109
|
|
|
120
110
|
|
|
121
|
-
def jwt_transport(
|
|
122
|
-
url: str, access_token_provider: Callable[[], str]
|
|
123
|
-
) -> TransportFactory:
|
|
111
|
+
def jwt_transport(url: str, access_token_provider: Callable[[], str]) -> TransportFactory:
|
|
124
112
|
"""Bearer-token transport factory for a resolved AgentCore endpoint."""
|
|
125
113
|
return _factory(url, jwt_auth(access_token_provider))
|
|
126
114
|
|
|
@@ -134,13 +122,14 @@ def no_auth_transport(url: str) -> TransportFactory:
|
|
|
134
122
|
exports[`py#agent#mcp-connection generator > should match snapshot for generated files > inventory_mcp_client_strands.py 1`] = `
|
|
135
123
|
"import os
|
|
136
124
|
|
|
125
|
+
from strands.tools.mcp.mcp_client import MCPClient
|
|
126
|
+
|
|
137
127
|
from proj_agent_connection.core.agentcore_mcp_client_strands import (
|
|
138
128
|
AgentCoreMCPClientStrands,
|
|
139
129
|
)
|
|
140
130
|
from proj_agent_connection.core.runtime_config import (
|
|
141
131
|
get_agentcore_runtime_config,
|
|
142
132
|
)
|
|
143
|
-
from strands.tools.mcp.mcp_client import MCPClient
|
|
144
133
|
|
|
145
134
|
|
|
146
135
|
class InventoryMcpClientStrands:
|
|
@@ -153,9 +142,7 @@ class InventoryMcpClientStrands:
|
|
|
153
142
|
config = get_agentcore_runtime_config()
|
|
154
143
|
agent_runtime_arn = config.get("agentRuntimes", {}).get("InventoryMcp")
|
|
155
144
|
if not agent_runtime_arn:
|
|
156
|
-
raise RuntimeError(
|
|
157
|
-
"No connected MCP server runtime named 'InventoryMcp' found in runtime configuration."
|
|
158
|
-
)
|
|
145
|
+
raise RuntimeError("No connected MCP server runtime named 'InventoryMcp' found in runtime configuration.")
|
|
159
146
|
return AgentCoreMCPClientStrands.with_iam_auth(agent_runtime_arn)
|
|
160
147
|
"
|
|
161
148
|
`;
|
|
@@ -191,9 +178,7 @@ class SessionHeaderAuth(httpx.Auth):
|
|
|
191
178
|
yield from self._inner.auth_flow(request)
|
|
192
179
|
|
|
193
180
|
|
|
194
|
-
def sigv4_auth(
|
|
195
|
-
credentials, region: str, service: str = "bedrock-agentcore"
|
|
196
|
-
) -> httpx.Auth:
|
|
181
|
+
def sigv4_auth(credentials, region: str, service: str = "bedrock-agentcore") -> httpx.Auth:
|
|
197
182
|
"""Session-forwarding SigV4 auth (per-request, body-aware)."""
|
|
198
183
|
return SessionHeaderAuth(SigV4HTTPXAuth(credentials, service, region))
|
|
199
184
|
|
|
@@ -70,9 +70,7 @@ class JsonStreamingResponse(StreamingResponse):
|
|
|
70
70
|
"description": description,
|
|
71
71
|
"content": {
|
|
72
72
|
"application/jsonl": {
|
|
73
|
-
"itemSchema": {
|
|
74
|
-
"$ref": f"#/components/schemas/{item_model.__name__}"
|
|
75
|
-
},
|
|
73
|
+
"itemSchema": {"$ref": f"#/components/schemas/{item_model.__name__}"},
|
|
76
74
|
}
|
|
77
75
|
},
|
|
78
76
|
# Include the model so FastAPI registers the schema in components/schemas
|
|
@@ -93,11 +91,7 @@ async def cors_middleware(request: Request, call_next):
|
|
|
93
91
|
response = await call_next(request)
|
|
94
92
|
|
|
95
93
|
origin = request.headers.get("origin")
|
|
96
|
-
allowed_origins = (
|
|
97
|
-
os.environ.get("ALLOWED_ORIGINS", "").split(",")
|
|
98
|
-
if os.environ.get("ALLOWED_ORIGINS")
|
|
99
|
-
else []
|
|
100
|
-
)
|
|
94
|
+
allowed_origins = os.environ.get("ALLOWED_ORIGINS", "").split(",") if os.environ.get("ALLOWED_ORIGINS") else []
|
|
101
95
|
|
|
102
96
|
is_localhost = origin and urlparse(origin).hostname in ["localhost", "127.0.0.1"]
|
|
103
97
|
is_allowed_origin = origin and origin in allowed_origins
|
|
@@ -124,8 +118,7 @@ async def unhandled_exception_handler(request, err):
|
|
|
124
118
|
metrics.add_metric(name="Failure", unit=MetricUnit.Count, value=1)
|
|
125
119
|
|
|
126
120
|
return JSONResponse(
|
|
127
|
-
status_code=500,
|
|
128
|
-
content=InternalServerErrorDetails(detail="Internal Server Error").model_dump(),
|
|
121
|
+
status_code=500, content=InternalServerErrorDetails(detail="Internal Server Error").model_dump()
|
|
129
122
|
)
|
|
130
123
|
|
|
131
124
|
|
|
@@ -552,6 +552,7 @@ resource "aws_cloudfront_response_headers_policy" "website" {
|
|
|
552
552
|
|
|
553
553
|
# CloudFront Distribution
|
|
554
554
|
resource "aws_cloudfront_distribution" "website" {
|
|
555
|
+
# See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesGeneral.html
|
|
555
556
|
#checkov:skip=CKV_AWS_174:Using CloudFront default certificate which does not support TLS v1.2
|
|
556
557
|
#checkov:skip=CKV_AWS_310:Origin failover not required for single S3 origin static website
|
|
557
558
|
#checkov:skip=CKV_AWS_374:Geo restrictions not required for global web application
|
|
@@ -1731,6 +1732,7 @@ export class StaticWebsite extends Construct {
|
|
|
1731
1732
|
],
|
|
1732
1733
|
},
|
|
1733
1734
|
);
|
|
1735
|
+
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesGeneral.html
|
|
1734
1736
|
suppressRules(
|
|
1735
1737
|
this.cloudFrontDistribution,
|
|
1736
1738
|
['CKV_AWS_174'],
|
|
@@ -3306,6 +3308,7 @@ export class StaticWebsite extends Construct {
|
|
|
3306
3308
|
],
|
|
3307
3309
|
},
|
|
3308
3310
|
);
|
|
3311
|
+
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesGeneral.html
|
|
3309
3312
|
suppressRules(
|
|
3310
3313
|
this.cloudFrontDistribution,
|
|
3311
3314
|
['CKV_AWS_174'],
|
|
@@ -4808,6 +4811,7 @@ export class StaticWebsite extends Construct {
|
|
|
4808
4811
|
],
|
|
4809
4812
|
},
|
|
4810
4813
|
);
|
|
4814
|
+
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesGeneral.html
|
|
4811
4815
|
suppressRules(
|
|
4812
4816
|
this.cloudFrontDistribution,
|
|
4813
4817
|
['CKV_AWS_174'],
|
|
@@ -3,12 +3,21 @@ import { Construct } from 'constructs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import * as url from 'node:url';
|
|
5
5
|
<%_ } _%>
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
AgentCoreGateway,
|
|
8
|
+
AgentCoreGatewayProps,
|
|
9
|
+
} from '../../../core/agentcore-gateway/agentcore-gateway.js';
|
|
7
10
|
import { RuntimeConfig } from '../../../core/runtime-config.js';
|
|
8
11
|
<%_ if (cedarPolicy) { _%>
|
|
9
12
|
import { findWorkspaceRoot } from '../../../core/workspace.js';
|
|
10
13
|
<%_ } _%>
|
|
11
14
|
|
|
15
|
+
<%_ if (cedarPolicy) { _%>
|
|
16
|
+
export type <%- nameClassName %>Props = Omit<AgentCoreGatewayProps, 'cedarPolicyPath'>;
|
|
17
|
+
<%_ } else { _%>
|
|
18
|
+
export type <%- nameClassName %>Props = AgentCoreGatewayProps;
|
|
19
|
+
<%_ } _%>
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
22
|
* AgentCore Gateway wired for MCP protocol with IAM inbound auth.
|
|
14
23
|
<%_ if (cedarPolicy) { _%>
|
|
@@ -21,7 +30,7 @@ export class <%- nameClassName %> extends AgentCoreGateway {
|
|
|
21
30
|
/** Default Gateway target name when added to another Gateway. */
|
|
22
31
|
public readonly gatewayName = '<%- nameKebabCase %>';
|
|
23
32
|
|
|
24
|
-
constructor(scope: Construct, id: string) {
|
|
33
|
+
constructor(scope: Construct, id: string, props?: <%- nameClassName %>Props) {
|
|
25
34
|
super(scope, id, {
|
|
26
35
|
<%_ if (cedarPolicy) { _%>
|
|
27
36
|
cedarPolicyPath: path.join(
|
|
@@ -29,6 +38,7 @@ export class <%- nameClassName %> extends AgentCoreGateway {
|
|
|
29
38
|
'<%- projectDirectory %>/policies',
|
|
30
39
|
),
|
|
31
40
|
<%_ } _%>
|
|
41
|
+
...props,
|
|
32
42
|
});
|
|
33
43
|
|
|
34
44
|
const rc = RuntimeConfig.ensure(this);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import * as cdk from 'aws-cdk-lib';
|
|
2
2
|
import * as agentcore from 'aws-cdk-lib/aws-bedrockagentcore';
|
|
3
3
|
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
4
|
+
import * as kms from 'aws-cdk-lib/aws-kms';
|
|
4
5
|
import * as lambda from 'aws-cdk-lib/aws-lambda';
|
|
6
|
+
import * as logs from 'aws-cdk-lib/aws-logs';
|
|
5
7
|
import * as triggers from 'aws-cdk-lib/triggers';
|
|
8
|
+
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
|
|
6
9
|
import { Construct } from 'constructs';
|
|
7
10
|
import ejs from 'ejs';
|
|
8
11
|
import * as fs from 'node:fs';
|
|
@@ -24,6 +27,13 @@ export interface AgentCoreGatewayProps {
|
|
|
24
27
|
* Additional variables available to the Cedar policy EJS templates.
|
|
25
28
|
*/
|
|
26
29
|
readonly cedarPolicyVariables?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Associate a regional WAFv2 web ACL (default AWS managed rule groups) with
|
|
32
|
+
* the gateway to inspect inbound requests.
|
|
33
|
+
*
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
readonly enableWaf?: boolean;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
/**
|
|
@@ -33,6 +43,8 @@ export interface AgentCoreGatewayProps {
|
|
|
33
43
|
export class AgentCoreGateway extends Construct implements iam.IGrantable {
|
|
34
44
|
public readonly gateway: agentcore.Gateway;
|
|
35
45
|
public readonly policyEngine?: agentcore.CfnPolicyEngine;
|
|
46
|
+
/** The WAFv2 web ACL associated with the gateway, if WAF is enabled. */
|
|
47
|
+
public readonly webAcl?: wafv2.CfnWebACL;
|
|
36
48
|
private readonly policies: agentcore.CfnPolicy[] = [];
|
|
37
49
|
private targetReadinessProbe?: triggers.TriggerFunction;
|
|
38
50
|
private readonly targetRuntimeArns: string[] = [];
|
|
@@ -41,6 +53,9 @@ export class AgentCoreGateway extends Construct implements iam.IGrantable {
|
|
|
41
53
|
super(scope, id);
|
|
42
54
|
|
|
43
55
|
this.gateway = new agentcore.Gateway(this, 'Gateway', {
|
|
56
|
+
// Cap at 39 chars so the 11-char service suffix keeps the gateway id
|
|
57
|
+
// within its 50-char limit.
|
|
58
|
+
gatewayName: cdk.Names.uniqueResourceName(this, { maxLength: 39 }),
|
|
44
59
|
protocolConfiguration: new agentcore.McpProtocolConfiguration({
|
|
45
60
|
searchType: agentcore.McpGatewaySearchType.SEMANTIC,
|
|
46
61
|
supportedVersions: [agentcore.MCPProtocolVersion.MCP_2025_03_26],
|
|
@@ -128,6 +143,103 @@ export class AgentCoreGateway extends Construct implements iam.IGrantable {
|
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
145
|
}
|
|
146
|
+
|
|
147
|
+
if (props?.enableWaf ?? true) {
|
|
148
|
+
this.webAcl = this.createWebAcl();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create a regional WAFv2 web ACL with the default AWS managed rule groups,
|
|
154
|
+
* associate it with the gateway, and log requests to CloudWatch.
|
|
155
|
+
*/
|
|
156
|
+
private createWebAcl(): wafv2.CfnWebACL {
|
|
157
|
+
const metricPrefix = cdk.Names.uniqueResourceName(this, { maxLength: 40 });
|
|
158
|
+
|
|
159
|
+
const webAcl = new wafv2.CfnWebACL(this, 'WebAcl', {
|
|
160
|
+
defaultAction: { allow: {} },
|
|
161
|
+
// AgentCore Gateway requires a REGIONAL web ACL; CloudFront (global) is
|
|
162
|
+
// not supported.
|
|
163
|
+
scope: 'REGIONAL',
|
|
164
|
+
visibilityConfig: {
|
|
165
|
+
cloudWatchMetricsEnabled: true,
|
|
166
|
+
metricName: `${metricPrefix}WebAcl`,
|
|
167
|
+
sampledRequestsEnabled: true,
|
|
168
|
+
},
|
|
169
|
+
rules: [
|
|
170
|
+
{
|
|
171
|
+
name: 'CRSRule',
|
|
172
|
+
priority: 0,
|
|
173
|
+
statement: {
|
|
174
|
+
managedRuleGroupStatement: {
|
|
175
|
+
name: 'AWSManagedRulesCommonRuleSet',
|
|
176
|
+
vendorName: 'AWS',
|
|
177
|
+
// Count instead of Block: the CRS 8 KB body limit is too
|
|
178
|
+
// restrictive for typical MCP tool payloads.
|
|
179
|
+
ruleActionOverrides: [
|
|
180
|
+
{
|
|
181
|
+
name: 'SizeRestrictions_BODY',
|
|
182
|
+
actionToUse: { count: {} },
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
visibilityConfig: {
|
|
188
|
+
cloudWatchMetricsEnabled: true,
|
|
189
|
+
metricName: `${metricPrefix}WebAcl-CRS`,
|
|
190
|
+
sampledRequestsEnabled: true,
|
|
191
|
+
},
|
|
192
|
+
overrideAction: {
|
|
193
|
+
none: {},
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: 'KnownBadInputsRule',
|
|
198
|
+
priority: 1,
|
|
199
|
+
statement: {
|
|
200
|
+
managedRuleGroupStatement: {
|
|
201
|
+
name: 'AWSManagedRulesKnownBadInputsRuleSet',
|
|
202
|
+
vendorName: 'AWS',
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
visibilityConfig: {
|
|
206
|
+
cloudWatchMetricsEnabled: true,
|
|
207
|
+
metricName: `${metricPrefix}WebAcl-KnownBadInputs`,
|
|
208
|
+
sampledRequestsEnabled: true,
|
|
209
|
+
},
|
|
210
|
+
overrideAction: {
|
|
211
|
+
none: {},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
new wafv2.CfnWebACLAssociation(this, 'WebAclAssociation', {
|
|
218
|
+
resourceArn: this.gateway.gatewayArn,
|
|
219
|
+
webAclArn: webAcl.attrArn,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// WAFv2 logging destination requires the `aws-waf-logs-` name prefix.
|
|
223
|
+
const logsKey = new kms.Key(this, 'WebAclLogsKey', {
|
|
224
|
+
enableKeyRotation: true,
|
|
225
|
+
});
|
|
226
|
+
logsKey.grantEncryptDecrypt(
|
|
227
|
+
new iam.ServicePrincipal(
|
|
228
|
+
`logs.${cdk.Stack.of(this).region}.amazonaws.com`,
|
|
229
|
+
),
|
|
230
|
+
);
|
|
231
|
+
const wafLogGroup = new logs.LogGroup(this, 'WebAclLogs', {
|
|
232
|
+
logGroupName: `aws-waf-logs-${metricPrefix}-${this.node.addr.slice(-8)}`,
|
|
233
|
+
retention: logs.RetentionDays.ONE_YEAR,
|
|
234
|
+
encryptionKey: logsKey,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
new wafv2.CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
|
|
238
|
+
resourceArn: webAcl.attrArn,
|
|
239
|
+
logDestinationConfigs: [wafLogGroup.logGroupArn],
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return webAcl;
|
|
131
243
|
}
|
|
132
244
|
|
|
133
245
|
/**
|
|
@@ -34,6 +34,12 @@ variable "tool_dependencies" {
|
|
|
34
34
|
type = list(string)
|
|
35
35
|
default = []
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
variable "enable_waf" {
|
|
39
|
+
description = "Associate a regional WAFv2 web ACL (default AWS managed rule groups) with the gateway to inspect inbound requests."
|
|
40
|
+
type = bool
|
|
41
|
+
default = true
|
|
42
|
+
}
|
|
37
43
|
<%_ if (cedarPolicy) { _%>
|
|
38
44
|
|
|
39
45
|
variable "policy_dependencies" {
|
|
@@ -141,7 +147,9 @@ resource "aws_bedrockagentcore_policy_engine" "this" {
|
|
|
141
147
|
<%_ } _%>
|
|
142
148
|
|
|
143
149
|
resource "aws_bedrockagentcore_gateway" "this" {
|
|
144
|
-
name
|
|
150
|
+
# Cap the name so the 11-char service suffix keeps the gateway id within its
|
|
151
|
+
# 50-char limit (30 prefix + "-<8 hex>" + 11 = 50).
|
|
152
|
+
name = "${substr("<%- nameClassName %>", 0, 30)}-${random_id.unique_suffix.hex}"
|
|
145
153
|
description = "AgentCore Gateway for <%- nameClassName %>"
|
|
146
154
|
role_arn = aws_iam_role.gateway_role.arn
|
|
147
155
|
|
|
@@ -164,6 +172,107 @@ resource "aws_bedrockagentcore_gateway" "this" {
|
|
|
164
172
|
depends_on = [aws_iam_role_policy.gateway_role_policy]
|
|
165
173
|
<%_ } _%>
|
|
166
174
|
}
|
|
175
|
+
|
|
176
|
+
# WAFv2 protection for the gateway. AgentCore Gateway requires a REGIONAL web
|
|
177
|
+
# ACL; CloudFront (global) is not supported.
|
|
178
|
+
resource "aws_wafv2_web_acl" "gateway_waf" {
|
|
179
|
+
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.gateway_waf_logging; Checkov does not resolve the separate resource
|
|
180
|
+
count = var.enable_waf ? 1 : 0
|
|
181
|
+
|
|
182
|
+
name = "<%- nameClassName %>-waf-${random_id.unique_suffix.hex}"
|
|
183
|
+
scope = "REGIONAL"
|
|
184
|
+
|
|
185
|
+
default_action {
|
|
186
|
+
allow {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
rule {
|
|
190
|
+
name = "CRSRule"
|
|
191
|
+
priority = 0
|
|
192
|
+
|
|
193
|
+
override_action {
|
|
194
|
+
none {}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
statement {
|
|
198
|
+
managed_rule_group_statement {
|
|
199
|
+
name = "AWSManagedRulesCommonRuleSet"
|
|
200
|
+
vendor_name = "AWS"
|
|
201
|
+
|
|
202
|
+
# Count instead of Block: the CRS 8 KB body limit is too restrictive
|
|
203
|
+
# for typical MCP tool payloads.
|
|
204
|
+
rule_action_override {
|
|
205
|
+
name = "SizeRestrictions_BODY"
|
|
206
|
+
action_to_use {
|
|
207
|
+
count {}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
visibility_config {
|
|
214
|
+
cloudwatch_metrics_enabled = true
|
|
215
|
+
metric_name = "<%- nameClassName %>WebAcl-CRS"
|
|
216
|
+
sampled_requests_enabled = true
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
rule {
|
|
221
|
+
name = "KnownBadInputsRule"
|
|
222
|
+
priority = 1
|
|
223
|
+
|
|
224
|
+
override_action {
|
|
225
|
+
none {}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
statement {
|
|
229
|
+
managed_rule_group_statement {
|
|
230
|
+
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
231
|
+
vendor_name = "AWS"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
visibility_config {
|
|
236
|
+
cloudwatch_metrics_enabled = true
|
|
237
|
+
metric_name = "<%- nameClassName %>WebAcl-KnownBadInputs"
|
|
238
|
+
sampled_requests_enabled = true
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
visibility_config {
|
|
243
|
+
cloudwatch_metrics_enabled = true
|
|
244
|
+
metric_name = "<%- nameClassName %>WebAcl"
|
|
245
|
+
sampled_requests_enabled = true
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
lifecycle {
|
|
249
|
+
create_before_destroy = true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
resource "aws_wafv2_web_acl_association" "gateway_waf" {
|
|
254
|
+
count = var.enable_waf ? 1 : 0
|
|
255
|
+
|
|
256
|
+
resource_arn = aws_bedrockagentcore_gateway.this.gateway_arn
|
|
257
|
+
web_acl_arn = aws_wafv2_web_acl.gateway_waf[0].arn
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# WAF request logs. WAFv2 requires the `aws-waf-logs-` name prefix.
|
|
261
|
+
resource "aws_cloudwatch_log_group" "gateway_waf_logs" {
|
|
262
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
263
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one year which is sufficient for WAF logs
|
|
264
|
+
count = var.enable_waf ? 1 : 0
|
|
265
|
+
|
|
266
|
+
name = "aws-waf-logs-<%- nameKebabCase %>-${random_id.unique_suffix.hex}"
|
|
267
|
+
retention_in_days = 365
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
resource "aws_wafv2_web_acl_logging_configuration" "gateway_waf_logging" {
|
|
271
|
+
count = var.enable_waf ? 1 : 0
|
|
272
|
+
|
|
273
|
+
log_destination_configs = [aws_cloudwatch_log_group.gateway_waf_logs[0].arn]
|
|
274
|
+
resource_arn = aws_wafv2_web_acl.gateway_waf[0].arn
|
|
275
|
+
}
|
|
167
276
|
<%_ if (cedarPolicy) { _%>
|
|
168
277
|
|
|
169
278
|
# Anchor for the policy_dependencies variable — depends_on only accepts
|
|
@@ -333,3 +442,8 @@ output "gateway_role_arn" {
|
|
|
333
442
|
description = "ARN of the IAM role assumed by the gateway service"
|
|
334
443
|
value = aws_iam_role.gateway_role.arn
|
|
335
444
|
}
|
|
445
|
+
|
|
446
|
+
output "waf_web_acl_arn" {
|
|
447
|
+
description = "ARN of the WAFv2 web ACL associated with the gateway, or null if WAF is disabled"
|
|
448
|
+
value = var.enable_waf ? aws_wafv2_web_acl.gateway_waf[0].arn : null
|
|
449
|
+
}
|
package/src/utils/format.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
import type
|
|
5
|
+
import { type Tree } from '@nx/devkit';
|
|
6
6
|
export declare const DEFAULT_BIOME_CONFIG: {
|
|
7
7
|
$schema: string;
|
|
8
8
|
root: boolean;
|
package/src/utils/format.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/ import { Biome } from "@biomejs/js-api/nodejs";
|
|
5
|
+
import { getProjects } from "@nx/devkit";
|
|
5
6
|
import { execFileSync, execSync } from "child_process";
|
|
6
7
|
import { existsSync, readFileSync } from "fs";
|
|
7
8
|
import { createRequire } from "module";
|
|
8
9
|
import path from "path";
|
|
10
|
+
import { readToml } from "./toml.js";
|
|
9
11
|
const require = createRequire(import.meta.url);
|
|
10
12
|
export const DEFAULT_BIOME_CONFIG = {
|
|
11
13
|
$schema: 'https://biomejs.dev/schemas/2.4.16/schema.json',
|
|
@@ -79,10 +81,13 @@ const BIOME_FORMATTABLE_EXTENSIONS = new Set([
|
|
|
79
81
|
const changedFiles = tree.listChanges().filter((file)=>file.type !== 'DELETE').filter((file)=>dir ? file.path.startsWith(dir) : true);
|
|
80
82
|
const pyFiles = changedFiles.filter((file)=>file.path.endsWith('.py'));
|
|
81
83
|
const otherFiles = changedFiles.filter((file)=>BIOME_FORMATTABLE_EXTENSIONS.has(path.extname(file.path)));
|
|
84
|
+
// Resolve each project's ruff settings (module names, line-length) so files
|
|
85
|
+
// are formatted to match the on-disk build (see getPythonProjectRuffConfigs).
|
|
86
|
+
const pythonProjectConfigs = pyFiles.length ? getPythonProjectRuffConfigs(tree) : [];
|
|
82
87
|
// Format Python files with ruff (lint fixes + formatting)
|
|
83
88
|
for (const file of pyFiles){
|
|
84
89
|
try {
|
|
85
|
-
const content = ruffFixAndFormat(file.content.toString('utf-8'), file.path, hasRuffConfigOnDisk(tree, file.path));
|
|
90
|
+
const content = ruffFixAndFormat(file.content.toString('utf-8'), file.path, hasRuffConfigOnDisk(tree, file.path), getOwningProjectRuffConfig(file.path, pythonProjectConfigs));
|
|
86
91
|
tree.write(file.path, content);
|
|
87
92
|
} catch {
|
|
88
93
|
// Silently skip ruff formatting failures
|
|
@@ -263,6 +268,52 @@ function getRuffCommand() {
|
|
|
263
268
|
dir = parent;
|
|
264
269
|
}
|
|
265
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Map each Nx project with a `pyproject.toml` to the ruff settings the on-disk
|
|
273
|
+
* build enforces for it: its top-level module names (from
|
|
274
|
+
* `[tool.hatch.build.targets.wheel].packages`) and its `[tool.ruff].line-length`.
|
|
275
|
+
*/ function getPythonProjectRuffConfigs(tree) {
|
|
276
|
+
const configs = [];
|
|
277
|
+
for (const project of getProjects(tree).values()){
|
|
278
|
+
const pyprojectPath = path.join(project.root, 'pyproject.toml');
|
|
279
|
+
if (tree.exists(pyprojectPath)) {
|
|
280
|
+
try {
|
|
281
|
+
const pyproject = readToml(tree, pyprojectPath);
|
|
282
|
+
const wheelPackages = pyproject?.tool?.hatch?.build?.targets?.wheel?.packages;
|
|
283
|
+
// Record the top-level module segment (`pkg/sub` -> `pkg`), which is
|
|
284
|
+
// all `known-first-party` keys off.
|
|
285
|
+
const modules = Array.isArray(wheelPackages) ? wheelPackages.filter((pkg)=>typeof pkg === 'string' && !!pkg).map((pkg)=>pkg.split('/')[0]) : [];
|
|
286
|
+
const lineLength = pyproject?.tool?.ruff?.['line-length'];
|
|
287
|
+
if (modules.length || typeof lineLength === 'number') {
|
|
288
|
+
configs.push({
|
|
289
|
+
root: project.root.split(path.sep).join('/'),
|
|
290
|
+
modules,
|
|
291
|
+
lineLength: typeof lineLength === 'number' ? lineLength : undefined
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
} catch {
|
|
295
|
+
// Skip projects whose pyproject.toml cannot be parsed
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return configs;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Resolve the ruff config for the project that owns a file (the project with
|
|
303
|
+
* the longest root that is a prefix of the file path). Ruff runs per-project on
|
|
304
|
+
* disk, so a file's settings come from its own project — only its own module is
|
|
305
|
+
* first-party (sibling workspace packages are third-party) and its own
|
|
306
|
+
* line-length applies — and scoping this way keeps in-tree formatting
|
|
307
|
+
* consistent with the on-disk build.
|
|
308
|
+
*/ function getOwningProjectRuffConfig(filePath, configs) {
|
|
309
|
+
let owner;
|
|
310
|
+
for (const config of configs){
|
|
311
|
+
if ((filePath === config.root || filePath.startsWith(`${config.root}/`)) && (!owner || config.root.length > owner.root.length)) {
|
|
312
|
+
owner = config;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return owner;
|
|
316
|
+
}
|
|
266
317
|
/**
|
|
267
318
|
* Run ruff check --fix and ruff format on Python file content via stdin.
|
|
268
319
|
* Applies all configured lint fixes (including import sorting) and formatting.
|
|
@@ -272,13 +323,29 @@ function getRuffCommand() {
|
|
|
272
323
|
* build fails on unsorted imports (I001). In that case we add `--extend-select
|
|
273
324
|
* I` so import sorting matches what the build enforces. When a config does
|
|
274
325
|
* exist we defer to it entirely, honouring the user's rule selection.
|
|
275
|
-
|
|
326
|
+
*
|
|
327
|
+
* `projectConfig` carries the owning project's ruff settings, which ruff cannot
|
|
328
|
+
* detect from the filesystem during generation because the project lives only
|
|
329
|
+
* in the tree. We pass them via `--config` so in-tree formatting matches the
|
|
330
|
+
* on-disk build: `known-first-party` (the project's own modules) keeps its
|
|
331
|
+
* imports in their own group, and `line-length` keeps wrapping consistent (the
|
|
332
|
+
* generated config raises it above ruff's default of 88). These are additive to
|
|
333
|
+
* any on-disk config, so they are safe to pass regardless of `hasConfig`.
|
|
334
|
+
*/ function ruffFixAndFormat(content, filePath, hasConfig, projectConfig) {
|
|
276
335
|
const ruff = getRuffCommand();
|
|
277
336
|
if (!ruff) return content;
|
|
278
337
|
const extendSelect = hasConfig ? '' : ' --extend-select I';
|
|
338
|
+
const configArgs = [];
|
|
339
|
+
if (projectConfig?.modules.length) {
|
|
340
|
+
configArgs.push(`lint.isort.known-first-party = ${JSON.stringify(projectConfig.modules)}`);
|
|
341
|
+
}
|
|
342
|
+
if (typeof projectConfig?.lineLength === 'number') {
|
|
343
|
+
configArgs.push(`line-length = ${projectConfig.lineLength}`);
|
|
344
|
+
}
|
|
345
|
+
const config = configArgs.map((arg)=>` --config ${JSON.stringify(arg)}`).join('');
|
|
279
346
|
// First apply lint fixes (import sorting, unused imports, etc.)
|
|
280
347
|
try {
|
|
281
|
-
const result = execSync(`${ruff} check --fix${extendSelect} --stdin-filename ${filePath} -`, {
|
|
348
|
+
const result = execSync(`${ruff} check --fix${extendSelect}${config} --stdin-filename ${filePath} -`, {
|
|
282
349
|
input: content,
|
|
283
350
|
encoding: 'utf-8',
|
|
284
351
|
stdio: [
|
|
@@ -297,7 +364,7 @@ function getRuffCommand() {
|
|
|
297
364
|
}
|
|
298
365
|
// Then apply formatting
|
|
299
366
|
try {
|
|
300
|
-
content = execSync(`${ruff} format --stdin-filename ${filePath} -`, {
|
|
367
|
+
content = execSync(`${ruff} format${config} --stdin-filename ${filePath} -`, {
|
|
301
368
|
input: content,
|
|
302
369
|
encoding: 'utf-8',
|
|
303
370
|
stdio: [
|
package/src/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/format.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Biome } from '@biomejs/js-api/nodejs';\nimport type { Tree } from '@nx/devkit';\nimport { execFileSync, execSync } from 'child_process';\nimport { existsSync, readFileSync } from 'fs';\nimport { createRequire } from 'module';\nimport path from 'path';\n\nconst require = createRequire(import.meta.url);\n\nexport const DEFAULT_BIOME_CONFIG = {\n $schema: 'https://biomejs.dev/schemas/2.4.16/schema.json',\n root: true,\n formatter: {\n enabled: true,\n indentStyle: 'space',\n indentWidth: 2,\n lineWidth: 80,\n },\n javascript: {\n formatter: {\n quoteStyle: 'single',\n trailingCommas: 'all',\n },\n },\n css: {\n formatter: {\n quoteStyle: 'single',\n },\n linter: {\n enabled: false,\n },\n },\n linter: {\n enabled: true,\n rules: {\n recommended: false,\n correctness: {\n noUndeclaredDependencies: 'warn',\n },\n },\n },\n assist: {\n actions: {\n source: {\n organizeImports: 'on',\n },\n },\n },\n files: {\n includes: [\n '**',\n '!**/dist',\n '!**/out-tsc',\n '!**/node_modules',\n '!**/.nx',\n '!**/.venv',\n '!**/*.css',\n ],\n },\n};\n\nconst BIOME_FORMATTABLE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.mts',\n '.cts',\n '.json',\n '.jsonc',\n '.css',\n]);\n\n/**\n * Format files in the given directory within the tree.\n * Handles both TypeScript/JavaScript/JSON (via biome) and Python (via ruff) files.\n * See https://github.com/nrwl/nx/blob/4cd640a9187954505d12de5b6d76a90d8ce4c2eb/packages/devkit/src/generators/format-files.ts#L11\n */\nexport async function formatFilesInSubtree(\n tree: Tree,\n dir?: string,\n): Promise<void> {\n const changedFiles = tree\n .listChanges()\n .filter((file) => file.type !== 'DELETE')\n .filter((file) => (dir ? file.path.startsWith(dir) : true));\n\n const pyFiles = changedFiles.filter((file) => file.path.endsWith('.py'));\n const otherFiles = changedFiles.filter((file) =>\n BIOME_FORMATTABLE_EXTENSIONS.has(path.extname(file.path)),\n );\n\n // Format Python files with ruff (lint fixes + formatting)\n for (const file of pyFiles) {\n try {\n const content = ruffFixAndFormat(\n file.content.toString('utf-8'),\n file.path,\n hasRuffConfigOnDisk(tree, file.path),\n );\n tree.write(file.path, content);\n } catch {\n // Silently skip ruff formatting failures\n }\n }\n\n if (otherFiles.length === 0) return;\n\n // Use the workspace's own Biome CLI (its version and config) when biome.json\n // exists on disk; otherwise format via the bundled library API with the\n // in-memory tree config. The CLI path does not see in-tree config changes.\n if (existsSync(path.join(tree.root, 'biome.json'))) {\n formatWithBiomeCli(tree, otherFiles);\n } else {\n formatWithBiomeApi(tree, otherFiles);\n }\n}\n\n/**\n * Format files via the workspace's Biome CLI, run from the workspace root so it\n * discovers the on-disk biome.json.\n */\nfunction formatWithBiomeCli(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n const biome = getBiomeCommand(tree.root);\n if (!biome) {\n // Fall back to the library API if the CLI cannot be resolved\n formatWithBiomeApi(tree, files);\n return;\n }\n\n for (const file of files) {\n try {\n const content = execFileSync(\n biome.command,\n [...biome.args, 'format', `--stdin-file-path=${file.path}`],\n {\n input: file.content?.toString('utf-8') ?? '',\n encoding: 'utf-8',\n cwd: tree.root,\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n}\n\n/**\n * Format files via the bundled Biome library API, applying the in-memory tree\n * config.\n */\nfunction formatWithBiomeApi(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n try {\n const biome = new Biome();\n const { projectKey } = biome.openProject();\n // Apply the workspace biome.json if it exists in the tree, otherwise the defaults.\n const treeConfig = tree.read('biome.json', 'utf-8');\n biome.applyConfiguration(\n projectKey,\n treeConfig ? JSON.parse(treeConfig) : DEFAULT_BIOME_CONFIG,\n );\n\n for (const file of files) {\n try {\n const { content } = biome.formatContent(\n projectKey,\n file.content?.toString('utf-8') ?? '',\n { filePath: file.path },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n } catch {\n // Silently skip formatting failures\n }\n}\n\ninterface BiomeCommand {\n command: string;\n args: string[];\n}\n\n/**\n * Resolve the `@biomejs/biome` CLI from the user's workspace, falling back to a\n * `biome` binary on the PATH.\n */\nconst _biomeCommands = new Map<string, BiomeCommand | null>();\nfunction getBiomeCommand(root: string): BiomeCommand | undefined {\n if (_biomeCommands.has(root)) {\n return _biomeCommands.get(root) ?? undefined;\n }\n\n // Run via node for cross-platform execution of the bin shim.\n try {\n const pkgJsonPath = require.resolve('@biomejs/biome/package.json', {\n paths: [root, import.meta.dirname],\n });\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n const binRelative =\n typeof pkgJson.bin === 'string' ? pkgJson.bin : pkgJson.bin?.biome;\n if (binRelative) {\n const binPath = path.join(path.dirname(pkgJsonPath), binRelative);\n const command = { command: process.execPath, args: [binPath] };\n _biomeCommands.set(root, command);\n return command;\n }\n } catch {\n // Fall back to a biome binary on the PATH\n }\n\n try {\n execSync('biome --version', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const command = { command: 'biome', args: [] };\n _biomeCommands.set(root, command);\n return command;\n } catch {\n _biomeCommands.set(root, null);\n return undefined;\n }\n}\n\n/**\n * Find the ruff command. Tries 'uv run ruff', then 'uvx ruff'.\n * Matches how @nxlv/python runs ruff via the UV provider.\n */\nlet _ruffCommand: string | undefined;\nfunction getRuffCommand(): string | undefined {\n if (_ruffCommand !== undefined) {\n return _ruffCommand || undefined;\n }\n for (const cmd of ['uv run ruff', 'uvx ruff']) {\n try {\n execSync(`${cmd} --version`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n _ruffCommand = cmd;\n return cmd;\n } catch {\n // Try next command\n }\n }\n _ruffCommand = '';\n return undefined;\n}\n\n/**\n * Whether ruff would discover a config on disk for a file, by walking from its\n * directory up to the workspace root looking for `.ruff.toml`, `ruff.toml`, or a\n * `pyproject.toml` with a `[tool.ruff]` section — the same files ruff itself\n * resolves. The walk stops at `tree.root` so a stray config in a parent of the\n * workspace (or the home directory) is never treated as the project's. Used to\n * decide whether to nudge ruff towards import sorting (see\n * {@link ruffFixAndFormat}).\n */\nfunction hasRuffConfigOnDisk(tree: Tree, filePath: string): boolean {\n const root = path.resolve(tree.root);\n let dir = path.resolve(root, path.dirname(filePath));\n while (true) {\n if (\n existsSync(path.join(dir, '.ruff.toml')) ||\n existsSync(path.join(dir, 'ruff.toml'))\n ) {\n return true;\n }\n const pyproject = path.join(dir, 'pyproject.toml');\n if (\n existsSync(pyproject) &&\n readFileSync(pyproject, 'utf-8').includes('[tool.ruff')\n ) {\n return true;\n }\n const parent = path.dirname(dir);\n // Stop once the workspace root has been checked (or we hit the FS root).\n if (dir === root || parent === dir) {\n return false;\n }\n dir = parent;\n }\n}\n\n/**\n * Run ruff check --fix and ruff format on Python file content via stdin.\n * Applies all configured lint fixes (including import sorting) and formatting.\n *\n * When no ruff config exists on disk (`hasConfig` false) ruff falls back to its\n * defaults, which omit isort — but generated projects enable rule `I` and their\n * build fails on unsorted imports (I001). In that case we add `--extend-select\n * I` so import sorting matches what the build enforces. When a config does\n * exist we defer to it entirely, honouring the user's rule selection.\n */\nfunction ruffFixAndFormat(\n content: string,\n filePath: string,\n hasConfig: boolean,\n): string {\n const ruff = getRuffCommand();\n if (!ruff) return content;\n\n const extendSelect = hasConfig ? '' : ' --extend-select I';\n\n // First apply lint fixes (import sorting, unused imports, etc.)\n try {\n const result = execSync(\n `${ruff} check --fix${extendSelect} --stdin-filename ${filePath} -`,\n { input: content, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },\n );\n content = result;\n } catch (e: any) {\n // ruff check exits non-zero when it finds unfixable issues,\n // but stdout still contains the fixed content\n if (e.stdout) {\n content = e.stdout;\n }\n }\n\n // Then apply formatting\n try {\n content = execSync(`${ruff} format --stdin-filename ${filePath} -`, {\n input: content,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch {\n // Fall through with whatever content we have\n }\n\n return content;\n}\n"],"names":["Biome","execFileSync","execSync","existsSync","readFileSync","createRequire","path","require","url","DEFAULT_BIOME_CONFIG","$schema","root","formatter","enabled","indentStyle","indentWidth","lineWidth","javascript","quoteStyle","trailingCommas","css","linter","rules","recommended","correctness","noUndeclaredDependencies","assist","actions","source","organizeImports","files","includes","BIOME_FORMATTABLE_EXTENSIONS","Set","formatFilesInSubtree","tree","dir","changedFiles","listChanges","filter","file","type","startsWith","pyFiles","endsWith","otherFiles","has","extname","content","ruffFixAndFormat","toString","hasRuffConfigOnDisk","write","length","join","formatWithBiomeCli","formatWithBiomeApi","biome","getBiomeCommand","command","args","input","encoding","cwd","stdio","projectKey","openProject","treeConfig","read","applyConfiguration","JSON","parse","formatContent","filePath","_biomeCommands","Map","get","undefined","pkgJsonPath","resolve","paths","dirname","pkgJson","binRelative","bin","binPath","process","execPath","set","_ruffCommand","getRuffCommand","cmd","pyproject","parent","hasConfig","ruff","extendSelect","result","e","stdout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/format.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { Biome } from '@biomejs/js-api/nodejs';\nimport { getProjects, type Tree } from '@nx/devkit';\nimport { execFileSync, execSync } from 'child_process';\nimport { existsSync, readFileSync } from 'fs';\nimport { createRequire } from 'module';\nimport path from 'path';\nimport { readToml } from './toml';\n\nconst require = createRequire(import.meta.url);\n\nexport const DEFAULT_BIOME_CONFIG = {\n $schema: 'https://biomejs.dev/schemas/2.4.16/schema.json',\n root: true,\n formatter: {\n enabled: true,\n indentStyle: 'space',\n indentWidth: 2,\n lineWidth: 80,\n },\n javascript: {\n formatter: {\n quoteStyle: 'single',\n trailingCommas: 'all',\n },\n },\n css: {\n formatter: {\n quoteStyle: 'single',\n },\n linter: {\n enabled: false,\n },\n },\n linter: {\n enabled: true,\n rules: {\n recommended: false,\n correctness: {\n noUndeclaredDependencies: 'warn',\n },\n },\n },\n assist: {\n actions: {\n source: {\n organizeImports: 'on',\n },\n },\n },\n files: {\n includes: [\n '**',\n '!**/dist',\n '!**/out-tsc',\n '!**/node_modules',\n '!**/.nx',\n '!**/.venv',\n '!**/*.css',\n ],\n },\n};\n\nconst BIOME_FORMATTABLE_EXTENSIONS = new Set([\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.mjs',\n '.cjs',\n '.mts',\n '.cts',\n '.json',\n '.jsonc',\n '.css',\n]);\n\n/**\n * Format files in the given directory within the tree.\n * Handles both TypeScript/JavaScript/JSON (via biome) and Python (via ruff) files.\n * See https://github.com/nrwl/nx/blob/4cd640a9187954505d12de5b6d76a90d8ce4c2eb/packages/devkit/src/generators/format-files.ts#L11\n */\nexport async function formatFilesInSubtree(\n tree: Tree,\n dir?: string,\n): Promise<void> {\n const changedFiles = tree\n .listChanges()\n .filter((file) => file.type !== 'DELETE')\n .filter((file) => (dir ? file.path.startsWith(dir) : true));\n\n const pyFiles = changedFiles.filter((file) => file.path.endsWith('.py'));\n const otherFiles = changedFiles.filter((file) =>\n BIOME_FORMATTABLE_EXTENSIONS.has(path.extname(file.path)),\n );\n\n // Resolve each project's ruff settings (module names, line-length) so files\n // are formatted to match the on-disk build (see getPythonProjectRuffConfigs).\n const pythonProjectConfigs = pyFiles.length\n ? getPythonProjectRuffConfigs(tree)\n : [];\n\n // Format Python files with ruff (lint fixes + formatting)\n for (const file of pyFiles) {\n try {\n const content = ruffFixAndFormat(\n file.content.toString('utf-8'),\n file.path,\n hasRuffConfigOnDisk(tree, file.path),\n getOwningProjectRuffConfig(file.path, pythonProjectConfigs),\n );\n tree.write(file.path, content);\n } catch {\n // Silently skip ruff formatting failures\n }\n }\n\n if (otherFiles.length === 0) return;\n\n // Use the workspace's own Biome CLI (its version and config) when biome.json\n // exists on disk; otherwise format via the bundled library API with the\n // in-memory tree config. The CLI path does not see in-tree config changes.\n if (existsSync(path.join(tree.root, 'biome.json'))) {\n formatWithBiomeCli(tree, otherFiles);\n } else {\n formatWithBiomeApi(tree, otherFiles);\n }\n}\n\n/**\n * Format files via the workspace's Biome CLI, run from the workspace root so it\n * discovers the on-disk biome.json.\n */\nfunction formatWithBiomeCli(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n const biome = getBiomeCommand(tree.root);\n if (!biome) {\n // Fall back to the library API if the CLI cannot be resolved\n formatWithBiomeApi(tree, files);\n return;\n }\n\n for (const file of files) {\n try {\n const content = execFileSync(\n biome.command,\n [...biome.args, 'format', `--stdin-file-path=${file.path}`],\n {\n input: file.content?.toString('utf-8') ?? '',\n encoding: 'utf-8',\n cwd: tree.root,\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n}\n\n/**\n * Format files via the bundled Biome library API, applying the in-memory tree\n * config.\n */\nfunction formatWithBiomeApi(\n tree: Tree,\n files: { path: string; content: Buffer | null }[],\n): void {\n try {\n const biome = new Biome();\n const { projectKey } = biome.openProject();\n // Apply the workspace biome.json if it exists in the tree, otherwise the defaults.\n const treeConfig = tree.read('biome.json', 'utf-8');\n biome.applyConfiguration(\n projectKey,\n treeConfig ? JSON.parse(treeConfig) : DEFAULT_BIOME_CONFIG,\n );\n\n for (const file of files) {\n try {\n const { content } = biome.formatContent(\n projectKey,\n file.content?.toString('utf-8') ?? '',\n { filePath: file.path },\n );\n tree.write(file.path, content);\n } catch {\n // Leave individual files that fail to format untouched\n }\n }\n } catch {\n // Silently skip formatting failures\n }\n}\n\ninterface BiomeCommand {\n command: string;\n args: string[];\n}\n\n/**\n * Resolve the `@biomejs/biome` CLI from the user's workspace, falling back to a\n * `biome` binary on the PATH.\n */\nconst _biomeCommands = new Map<string, BiomeCommand | null>();\nfunction getBiomeCommand(root: string): BiomeCommand | undefined {\n if (_biomeCommands.has(root)) {\n return _biomeCommands.get(root) ?? undefined;\n }\n\n // Run via node for cross-platform execution of the bin shim.\n try {\n const pkgJsonPath = require.resolve('@biomejs/biome/package.json', {\n paths: [root, import.meta.dirname],\n });\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));\n const binRelative =\n typeof pkgJson.bin === 'string' ? pkgJson.bin : pkgJson.bin?.biome;\n if (binRelative) {\n const binPath = path.join(path.dirname(pkgJsonPath), binRelative);\n const command = { command: process.execPath, args: [binPath] };\n _biomeCommands.set(root, command);\n return command;\n }\n } catch {\n // Fall back to a biome binary on the PATH\n }\n\n try {\n execSync('biome --version', {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const command = { command: 'biome', args: [] };\n _biomeCommands.set(root, command);\n return command;\n } catch {\n _biomeCommands.set(root, null);\n return undefined;\n }\n}\n\n/**\n * Find the ruff command. Tries 'uv run ruff', then 'uvx ruff'.\n * Matches how @nxlv/python runs ruff via the UV provider.\n */\nlet _ruffCommand: string | undefined;\nfunction getRuffCommand(): string | undefined {\n if (_ruffCommand !== undefined) {\n return _ruffCommand || undefined;\n }\n for (const cmd of ['uv run ruff', 'uvx ruff']) {\n try {\n execSync(`${cmd} --version`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n _ruffCommand = cmd;\n return cmd;\n } catch {\n // Try next command\n }\n }\n _ruffCommand = '';\n return undefined;\n}\n\n/**\n * Whether ruff would discover a config on disk for a file, by walking from its\n * directory up to the workspace root looking for `.ruff.toml`, `ruff.toml`, or a\n * `pyproject.toml` with a `[tool.ruff]` section — the same files ruff itself\n * resolves. The walk stops at `tree.root` so a stray config in a parent of the\n * workspace (or the home directory) is never treated as the project's. Used to\n * decide whether to nudge ruff towards import sorting (see\n * {@link ruffFixAndFormat}).\n */\nfunction hasRuffConfigOnDisk(tree: Tree, filePath: string): boolean {\n const root = path.resolve(tree.root);\n let dir = path.resolve(root, path.dirname(filePath));\n while (true) {\n if (\n existsSync(path.join(dir, '.ruff.toml')) ||\n existsSync(path.join(dir, 'ruff.toml'))\n ) {\n return true;\n }\n const pyproject = path.join(dir, 'pyproject.toml');\n if (\n existsSync(pyproject) &&\n readFileSync(pyproject, 'utf-8').includes('[tool.ruff')\n ) {\n return true;\n }\n const parent = path.dirname(dir);\n // Stop once the workspace root has been checked (or we hit the FS root).\n if (dir === root || parent === dir) {\n return false;\n }\n dir = parent;\n }\n}\n\ninterface PythonProjectRuffConfig {\n /** Project root, normalised to use forward slashes. */\n readonly root: string;\n /** Top-level importable module names declared by the project. */\n readonly modules: string[];\n /** The project's `[tool.ruff].line-length`, if set. */\n readonly lineLength?: number;\n}\n\n/**\n * Map each Nx project with a `pyproject.toml` to the ruff settings the on-disk\n * build enforces for it: its top-level module names (from\n * `[tool.hatch.build.targets.wheel].packages`) and its `[tool.ruff].line-length`.\n */\nfunction getPythonProjectRuffConfigs(tree: Tree): PythonProjectRuffConfig[] {\n const configs: PythonProjectRuffConfig[] = [];\n\n for (const project of getProjects(tree).values()) {\n const pyprojectPath = path.join(project.root, 'pyproject.toml');\n if (tree.exists(pyprojectPath)) {\n try {\n const pyproject = readToml(tree, pyprojectPath) as any;\n const wheelPackages: unknown =\n pyproject?.tool?.hatch?.build?.targets?.wheel?.packages;\n // Record the top-level module segment (`pkg/sub` -> `pkg`), which is\n // all `known-first-party` keys off.\n const modules = Array.isArray(wheelPackages)\n ? wheelPackages\n .filter((pkg): pkg is string => typeof pkg === 'string' && !!pkg)\n .map((pkg) => pkg.split('/')[0])\n : [];\n const lineLength: unknown = pyproject?.tool?.ruff?.['line-length'];\n if (modules.length || typeof lineLength === 'number') {\n configs.push({\n root: project.root.split(path.sep).join('/'),\n modules,\n lineLength: typeof lineLength === 'number' ? lineLength : undefined,\n });\n }\n } catch {\n // Skip projects whose pyproject.toml cannot be parsed\n }\n }\n }\n\n return configs;\n}\n\n/**\n * Resolve the ruff config for the project that owns a file (the project with\n * the longest root that is a prefix of the file path). Ruff runs per-project on\n * disk, so a file's settings come from its own project — only its own module is\n * first-party (sibling workspace packages are third-party) and its own\n * line-length applies — and scoping this way keeps in-tree formatting\n * consistent with the on-disk build.\n */\nfunction getOwningProjectRuffConfig(\n filePath: string,\n configs: PythonProjectRuffConfig[],\n): PythonProjectRuffConfig | undefined {\n let owner: PythonProjectRuffConfig | undefined;\n for (const config of configs) {\n if (\n (filePath === config.root || filePath.startsWith(`${config.root}/`)) &&\n (!owner || config.root.length > owner.root.length)\n ) {\n owner = config;\n }\n }\n return owner;\n}\n\n/**\n * Run ruff check --fix and ruff format on Python file content via stdin.\n * Applies all configured lint fixes (including import sorting) and formatting.\n *\n * When no ruff config exists on disk (`hasConfig` false) ruff falls back to its\n * defaults, which omit isort — but generated projects enable rule `I` and their\n * build fails on unsorted imports (I001). In that case we add `--extend-select\n * I` so import sorting matches what the build enforces. When a config does\n * exist we defer to it entirely, honouring the user's rule selection.\n *\n * `projectConfig` carries the owning project's ruff settings, which ruff cannot\n * detect from the filesystem during generation because the project lives only\n * in the tree. We pass them via `--config` so in-tree formatting matches the\n * on-disk build: `known-first-party` (the project's own modules) keeps its\n * imports in their own group, and `line-length` keeps wrapping consistent (the\n * generated config raises it above ruff's default of 88). These are additive to\n * any on-disk config, so they are safe to pass regardless of `hasConfig`.\n */\nfunction ruffFixAndFormat(\n content: string,\n filePath: string,\n hasConfig: boolean,\n projectConfig?: PythonProjectRuffConfig,\n): string {\n const ruff = getRuffCommand();\n if (!ruff) return content;\n\n const extendSelect = hasConfig ? '' : ' --extend-select I';\n const configArgs: string[] = [];\n if (projectConfig?.modules.length) {\n configArgs.push(\n `lint.isort.known-first-party = ${JSON.stringify(projectConfig.modules)}`,\n );\n }\n if (typeof projectConfig?.lineLength === 'number') {\n configArgs.push(`line-length = ${projectConfig.lineLength}`);\n }\n const config = configArgs\n .map((arg) => ` --config ${JSON.stringify(arg)}`)\n .join('');\n\n // First apply lint fixes (import sorting, unused imports, etc.)\n try {\n const result = execSync(\n `${ruff} check --fix${extendSelect}${config} --stdin-filename ${filePath} -`,\n { input: content, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] },\n );\n content = result;\n } catch (e: any) {\n // ruff check exits non-zero when it finds unfixable issues,\n // but stdout still contains the fixed content\n if (e.stdout) {\n content = e.stdout;\n }\n }\n\n // Then apply formatting\n try {\n content = execSync(\n `${ruff} format${config} --stdin-filename ${filePath} -`,\n {\n input: content,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n },\n );\n } catch {\n // Fall through with whatever content we have\n }\n\n return content;\n}\n"],"names":["Biome","getProjects","execFileSync","execSync","existsSync","readFileSync","createRequire","path","readToml","require","url","DEFAULT_BIOME_CONFIG","$schema","root","formatter","enabled","indentStyle","indentWidth","lineWidth","javascript","quoteStyle","trailingCommas","css","linter","rules","recommended","correctness","noUndeclaredDependencies","assist","actions","source","organizeImports","files","includes","BIOME_FORMATTABLE_EXTENSIONS","Set","formatFilesInSubtree","tree","dir","changedFiles","listChanges","filter","file","type","startsWith","pyFiles","endsWith","otherFiles","has","extname","pythonProjectConfigs","length","getPythonProjectRuffConfigs","content","ruffFixAndFormat","toString","hasRuffConfigOnDisk","getOwningProjectRuffConfig","write","join","formatWithBiomeCli","formatWithBiomeApi","biome","getBiomeCommand","command","args","input","encoding","cwd","stdio","projectKey","openProject","treeConfig","read","applyConfiguration","JSON","parse","formatContent","filePath","_biomeCommands","Map","get","undefined","pkgJsonPath","resolve","paths","dirname","pkgJson","binRelative","bin","binPath","process","execPath","set","_ruffCommand","getRuffCommand","cmd","pyproject","parent","configs","project","values","pyprojectPath","exists","wheelPackages","tool","hatch","build","targets","wheel","packages","modules","Array","isArray","pkg","map","split","lineLength","ruff","push","sep","owner","config","hasConfig","projectConfig","extendSelect","configArgs","stringify","arg","result","e","stdout"],"mappings":"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"}
|
package/src/utils/website-constructs/files/terraform/core/static-website/static-website.tf.template
CHANGED
|
@@ -423,6 +423,7 @@ resource "aws_cloudfront_response_headers_policy" "website" {
|
|
|
423
423
|
|
|
424
424
|
# CloudFront Distribution
|
|
425
425
|
resource "aws_cloudfront_distribution" "website" {
|
|
426
|
+
# See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesGeneral.html
|
|
426
427
|
#checkov:skip=CKV_AWS_174:Using CloudFront default certificate which does not support TLS v1.2
|
|
427
428
|
#checkov:skip=CKV_AWS_310:Origin failover not required for single S3 origin static website
|
|
428
429
|
#checkov:skip=CKV_AWS_374:Geo restrictions not required for global web application
|