@aws/nx-plugin 1.0.0-rc.47 → 1.0.0-rc.49

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.
Files changed (83) hide show
  1. package/LICENSE-THIRD-PARTY +15 -0
  2. package/generators.json +7 -0
  3. package/migrations.json +5 -0
  4. package/package.json +4 -4
  5. package/src/mcp-server/dev-server.d.ts +1 -0
  6. package/src/mcp-server/dev-server.js +18 -0
  7. package/src/mcp-server/dev-server.js.map +1 -0
  8. package/src/mcp-server/guide-pipeline.js +1 -1
  9. package/src/mcp-server/guide-pipeline.js.map +1 -1
  10. package/src/mcp-server/server.js +2 -0
  11. package/src/mcp-server/server.js.map +1 -1
  12. package/src/mcp-server/tools/general-guidance.d.ts +1 -1
  13. package/src/mcp-server/tools/general-guidance.js +1 -0
  14. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  15. package/src/mcp-server/tools/upgrade-workspace.d.ts +11 -0
  16. package/src/mcp-server/tools/upgrade-workspace.js +32 -0
  17. package/src/mcp-server/tools/upgrade-workspace.js.map +1 -0
  18. package/src/preset/__snapshots__/generator.spec.ts.snap +6 -0
  19. package/src/sdk/ts.d.ts +4 -2
  20. package/src/sdk/ts.js +4 -2
  21. package/src/sdk/ts.js.map +1 -1
  22. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +9 -4
  23. package/src/ts/agent/a2a-connection/__snapshots__/generator.spec.ts.snap +1 -0
  24. package/src/ts/agent/files/ag-ui/index.ts.template +25 -9
  25. package/src/ts/agent/files/common/agent.ts.template +2 -1
  26. package/src/ts/agent/files/deploy/Dockerfile.template +7 -3
  27. package/src/ts/agent/gateway-connection/__snapshots__/generator.spec.ts.snap +1 -0
  28. package/src/ts/agent/generator.js +2 -2
  29. package/src/ts/agent/generator.js.map +1 -1
  30. package/src/ts/agent/mcp-connection/__snapshots__/generator.spec.ts.snap +1 -0
  31. package/src/ts/mcp-server/files/Dockerfile.template +7 -3
  32. package/src/ts/mcp-server/generator.js +2 -2
  33. package/src/ts/mcp-server/generator.js.map +1 -1
  34. package/src/ts/nx-migration/files/migration.spec.ts.template +26 -0
  35. package/src/ts/nx-migration/files/migration.ts.template +55 -0
  36. package/src/ts/nx-migration/files/prompt.md.template +46 -0
  37. package/src/ts/nx-migration/generator.d.ts +24 -0
  38. package/src/ts/nx-migration/generator.js +122 -0
  39. package/src/ts/nx-migration/generator.js.map +1 -0
  40. package/src/ts/nx-migration/schema.d.js +6 -0
  41. package/src/ts/nx-migration/schema.d.js.map +1 -0
  42. package/src/ts/nx-migration/schema.d.ts +13 -0
  43. package/src/ts/nx-migration/schema.json +63 -0
  44. package/src/ts/nx-plugin/utils.d.ts +21 -1
  45. package/src/ts/nx-plugin/utils.js +46 -35
  46. package/src/ts/nx-plugin/utils.js.map +1 -1
  47. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +11 -3
  48. package/src/ts/rdb/files/Dockerfile.template +5 -1
  49. package/src/ts/rdb/files/src/migration-handler.ts.template +3 -1
  50. package/src/ts/rdb/generator.js +2 -2
  51. package/src/ts/rdb/generator.js.map +1 -1
  52. package/src/utils/agent-connection/agent-connection.js +4 -0
  53. package/src/utils/agent-connection/agent-connection.js.map +1 -1
  54. package/src/utils/agent-connection/files/core-strands/base/tool-errors-strands.ts.template +22 -0
  55. package/src/utils/agent-core-constructs/files/terraform/app/agentcore-gateway/__nameKebabCase__/__nameKebabCase__.tf.template +19 -1
  56. package/src/utils/commands.d.ts +4 -3
  57. package/src/utils/commands.js +6 -2
  58. package/src/utils/commands.js.map +1 -1
  59. package/src/utils/docker.d.ts +8 -0
  60. package/src/utils/docker.js +8 -1
  61. package/src/utils/docker.js.map +1 -1
  62. package/src/utils/format.js +54 -25
  63. package/src/utils/format.js.map +1 -1
  64. package/src/utils/init.js +7 -2
  65. package/src/utils/init.js.map +1 -1
  66. package/src/utils/mcp.d.ts +9 -0
  67. package/src/utils/mcp.js +9 -1
  68. package/src/utils/mcp.js.map +1 -1
  69. package/src/utils/migration-versions.d.ts +95 -0
  70. package/src/utils/migration-versions.js +128 -0
  71. package/src/utils/migration-versions.js.map +1 -0
  72. package/src/utils/nx.d.ts +18 -0
  73. package/src/utils/nx.js +24 -1
  74. package/src/utils/nx.js.map +1 -1
  75. package/src/utils/test-git.d.ts +39 -0
  76. package/src/utils/test-git.js +117 -0
  77. package/src/utils/test-git.js.map +1 -0
  78. package/src/utils/versions.d.ts +4 -1
  79. package/src/utils/versions.js +10 -2
  80. package/src/utils/versions.js.map +1 -1
  81. package/src/bin/aws-nx-mcp.d.ts +0 -2
  82. package/src/bin/aws-nx-mcp.js +0 -14
  83. package/src/bin/aws-nx-mcp.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/agent-connection/agent-connection.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport pyProjectGenerator, {\n getPyProjectDetails,\n} from '../../py/project/generator';\nimport tsProjectGenerator from '../../ts/lib/generator';\nimport { addStarExport, applyGritQL, matchGritQL } from '../ast';\nimport { addDependenciesToPackageJson } from '../dependencies';\nimport { esmVars } from '../module-format';\nimport { addDependenciesToPyProjectToml } from '../py';\nimport {\n type IPyDepVersion,\n type ITsDepVersion,\n withVersions,\n} from '../versions';\n\n/** Prefix a GritQL pattern with `language python` */\nconst py = (pattern: string) => `language python\\n${pattern}`;\n\nconst AGENT_CONNECTION_DIR = 'agent-connection';\nconst PACKAGES_DIR = 'packages';\nconst COMMON_DIR = 'common';\n\nexport const AGENT_CONNECTION_PROJECT_DIR = joinPathFragments(\n PACKAGES_DIR,\n COMMON_DIR,\n AGENT_CONNECTION_DIR,\n);\n\nconst PY_AGENT_CONNECTION_NAME = 'agent_connection';\n\n/**\n * Get the directory of the shared Python agent-connection project.\n */\nexport function getPythonAgentConnectionProjectDir(tree: Tree): string {\n const { dir } = getPyProjectDetails(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n return dir;\n}\n\n/**\n * Get the Python module name of the shared agent-connection project.\n */\nexport function getPythonAgentConnectionModuleName(tree: Tree): string {\n const { normalizedModuleName } = getPyProjectDetails(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n return normalizedModuleName;\n}\n\n/**\n * Get the shared agent-connection project as `{ name, root }`, suitable for\n * passing to {@link addWorkspaceDependencyToPyProject} (which derives the\n * dependency's PEP 503 distribution name from the project itself, so callers\n * never construct the name).\n */\nexport function getPythonAgentConnectionProject(tree: Tree): {\n name: string;\n root: string;\n} {\n const { fullyQualifiedName, dir } = getPyProjectDetails(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n return { name: fullyQualifiedName, root: dir };\n}\n\n/**\n * The connection protocols an agent framework can connect over. Each maps to a\n * framework-agnostic Layer-0/1 template directory plus a per-framework Layer-2\n * client directory (see `FRAMEWORKS`).\n */\nexport type ConnectionProtocol = 'mcp' | 'gateway' | 'a2a';\n\n/**\n * The agent frameworks we can generate connection clients for. Adding a new\n * framework is an additive change: append an entry to `FRAMEWORKS` with its\n * Layer-2 template directories and dependencies — the framework-agnostic\n * Layer-0/1 (`core-auth`, `core-shared`, the per-protocol transports/configs)\n * is reused unchanged.\n */\nexport type AgentFramework = 'strands' | 'langchain';\n\n/**\n * Resolve an agent component's framework, defaulting to `strands`. Connection\n * generators dispatch on the returned framework to pick the matching templates\n * and agent.py/agent.ts transforms.\n */\nexport function resolveAgentFramework(\n framework: string | undefined,\n): AgentFramework {\n return framework === 'langchain' ? 'langchain' : 'strands';\n}\n\n/**\n * Framework-agnostic Layer-1 templates, keyed by protocol. These resolve the\n * endpoint and build the signed transport/client-config; they contain no\n * framework dependency.\n */\nconst TS_PROTOCOL_TEMPLATES: Record<ConnectionProtocol, string> = {\n mcp: 'core-mcp',\n gateway: 'core-gateway',\n a2a: 'core-a2a',\n};\nconst PY_PROTOCOL_TEMPLATES: Record<ConnectionProtocol, string> = {\n mcp: 'py-core-mcp',\n gateway: 'py-core-gateway',\n a2a: 'py-core-a2a',\n};\n\n/**\n * A re-export the framework base emits into the agent-connection module's\n * `__init__.py` (the agent server entry point imports these by name).\n */\ninterface BaseReExport {\n fromModule: string;\n importName: string;\n}\n\n/**\n * A framework's Layer-2 templates for one language. `base` (optional) holds the\n * helpers every connection of that framework needs (error logging, per-session\n * agent cache); `baseReExports` lists what those helpers expose at the module\n * root. `protocols` maps each *supported* protocol to the thin client that wraps\n * the matching Layer-1 transport/config in the framework's client type.\n *\n * A framework whose Layer-2 needs no base helpers (e.g. LangChain, whose AG-UI\n * foundation reuses only the framework-agnostic session context) omits `base`\n * and leaves `baseReExports` empty.\n *\n * A protocol absent from `protocols` is not supported by that framework in that\n * language — callers must check before emitting. `deps` are the extra package\n * dependencies the base helpers require (per-connection client deps are added\n * by the connection generators, not here).\n */\ninterface FrameworkLanguageTemplates<Dep extends string> {\n base?: string;\n baseReExports: BaseReExport[];\n protocols: Partial<Record<ConnectionProtocol, string>>;\n deps: Dep[];\n}\n\n/**\n * Per-framework Layer-2 templates. Support is asymmetric by design: a framework\n * may target only one language (`ts`/`py` is optional) and only a subset of\n * protocols. Adding a framework — or extending an existing one to a new\n * language/protocol — is an additive change to this registry.\n */\ninterface FrameworkTemplates {\n ts?: FrameworkLanguageTemplates<string>;\n py?: FrameworkLanguageTemplates<IPyDepVersion>;\n}\n\nconst FRAMEWORKS: Record<AgentFramework, FrameworkTemplates> = {\n strands: {\n ts: {\n base: 'core-strands/base',\n baseReExports: [\n {\n fromModule: './core/with-session-id-strands.js',\n importName: '*',\n },\n {\n fromModule: './core/model-errors-strands.js',\n importName: '*',\n },\n ],\n protocols: {\n mcp: 'core-strands/mcp',\n gateway: 'core-strands/gateway',\n a2a: 'core-strands/a2a',\n },\n deps: [],\n },\n py: {\n base: 'py-core-strands/base',\n baseReExports: [\n {\n fromModule: '.core.with_session_id_strands',\n importName: 'with_session_id',\n },\n {\n fromModule: '.core.model_errors_strands',\n importName: 'log_model_errors',\n },\n ],\n protocols: {\n mcp: 'py-core-strands/mcp',\n gateway: 'py-core-strands/gateway',\n a2a: 'py-core-strands/a2a',\n },\n deps: ['strands-agents'],\n },\n },\n langchain: {\n // LangChain is supported for Python agents only (the TypeScript LangChain\n // agent foundation has no in-process AG-UI <-> LangGraph adapter, so it is\n // not scaffolded — see ts#agent). Its Layer-2 clients stay usable after\n // agent construction, so there is no base helper layer (no model-error\n // logger / per-session agent cache) and no strands-agents dependency — its\n // AG-UI foundation reuses only the framework-agnostic session context, and\n // its A2A client drives the a2a SDK directly rather than wrapping Strands'\n // A2AAgent. The per-connection adapter dependency (langchain-mcp-adapters /\n // a2a-sdk) is added by the connection generators.\n py: {\n baseReExports: [],\n protocols: {\n mcp: 'py-core-langchain/mcp',\n gateway: 'py-core-langchain/gateway',\n a2a: 'py-core-langchain/a2a',\n },\n deps: [],\n },\n },\n};\n\n/** Resolve a framework's templates for a language + protocol, or throw. */\nfunction frameworkLanguage<L extends 'ts' | 'py'>(\n framework: AgentFramework,\n language: L,\n protocol: ConnectionProtocol,\n): NonNullable<FrameworkTemplates[L]> & { protocol: string } {\n const lang = FRAMEWORKS[framework][language];\n if (!lang) {\n throw new Error(\n `The '${framework}' framework does not support ${language === 'ts' ? 'TypeScript' : 'Python'} agents.`,\n );\n }\n const protocolDir = lang.protocols[protocol];\n if (!protocolDir) {\n throw new Error(\n `The '${framework}' framework does not support ${protocol} connections for ${language === 'ts' ? 'TypeScript' : 'Python'} agents.`,\n );\n }\n return { ...lang, protocol: protocolDir } as NonNullable<\n FrameworkTemplates[L]\n > & { protocol: string };\n}\n\n/** Protocols whose Layer-1 needs the shared MCP transport (`core-shared`). */\nconst MCP_TRANSPORT_PROTOCOLS: ConnectionProtocol[] = ['mcp', 'gateway'];\n\nconst tsCoreDir = () =>\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'src', 'core');\n\n/**\n * Runtime dependencies each TypeScript core template directory imports. Emitting\n * a template also declares its dependencies in the agent-connection project's\n * package.json so the `noUndeclaredDependencies` lint rule passes.\n */\nconst TS_TEMPLATE_DEPS: Record<string, ITsDepVersion[]> = {\n 'core-runtime-config': ['@aws-lambda-powertools/parameters'],\n 'core-auth': ['@aws-sdk/credential-providers', 'aws4fetch'],\n 'core-shared': ['@aws-sdk/credential-providers', '@modelcontextprotocol/sdk'],\n 'core-mcp': ['@aws-sdk/credential-providers', '@modelcontextprotocol/sdk'],\n 'core-gateway': [\n '@aws-sdk/credential-providers',\n '@modelcontextprotocol/sdk',\n ],\n 'core-a2a': ['@aws-sdk/credential-providers', '@a2a-js/sdk'],\n 'core-strands/base': ['@strands-agents/sdk'],\n 'core-strands/mcp': ['@strands-agents/sdk'],\n 'core-strands/gateway': ['@strands-agents/sdk'],\n 'core-strands/a2a': ['@strands-agents/sdk'],\n};\n\nconst emitTs = (tree: Tree, templateDir: string) => {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', templateDir),\n tsCoreDir(),\n { ...esmVars(tree) },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n const deps = TS_TEMPLATE_DEPS[templateDir];\n if (deps?.length) {\n addDependenciesToPackageJson(\n tree,\n withVersions(deps),\n {},\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'package.json'),\n );\n }\n};\n\n/**\n * Ensure the shared TypeScript agent-connection project exists and has the\n * framework-agnostic core helpers (runtime-config loader, session context).\n * Per-connection generators emit their own protocol + framework client\n * templates via `addTypeScriptCoreClient`.\n */\nexport async function ensureTypeScriptAgentConnectionProject(\n tree: Tree,\n): Promise<void> {\n const projectJsonPath = joinPathFragments(\n AGENT_CONNECTION_PROJECT_DIR,\n 'project.json',\n );\n if (!tree.exists(projectJsonPath)) {\n await tsProjectGenerator(tree, {\n name: AGENT_CONNECTION_DIR,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n }\n\n // Framework-agnostic runtime-config + session-context helpers.\n emitTs(tree, 'core-runtime-config');\n\n // Re-export session-context helpers so agent server entry points can set the\n // current session on each inbound request without pulling in internals.\n await addStarExport(\n tree,\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'src', 'index.ts'),\n './core/session-context.js',\n );\n}\n\n/**\n * Emit a framework's base Layer-2 helpers (model-error logging + per-session\n * agent cache) into the agent-connection project and re-export them. The agent\n * server entry point imports these regardless of whether any connection client\n * is wired in, so the agent generator calls this directly; connection\n * generators call it transitively via `addTypeScriptCoreClient`.\n */\nexport async function addTypeScriptFrameworkBase(\n tree: Tree,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const lang = FRAMEWORKS[framework].ts;\n if (!lang) {\n throw new Error(\n `The '${framework}' framework does not support TypeScript agents.`,\n );\n }\n if (lang.base) {\n emitTs(tree, lang.base);\n }\n\n const indexPath = joinPathFragments(\n AGENT_CONNECTION_PROJECT_DIR,\n 'src',\n 'index.ts',\n );\n for (const reExport of lang.baseReExports) {\n await addStarExport(tree, indexPath, reExport.fromModule);\n }\n}\n\n/**\n * Emit the TypeScript client templates for a connection protocol and agent\n * framework into the agent-connection project's `src/core/` directory. Safe to\n * call multiple times — `KeepExisting` preserves customised files.\n *\n * Layers emitted:\n * - Layer 0 `core-auth` (signed fetch + endpoint resolution) — always\n * - Layer 1 `core-shared` MCP transport — for mcp/gateway protocols\n * - Layer 1 the protocol's transport/client-config — always\n * - Layer 2 the framework's base helpers + protocol client — per framework\n */\nexport async function addTypeScriptCoreClient(\n tree: Tree,\n protocol: ConnectionProtocol,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const fw = frameworkLanguage(framework, 'ts', protocol);\n\n emitTs(tree, 'core-auth');\n if (MCP_TRANSPORT_PROTOCOLS.includes(protocol)) {\n emitTs(tree, 'core-shared');\n }\n emitTs(tree, TS_PROTOCOL_TEMPLATES[protocol]);\n\n // Framework Layer 2: base helpers (re-exported for the agent server) + the\n // thin protocol client.\n await addTypeScriptFrameworkBase(tree, framework);\n emitTs(tree, fw.protocol);\n}\n\n/**\n * Ensure the shared Python agent-connection project exists.\n * Only creates the project shell — per-connection generators are responsible\n * for emitting their own core client templates.\n */\nexport async function ensurePythonAgentConnectionProject(\n tree: Tree,\n): Promise<void> {\n const projectDir = getPythonAgentConnectionProjectDir(tree);\n const moduleName = getPythonAgentConnectionModuleName(tree);\n const projectJsonPath = joinPathFragments(projectDir, 'project.json');\n\n if (!tree.exists(projectJsonPath)) {\n // Create the Python project using the standard py#project generator\n await pyProjectGenerator(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n type: 'library',\n });\n }\n\n const moduleDir = joinPathFragments(projectDir, moduleName);\n\n // Ensure core/__init__.py exists\n const coreDir = joinPathFragments(moduleDir, 'core');\n if (!tree.exists(joinPathFragments(coreDir, '__init__.py'))) {\n tree.write(joinPathFragments(coreDir, '__init__.py'), '');\n }\n\n // Ensure app/__init__.py exists\n const appInitPath = joinPathFragments(moduleDir, 'app', '__init__.py');\n if (!tree.exists(appInitPath)) {\n tree.write(appInitPath, '');\n }\n\n // Framework-agnostic runtime-config + session-context helpers.\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'py-core-runtime-config'),\n coreDir,\n {},\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Re-export the session id context so agent server entry points can set the\n // current session on each inbound request without importing internals.\n const moduleInitPath = joinPathFragments(moduleDir, '__init__.py');\n await addPythonReExport(\n tree,\n moduleInitPath,\n '.core.session_context',\n 'get_current_session_id',\n );\n await addPythonReExport(\n tree,\n moduleInitPath,\n '.core.session_context',\n 'session_id_context',\n );\n\n // The runtime-config loader reads AppConfig via aws-lambda-powertools.\n addDependenciesToPyProjectToml(tree, projectDir, ['aws-lambda-powertools']);\n}\n\nconst pyCoreDir = (tree: Tree) =>\n joinPathFragments(\n getPythonAgentConnectionProjectDir(tree),\n getPythonAgentConnectionModuleName(tree),\n 'core',\n );\n\nconst emitPy = (tree: Tree, templateDir: string) =>\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', templateDir),\n pyCoreDir(tree),\n {},\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n/**\n * Emit a framework's base Layer-2 helpers (model-error logging + per-session\n * agent cache) into the Python agent-connection project and re-export them.\n * The agent server entry point imports these regardless of whether a connection\n * client is wired in, so the agent generator calls this directly; connection\n * generators call it transitively via `addPythonCoreClient`.\n */\nexport async function addPythonFrameworkBase(\n tree: Tree,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const lang = FRAMEWORKS[framework].py;\n if (!lang) {\n throw new Error(\n `The '${framework}' framework does not support Python agents.`,\n );\n }\n if (lang.base) {\n emitPy(tree, lang.base);\n }\n\n const moduleInitPath = joinPathFragments(\n getPythonAgentConnectionProjectDir(tree),\n getPythonAgentConnectionModuleName(tree),\n '__init__.py',\n );\n for (const reExport of lang.baseReExports) {\n await addPythonReExport(\n tree,\n moduleInitPath,\n reExport.fromModule,\n reExport.importName,\n );\n }\n\n if (lang.deps.length > 0) {\n addDependenciesToPyProjectToml(\n tree,\n getPythonAgentConnectionProjectDir(tree),\n lang.deps,\n );\n }\n}\n\n/**\n * Emit the Python client templates for a connection protocol and agent\n * framework into the agent-connection project's core directory.\n *\n * Layers emitted:\n * - Layer 0 `py-core-auth` (SigV4 httpx.Auth, session auth, endpoints) — always\n * - Layer 1 `py-core-shared` MCP transport — for mcp/gateway protocols\n * - Layer 1 the protocol's transport/client-config — always\n * - Layer 2 the framework's base helpers + protocol client — per framework\n */\nexport async function addPythonCoreClient(\n tree: Tree,\n protocol: ConnectionProtocol,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const fw = frameworkLanguage(framework, 'py', protocol);\n\n emitPy(tree, 'py-core-auth');\n if (MCP_TRANSPORT_PROTOCOLS.includes(protocol)) {\n emitPy(tree, 'py-core-shared');\n }\n emitPy(tree, PY_PROTOCOL_TEMPLATES[protocol]);\n\n // Framework Layer 2: base helpers + the thin protocol client.\n await addPythonFrameworkBase(tree, framework);\n emitPy(tree, fw.protocol);\n}\n\n/**\n * Patch a TypeScript agent.ts to create a connection client and register it\n * in the Agent's tools. Used by the MCP and gateway connection generators —\n * the client class must expose a static async `create()` returning a value\n * accepted by `Agent.tools` (e.g. an `McpClient`).\n */\nexport async function addTypeScriptClientToAgent(\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> {\n const clientCreationStmt = `const ${clientVarName} = await ${clientClassName}.create();`;\n\n // Transform the arrow function that contains `new Agent`:\n // - Expression body: wrap in block with client creation and return\n // - Block body: prepend client creation statement\n await applyGritQL(\n tree,\n agentFilePath,\n `or { \\`async ($p) => new Agent($args)\\` => raw\\`async ($p) => {\n ${clientCreationStmt}\n return new Agent($args);\n}\\` where { $program <: not contains \\`${clientClassName}.create\\` }, \\`async ($p) => { $body }\\` => raw\\`async ($p) => {\n ${clientCreationStmt}\n $body\n}\\` where { $body <: contains \\`new Agent($_)\\`, $program <: not contains \\`${clientClassName}.create\\` } }`,\n );\n\n // Prepend the client to the tools array\n await applyGritQL(\n tree,\n agentFilePath,\n `\\`tools: [$items]\\` => \\`tools: [${clientVarName}, $items]\\` where { $items <: within \\`new Agent($_)\\`, $items <: not contains \\`${clientVarName}\\` }`,\n );\n}\n\n/**\n * Patch a Python agent.py to create a context-managed connection client,\n * enter it in the `with` block around `yield Agent(...)`, and spread its\n * `list_tools_sync()` into the Agent's tools. Used by the MCP and gateway\n * connection generators — the client class must expose a static `create()`\n * returning a context manager with `list_tools_sync()` (e.g. an `MCPClient`).\n */\nasync function addPythonClientToAgent(\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> {\n await addPythonClientTools(tree, agentFilePath, clientVarName);\n await addPythonClientToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n );\n}\n\n/**\n * Append `*<clientVarName>.list_tools_sync()` to the Agent's tools list.\n */\nconst addPythonClientTools = async (\n tree: Tree,\n filePath: string,\n clientVarName: string,\n): Promise<void> => {\n if (\n await matchGritQL(\n tree,\n filePath,\n py(`\\`${clientVarName}.list_tools_sync()\\``),\n )\n ) {\n return;\n }\n\n await applyGritQL(\n tree,\n filePath,\n py(`\\`tools=$old\\` where {\n $old <: not contains \\`${clientVarName}\\`,\n if ($old <: \\`[]\\`) {\n $old => \\`[*${clientVarName}.list_tools_sync()]\\`\n } else {\n $old <: \\`[$items]\\` where { $items += \\`, *${clientVarName}.list_tools_sync()\\` }\n }\n}`),\n );\n};\n\n/**\n * Create the client in get_agent and enter it in the `with` block.\n *\n * First connection: rewrites `def get_agent` to wrap the body in a with\n * block. Subsequent connections: adds to the existing with items and\n * prepends the creation line.\n */\nconst addPythonClientToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> => {\n if (\n await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create()\\``))\n ) {\n return;\n }\n\n // Try the \"add to existing with block\" pattern first.\n // If it succeeds, there was already a with block from a previous connection.\n const addedToWith = await applyGritQL(\n tree,\n filePath,\n py(`\\`with ($items,): $body\\` where {\n $items <: not contains \\`${clientVarName}\\`,\n $items += \\`, ${clientVarName}\\`\n}`),\n );\n\n if (addedToWith) {\n // Subsequent connection — prepend the creation line to the function body\n // (a single anchor, so the line is inserted exactly once).\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`yield Agent($_)\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${clientVarName} = ${clientClassName}.create()\n $body\\``),\n );\n return;\n }\n\n // First connection — wrap the existing `def get_agent` body in a single\n // `with (<var>,):` block.\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`yield Agent($_)\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${clientVarName} = ${clientClassName}.create()\n with (\n ${clientVarName},\n ):\n $body\\``),\n );\n};\n\n/**\n * Patch a LangChain agent.py to load connection tools and spread them into the\n * `create_agent(...)` tools list. Used by the MCP and gateway connection\n * generators when the source agent uses the `langchain` framework — the client\n * class must expose a static `create()` returning a `list[BaseTool]`. The tools\n * come from langchain-mcp-adapters' `MultiServerMCPClient`, which keeps the\n * connection config on each tool and opens a fresh MCP session per call, so the\n * tools stay usable after `get_agent` returns — no `with` block is needed.\n * Mirrors {@link addPythonClientToAgent} for the Strands shape.\n */\nasync function addPythonLangchainClientToAgent(\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> {\n await addPythonLangchainClientTools(tree, agentFilePath, clientVarName);\n await addPythonLangchainClientToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n );\n}\n\n/**\n * Spread `*<clientVarName>` into the `create_agent(...)` tools list.\n *\n * Scoped via `$old <: within \\`create_agent($_)\\`` so only the agent's own\n * tools= keyword argument is touched (not an unrelated function with a\n * `tools=` parameter).\n */\nconst addPythonLangchainClientTools = async (\n tree: Tree,\n filePath: string,\n clientVarName: string,\n): Promise<void> => {\n await applyGritQL(\n tree,\n filePath,\n py(`\\`tools=$old\\` where {\n $old <: within \\`create_agent($_)\\`,\n $old <: not contains \\`*${clientVarName}\\`,\n if ($old <: \\`[]\\`) {\n $old => \\`[*${clientVarName}]\\`\n } else {\n $old <: \\`[$items]\\` where { $items += \\`, *${clientVarName}\\` }\n }\n}`),\n );\n};\n\n/**\n * Prepend `<clientVarName> = <clientClassName>.create()` to the get_agent body\n * (which loads the connection's tools). No `with` block — the LangChain client\n * hands langchain-mcp-adapters a connection config, so each tool opens its own\n * MCP session per call and the tools are safe to use after get_agent returns.\n * Idempotent via the `not contains <clientClassName>.create` guard.\n */\nconst addPythonLangchainClientToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> => {\n if (\n await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create()\\``))\n ) {\n return;\n }\n\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`create_agent($_)\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${clientVarName} = ${clientClassName}.create()\n $body\\``),\n );\n};\n\n/**\n * Per-framework naming + template layout for a Python per-connection client.\n * Every py#agent connection generator (mcp, gateway, a2a) names its client\n * class `<Name>Client<suffix>`, its module `<name>_client_<suffix>`, and reads\n * the per-connection app-client template from a per-framework sub-path of its\n * own `files/` dir — all of which vary only by framework, so they live here as\n * one source of truth.\n *\n * Keyed by {@link AgentFramework} rather than a boolean so a third framework is\n * an additive entry here — never another `if (isLangchain)` branch in each\n * generator.\n */\nexport interface PyClientNaming {\n /** Class-name suffix on the per-connection app client (`<Name>Client<suffix>`). */\n clientClassSuffix: string;\n /** Module/file-name suffix on the per-connection app client (`<name>_client_<suffix>`). */\n clientModuleSuffix: string;\n /**\n * Sub-path, under a connection generator's `files/` dir, of the per-connection\n * app client template for this framework. Every connection generator lays its\n * templates out the same way, so this resolves against any of them.\n */\n appTemplateSubdir: string;\n}\n\nexport const PY_CLIENT_NAMING: Record<AgentFramework, PyClientNaming> = {\n strands: {\n clientClassSuffix: 'Strands',\n clientModuleSuffix: 'strands',\n appTemplateSubdir: joinPathFragments('agent-connection', 'app'),\n },\n langchain: {\n clientClassSuffix: 'LangChain',\n clientModuleSuffix: 'langchain',\n appTemplateSubdir: joinPathFragments(\n 'langchain',\n 'agent-connection',\n 'app',\n ),\n },\n};\n\n/**\n * How an MCP-family connection (an MCP server or an AgentCore Gateway, both of\n * which expose tools over MCP) is wired into a Python agent of a given\n * framework. The mcp-connection and gateway-connection generators share this:\n * they differ only in the metadata of the thing being connected, not in how\n * the framework's client is shaped or spliced into agent.py. A2A connections\n * wire tools differently, so they reuse only {@link PY_CLIENT_NAMING}.\n */\nexport interface PyMcpFamilyConnection extends PyClientNaming {\n /** Extra Python deps this framework's MCP-family client needs (beyond the shared transport). */\n deps: IPyDepVersion[];\n /**\n * Splice the per-connection client into the agent's `get_agent` in agent.py.\n * Strands enters a context-managed `MCPClient` and spreads `list_tools_sync()`;\n * LangChain loads `BaseTool`s and spreads them into `create_agent(...)`.\n */\n wireClientIntoAgent: (\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n ) => Promise<void>;\n}\n\nexport const PY_MCP_FAMILY_CONNECTIONS: Record<\n AgentFramework,\n PyMcpFamilyConnection\n> = {\n strands: {\n ...PY_CLIENT_NAMING.strands,\n deps: [],\n wireClientIntoAgent: addPythonClientToAgent,\n },\n langchain: {\n ...PY_CLIENT_NAMING.langchain,\n // langchain-mcp-adapters backs the LangChain client; it must not pull Strands in.\n deps: ['langchain-mcp-adapters'],\n wireClientIntoAgent: addPythonLangchainClientToAgent,\n },\n};\n\n/**\n * Add a re-export to the Python agent-connection module's __init__.py.\n * Equivalent to addStarExport for TS.\n * Uses `__all__` to mark re-exports as public (satisfies ruff F401).\n * Uses GritQL for AST-aware transforms where possible.\n */\nexport async function addPythonReExport(\n tree: Tree,\n initFilePath: string,\n fromModule: string,\n importName: string,\n): Promise<void> {\n // Check if already exported\n if (await matchGritQL(tree, initFilePath, `\\`${importName}\\``)) {\n return;\n }\n\n const importLine = `from ${fromModule} import ${importName}`;\n const allEntry = `\"${importName}\"`;\n\n // If there's already an import from the same module, merge into its name list\n // (ruff I001 rejects two `from X import ...` lines for the same module).\n const mergedImport = await applyGritQL(\n tree,\n initFilePath,\n py(`\\`from ${fromModule} import $names\\` where {\n $names <: not contains \\`${importName}\\`,\n $names += \\`, ${importName}\\`\n}`),\n );\n\n if (mergedImport) {\n await applyGritQL(\n tree,\n initFilePath,\n py(`\\`__all__ = [$items]\\` where { $items += \\`, ${allEntry}\\` }`),\n );\n return;\n }\n\n // Try to append import after existing import using +=\n const appendedImport = await applyGritQL(\n tree,\n initFilePath,\n py(\n `\\`from $mod import $name\\` as $stmt where {\n $program <: not contains \\`${importName}\\`,\n $stmt += \\`\\n${importLine}\\`\n}`,\n ),\n );\n\n if (appendedImport) {\n // Also append to existing __all__\n await applyGritQL(\n tree,\n initFilePath,\n py(`\\`__all__ = [$items]\\` where { $items += \\`, ${allEntry}\\` }`),\n );\n return;\n }\n\n // No existing imports — first re-export. Try to anchor after docstring.\n const anchoredImport = await applyGritQL(\n tree,\n initFilePath,\n py(\n `\\`\"\"\"Automatically generated by Nx.\"\"\"\\` as $doc => \\`$doc\\n${importLine}\\n\\n__all__ = [${allEntry}]\\``,\n ),\n );\n\n if (!anchoredImport) {\n // Completely empty file or no docstring — write directly\n tree.write(initFilePath, `${importLine}\\n\\n__all__ = [${allEntry}]\\n`);\n }\n}\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","pyProjectGenerator","getPyProjectDetails","tsProjectGenerator","addStarExport","applyGritQL","matchGritQL","addDependenciesToPackageJson","esmVars","addDependenciesToPyProjectToml","withVersions","py","pattern","AGENT_CONNECTION_DIR","PACKAGES_DIR","COMMON_DIR","AGENT_CONNECTION_PROJECT_DIR","PY_AGENT_CONNECTION_NAME","getPythonAgentConnectionProjectDir","tree","dir","name","directory","getPythonAgentConnectionModuleName","normalizedModuleName","getPythonAgentConnectionProject","fullyQualifiedName","root","resolveAgentFramework","framework","TS_PROTOCOL_TEMPLATES","mcp","gateway","a2a","PY_PROTOCOL_TEMPLATES","FRAMEWORKS","strands","ts","base","baseReExports","fromModule","importName","protocols","deps","langchain","frameworkLanguage","language","protocol","lang","Error","protocolDir","MCP_TRANSPORT_PROTOCOLS","tsCoreDir","TS_TEMPLATE_DEPS","emitTs","templateDir","dirname","overwriteStrategy","KeepExisting","length","ensureTypeScriptAgentConnectionProject","projectJsonPath","exists","addTypeScriptFrameworkBase","indexPath","reExport","addTypeScriptCoreClient","fw","includes","ensurePythonAgentConnectionProject","projectDir","moduleName","type","moduleDir","coreDir","write","appInitPath","moduleInitPath","addPythonReExport","pyCoreDir","emitPy","addPythonFrameworkBase","addPythonCoreClient","addTypeScriptClientToAgent","agentFilePath","clientClassName","clientVarName","clientCreationStmt","addPythonClientToAgent","addPythonClientTools","addPythonClientToGetAgent","filePath","addedToWith","addPythonLangchainClientToAgent","addPythonLangchainClientTools","addPythonLangchainClientToGetAgent","PY_CLIENT_NAMING","clientClassSuffix","clientModuleSuffix","appTemplateSubdir","PY_MCP_FAMILY_CONNECTIONS","wireClientIntoAgent","initFilePath","importLine","allEntry","mergedImport","appendedImport","anchoredImport"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/agent-connection/agent-connection.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport pyProjectGenerator, {\n getPyProjectDetails,\n} from '../../py/project/generator';\nimport tsProjectGenerator from '../../ts/lib/generator';\nimport { addStarExport, applyGritQL, matchGritQL } from '../ast';\nimport { addDependenciesToPackageJson } from '../dependencies';\nimport { esmVars } from '../module-format';\nimport { addDependenciesToPyProjectToml } from '../py';\nimport {\n type IPyDepVersion,\n type ITsDepVersion,\n withVersions,\n} from '../versions';\n\n/** Prefix a GritQL pattern with `language python` */\nconst py = (pattern: string) => `language python\\n${pattern}`;\n\nconst AGENT_CONNECTION_DIR = 'agent-connection';\nconst PACKAGES_DIR = 'packages';\nconst COMMON_DIR = 'common';\n\nexport const AGENT_CONNECTION_PROJECT_DIR = joinPathFragments(\n PACKAGES_DIR,\n COMMON_DIR,\n AGENT_CONNECTION_DIR,\n);\n\nconst PY_AGENT_CONNECTION_NAME = 'agent_connection';\n\n/**\n * Get the directory of the shared Python agent-connection project.\n */\nexport function getPythonAgentConnectionProjectDir(tree: Tree): string {\n const { dir } = getPyProjectDetails(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n return dir;\n}\n\n/**\n * Get the Python module name of the shared agent-connection project.\n */\nexport function getPythonAgentConnectionModuleName(tree: Tree): string {\n const { normalizedModuleName } = getPyProjectDetails(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n return normalizedModuleName;\n}\n\n/**\n * Get the shared agent-connection project as `{ name, root }`, suitable for\n * passing to {@link addWorkspaceDependencyToPyProject} (which derives the\n * dependency's PEP 503 distribution name from the project itself, so callers\n * never construct the name).\n */\nexport function getPythonAgentConnectionProject(tree: Tree): {\n name: string;\n root: string;\n} {\n const { fullyQualifiedName, dir } = getPyProjectDetails(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n return { name: fullyQualifiedName, root: dir };\n}\n\n/**\n * The connection protocols an agent framework can connect over. Each maps to a\n * framework-agnostic Layer-0/1 template directory plus a per-framework Layer-2\n * client directory (see `FRAMEWORKS`).\n */\nexport type ConnectionProtocol = 'mcp' | 'gateway' | 'a2a';\n\n/**\n * The agent frameworks we can generate connection clients for. Adding a new\n * framework is an additive change: append an entry to `FRAMEWORKS` with its\n * Layer-2 template directories and dependencies — the framework-agnostic\n * Layer-0/1 (`core-auth`, `core-shared`, the per-protocol transports/configs)\n * is reused unchanged.\n */\nexport type AgentFramework = 'strands' | 'langchain';\n\n/**\n * Resolve an agent component's framework, defaulting to `strands`. Connection\n * generators dispatch on the returned framework to pick the matching templates\n * and agent.py/agent.ts transforms.\n */\nexport function resolveAgentFramework(\n framework: string | undefined,\n): AgentFramework {\n return framework === 'langchain' ? 'langchain' : 'strands';\n}\n\n/**\n * Framework-agnostic Layer-1 templates, keyed by protocol. These resolve the\n * endpoint and build the signed transport/client-config; they contain no\n * framework dependency.\n */\nconst TS_PROTOCOL_TEMPLATES: Record<ConnectionProtocol, string> = {\n mcp: 'core-mcp',\n gateway: 'core-gateway',\n a2a: 'core-a2a',\n};\nconst PY_PROTOCOL_TEMPLATES: Record<ConnectionProtocol, string> = {\n mcp: 'py-core-mcp',\n gateway: 'py-core-gateway',\n a2a: 'py-core-a2a',\n};\n\n/**\n * A re-export the framework base emits into the agent-connection module's\n * `__init__.py` (the agent server entry point imports these by name).\n */\ninterface BaseReExport {\n fromModule: string;\n importName: string;\n}\n\n/**\n * A framework's Layer-2 templates for one language. `base` (optional) holds the\n * helpers every connection of that framework needs (error logging, per-session\n * agent cache); `baseReExports` lists what those helpers expose at the module\n * root. `protocols` maps each *supported* protocol to the thin client that wraps\n * the matching Layer-1 transport/config in the framework's client type.\n *\n * A framework whose Layer-2 needs no base helpers (e.g. LangChain, whose AG-UI\n * foundation reuses only the framework-agnostic session context) omits `base`\n * and leaves `baseReExports` empty.\n *\n * A protocol absent from `protocols` is not supported by that framework in that\n * language — callers must check before emitting. `deps` are the extra package\n * dependencies the base helpers require (per-connection client deps are added\n * by the connection generators, not here).\n */\ninterface FrameworkLanguageTemplates<Dep extends string> {\n base?: string;\n baseReExports: BaseReExport[];\n protocols: Partial<Record<ConnectionProtocol, string>>;\n deps: Dep[];\n}\n\n/**\n * Per-framework Layer-2 templates. Support is asymmetric by design: a framework\n * may target only one language (`ts`/`py` is optional) and only a subset of\n * protocols. Adding a framework — or extending an existing one to a new\n * language/protocol — is an additive change to this registry.\n */\ninterface FrameworkTemplates {\n ts?: FrameworkLanguageTemplates<string>;\n py?: FrameworkLanguageTemplates<IPyDepVersion>;\n}\n\nconst FRAMEWORKS: Record<AgentFramework, FrameworkTemplates> = {\n strands: {\n ts: {\n base: 'core-strands/base',\n baseReExports: [\n {\n fromModule: './core/with-session-id-strands.js',\n importName: '*',\n },\n {\n fromModule: './core/model-errors-strands.js',\n importName: '*',\n },\n {\n fromModule: './core/tool-errors-strands.js',\n importName: '*',\n },\n ],\n protocols: {\n mcp: 'core-strands/mcp',\n gateway: 'core-strands/gateway',\n a2a: 'core-strands/a2a',\n },\n deps: [],\n },\n py: {\n base: 'py-core-strands/base',\n baseReExports: [\n {\n fromModule: '.core.with_session_id_strands',\n importName: 'with_session_id',\n },\n {\n fromModule: '.core.model_errors_strands',\n importName: 'log_model_errors',\n },\n ],\n protocols: {\n mcp: 'py-core-strands/mcp',\n gateway: 'py-core-strands/gateway',\n a2a: 'py-core-strands/a2a',\n },\n deps: ['strands-agents'],\n },\n },\n langchain: {\n // LangChain is supported for Python agents only (the TypeScript LangChain\n // agent foundation has no in-process AG-UI <-> LangGraph adapter, so it is\n // not scaffolded — see ts#agent). Its Layer-2 clients stay usable after\n // agent construction, so there is no base helper layer (no model-error\n // logger / per-session agent cache) and no strands-agents dependency — its\n // AG-UI foundation reuses only the framework-agnostic session context, and\n // its A2A client drives the a2a SDK directly rather than wrapping Strands'\n // A2AAgent. The per-connection adapter dependency (langchain-mcp-adapters /\n // a2a-sdk) is added by the connection generators.\n py: {\n baseReExports: [],\n protocols: {\n mcp: 'py-core-langchain/mcp',\n gateway: 'py-core-langchain/gateway',\n a2a: 'py-core-langchain/a2a',\n },\n deps: [],\n },\n },\n};\n\n/** Resolve a framework's templates for a language + protocol, or throw. */\nfunction frameworkLanguage<L extends 'ts' | 'py'>(\n framework: AgentFramework,\n language: L,\n protocol: ConnectionProtocol,\n): NonNullable<FrameworkTemplates[L]> & { protocol: string } {\n const lang = FRAMEWORKS[framework][language];\n if (!lang) {\n throw new Error(\n `The '${framework}' framework does not support ${language === 'ts' ? 'TypeScript' : 'Python'} agents.`,\n );\n }\n const protocolDir = lang.protocols[protocol];\n if (!protocolDir) {\n throw new Error(\n `The '${framework}' framework does not support ${protocol} connections for ${language === 'ts' ? 'TypeScript' : 'Python'} agents.`,\n );\n }\n return { ...lang, protocol: protocolDir } as NonNullable<\n FrameworkTemplates[L]\n > & { protocol: string };\n}\n\n/** Protocols whose Layer-1 needs the shared MCP transport (`core-shared`). */\nconst MCP_TRANSPORT_PROTOCOLS: ConnectionProtocol[] = ['mcp', 'gateway'];\n\nconst tsCoreDir = () =>\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'src', 'core');\n\n/**\n * Runtime dependencies each TypeScript core template directory imports. Emitting\n * a template also declares its dependencies in the agent-connection project's\n * package.json so the `noUndeclaredDependencies` lint rule passes.\n */\nconst TS_TEMPLATE_DEPS: Record<string, ITsDepVersion[]> = {\n 'core-runtime-config': ['@aws-lambda-powertools/parameters'],\n 'core-auth': ['@aws-sdk/credential-providers', 'aws4fetch'],\n 'core-shared': ['@aws-sdk/credential-providers', '@modelcontextprotocol/sdk'],\n 'core-mcp': ['@aws-sdk/credential-providers', '@modelcontextprotocol/sdk'],\n 'core-gateway': [\n '@aws-sdk/credential-providers',\n '@modelcontextprotocol/sdk',\n ],\n 'core-a2a': ['@aws-sdk/credential-providers', '@a2a-js/sdk'],\n 'core-strands/base': ['@strands-agents/sdk'],\n 'core-strands/mcp': ['@strands-agents/sdk'],\n 'core-strands/gateway': ['@strands-agents/sdk'],\n 'core-strands/a2a': ['@strands-agents/sdk'],\n};\n\nconst emitTs = (tree: Tree, templateDir: string) => {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', templateDir),\n tsCoreDir(),\n { ...esmVars(tree) },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n const deps = TS_TEMPLATE_DEPS[templateDir];\n if (deps?.length) {\n addDependenciesToPackageJson(\n tree,\n withVersions(deps),\n {},\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'package.json'),\n );\n }\n};\n\n/**\n * Ensure the shared TypeScript agent-connection project exists and has the\n * framework-agnostic core helpers (runtime-config loader, session context).\n * Per-connection generators emit their own protocol + framework client\n * templates via `addTypeScriptCoreClient`.\n */\nexport async function ensureTypeScriptAgentConnectionProject(\n tree: Tree,\n): Promise<void> {\n const projectJsonPath = joinPathFragments(\n AGENT_CONNECTION_PROJECT_DIR,\n 'project.json',\n );\n if (!tree.exists(projectJsonPath)) {\n await tsProjectGenerator(tree, {\n name: AGENT_CONNECTION_DIR,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n });\n }\n\n // Framework-agnostic runtime-config + session-context helpers.\n emitTs(tree, 'core-runtime-config');\n\n // Re-export session-context helpers so agent server entry points can set the\n // current session on each inbound request without pulling in internals.\n await addStarExport(\n tree,\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'src', 'index.ts'),\n './core/session-context.js',\n );\n}\n\n/**\n * Emit a framework's base Layer-2 helpers (model-error logging + per-session\n * agent cache) into the agent-connection project and re-export them. The agent\n * server entry point imports these regardless of whether any connection client\n * is wired in, so the agent generator calls this directly; connection\n * generators call it transitively via `addTypeScriptCoreClient`.\n */\nexport async function addTypeScriptFrameworkBase(\n tree: Tree,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const lang = FRAMEWORKS[framework].ts;\n if (!lang) {\n throw new Error(\n `The '${framework}' framework does not support TypeScript agents.`,\n );\n }\n if (lang.base) {\n emitTs(tree, lang.base);\n }\n\n const indexPath = joinPathFragments(\n AGENT_CONNECTION_PROJECT_DIR,\n 'src',\n 'index.ts',\n );\n for (const reExport of lang.baseReExports) {\n await addStarExport(tree, indexPath, reExport.fromModule);\n }\n}\n\n/**\n * Emit the TypeScript client templates for a connection protocol and agent\n * framework into the agent-connection project's `src/core/` directory. Safe to\n * call multiple times — `KeepExisting` preserves customised files.\n *\n * Layers emitted:\n * - Layer 0 `core-auth` (signed fetch + endpoint resolution) — always\n * - Layer 1 `core-shared` MCP transport — for mcp/gateway protocols\n * - Layer 1 the protocol's transport/client-config — always\n * - Layer 2 the framework's base helpers + protocol client — per framework\n */\nexport async function addTypeScriptCoreClient(\n tree: Tree,\n protocol: ConnectionProtocol,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const fw = frameworkLanguage(framework, 'ts', protocol);\n\n emitTs(tree, 'core-auth');\n if (MCP_TRANSPORT_PROTOCOLS.includes(protocol)) {\n emitTs(tree, 'core-shared');\n }\n emitTs(tree, TS_PROTOCOL_TEMPLATES[protocol]);\n\n // Framework Layer 2: base helpers (re-exported for the agent server) + the\n // thin protocol client.\n await addTypeScriptFrameworkBase(tree, framework);\n emitTs(tree, fw.protocol);\n}\n\n/**\n * Ensure the shared Python agent-connection project exists.\n * Only creates the project shell — per-connection generators are responsible\n * for emitting their own core client templates.\n */\nexport async function ensurePythonAgentConnectionProject(\n tree: Tree,\n): Promise<void> {\n const projectDir = getPythonAgentConnectionProjectDir(tree);\n const moduleName = getPythonAgentConnectionModuleName(tree);\n const projectJsonPath = joinPathFragments(projectDir, 'project.json');\n\n if (!tree.exists(projectJsonPath)) {\n // Create the Python project using the standard py#project generator\n await pyProjectGenerator(tree, {\n name: PY_AGENT_CONNECTION_NAME,\n directory: joinPathFragments(PACKAGES_DIR, COMMON_DIR),\n type: 'library',\n });\n }\n\n const moduleDir = joinPathFragments(projectDir, moduleName);\n\n // Ensure core/__init__.py exists\n const coreDir = joinPathFragments(moduleDir, 'core');\n if (!tree.exists(joinPathFragments(coreDir, '__init__.py'))) {\n tree.write(joinPathFragments(coreDir, '__init__.py'), '');\n }\n\n // Ensure app/__init__.py exists\n const appInitPath = joinPathFragments(moduleDir, 'app', '__init__.py');\n if (!tree.exists(appInitPath)) {\n tree.write(appInitPath, '');\n }\n\n // Framework-agnostic runtime-config + session-context helpers.\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'py-core-runtime-config'),\n coreDir,\n {},\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Re-export the session id context so agent server entry points can set the\n // current session on each inbound request without importing internals.\n const moduleInitPath = joinPathFragments(moduleDir, '__init__.py');\n await addPythonReExport(\n tree,\n moduleInitPath,\n '.core.session_context',\n 'get_current_session_id',\n );\n await addPythonReExport(\n tree,\n moduleInitPath,\n '.core.session_context',\n 'session_id_context',\n );\n\n // The runtime-config loader reads AppConfig via aws-lambda-powertools.\n addDependenciesToPyProjectToml(tree, projectDir, ['aws-lambda-powertools']);\n}\n\nconst pyCoreDir = (tree: Tree) =>\n joinPathFragments(\n getPythonAgentConnectionProjectDir(tree),\n getPythonAgentConnectionModuleName(tree),\n 'core',\n );\n\nconst emitPy = (tree: Tree, templateDir: string) =>\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', templateDir),\n pyCoreDir(tree),\n {},\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n/**\n * Emit a framework's base Layer-2 helpers (model-error logging + per-session\n * agent cache) into the Python agent-connection project and re-export them.\n * The agent server entry point imports these regardless of whether a connection\n * client is wired in, so the agent generator calls this directly; connection\n * generators call it transitively via `addPythonCoreClient`.\n */\nexport async function addPythonFrameworkBase(\n tree: Tree,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const lang = FRAMEWORKS[framework].py;\n if (!lang) {\n throw new Error(\n `The '${framework}' framework does not support Python agents.`,\n );\n }\n if (lang.base) {\n emitPy(tree, lang.base);\n }\n\n const moduleInitPath = joinPathFragments(\n getPythonAgentConnectionProjectDir(tree),\n getPythonAgentConnectionModuleName(tree),\n '__init__.py',\n );\n for (const reExport of lang.baseReExports) {\n await addPythonReExport(\n tree,\n moduleInitPath,\n reExport.fromModule,\n reExport.importName,\n );\n }\n\n if (lang.deps.length > 0) {\n addDependenciesToPyProjectToml(\n tree,\n getPythonAgentConnectionProjectDir(tree),\n lang.deps,\n );\n }\n}\n\n/**\n * Emit the Python client templates for a connection protocol and agent\n * framework into the agent-connection project's core directory.\n *\n * Layers emitted:\n * - Layer 0 `py-core-auth` (SigV4 httpx.Auth, session auth, endpoints) — always\n * - Layer 1 `py-core-shared` MCP transport — for mcp/gateway protocols\n * - Layer 1 the protocol's transport/client-config — always\n * - Layer 2 the framework's base helpers + protocol client — per framework\n */\nexport async function addPythonCoreClient(\n tree: Tree,\n protocol: ConnectionProtocol,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const fw = frameworkLanguage(framework, 'py', protocol);\n\n emitPy(tree, 'py-core-auth');\n if (MCP_TRANSPORT_PROTOCOLS.includes(protocol)) {\n emitPy(tree, 'py-core-shared');\n }\n emitPy(tree, PY_PROTOCOL_TEMPLATES[protocol]);\n\n // Framework Layer 2: base helpers + the thin protocol client.\n await addPythonFrameworkBase(tree, framework);\n emitPy(tree, fw.protocol);\n}\n\n/**\n * Patch a TypeScript agent.ts to create a connection client and register it\n * in the Agent's tools. Used by the MCP and gateway connection generators —\n * the client class must expose a static async `create()` returning a value\n * accepted by `Agent.tools` (e.g. an `McpClient`).\n */\nexport async function addTypeScriptClientToAgent(\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> {\n const clientCreationStmt = `const ${clientVarName} = await ${clientClassName}.create();`;\n\n // Transform the arrow function that contains `new Agent`:\n // - Expression body: wrap in block with client creation and return\n // - Block body: prepend client creation statement\n await applyGritQL(\n tree,\n agentFilePath,\n `or { \\`async ($p) => new Agent($args)\\` => raw\\`async ($p) => {\n ${clientCreationStmt}\n return new Agent($args);\n}\\` where { $program <: not contains \\`${clientClassName}.create\\` }, \\`async ($p) => { $body }\\` => raw\\`async ($p) => {\n ${clientCreationStmt}\n $body\n}\\` where { $body <: contains \\`new Agent($_)\\`, $program <: not contains \\`${clientClassName}.create\\` } }`,\n );\n\n // Prepend the client to the tools array\n await applyGritQL(\n tree,\n agentFilePath,\n `\\`tools: [$items]\\` => \\`tools: [${clientVarName}, $items]\\` where { $items <: within \\`new Agent($_)\\`, $items <: not contains \\`${clientVarName}\\` }`,\n );\n}\n\n/**\n * Patch a Python agent.py to create a context-managed connection client,\n * enter it in the `with` block around `yield Agent(...)`, and spread its\n * `list_tools_sync()` into the Agent's tools. Used by the MCP and gateway\n * connection generators — the client class must expose a static `create()`\n * returning a context manager with `list_tools_sync()` (e.g. an `MCPClient`).\n */\nasync function addPythonClientToAgent(\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> {\n await addPythonClientTools(tree, agentFilePath, clientVarName);\n await addPythonClientToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n );\n}\n\n/**\n * Append `*<clientVarName>.list_tools_sync()` to the Agent's tools list.\n */\nconst addPythonClientTools = async (\n tree: Tree,\n filePath: string,\n clientVarName: string,\n): Promise<void> => {\n if (\n await matchGritQL(\n tree,\n filePath,\n py(`\\`${clientVarName}.list_tools_sync()\\``),\n )\n ) {\n return;\n }\n\n await applyGritQL(\n tree,\n filePath,\n py(`\\`tools=$old\\` where {\n $old <: not contains \\`${clientVarName}\\`,\n if ($old <: \\`[]\\`) {\n $old => \\`[*${clientVarName}.list_tools_sync()]\\`\n } else {\n $old <: \\`[$items]\\` where { $items += \\`, *${clientVarName}.list_tools_sync()\\` }\n }\n}`),\n );\n};\n\n/**\n * Create the client in get_agent and enter it in the `with` block.\n *\n * First connection: rewrites `def get_agent` to wrap the body in a with\n * block. Subsequent connections: adds to the existing with items and\n * prepends the creation line.\n */\nconst addPythonClientToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> => {\n if (\n await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create()\\``))\n ) {\n return;\n }\n\n // Try the \"add to existing with block\" pattern first.\n // If it succeeds, there was already a with block from a previous connection.\n const addedToWith = await applyGritQL(\n tree,\n filePath,\n py(`\\`with ($items,): $body\\` where {\n $items <: not contains \\`${clientVarName}\\`,\n $items += \\`, ${clientVarName}\\`\n}`),\n );\n\n if (addedToWith) {\n // Subsequent connection — prepend the creation line to the function body\n // (a single anchor, so the line is inserted exactly once).\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`yield Agent($_)\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${clientVarName} = ${clientClassName}.create()\n $body\\``),\n );\n return;\n }\n\n // First connection — wrap the existing `def get_agent` body in a single\n // `with (<var>,):` block.\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`yield Agent($_)\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${clientVarName} = ${clientClassName}.create()\n with (\n ${clientVarName},\n ):\n $body\\``),\n );\n};\n\n/**\n * Patch a LangChain agent.py to load connection tools and spread them into the\n * `create_agent(...)` tools list. Used by the MCP and gateway connection\n * generators when the source agent uses the `langchain` framework — the client\n * class must expose a static `create()` returning a `list[BaseTool]`. The tools\n * come from langchain-mcp-adapters' `MultiServerMCPClient`, which keeps the\n * connection config on each tool and opens a fresh MCP session per call, so the\n * tools stay usable after `get_agent` returns — no `with` block is needed.\n * Mirrors {@link addPythonClientToAgent} for the Strands shape.\n */\nasync function addPythonLangchainClientToAgent(\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> {\n await addPythonLangchainClientTools(tree, agentFilePath, clientVarName);\n await addPythonLangchainClientToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n );\n}\n\n/**\n * Spread `*<clientVarName>` into the `create_agent(...)` tools list.\n *\n * Scoped via `$old <: within \\`create_agent($_)\\`` so only the agent's own\n * tools= keyword argument is touched (not an unrelated function with a\n * `tools=` parameter).\n */\nconst addPythonLangchainClientTools = async (\n tree: Tree,\n filePath: string,\n clientVarName: string,\n): Promise<void> => {\n await applyGritQL(\n tree,\n filePath,\n py(`\\`tools=$old\\` where {\n $old <: within \\`create_agent($_)\\`,\n $old <: not contains \\`*${clientVarName}\\`,\n if ($old <: \\`[]\\`) {\n $old => \\`[*${clientVarName}]\\`\n } else {\n $old <: \\`[$items]\\` where { $items += \\`, *${clientVarName}\\` }\n }\n}`),\n );\n};\n\n/**\n * Prepend `<clientVarName> = <clientClassName>.create()` to the get_agent body\n * (which loads the connection's tools). No `with` block — the LangChain client\n * hands langchain-mcp-adapters a connection config, so each tool opens its own\n * MCP session per call and the tools are safe to use after get_agent returns.\n * Idempotent via the `not contains <clientClassName>.create` guard.\n */\nconst addPythonLangchainClientToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n): Promise<void> => {\n if (\n await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create()\\``))\n ) {\n return;\n }\n\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`create_agent($_)\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${clientVarName} = ${clientClassName}.create()\n $body\\``),\n );\n};\n\n/**\n * Per-framework naming + template layout for a Python per-connection client.\n * Every py#agent connection generator (mcp, gateway, a2a) names its client\n * class `<Name>Client<suffix>`, its module `<name>_client_<suffix>`, and reads\n * the per-connection app-client template from a per-framework sub-path of its\n * own `files/` dir — all of which vary only by framework, so they live here as\n * one source of truth.\n *\n * Keyed by {@link AgentFramework} rather than a boolean so a third framework is\n * an additive entry here — never another `if (isLangchain)` branch in each\n * generator.\n */\nexport interface PyClientNaming {\n /** Class-name suffix on the per-connection app client (`<Name>Client<suffix>`). */\n clientClassSuffix: string;\n /** Module/file-name suffix on the per-connection app client (`<name>_client_<suffix>`). */\n clientModuleSuffix: string;\n /**\n * Sub-path, under a connection generator's `files/` dir, of the per-connection\n * app client template for this framework. Every connection generator lays its\n * templates out the same way, so this resolves against any of them.\n */\n appTemplateSubdir: string;\n}\n\nexport const PY_CLIENT_NAMING: Record<AgentFramework, PyClientNaming> = {\n strands: {\n clientClassSuffix: 'Strands',\n clientModuleSuffix: 'strands',\n appTemplateSubdir: joinPathFragments('agent-connection', 'app'),\n },\n langchain: {\n clientClassSuffix: 'LangChain',\n clientModuleSuffix: 'langchain',\n appTemplateSubdir: joinPathFragments(\n 'langchain',\n 'agent-connection',\n 'app',\n ),\n },\n};\n\n/**\n * How an MCP-family connection (an MCP server or an AgentCore Gateway, both of\n * which expose tools over MCP) is wired into a Python agent of a given\n * framework. The mcp-connection and gateway-connection generators share this:\n * they differ only in the metadata of the thing being connected, not in how\n * the framework's client is shaped or spliced into agent.py. A2A connections\n * wire tools differently, so they reuse only {@link PY_CLIENT_NAMING}.\n */\nexport interface PyMcpFamilyConnection extends PyClientNaming {\n /** Extra Python deps this framework's MCP-family client needs (beyond the shared transport). */\n deps: IPyDepVersion[];\n /**\n * Splice the per-connection client into the agent's `get_agent` in agent.py.\n * Strands enters a context-managed `MCPClient` and spreads `list_tools_sync()`;\n * LangChain loads `BaseTool`s and spreads them into `create_agent(...)`.\n */\n wireClientIntoAgent: (\n tree: Tree,\n agentFilePath: string,\n clientClassName: string,\n clientVarName: string,\n ) => Promise<void>;\n}\n\nexport const PY_MCP_FAMILY_CONNECTIONS: Record<\n AgentFramework,\n PyMcpFamilyConnection\n> = {\n strands: {\n ...PY_CLIENT_NAMING.strands,\n deps: [],\n wireClientIntoAgent: addPythonClientToAgent,\n },\n langchain: {\n ...PY_CLIENT_NAMING.langchain,\n // langchain-mcp-adapters backs the LangChain client; it must not pull Strands in.\n deps: ['langchain-mcp-adapters'],\n wireClientIntoAgent: addPythonLangchainClientToAgent,\n },\n};\n\n/**\n * Add a re-export to the Python agent-connection module's __init__.py.\n * Equivalent to addStarExport for TS.\n * Uses `__all__` to mark re-exports as public (satisfies ruff F401).\n * Uses GritQL for AST-aware transforms where possible.\n */\nexport async function addPythonReExport(\n tree: Tree,\n initFilePath: string,\n fromModule: string,\n importName: string,\n): Promise<void> {\n // Check if already exported\n if (await matchGritQL(tree, initFilePath, `\\`${importName}\\``)) {\n return;\n }\n\n const importLine = `from ${fromModule} import ${importName}`;\n const allEntry = `\"${importName}\"`;\n\n // If there's already an import from the same module, merge into its name list\n // (ruff I001 rejects two `from X import ...` lines for the same module).\n const mergedImport = await applyGritQL(\n tree,\n initFilePath,\n py(`\\`from ${fromModule} import $names\\` where {\n $names <: not contains \\`${importName}\\`,\n $names += \\`, ${importName}\\`\n}`),\n );\n\n if (mergedImport) {\n await applyGritQL(\n tree,\n initFilePath,\n py(`\\`__all__ = [$items]\\` where { $items += \\`, ${allEntry}\\` }`),\n );\n return;\n }\n\n // Try to append import after existing import using +=\n const appendedImport = await applyGritQL(\n tree,\n initFilePath,\n py(\n `\\`from $mod import $name\\` as $stmt where {\n $program <: not contains \\`${importName}\\`,\n $stmt += \\`\\n${importLine}\\`\n}`,\n ),\n );\n\n if (appendedImport) {\n // Also append to existing __all__\n await applyGritQL(\n tree,\n initFilePath,\n py(`\\`__all__ = [$items]\\` where { $items += \\`, ${allEntry}\\` }`),\n );\n return;\n }\n\n // No existing imports — first re-export. Try to anchor after docstring.\n const anchoredImport = await applyGritQL(\n tree,\n initFilePath,\n py(\n `\\`\"\"\"Automatically generated by Nx.\"\"\"\\` as $doc => \\`$doc\\n${importLine}\\n\\n__all__ = [${allEntry}]\\``,\n ),\n );\n\n if (!anchoredImport) {\n // Completely empty file or no docstring — write directly\n tree.write(initFilePath, `${importLine}\\n\\n__all__ = [${allEntry}]\\n`);\n }\n}\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","pyProjectGenerator","getPyProjectDetails","tsProjectGenerator","addStarExport","applyGritQL","matchGritQL","addDependenciesToPackageJson","esmVars","addDependenciesToPyProjectToml","withVersions","py","pattern","AGENT_CONNECTION_DIR","PACKAGES_DIR","COMMON_DIR","AGENT_CONNECTION_PROJECT_DIR","PY_AGENT_CONNECTION_NAME","getPythonAgentConnectionProjectDir","tree","dir","name","directory","getPythonAgentConnectionModuleName","normalizedModuleName","getPythonAgentConnectionProject","fullyQualifiedName","root","resolveAgentFramework","framework","TS_PROTOCOL_TEMPLATES","mcp","gateway","a2a","PY_PROTOCOL_TEMPLATES","FRAMEWORKS","strands","ts","base","baseReExports","fromModule","importName","protocols","deps","langchain","frameworkLanguage","language","protocol","lang","Error","protocolDir","MCP_TRANSPORT_PROTOCOLS","tsCoreDir","TS_TEMPLATE_DEPS","emitTs","templateDir","dirname","overwriteStrategy","KeepExisting","length","ensureTypeScriptAgentConnectionProject","projectJsonPath","exists","addTypeScriptFrameworkBase","indexPath","reExport","addTypeScriptCoreClient","fw","includes","ensurePythonAgentConnectionProject","projectDir","moduleName","type","moduleDir","coreDir","write","appInitPath","moduleInitPath","addPythonReExport","pyCoreDir","emitPy","addPythonFrameworkBase","addPythonCoreClient","addTypeScriptClientToAgent","agentFilePath","clientClassName","clientVarName","clientCreationStmt","addPythonClientToAgent","addPythonClientTools","addPythonClientToGetAgent","filePath","addedToWith","addPythonLangchainClientToAgent","addPythonLangchainClientTools","addPythonLangchainClientToGetAgent","PY_CLIENT_NAMING","clientClassSuffix","clientModuleSuffix","appTemplateSubdir","PY_MCP_FAMILY_CONNECTIONS","wireClientIntoAgent","initFilePath","importLine","allEntry","mergedImport","appendedImport","anchoredImport"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import {
2
+ AfterToolCallEvent,
3
+ type LocalAgent,
4
+ TextBlock,
5
+ } from '@strands-agents/sdk';
6
+
7
+ /** Logs tool execution errors, including the tool name and underlying error or message when available. */
8
+ export const logToolErrors = (agent: LocalAgent): void => {
9
+ agent.addHook(AfterToolCallEvent, (event) => {
10
+ if (event.result.status !== 'error') return;
11
+ const error = event.result.error ?? event.error;
12
+ const message =
13
+ error?.message ??
14
+ event.result.content
15
+ .filter((block): block is TextBlock => block instanceof TextBlock)
16
+ .map((block) => block.text)
17
+ .join(' ');
18
+ console.error(
19
+ `Tool '${event.toolUse.name}' failed${message ? `: ${message}` : ''}`,
20
+ );
21
+ });
22
+ };
@@ -11,6 +11,10 @@ terraform {
11
11
  source = "hashicorp/external"
12
12
  version = "<%- externalProviderVersion %>"
13
13
  }
14
+ time = {
15
+ source = "hashicorp/time"
16
+ version = "<%- timeProviderVersion %>"
17
+ }
14
18
  <%_ } _%>
15
19
  random = {
16
20
  source = "hashicorp/random"
@@ -135,6 +139,20 @@ resource "aws_iam_role_policy" "gateway_role_policy" {
135
139
  ], var.additional_iam_policy_statements)
136
140
  })
137
141
  }
142
+
143
+ # IAM is eventually consistent, and CreateGateway assumes the gateway role to
144
+ # call AuthorizeAction as part of its policy engine check. Without this wait
145
+ # the check intermittently runs before the policy above has propagated and
146
+ # CreateGateway fails with AccessDeniedException.
147
+ resource "time_sleep" "gateway_role_policy_propagation" {
148
+ depends_on = [aws_iam_role_policy.gateway_role_policy]
149
+
150
+ create_duration = "30s"
151
+
152
+ triggers = {
153
+ policy = aws_iam_role_policy.gateway_role_policy.policy
154
+ }
155
+ }
138
156
  <%_ } else { _%>
139
157
  # Inline policy for statements appended by connection generators
140
158
  # (e.g. InvokeAgentRuntime*)
@@ -190,7 +208,7 @@ resource "aws_bedrockagentcore_gateway" "this" {
190
208
  mode = "ENFORCE"
191
209
  }
192
210
 
193
- depends_on = [aws_iam_role_policy.gateway_role_policy]
211
+ depends_on = [time_sleep.gateway_role_policy_propagation]
194
212
  <%_ } _%>
195
213
  }
196
214
 
@@ -31,11 +31,12 @@ export declare const buildPackageManagerExecCommand: (pm: string, command: strin
31
31
  */
32
32
  export declare const buildPackageManagerShortCommand: (pm: string, command: string) => string;
33
33
  /**
34
- * Build an install command. With `project`, targets that project's manifest
35
- * (pnpm `--filter` by unscoped name, npm/bun by `projectDir` defaulting to
34
+ * Build an install command. Without `pkg`, installs the workspace's existing
35
+ * dependencies. With `project`, targets that project's manifest (pnpm
36
+ * `--filter` by unscoped name, npm/bun by `projectDir` defaulting to
36
37
  * `packages/<name>`, yarn by fully-qualified name); otherwise the root.
37
38
  */
38
- export declare const buildInstallCommand: (pm: string, pkg: string, dev: boolean, project?: string, projectDir?: string) => string;
39
+ export declare const buildInstallCommand: (pm: string, pkg?: string, dev?: boolean, project?: string, projectDir?: string) => string;
39
40
  /**
40
41
  * Build the command that adds Nx to a non-Nx project, pinned to the nx
41
42
  * version the plugin is built against.
@@ -42,10 +42,14 @@ export const PACKAGE_MANAGER_COMMANDS = {
42
42
  return `${prefix} ${command}`;
43
43
  };
44
44
  /**
45
- * Build an install command. With `project`, targets that project's manifest
46
- * (pnpm `--filter` by unscoped name, npm/bun by `projectDir` defaulting to
45
+ * Build an install command. Without `pkg`, installs the workspace's existing
46
+ * dependencies. With `project`, targets that project's manifest (pnpm
47
+ * `--filter` by unscoped name, npm/bun by `projectDir` defaulting to
47
48
  * `packages/<name>`, yarn by fully-qualified name); otherwise the root.
48
49
  */ export const buildInstallCommand = (pm, pkg, dev, project, projectDir)=>{
50
+ if (!pkg) {
51
+ return pm === 'yarn' ? 'yarn' : `${pm} install`;
52
+ }
49
53
  if (project) {
50
54
  const shortName = project.split('/').pop();
51
55
  const dir = projectDir ?? `packages/${shortName}`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/commands.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { TS_VERSIONS } from './versions';\n\n/**\n * The nx version the plugin is built against. Commands that download nx\n * (e.g. `nx init`) pin to this version so the workspace's nx matches the\n * plugin's `@nx/*` packages — a mismatch deadlocks `nx sync`.\n */\nexport const NX_VERSION = TS_VERSIONS['@nx/devkit'];\n\n/**\n * Display-friendly command prefixes for each package manager.\n *\n * - `exec`: prefix for running local binaries (e.g. `npx nx`, `pnpm nx`, `bunx nx`)\n * - `run`: prefix for running package.json scripts (e.g. `npm run build`, `pnpm build`)\n *\n * @see docs/src/components/package-manager-exec-command.astro\n * @see docs/src/components/package-manager-short-command.astro\n */\nexport interface PackageManagerDisplayCommands {\n /** Prefix for running local binaries: npx, pnpm, yarn, bunx */\n exec: string;\n /** Prefix for running package.json scripts: `npm run`, pnpm, yarn, bun */\n run: string;\n /** Prefix for `create` commands: `npm create`, `pnpm create`, etc. */\n create: string;\n}\n\nexport const PACKAGE_MANAGER_COMMANDS: Record<\n string,\n PackageManagerDisplayCommands\n> = {\n npm: { exec: 'npx', run: 'npm run', create: 'npm create' },\n pnpm: { exec: 'pnpm', run: 'pnpm', create: 'pnpm create' },\n yarn: { exec: 'yarn', run: 'yarn', create: 'yarn create' },\n bun: { exec: 'bunx', run: 'bun', create: 'bun create' },\n};\n\n/**\n * Build a package manager exec command (e.g. pnpm cmd, npx cmd, bunx cmd)\n */\nexport const buildPackageManagerExecCommand = (pm: string, command: string) => {\n const prefix = PACKAGE_MANAGER_COMMANDS[pm]?.exec ?? pm;\n return `${prefix} ${command}`;\n};\n\n/**\n * Build a short package manager command (e.g. pnpm build, npm run build)\n */\nexport const buildPackageManagerShortCommand = (\n pm: string,\n command: string,\n) => {\n const prefix = PACKAGE_MANAGER_COMMANDS[pm]?.run ?? pm;\n return `${prefix} ${command}`;\n};\n\n/**\n * Build an install command. With `project`, targets that project's manifest\n * (pnpm `--filter` by unscoped name, npm/bun by `projectDir` defaulting to\n * `packages/<name>`, yarn by fully-qualified name); otherwise the root.\n */\nexport const buildInstallCommand = (\n pm: string,\n pkg: string,\n dev: boolean,\n project?: string,\n projectDir?: string,\n) => {\n if (project) {\n const shortName = project.split('/').pop();\n const dir = projectDir ?? `packages/${shortName}`;\n switch (pm) {\n case 'pnpm':\n return `pnpm add ${dev ? '-D ' : ''}${pkg} --filter ${shortName}`;\n case 'yarn':\n return `yarn workspace ${project} add ${dev ? '-D ' : ''}${pkg}`;\n case 'npm':\n return `npm install --legacy-peer-deps ${dev ? '-D ' : ''}${pkg} -w ${dir}`;\n case 'bun':\n return `bun add ${dev ? '-D ' : ''}${pkg} --cwd ${dir}`;\n default:\n return `${pm} install ${dev ? '-D ' : ''}${pkg}`;\n }\n }\n switch (pm) {\n case 'pnpm':\n return `pnpm add ${dev ? '-D' : '-'}w ${pkg}`;\n case 'yarn':\n return `yarn add ${dev ? '-D ' : ''}${pkg}`;\n case 'npm':\n return `npm install --legacy-peer-deps ${dev ? '-D ' : ''}${pkg}`;\n case 'bun':\n return `bun add ${dev ? '-D ' : ''}${pkg}`;\n default:\n return `${pm} install ${dev ? '-D ' : ''}${pkg}`;\n }\n};\n\n/**\n * Build the command that adds Nx to a non-Nx project, pinned to the nx\n * version the plugin is built against.\n */\nexport const buildNxInitCommand = (pm: string) => {\n const dlxPrefix =\n { npm: 'npx', pnpm: 'pnpm dlx', yarn: 'yarn dlx', bun: 'bunx' }[pm] ??\n 'npx';\n return `${dlxPrefix} nx@${NX_VERSION} init`;\n};\n\n/**\n * Build a command to create a new workspace using @aws/create-nx-workspace.\n *\n * Uses the `create` shorthand for each package manager:\n * npm create @aws/nx-workspace my-project\n * pnpm create @aws/nx-workspace my-project\n * yarn create @aws/nx-workspace my-project\n * bun create @aws/nx-workspace my-project\n *\n * The package manager is auto-detected by @aws/create-nx-workspace from the\n * invoking command, so --pm is not needed.\n */\nexport const buildCreateNxWorkspaceCommand = (\n pm: string,\n workspace: string,\n iac?: 'cdk' | 'terraform',\n tag?: string,\n module?: 'esm' | 'cjs',\n extraArgs?: string,\n) => {\n const createPrefix = PACKAGE_MANAGER_COMMANDS[pm]?.create ?? `${pm} create`;\n const pkgName = tag ? `@aws/nx-workspace@${tag}` : '@aws/nx-workspace';\n const parts = [\n createPrefix,\n pkgName,\n // npm requires '--' to stop interpreting subsequent flags as npm config\n ...(pm === 'npm' ? ['--'] : []),\n workspace,\n ...(iac ? [`--iac=${iac}`] : []),\n ...(module ? [`--module=${module}`] : []),\n ...(extraArgs ? [extraArgs] : []),\n ];\n return parts.join(' ');\n};\n"],"names":["TS_VERSIONS","NX_VERSION","PACKAGE_MANAGER_COMMANDS","npm","exec","run","create","pnpm","yarn","bun","buildPackageManagerExecCommand","pm","command","prefix","buildPackageManagerShortCommand","buildInstallCommand","pkg","dev","project","projectDir","shortName","split","pop","dir","buildNxInitCommand","dlxPrefix","buildCreateNxWorkspaceCommand","workspace","iac","tag","module","extraArgs","createPrefix","pkgName","parts","join"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/commands.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { TS_VERSIONS } from './versions';\n\n/**\n * The nx version the plugin is built against. Commands that download nx\n * (e.g. `nx init`) pin to this version so the workspace's nx matches the\n * plugin's `@nx/*` packages — a mismatch deadlocks `nx sync`.\n */\nexport const NX_VERSION = TS_VERSIONS['@nx/devkit'];\n\n/**\n * Display-friendly command prefixes for each package manager.\n *\n * - `exec`: prefix for running local binaries (e.g. `npx nx`, `pnpm nx`, `bunx nx`)\n * - `run`: prefix for running package.json scripts (e.g. `npm run build`, `pnpm build`)\n *\n * @see docs/src/components/package-manager-exec-command.astro\n * @see docs/src/components/package-manager-short-command.astro\n */\nexport interface PackageManagerDisplayCommands {\n /** Prefix for running local binaries: npx, pnpm, yarn, bunx */\n exec: string;\n /** Prefix for running package.json scripts: `npm run`, pnpm, yarn, bun */\n run: string;\n /** Prefix for `create` commands: `npm create`, `pnpm create`, etc. */\n create: string;\n}\n\nexport const PACKAGE_MANAGER_COMMANDS: Record<\n string,\n PackageManagerDisplayCommands\n> = {\n npm: { exec: 'npx', run: 'npm run', create: 'npm create' },\n pnpm: { exec: 'pnpm', run: 'pnpm', create: 'pnpm create' },\n yarn: { exec: 'yarn', run: 'yarn', create: 'yarn create' },\n bun: { exec: 'bunx', run: 'bun', create: 'bun create' },\n};\n\n/**\n * Build a package manager exec command (e.g. pnpm cmd, npx cmd, bunx cmd)\n */\nexport const buildPackageManagerExecCommand = (pm: string, command: string) => {\n const prefix = PACKAGE_MANAGER_COMMANDS[pm]?.exec ?? pm;\n return `${prefix} ${command}`;\n};\n\n/**\n * Build a short package manager command (e.g. pnpm build, npm run build)\n */\nexport const buildPackageManagerShortCommand = (\n pm: string,\n command: string,\n) => {\n const prefix = PACKAGE_MANAGER_COMMANDS[pm]?.run ?? pm;\n return `${prefix} ${command}`;\n};\n\n/**\n * Build an install command. Without `pkg`, installs the workspace's existing\n * dependencies. With `project`, targets that project's manifest (pnpm\n * `--filter` by unscoped name, npm/bun by `projectDir` defaulting to\n * `packages/<name>`, yarn by fully-qualified name); otherwise the root.\n */\nexport const buildInstallCommand = (\n pm: string,\n pkg?: string,\n dev?: boolean,\n project?: string,\n projectDir?: string,\n) => {\n if (!pkg) {\n return pm === 'yarn' ? 'yarn' : `${pm} install`;\n }\n if (project) {\n const shortName = project.split('/').pop();\n const dir = projectDir ?? `packages/${shortName}`;\n switch (pm) {\n case 'pnpm':\n return `pnpm add ${dev ? '-D ' : ''}${pkg} --filter ${shortName}`;\n case 'yarn':\n return `yarn workspace ${project} add ${dev ? '-D ' : ''}${pkg}`;\n case 'npm':\n return `npm install --legacy-peer-deps ${dev ? '-D ' : ''}${pkg} -w ${dir}`;\n case 'bun':\n return `bun add ${dev ? '-D ' : ''}${pkg} --cwd ${dir}`;\n default:\n return `${pm} install ${dev ? '-D ' : ''}${pkg}`;\n }\n }\n switch (pm) {\n case 'pnpm':\n return `pnpm add ${dev ? '-D' : '-'}w ${pkg}`;\n case 'yarn':\n return `yarn add ${dev ? '-D ' : ''}${pkg}`;\n case 'npm':\n return `npm install --legacy-peer-deps ${dev ? '-D ' : ''}${pkg}`;\n case 'bun':\n return `bun add ${dev ? '-D ' : ''}${pkg}`;\n default:\n return `${pm} install ${dev ? '-D ' : ''}${pkg}`;\n }\n};\n\n/**\n * Build the command that adds Nx to a non-Nx project, pinned to the nx\n * version the plugin is built against.\n */\nexport const buildNxInitCommand = (pm: string) => {\n const dlxPrefix =\n { npm: 'npx', pnpm: 'pnpm dlx', yarn: 'yarn dlx', bun: 'bunx' }[pm] ??\n 'npx';\n return `${dlxPrefix} nx@${NX_VERSION} init`;\n};\n\n/**\n * Build a command to create a new workspace using @aws/create-nx-workspace.\n *\n * Uses the `create` shorthand for each package manager:\n * npm create @aws/nx-workspace my-project\n * pnpm create @aws/nx-workspace my-project\n * yarn create @aws/nx-workspace my-project\n * bun create @aws/nx-workspace my-project\n *\n * The package manager is auto-detected by @aws/create-nx-workspace from the\n * invoking command, so --pm is not needed.\n */\nexport const buildCreateNxWorkspaceCommand = (\n pm: string,\n workspace: string,\n iac?: 'cdk' | 'terraform',\n tag?: string,\n module?: 'esm' | 'cjs',\n extraArgs?: string,\n) => {\n const createPrefix = PACKAGE_MANAGER_COMMANDS[pm]?.create ?? `${pm} create`;\n const pkgName = tag ? `@aws/nx-workspace@${tag}` : '@aws/nx-workspace';\n const parts = [\n createPrefix,\n pkgName,\n // npm requires '--' to stop interpreting subsequent flags as npm config\n ...(pm === 'npm' ? ['--'] : []),\n workspace,\n ...(iac ? [`--iac=${iac}`] : []),\n ...(module ? [`--module=${module}`] : []),\n ...(extraArgs ? [extraArgs] : []),\n ];\n return parts.join(' ');\n};\n"],"names":["TS_VERSIONS","NX_VERSION","PACKAGE_MANAGER_COMMANDS","npm","exec","run","create","pnpm","yarn","bun","buildPackageManagerExecCommand","pm","command","prefix","buildPackageManagerShortCommand","buildInstallCommand","pkg","dev","project","projectDir","shortName","split","pop","dir","buildNxInitCommand","dlxPrefix","buildCreateNxWorkspaceCommand","workspace","iac","tag","module","extraArgs","createPrefix","pkgName","parts","join"],"mappings":"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"}
@@ -3,6 +3,14 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { type ProjectConfiguration, type Tree } from '@nx/devkit';
6
+ /**
7
+ * Substitution variables for the vended Node `Dockerfile` templates: the npm
8
+ * version to install globally, and the versions its dependency overrides pin.
9
+ */
10
+ export declare const nodeImageVersions: () => {
11
+ npmVersion: "12.0.1";
12
+ minimatchVersion: "10.2.5";
13
+ };
6
14
  export interface DockerScanTargetOptions {
7
15
  /**
8
16
  * Project configuration to add the scan target to (mutated in place).
@@ -4,12 +4,19 @@
4
4
  */ import { joinPathFragments } from "@nx/devkit";
5
5
  import { FsCommands } from "./fs.js";
6
6
  import { addDependencyToTargetIfNotPresent } from "./nx.js";
7
- import { CONTAINER_VERSIONS } from "./versions.js";
7
+ import { CONTAINER_VERSIONS, TS_VERSIONS } from "./versions.js";
8
8
  const TRIVY_IGNORE_FILE = '.trivyignore';
9
9
  const TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to
10
10
  # suppress it during the image scan (\`nx run-many --target trivy\`).
11
11
  # https://trivy.dev/latest/docs/configuration/filtering/#by-finding-ids
12
12
  `;
13
+ /**
14
+ * Substitution variables for the vended Node `Dockerfile` templates: the npm
15
+ * version to install globally, and the versions its dependency overrides pin.
16
+ */ export const nodeImageVersions = ()=>({
17
+ npmVersion: TS_VERSIONS.npm,
18
+ minimatchVersion: TS_VERSIONS.minimatch
19
+ });
13
20
  /**
14
21
  * Add a cacheable Trivy scan target for the images built by a docker target
15
22
  * and wire it under the aggregate \`trivy\` target. The scan is not part of
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/docker.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n} from '@nx/devkit';\nimport { FsCommands } from './fs';\nimport { addDependencyToTargetIfNotPresent } from './nx';\nimport { CONTAINER_VERSIONS } from './versions';\n\nconst TRIVY_IGNORE_FILE = '.trivyignore';\n\nconst TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to\n# suppress it during the image scan (\\`nx run-many --target trivy\\`).\n# https://trivy.dev/latest/docs/configuration/filtering/#by-finding-ids\n`;\n\nexport interface DockerScanTargetOptions {\n /**\n * Project configuration to add the scan target to (mutated in place).\n */\n readonly project: ProjectConfiguration;\n /**\n * Container engine command to invoke (\\`docker\\` or \\`finch\\`).\n */\n readonly containerEngine: string;\n /**\n * Name of the scan target to add, e.g. \\`my-agent-trivy\\`.\n */\n readonly trivyTargetName: string;\n /**\n * Name of the docker target that builds the image(s) to scan. The scan\n * target depends on it.\n */\n readonly dockerTargetName: string;\n /**\n * Tags of the images built by the docker target which should be scanned.\n */\n readonly imageTags: string[];\n}\n\n/**\n * Add a cacheable Trivy scan target for the images built by a docker target\n * and wire it under the aggregate \\`trivy\\` target. The scan is not part of\n * \\`build\\` — run \\`nx run-many --target trivy\\` (the root \\`trivy\\` script) in CI.\n *\n * The scan target depends on the docker target and declares the project source\n * as its inputs, so an unchanged image is never re-scanned (a cache hit skips\n * the scan entirely). Each image is saved to a tarball under\n * \\`dist/<projectRoot>/trivy/<scan-key>\\` (kept out of any Docker build context)\n * and scanned with the pinned ECR-hosted Trivy image via a workspace-relative\n * bind mount, so the same commands work under both docker and finch. The scan\n * exits non-zero (exit code 1) on HIGH/CRITICAL vulnerabilities.\n *\n * A \\`.trivyignore\\` is vended at the project root (kept if it already exists)\n * for suppressing findings.\n */\nexport const addDockerScanTarget = (\n tree: Tree,\n options: DockerScanTargetOptions,\n): void => {\n const { project, containerEngine, trivyTargetName, dockerTargetName } =\n options;\n const { imageTags } = options;\n const projectRoot = project.root;\n\n const ignoreFilePath = joinPathFragments(projectRoot, TRIVY_IGNORE_FILE);\n if (!tree.exists(ignoreFilePath)) {\n tree.write(ignoreFilePath, TRIVY_IGNORE_CONTENTS);\n }\n\n // Stage scan artifacts in a directory unique to this scan target so that\n // sibling targets in the same project (e.g. multiple agents) don't clobber\n // each other's tarballs when run in parallel. The first image tag is unique\n // per target, so it makes a stable, collision-free key.\n const scanKey = imageTags[0].replace(/[^a-zA-Z0-9-]/g, '-');\n const scanDir = joinPathFragments('dist', projectRoot, 'trivy', scanKey);\n const trivyImage = `public.ecr.aws/aquasecurity/trivy:${CONTAINER_VERSIONS.trivy}`;\n\n const fs = new FsCommands(tree);\n const commands = [\n fs.rm(scanDir),\n fs.mkdir(scanDir),\n fs.cp(ignoreFilePath, joinPathFragments(scanDir, TRIVY_IGNORE_FILE)),\n ];\n\n imageTags.forEach((imageTag, index) => {\n const tarName = `image-${index}.tar`;\n commands.push(\n `${containerEngine} save -o ${joinPathFragments(scanDir, tarName)} ${imageTag}`,\n `${containerEngine} run --rm -v \"./${scanDir}\":/scan ${trivyImage} image --input /scan/${tarName} --ignorefile /scan/${TRIVY_IGNORE_FILE} --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 --no-progress -q`,\n );\n });\n\n project.targets ??= {};\n project.targets[trivyTargetName] = {\n cache: true,\n // The scanned image is fully determined by the project source, so caching\n // on the source ensures an unchanged image is never re-scanned.\n inputs: ['default', '^production'],\n outputs: [`{workspaceRoot}/${scanDir}`],\n executor: 'nx:run-commands',\n options: {\n commands,\n parallel: false,\n },\n dependsOn: [dockerTargetName],\n };\n\n // Aggregate per-component scan targets under a single `trivy` target. The\n // scan is intentionally NOT wired into `build`: image scanning is slow and\n // its result depends on the ever-changing vulnerability database (a scan\n // that passes today can fail tomorrow when a new CVE is published), so\n // coupling it to `build` would make local and CI builds non-deterministic.\n // Run `nx run-many --target trivy` (the root `trivy` script) in CI instead.\n if (trivyTargetName !== 'trivy') {\n addDependencyToTargetIfNotPresent(project, 'trivy', trivyTargetName);\n }\n};\n"],"names":["joinPathFragments","FsCommands","addDependencyToTargetIfNotPresent","CONTAINER_VERSIONS","TRIVY_IGNORE_FILE","TRIVY_IGNORE_CONTENTS","addDockerScanTarget","tree","options","project","containerEngine","trivyTargetName","dockerTargetName","imageTags","projectRoot","root","ignoreFilePath","exists","write","scanKey","replace","scanDir","trivyImage","trivy","fs","commands","rm","mkdir","cp","forEach","imageTag","index","tarName","push","targets","cache","inputs","outputs","executor","parallel","dependsOn"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SAASC,UAAU,QAAQ,UAAO;AAClC,SAASC,iCAAiC,QAAQ,UAAO;AACzD,SAASC,kBAAkB,QAAQ,gBAAa;AAEhD,MAAMC,oBAAoB;AAE1B,MAAMC,wBAAwB,CAAC;;;AAG/B,CAAC;AA0BD;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMC,sBAAsB,CACjCC,MACAC;IAEA,MAAM,EAAEC,OAAO,EAAEC,eAAe,EAAEC,eAAe,EAAEC,gBAAgB,EAAE,GACnEJ;IACF,MAAM,EAAEK,SAAS,EAAE,GAAGL;IACtB,MAAMM,cAAcL,QAAQM,IAAI;IAEhC,MAAMC,iBAAiBhB,kBAAkBc,aAAaV;IACtD,IAAI,CAACG,KAAKU,MAAM,CAACD,iBAAiB;QAChCT,KAAKW,KAAK,CAACF,gBAAgBX;IAC7B;IAEA,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,wDAAwD;IACxD,MAAMc,UAAUN,SAAS,CAAC,EAAE,CAACO,OAAO,CAAC,kBAAkB;IACvD,MAAMC,UAAUrB,kBAAkB,QAAQc,aAAa,SAASK;IAChE,MAAMG,aAAa,CAAC,kCAAkC,EAAEnB,mBAAmBoB,KAAK,EAAE;IAElF,MAAMC,KAAK,IAAIvB,WAAWM;IAC1B,MAAMkB,WAAW;QACfD,GAAGE,EAAE,CAACL;QACNG,GAAGG,KAAK,CAACN;QACTG,GAAGI,EAAE,CAACZ,gBAAgBhB,kBAAkBqB,SAASjB;KAClD;IAEDS,UAAUgB,OAAO,CAAC,CAACC,UAAUC;QAC3B,MAAMC,UAAU,CAAC,MAAM,EAAED,MAAM,IAAI,CAAC;QACpCN,SAASQ,IAAI,CACX,GAAGvB,gBAAgB,SAAS,EAAEV,kBAAkBqB,SAASW,SAAS,CAAC,EAAEF,UAAU,EAC/E,GAAGpB,gBAAgB,gBAAgB,EAAEW,QAAQ,QAAQ,EAAEC,WAAW,qBAAqB,EAAEU,QAAQ,oBAAoB,EAAE5B,kBAAkB,yFAAyF,CAAC;IAEvO;IAEAK,QAAQyB,OAAO,KAAK,CAAC;IACrBzB,QAAQyB,OAAO,CAACvB,gBAAgB,GAAG;QACjCwB,OAAO;QACP,0EAA0E;QAC1E,gEAAgE;QAChEC,QAAQ;YAAC;YAAW;SAAc;QAClCC,SAAS;YAAC,CAAC,gBAAgB,EAAEhB,SAAS;SAAC;QACvCiB,UAAU;QACV9B,SAAS;YACPiB;YACAc,UAAU;QACZ;QACAC,WAAW;YAAC5B;SAAiB;IAC/B;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAID,oBAAoB,SAAS;QAC/BT,kCAAkCO,SAAS,SAASE;IACtD;AACF,EAAE"}
1
+ {"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/docker.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type ProjectConfiguration,\n type Tree,\n} from '@nx/devkit';\nimport { FsCommands } from './fs';\nimport { addDependencyToTargetIfNotPresent } from './nx';\nimport { CONTAINER_VERSIONS, TS_VERSIONS } from './versions';\n\nconst TRIVY_IGNORE_FILE = '.trivyignore';\n\nconst TRIVY_IGNORE_CONTENTS = `# Trivy ignore file. Add one vulnerability ID (e.g. CVE-2021-12345) per line to\n# suppress it during the image scan (\\`nx run-many --target trivy\\`).\n# https://trivy.dev/latest/docs/configuration/filtering/#by-finding-ids\n`;\n\n/**\n * Substitution variables for the vended Node `Dockerfile` templates: the npm\n * version to install globally, and the versions its dependency overrides pin.\n */\nexport const nodeImageVersions = () => ({\n npmVersion: TS_VERSIONS.npm,\n minimatchVersion: TS_VERSIONS.minimatch,\n});\n\nexport interface DockerScanTargetOptions {\n /**\n * Project configuration to add the scan target to (mutated in place).\n */\n readonly project: ProjectConfiguration;\n /**\n * Container engine command to invoke (\\`docker\\` or \\`finch\\`).\n */\n readonly containerEngine: string;\n /**\n * Name of the scan target to add, e.g. \\`my-agent-trivy\\`.\n */\n readonly trivyTargetName: string;\n /**\n * Name of the docker target that builds the image(s) to scan. The scan\n * target depends on it.\n */\n readonly dockerTargetName: string;\n /**\n * Tags of the images built by the docker target which should be scanned.\n */\n readonly imageTags: string[];\n}\n\n/**\n * Add a cacheable Trivy scan target for the images built by a docker target\n * and wire it under the aggregate \\`trivy\\` target. The scan is not part of\n * \\`build\\` — run \\`nx run-many --target trivy\\` (the root \\`trivy\\` script) in CI.\n *\n * The scan target depends on the docker target and declares the project source\n * as its inputs, so an unchanged image is never re-scanned (a cache hit skips\n * the scan entirely). Each image is saved to a tarball under\n * \\`dist/<projectRoot>/trivy/<scan-key>\\` (kept out of any Docker build context)\n * and scanned with the pinned ECR-hosted Trivy image via a workspace-relative\n * bind mount, so the same commands work under both docker and finch. The scan\n * exits non-zero (exit code 1) on HIGH/CRITICAL vulnerabilities.\n *\n * A \\`.trivyignore\\` is vended at the project root (kept if it already exists)\n * for suppressing findings.\n */\nexport const addDockerScanTarget = (\n tree: Tree,\n options: DockerScanTargetOptions,\n): void => {\n const { project, containerEngine, trivyTargetName, dockerTargetName } =\n options;\n const { imageTags } = options;\n const projectRoot = project.root;\n\n const ignoreFilePath = joinPathFragments(projectRoot, TRIVY_IGNORE_FILE);\n if (!tree.exists(ignoreFilePath)) {\n tree.write(ignoreFilePath, TRIVY_IGNORE_CONTENTS);\n }\n\n // Stage scan artifacts in a directory unique to this scan target so that\n // sibling targets in the same project (e.g. multiple agents) don't clobber\n // each other's tarballs when run in parallel. The first image tag is unique\n // per target, so it makes a stable, collision-free key.\n const scanKey = imageTags[0].replace(/[^a-zA-Z0-9-]/g, '-');\n const scanDir = joinPathFragments('dist', projectRoot, 'trivy', scanKey);\n const trivyImage = `public.ecr.aws/aquasecurity/trivy:${CONTAINER_VERSIONS.trivy}`;\n\n const fs = new FsCommands(tree);\n const commands = [\n fs.rm(scanDir),\n fs.mkdir(scanDir),\n fs.cp(ignoreFilePath, joinPathFragments(scanDir, TRIVY_IGNORE_FILE)),\n ];\n\n imageTags.forEach((imageTag, index) => {\n const tarName = `image-${index}.tar`;\n commands.push(\n `${containerEngine} save -o ${joinPathFragments(scanDir, tarName)} ${imageTag}`,\n `${containerEngine} run --rm -v \"./${scanDir}\":/scan ${trivyImage} image --input /scan/${tarName} --ignorefile /scan/${TRIVY_IGNORE_FILE} --scanners vuln --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 --no-progress -q`,\n );\n });\n\n project.targets ??= {};\n project.targets[trivyTargetName] = {\n cache: true,\n // The scanned image is fully determined by the project source, so caching\n // on the source ensures an unchanged image is never re-scanned.\n inputs: ['default', '^production'],\n outputs: [`{workspaceRoot}/${scanDir}`],\n executor: 'nx:run-commands',\n options: {\n commands,\n parallel: false,\n },\n dependsOn: [dockerTargetName],\n };\n\n // Aggregate per-component scan targets under a single `trivy` target. The\n // scan is intentionally NOT wired into `build`: image scanning is slow and\n // its result depends on the ever-changing vulnerability database (a scan\n // that passes today can fail tomorrow when a new CVE is published), so\n // coupling it to `build` would make local and CI builds non-deterministic.\n // Run `nx run-many --target trivy` (the root `trivy` script) in CI instead.\n if (trivyTargetName !== 'trivy') {\n addDependencyToTargetIfNotPresent(project, 'trivy', trivyTargetName);\n }\n};\n"],"names":["joinPathFragments","FsCommands","addDependencyToTargetIfNotPresent","CONTAINER_VERSIONS","TS_VERSIONS","TRIVY_IGNORE_FILE","TRIVY_IGNORE_CONTENTS","nodeImageVersions","npmVersion","npm","minimatchVersion","minimatch","addDockerScanTarget","tree","options","project","containerEngine","trivyTargetName","dockerTargetName","imageTags","projectRoot","root","ignoreFilePath","exists","write","scanKey","replace","scanDir","trivyImage","trivy","fs","commands","rm","mkdir","cp","forEach","imageTag","index","tarName","push","targets","cache","inputs","outputs","executor","parallel","dependsOn"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SAASC,UAAU,QAAQ,UAAO;AAClC,SAASC,iCAAiC,QAAQ,UAAO;AACzD,SAASC,kBAAkB,EAAEC,WAAW,QAAQ,gBAAa;AAE7D,MAAMC,oBAAoB;AAE1B,MAAMC,wBAAwB,CAAC;;;AAG/B,CAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,oBAAoB,IAAO,CAAA;QACtCC,YAAYJ,YAAYK,GAAG;QAC3BC,kBAAkBN,YAAYO,SAAS;IACzC,CAAA,EAAG;AA0BH;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMC,sBAAsB,CACjCC,MACAC;IAEA,MAAM,EAAEC,OAAO,EAAEC,eAAe,EAAEC,eAAe,EAAEC,gBAAgB,EAAE,GACnEJ;IACF,MAAM,EAAEK,SAAS,EAAE,GAAGL;IACtB,MAAMM,cAAcL,QAAQM,IAAI;IAEhC,MAAMC,iBAAiBtB,kBAAkBoB,aAAaf;IACtD,IAAI,CAACQ,KAAKU,MAAM,CAACD,iBAAiB;QAChCT,KAAKW,KAAK,CAACF,gBAAgBhB;IAC7B;IAEA,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,wDAAwD;IACxD,MAAMmB,UAAUN,SAAS,CAAC,EAAE,CAACO,OAAO,CAAC,kBAAkB;IACvD,MAAMC,UAAU3B,kBAAkB,QAAQoB,aAAa,SAASK;IAChE,MAAMG,aAAa,CAAC,kCAAkC,EAAEzB,mBAAmB0B,KAAK,EAAE;IAElF,MAAMC,KAAK,IAAI7B,WAAWY;IAC1B,MAAMkB,WAAW;QACfD,GAAGE,EAAE,CAACL;QACNG,GAAGG,KAAK,CAACN;QACTG,GAAGI,EAAE,CAACZ,gBAAgBtB,kBAAkB2B,SAAStB;KAClD;IAEDc,UAAUgB,OAAO,CAAC,CAACC,UAAUC;QAC3B,MAAMC,UAAU,CAAC,MAAM,EAAED,MAAM,IAAI,CAAC;QACpCN,SAASQ,IAAI,CACX,GAAGvB,gBAAgB,SAAS,EAAEhB,kBAAkB2B,SAASW,SAAS,CAAC,EAAEF,UAAU,EAC/E,GAAGpB,gBAAgB,gBAAgB,EAAEW,QAAQ,QAAQ,EAAEC,WAAW,qBAAqB,EAAEU,QAAQ,oBAAoB,EAAEjC,kBAAkB,yFAAyF,CAAC;IAEvO;IAEAU,QAAQyB,OAAO,KAAK,CAAC;IACrBzB,QAAQyB,OAAO,CAACvB,gBAAgB,GAAG;QACjCwB,OAAO;QACP,0EAA0E;QAC1E,gEAAgE;QAChEC,QAAQ;YAAC;YAAW;SAAc;QAClCC,SAAS;YAAC,CAAC,gBAAgB,EAAEhB,SAAS;SAAC;QACvCiB,UAAU;QACV9B,SAAS;YACPiB;YACAc,UAAU;QACZ;QACAC,WAAW;YAAC5B;SAAiB;IAC/B;IAEA,0EAA0E;IAC1E,2EAA2E;IAC3E,yEAAyE;IACzE,uEAAuE;IACvE,2EAA2E;IAC3E,4EAA4E;IAC5E,IAAID,oBAAoB,SAAS;QAC/Bf,kCAAkCa,SAAS,SAASE;IACtD;AACF,EAAE"}
@@ -129,10 +129,14 @@ const BIOME_FORMATTABLE_EXTENSIONS = new Set([
129
129
  // Resolve each project's ruff settings (module names, line-length) so files
130
130
  // are formatted to match the on-disk build (see getPythonProjectRuffConfigs).
131
131
  const pythonProjectConfigs = pyFiles.length ? getPythonProjectRuffConfigs(tree) : [];
132
+ // Run ruff from the workspace root so it resolves the same on-disk config
133
+ // hasRuffConfigOnDisk probed for. An in-memory tree has no root on disk, in
134
+ // which case there is no config to find and the process cwd is left alone.
135
+ const ruffCwd = pyFiles.length && existsSync(tree.root) ? tree.root : undefined;
132
136
  // Format Python files with ruff (lint fixes + formatting)
133
137
  for (const file of pyFiles){
134
138
  try {
135
- const content = ruffFixAndFormat(file.content.toString('utf-8'), file.path, hasRuffConfigOnDisk(tree, file.path), getOwningProjectRuffConfig(file.path, pythonProjectConfigs));
139
+ const content = ruffFixAndFormat(file.content.toString('utf-8'), file.path, hasRuffConfigOnDisk(tree, file.path), ruffCwd, getOwningProjectRuffConfig(file.path, pythonProjectConfigs));
136
140
  tree.write(file.path, content);
137
141
  } catch {
138
142
  // Silently skip ruff formatting failures
@@ -311,6 +315,19 @@ function getRuffCommand() {
311
315
  dir = parent;
312
316
  }
313
317
  }
318
+ /**
319
+ * The `[tool.ruff.lint].select` generated Python projects vend. Generation
320
+ * formats before that config lands on disk, so it is pinned here to keep
321
+ * generated files clean under the project's own `lint` target rather than under
322
+ * whatever ruff's defaults happen to be for the pinned release.
323
+ */ const DEFAULT_RUFF_SELECT = [
324
+ 'E',
325
+ 'F',
326
+ 'UP',
327
+ 'B',
328
+ 'SIM',
329
+ 'I'
330
+ ];
314
331
  /**
315
332
  * Derive ruff's `target-version` (eg `py314`) from a PEP 508
316
333
  * `requires-python` specifier (eg `>=3.14`). Ruff targets the minimum
@@ -335,7 +352,8 @@ function getRuffCommand() {
335
352
  /**
336
353
  * Map each Nx project with a `pyproject.toml` to the ruff settings the on-disk
337
354
  * build enforces for it: its top-level module names (from
338
- * `[tool.hatch.build.targets.wheel].packages`) and its `[tool.ruff].line-length`.
355
+ * `[tool.hatch.build.targets.wheel].packages`), its `[tool.ruff].line-length`
356
+ * and its `[tool.ruff.lint].select`.
339
357
  */ function getPythonProjectRuffConfigs(tree) {
340
358
  const configs = [];
341
359
  for (const project of getProjects(tree).values()){
@@ -349,12 +367,15 @@ function getRuffCommand() {
349
367
  const modules = Array.isArray(wheelPackages) ? wheelPackages.filter((pkg)=>typeof pkg === 'string' && !!pkg).map((pkg)=>pkg.split('/')[0]) : [];
350
368
  const lineLength = pyproject?.tool?.ruff?.['line-length'];
351
369
  const targetVersion = requiresPythonToRuffTarget(pyproject?.project?.['requires-python']);
352
- if (modules.length || typeof lineLength === 'number' || targetVersion) {
370
+ const selected = pyproject?.tool?.ruff?.lint?.select;
371
+ const select = Array.isArray(selected) ? selected.filter((rule)=>typeof rule === 'string' && !!rule) : undefined;
372
+ if (modules.length || typeof lineLength === 'number' || targetVersion || select?.length) {
353
373
  configs.push({
354
374
  root: project.root.split(path.sep).join('/'),
355
375
  modules,
356
376
  lineLength: typeof lineLength === 'number' ? lineLength : undefined,
357
- targetVersion
377
+ targetVersion,
378
+ select: select?.length ? select : undefined
358
379
  });
359
380
  }
360
381
  } catch {
@@ -384,11 +405,16 @@ function getRuffCommand() {
384
405
  * Run ruff check --fix and ruff format on Python file content via stdin.
385
406
  * Applies all configured lint fixes (including import sorting) and formatting.
386
407
  *
387
- * When no ruff config exists on disk (`hasConfig` false) ruff falls back to its
388
- * defaults, which omit isortbut generated projects enable rule `I` and their
389
- * build fails on unsorted imports (I001). In that case we add `--extend-select
390
- * I` so import sorting matches what the build enforces. When a config does
391
- * exist we defer to it entirely, honouring the user's rule selection.
408
+ * When no ruff config exists on disk (`hasConfig` false) ruff would fall back to
409
+ * its own defaults, which change between releases 0.16 widened them from `E`
410
+ * and `F` to 36 rule prefixes so generation would apply fixes the project's
411
+ * build never asks for (eg `RUF022` reordering `__all__`). Instead run
412
+ * `--isolated` with the project's own `lint.select` pinned, so generation
413
+ * enforces exactly what `lint` does and nothing more. `--isolated` also stops
414
+ * ruff walking above the workspace to a stray config on the host.
415
+ *
416
+ * When a config does exist we defer to it entirely, honouring the user's rule
417
+ * selection, and run from `tree.root` so ruff discovers it.
392
418
  *
393
419
  * `projectConfig` carries the owning project's ruff settings, which ruff cannot
394
420
  * detect from the filesystem during generation because the project lives only
@@ -397,11 +423,16 @@ function getRuffCommand() {
397
423
  * imports in their own group, and `line-length` keeps wrapping consistent (the
398
424
  * generated config raises it above ruff's default of 88). These are additive to
399
425
  * any on-disk config, so they are safe to pass regardless of `hasConfig`.
400
- */ function ruffFixAndFormat(content, filePath, hasConfig, projectConfig) {
426
+ */ function ruffFixAndFormat(content, filePath, hasConfig, cwd, projectConfig) {
401
427
  const ruff = getRuffCommand();
402
428
  if (!ruff) return content;
403
- const extendSelect = hasConfig ? '' : ' --extend-select I';
404
429
  const configArgs = [];
430
+ // Pin the rule selection only when deferring to ruff's defaults would
431
+ // otherwise apply rules the project's build does not enforce.
432
+ const isolated = !hasConfig;
433
+ if (isolated) {
434
+ configArgs.push(`lint.select = ${JSON.stringify(projectConfig?.select ?? DEFAULT_RUFF_SELECT)}`);
435
+ }
405
436
  if (projectConfig?.modules.length) {
406
437
  configArgs.push(`lint.isort.known-first-party = ${JSON.stringify(projectConfig.modules)}`);
407
438
  }
@@ -412,17 +443,23 @@ function getRuffCommand() {
412
443
  configArgs.push(`target-version = "${projectConfig.targetVersion}"`);
413
444
  }
414
445
  const config = configArgs.map((arg)=>` --config ${JSON.stringify(arg)}`).join('');
415
- // First apply lint fixes (import sorting, unused imports, etc.)
416
- try {
417
- const result = execSync(`${ruff} check --fix${extendSelect}${config} --stdin-filename ${filePath} -`, {
418
- input: content,
446
+ const flags = `${isolated ? ' --isolated' : ''}${config}`;
447
+ // Built per invocation: `input` carries the content the previous step emitted.
448
+ const options = (input)=>({
449
+ input,
419
450
  encoding: 'utf-8',
420
451
  stdio: [
421
452
  'pipe',
422
453
  'pipe',
423
454
  'pipe'
424
- ]
455
+ ],
456
+ ...cwd ? {
457
+ cwd
458
+ } : {}
425
459
  });
460
+ // First apply lint fixes (import sorting, unused imports, etc.)
461
+ try {
462
+ const result = execSync(`${ruff} check --fix${flags} --stdin-filename ${filePath} -`, options(content));
426
463
  content = result;
427
464
  } catch (e) {
428
465
  // ruff check exits non-zero when it finds unfixable issues,
@@ -433,15 +470,7 @@ function getRuffCommand() {
433
470
  }
434
471
  // Then apply formatting
435
472
  try {
436
- content = execSync(`${ruff} format${config} --stdin-filename ${filePath} -`, {
437
- input: content,
438
- encoding: 'utf-8',
439
- stdio: [
440
- 'pipe',
441
- 'pipe',
442
- 'pipe'
443
- ]
444
- });
473
+ content = execSync(`${ruff} format${flags} --stdin-filename ${filePath} -`, options(content));
445
474
  } catch {
446
475
  // Fall through with whatever content we have
447
476
  }