@aws/nx-plugin 1.0.0-rc.72 → 1.0.0-rc.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-THIRD-PARTY +210 -730
- package/migrations.json +34 -24
- package/package.json +4 -4
- package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/license/config.d.ts +9 -0
- package/src/license/config.js +28 -0
- package/src/license/config.js.map +1 -1
- package/src/license/dependency-check/collectors/collector.js.map +1 -1
- package/src/license/generator.js +3 -17
- package/src/license/generator.js.map +1 -1
- package/src/license/known-exceptions.d.ts +0 -1
- package/src/license/known-exceptions.js +1 -23
- package/src/license/known-exceptions.js.map +1 -1
- package/src/migrations/latest/apt-security-updates-in-container-images/__snapshots__/migration.spec.ts.snap +90 -0
- package/src/migrations/latest/apt-security-updates-in-container-images/metadata.json +3 -0
- package/src/migrations/latest/apt-security-updates-in-container-images/migration.js +90 -0
- package/src/migrations/latest/apt-security-updates-in-container-images/migration.js.map +1 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/metadata.json +3 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.d.ts +6 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js +38 -0
- package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.js.map +1 -0
- package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/migration.js +14 -2
- package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.d.ts +6 -0
- package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.js.map +1 -0
- package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +11 -4
- package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +8 -8
- package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +2 -2
- package/src/py/agent/a2a-connection/generator.js +5 -11
- package/src/py/agent/a2a-connection/generator.js.map +1 -1
- package/src/py/agent/files/deploy/Dockerfile.template +7 -0
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
- package/src/py/mcp-server/files/deploy/Dockerfile.template +7 -0
- package/src/py/mcp-server/generator.js +8 -3
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/rdb/__snapshots__/generator.spec.ts.snap +7 -5
- package/src/py/rdb/generator.js +36 -2
- package/src/py/rdb/generator.js.map +1 -1
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +14 -14
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +9 -2
- package/src/ts/agent/a2a-connection/generator.js +4 -2
- package/src/ts/agent/a2a-connection/generator.js.map +1 -1
- package/src/ts/agent/files/deploy/Dockerfile.template +7 -0
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/ts/mcp-server/files/Dockerfile.template +7 -0
- package/src/ts/mcp-server/generator.js +8 -3
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +8 -6
- package/src/ts/rdb/generator.js +29 -2
- package/src/ts/rdb/generator.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +15 -15
- package/src/ts/react-website/app/generator.js +4 -2
- package/src/ts/react-website/app/generator.js.map +1 -1
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +1 -1
- package/src/utils/agent-connection/agent-connection.js +5 -9
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/ast.d.ts +29 -0
- package/src/utils/ast.js +31 -0
- package/src/utils/ast.js.map +1 -1
- package/src/utils/metrics.js +2 -2
- package/src/utils/metrics.js.map +1 -1
- package/src/utils/versions.d.ts +73 -73
- package/src/utils/versions.js +70 -70
- package/src/utils/versions.js.map +1 -1
- package/src/migrations/latest/py-agent-a2a-httpx-client-per-call/migration.js.map +0 -1
- package/src/migrations/latest/py-agent-a2a-lifespan-construction/migration.js.map +0 -1
- package/src/migrations/latest/py-agent-session-management-support/migration.js.map +0 -1
- package/src/migrations/latest/smithy-server-package-rename/migration.js.map +0 -1
- package/src/migrations/latest/translate-script-whole-file-writes/migration.js.map +0 -1
- package/src/py/rdb/files/__name__/__init__.py.template +0 -3
- package/src/py/rdb/files/config.json.template +0 -14
- package/src/ts/rdb/files/config.json.template +0 -14
- package/src/ts/rdb/files/src/index.ts.template +0 -1
- /package/src/migrations/latest/{py-agent-a2a-httpx-client-per-call → apt-security-updates-in-container-images}/migration.d.ts +0 -0
- /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.67/0001-smithy-server-package-rename}/metadata.json +0 -0
- /package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.67/0001-smithy-server-package-rename}/migration.d.ts +0 -0
- /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.67/0001-smithy-server-package-rename}/migration.js +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/build-system-prompt.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/build-user-prompt.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/config-path.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/file-to-translate.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/get-files-to-translate.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/header.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/log.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/main-call.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/main.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/project-root.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/run-with-concurrency.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/scripts-dir.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/translate-file-for-language.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/already-migrated.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/build-system-prompt.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/build-user-prompt.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/config-path.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/file-to-translate.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/get-files-to-translate.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/log.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/main-call.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/main.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/project-root.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/run-with-concurrency.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/translate-file-for-language.grit +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/metadata.json +0 -0
- /package/src/migrations/{latest/py-agent-session-management-support → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/migration.d.ts +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/migration.js +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/test-fixtures/released-script-first.ts.fixture +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/test-fixtures/released-script.ts.fixture +0 -0
- /package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/metadata.json +0 -0
- /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/migration.d.ts +0 -0
- /package/src/migrations/{latest/py-agent-a2a-httpx-client-per-call → v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call}/metadata.json +0 -0
- /package/src/migrations/{latest/py-agent-a2a-httpx-client-per-call → v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call}/migration.js +0 -0
- /package/src/migrations/{latest/py-agent-session-management-support → v1.0.0-rc.72/0001-py-agent-session-management-support}/metadata.json +0 -0
- /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.72/0001-py-agent-session-management-support}/migration.d.ts +0 -0
- /package/src/migrations/{latest/py-agent-session-management-support → v1.0.0-rc.72/0001-py-agent-session-management-support}/migration.js +0 -0
|
@@ -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 {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n type MustDeclarePy,\n} from '../declared-dependencies';\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/** TypeScript dependencies a caller must declare to emit agent-connection clients. */\nexport const AGENT_CONNECTION_DEPENDENCIES = [\n { name: '@aws-lambda-powertools/parameters' },\n { name: '@aws-sdk/credential-providers' },\n { name: 'aws4fetch' },\n { name: '@modelcontextprotocol/sdk' },\n { name: '@a2a-js/sdk' },\n { name: '@strands-agents/sdk' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\n/** Python dependencies a caller must declare to emit agent-connection clients. */\nexport const AGENT_CONNECTION_PY_DEPENDENCIES = [\n { name: 'aws-lambda-powertools' },\n { name: 'strands-agents' },\n] as const satisfies readonly { name: IPyDepVersion }[];\n\ntype AgentConnectionPyDependency =\n (typeof AGENT_CONNECTION_PY_DEPENDENCIES)[number]['name'];\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<AgentConnectionPyDependency>;\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 fromModule: '.core.tool_errors_strands',\n importName: 'log_tool_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<\n string,\n (typeof AGENT_CONNECTION_DEPENDENCIES)[number]['name'][]\n> = {\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 = (\n tree: Tree,\n templateDir: string,\n declaration: DependencyDeclaration,\n) => {\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(\n forDependencies<typeof AGENT_CONNECTION_DEPENDENCIES>(declaration),\n deps,\n ),\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclare<typeof AGENT_CONNECTION_DEPENDENCIES, D>,\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', declaration);\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 // Re-export the runtime-config loader so agent server entry points can read\n // their own connected resources (e.g. session storage) without pulling in\n // internals.\n await addStarExport(\n tree,\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'src', 'index.ts'),\n './core/runtime-config.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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclare<typeof AGENT_CONNECTION_DEPENDENCIES, D>,\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, declaration);\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n protocol: ConnectionProtocol,\n declaration: D & MustDeclare<typeof AGENT_CONNECTION_DEPENDENCIES, D>,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const fw = frameworkLanguage(framework, 'ts', protocol);\n\n emitTs(tree, 'core-auth', declaration);\n if (MCP_TRANSPORT_PROTOCOLS.includes(protocol)) {\n emitTs(tree, 'core-shared', declaration);\n }\n emitTs(tree, TS_PROTOCOL_TEMPLATES[protocol], declaration);\n\n // Framework Layer 2: base helpers (re-exported for the agent server) + the\n // thin protocol client.\n await addTypeScriptFrameworkBase(\n tree,\n forDependencies<typeof AGENT_CONNECTION_DEPENDENCIES>(declaration),\n framework,\n );\n emitTs(tree, fw.protocol, declaration);\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclarePy<typeof AGENT_CONNECTION_PY_DEPENDENCIES, D>,\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(\n tree,\n projectDir,\n forDependencies<[], typeof AGENT_CONNECTION_PY_DEPENDENCIES>(declaration),\n ['aws-lambda-powertools'],\n );\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 * Ensure the shared Python agent-connection project has\n * `s3_checkpoint_saver_langchain.py` — a vendored `S3CheckpointSaver`\n * (from `langgraph-checkpoint-s3`, MIT licensed) that LangChain agents with\n * `session: 's3'` import from directly (`.core.s3_checkpoint_saver_langchain`),\n * mirroring how they import `.core.runtime_config`. Vendored rather than\n * installed as a dependency, and shared rather than per-agent, since it has\n * no agent-specific templating.\n */\nexport function ensureLangchainS3CheckpointSaver(tree: Tree): void {\n emitPy(tree, 'py-core-langchain/s3');\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclarePy<typeof AGENT_CONNECTION_PY_DEPENDENCIES, D>,\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 forDependencies<[], typeof AGENT_CONNECTION_PY_DEPENDENCIES>(declaration),\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n protocol: ConnectionProtocol,\n declaration: D & MustDeclarePy<typeof AGENT_CONNECTION_PY_DEPENDENCIES, D>,\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(\n tree,\n forDependencies<[], typeof AGENT_CONNECTION_PY_DEPENDENCIES>(declaration),\n framework,\n );\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","forDependencies","addDependenciesToPackageJson","esmVars","addDependenciesToPyProjectToml","withVersions","AGENT_CONNECTION_DEPENDENCIES","name","AGENT_CONNECTION_PY_DEPENDENCIES","py","pattern","AGENT_CONNECTION_DIR","PACKAGES_DIR","COMMON_DIR","AGENT_CONNECTION_PROJECT_DIR","PY_AGENT_CONNECTION_NAME","getPythonAgentConnectionProjectDir","tree","dir","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","declaration","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","ensureLangchainS3CheckpointSaver","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 {\n addStarExport,\n appendToArrayViaGritQL,\n applyGritQL,\n matchGritQL,\n} from '../ast';\nimport {\n type DependencyDeclaration,\n forDependencies,\n type MustDeclare,\n type MustDeclarePy,\n} from '../declared-dependencies';\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/** TypeScript dependencies a caller must declare to emit agent-connection clients. */\nexport const AGENT_CONNECTION_DEPENDENCIES = [\n { name: '@aws-lambda-powertools/parameters' },\n { name: '@aws-sdk/credential-providers' },\n { name: 'aws4fetch' },\n { name: '@modelcontextprotocol/sdk' },\n { name: '@a2a-js/sdk' },\n { name: '@strands-agents/sdk' },\n] as const satisfies readonly { name: ITsDepVersion }[];\n\n/** Python dependencies a caller must declare to emit agent-connection clients. */\nexport const AGENT_CONNECTION_PY_DEPENDENCIES = [\n { name: 'aws-lambda-powertools' },\n { name: 'strands-agents' },\n] as const satisfies readonly { name: IPyDepVersion }[];\n\ntype AgentConnectionPyDependency =\n (typeof AGENT_CONNECTION_PY_DEPENDENCIES)[number]['name'];\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<AgentConnectionPyDependency>;\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 fromModule: '.core.tool_errors_strands',\n importName: 'log_tool_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<\n string,\n (typeof AGENT_CONNECTION_DEPENDENCIES)[number]['name'][]\n> = {\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 = (\n tree: Tree,\n templateDir: string,\n declaration: DependencyDeclaration,\n) => {\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(\n forDependencies<typeof AGENT_CONNECTION_DEPENDENCIES>(declaration),\n deps,\n ),\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclare<typeof AGENT_CONNECTION_DEPENDENCIES, D>,\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', declaration);\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 // Re-export the runtime-config loader so agent server entry points can read\n // their own connected resources (e.g. session storage) without pulling in\n // internals.\n await addStarExport(\n tree,\n joinPathFragments(AGENT_CONNECTION_PROJECT_DIR, 'src', 'index.ts'),\n './core/runtime-config.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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclare<typeof AGENT_CONNECTION_DEPENDENCIES, D>,\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, declaration);\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n protocol: ConnectionProtocol,\n declaration: D & MustDeclare<typeof AGENT_CONNECTION_DEPENDENCIES, D>,\n framework: AgentFramework = 'strands',\n): Promise<void> {\n const fw = frameworkLanguage(framework, 'ts', protocol);\n\n emitTs(tree, 'core-auth', declaration);\n if (MCP_TRANSPORT_PROTOCOLS.includes(protocol)) {\n emitTs(tree, 'core-shared', declaration);\n }\n emitTs(tree, TS_PROTOCOL_TEMPLATES[protocol], declaration);\n\n // Framework Layer 2: base helpers (re-exported for the agent server) + the\n // thin protocol client.\n await addTypeScriptFrameworkBase(\n tree,\n forDependencies<typeof AGENT_CONNECTION_DEPENDENCIES>(declaration),\n framework,\n );\n emitTs(tree, fw.protocol, declaration);\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclarePy<typeof AGENT_CONNECTION_PY_DEPENDENCIES, D>,\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(\n tree,\n projectDir,\n forDependencies<[], typeof AGENT_CONNECTION_PY_DEPENDENCIES>(declaration),\n ['aws-lambda-powertools'],\n );\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 * Ensure the shared Python agent-connection project has\n * `s3_checkpoint_saver_langchain.py` — a vendored `S3CheckpointSaver`\n * (from `langgraph-checkpoint-s3`, MIT licensed) that LangChain agents with\n * `session: 's3'` import from directly (`.core.s3_checkpoint_saver_langchain`),\n * mirroring how they import `.core.runtime_config`. Vendored rather than\n * installed as a dependency, and shared rather than per-agent, since it has\n * no agent-specific templating.\n */\nexport function ensureLangchainS3CheckpointSaver(tree: Tree): void {\n emitPy(tree, 'py-core-langchain/s3');\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n declaration: D & MustDeclarePy<typeof AGENT_CONNECTION_PY_DEPENDENCIES, D>,\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 forDependencies<[], typeof AGENT_CONNECTION_PY_DEPENDENCIES>(declaration),\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 const D extends DependencyDeclaration,\n>(\n tree: Tree,\n protocol: ConnectionProtocol,\n declaration: D & MustDeclarePy<typeof AGENT_CONNECTION_PY_DEPENDENCIES, D>,\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(\n tree,\n forDependencies<[], typeof AGENT_CONNECTION_PY_DEPENDENCIES>(declaration),\n framework,\n );\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 appendToArrayViaGritQL(\n tree,\n filePath,\n 'tools=',\n `*${clientVarName}.list_tools_sync()`,\n { skipIfContains: clientVarName, language: 'py' },\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","appendToArrayViaGritQL","applyGritQL","matchGritQL","forDependencies","addDependenciesToPackageJson","esmVars","addDependenciesToPyProjectToml","withVersions","AGENT_CONNECTION_DEPENDENCIES","name","AGENT_CONNECTION_PY_DEPENDENCIES","py","pattern","AGENT_CONNECTION_DIR","PACKAGES_DIR","COMMON_DIR","AGENT_CONNECTION_PROJECT_DIR","PY_AGENT_CONNECTION_NAME","getPythonAgentConnectionProjectDir","tree","dir","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","declaration","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","ensureLangchainS3CheckpointSaver","addPythonFrameworkBase","addPythonCoreClient","addTypeScriptClientToAgent","agentFilePath","clientClassName","clientVarName","clientCreationStmt","addPythonClientToAgent","addPythonClientTools","addPythonClientToGetAgent","filePath","skipIfContains","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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"}
|
package/src/utils/ast.d.ts
CHANGED
|
@@ -83,3 +83,32 @@ export declare const GRIT_INSERT_PLACEHOLDER = "__GRIT_INSERT_PLACEHOLDER__";
|
|
|
83
83
|
* file changed.
|
|
84
84
|
*/
|
|
85
85
|
export declare const insertViaGritQL: (tree: Tree, filePath: string, pattern: string, text: string) => Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Append `entry` to an array, skipping arrays that already contain it, e.g.
|
|
88
|
+
* `` appendToArrayViaGritQL(tree, file, 'tools:', 'myTool', { scope: 'new Agent($_)' }) ``
|
|
89
|
+
* turns `tools: [a, b]` into `tools: [a, b, myTool]`.
|
|
90
|
+
*
|
|
91
|
+
* `arrayPrefix` is whatever precedes the array literal, including its separator —
|
|
92
|
+
* `'tools:'` for an object property, `'tools='` for a keyword argument or
|
|
93
|
+
* assignment, `'"tools":'` for a quoted key. Which separator applies depends on
|
|
94
|
+
* the construct rather than the language, so the caller states it.
|
|
95
|
+
*
|
|
96
|
+
* Appends after the last *element* rather than rewriting the element list with
|
|
97
|
+
* `=>`. GritQL binds a `[$items]` list to its trailing comma too, so
|
|
98
|
+
* `` `[$items]` => `[$items, entry]` `` emits the sparse array `[a, b, , entry]`
|
|
99
|
+
* once the formatter has wrapped the array — a hole that is `undefined` at
|
|
100
|
+
* runtime. (Accumulating with `$items += ", entry"` is safe by comparison, since
|
|
101
|
+
* it appends within the list rather than re-emitting it; both forms appear in
|
|
102
|
+
* this repo.)
|
|
103
|
+
*
|
|
104
|
+
* `scope` limits the match to an enclosing pattern, so same-named arrays
|
|
105
|
+
* elsewhere in the file are left alone. `skipIfContains` overrides what the
|
|
106
|
+
* append dedupes on, for when the rendered entry is not the identifier to match.
|
|
107
|
+
*
|
|
108
|
+
* The caller formats afterwards. Returns true if the entry was appended.
|
|
109
|
+
*/
|
|
110
|
+
export declare const appendToArrayViaGritQL: (tree: Tree, filePath: string, arrayPrefix: string, entry: string, options?: {
|
|
111
|
+
scope?: string;
|
|
112
|
+
skipIfContains?: string;
|
|
113
|
+
language?: "py";
|
|
114
|
+
}) => Promise<boolean>;
|
package/src/utils/ast.js
CHANGED
|
@@ -300,5 +300,36 @@ export const addDestructuredImport = async (tree, filePath, variableNames, from)
|
|
|
300
300
|
tree.write(filePath, content.replace(GRIT_INSERT_PLACEHOLDER, ()=>replacement));
|
|
301
301
|
return true;
|
|
302
302
|
};
|
|
303
|
+
/**
|
|
304
|
+
* Append `entry` to an array, skipping arrays that already contain it, e.g.
|
|
305
|
+
* `` appendToArrayViaGritQL(tree, file, 'tools:', 'myTool', { scope: 'new Agent($_)' }) ``
|
|
306
|
+
* turns `tools: [a, b]` into `tools: [a, b, myTool]`.
|
|
307
|
+
*
|
|
308
|
+
* `arrayPrefix` is whatever precedes the array literal, including its separator —
|
|
309
|
+
* `'tools:'` for an object property, `'tools='` for a keyword argument or
|
|
310
|
+
* assignment, `'"tools":'` for a quoted key. Which separator applies depends on
|
|
311
|
+
* the construct rather than the language, so the caller states it.
|
|
312
|
+
*
|
|
313
|
+
* Appends after the last *element* rather than rewriting the element list with
|
|
314
|
+
* `=>`. GritQL binds a `[$items]` list to its trailing comma too, so
|
|
315
|
+
* `` `[$items]` => `[$items, entry]` `` emits the sparse array `[a, b, , entry]`
|
|
316
|
+
* once the formatter has wrapped the array — a hole that is `undefined` at
|
|
317
|
+
* runtime. (Accumulating with `$items += ", entry"` is safe by comparison, since
|
|
318
|
+
* it appends within the list rather than re-emitting it; both forms appear in
|
|
319
|
+
* this repo.)
|
|
320
|
+
*
|
|
321
|
+
* `scope` limits the match to an enclosing pattern, so same-named arrays
|
|
322
|
+
* elsewhere in the file are left alone. `skipIfContains` overrides what the
|
|
323
|
+
* append dedupes on, for when the rendered entry is not the identifier to match.
|
|
324
|
+
*
|
|
325
|
+
* The caller formats afterwards. Returns true if the entry was appended.
|
|
326
|
+
*/ export const appendToArrayViaGritQL = async (tree, filePath, arrayPrefix, entry, options = {})=>{
|
|
327
|
+
const { scope, skipIfContains, language } = options;
|
|
328
|
+
const within = (variable)=>scope ? `${variable} <: within \`${scope}\`, ` : '';
|
|
329
|
+
const pattern = `or {` + // An empty array has no last element to append after.
|
|
330
|
+
` \`${arrayPrefix}$arr\` where { $arr <: \`[]\`, ${within('$arr')}` + ` $arr => \`[${GRIT_INSERT_PLACEHOLDER}]\` },` + // Dedupe on the list, append after its last element.
|
|
331
|
+
` \`${arrayPrefix}[$items]\` where {` + ` $items <: not contains \`${skipIfContains ?? entry}\`, ${within('$items')}` + ` $items <: [$..., $last], $last += \`, ${GRIT_INSERT_PLACEHOLDER}\` }` + ` }`;
|
|
332
|
+
return insertViaGritQL(tree, filePath, language === 'py' ? `language python\n${pattern}` : pattern, entry);
|
|
333
|
+
};
|
|
303
334
|
|
|
304
335
|
//# sourceMappingURL=ast.js.map
|
package/src/utils/ast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/ast.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { QueryBuilder } from '@getgrit/gritql';\nimport type { Tree } from '@nx/devkit';\nimport * as path from 'path';\nimport { updateGitIgnore } from './git';\nimport { isEsmWorkspace } from './module-format';\n\nconst GRIT_DIR = '.grit';\n\n/**\n * Normalize a relative module specifier for the workspace's module format.\n * ESM (`nodenext`) requires an explicit `.js` extension on relative imports;\n * CommonJS (`node16`) resolves them extensionless. Callers always pass the ESM\n * form (with `.js`); this strips the extension for CommonJS workspaces.\n */\nconst normalizeModuleSpecifier = (tree: Tree, from: string): string => {\n if (isEsmWorkspace(tree)) {\n return from;\n }\n const isRelative = from.startsWith('./') || from.startsWith('../');\n return isRelative && from.endsWith('.js')\n ? from.slice(0, -'.js'.length)\n : from;\n};\n\n// Pin the gritql native library's \"global\" stdlib directory to <workspace>/.grit so it doesn't try to write to /usr/local/.grit (or wherever node's grandparent resolves to).\nconst ensureGritDir = (tree: Tree) => {\n process.env.GRIT_GLOBAL_DIR ??= path.join(tree.root, GRIT_DIR);\n updateGitIgnore(tree, '.', (patterns) => [...patterns, GRIT_DIR]);\n};\n\nconst assertFilePath = (tree: Tree, filePath: string) => {\n if (!tree.exists(filePath)) {\n throw new Error(`No file located at ${filePath}`);\n }\n};\n\nexport const addDestructuredImport = async (\n tree: Tree,\n filePath: string,\n variableNames: string[],\n from: string,\n) => {\n assertFilePath(tree, filePath);\n from = normalizeModuleSpecifier(tree, from);\n\n // Check if there's an existing import from this module\n const hasExistingImport = await matchGritQL(\n tree,\n filePath,\n `\\`import { $_ } from '${from}'\\``,\n );\n\n if (hasExistingImport) {\n // For each new variable, use GritQL rewrite to add it if not already present\n for (const variableName of variableNames) {\n const localName = variableName.includes(' as ')\n ? variableName.split(' as ')[1]\n : variableName;\n // Use rewrite (=>) which correctly handles comma-separated import specifier lists\n await applyGritQL(\n tree,\n filePath,\n `\\`import { $imports } from '${from}'\\` => \\`import { $imports, ${variableName} } from '${from}'\\` where { $imports <: not contains \\`${localName}\\` }`,\n );\n }\n } else {\n // No existing import — prepend a new one (after shebang if present)\n const specifiers = variableNames.join(', ');\n const contents = tree.read(filePath)!.toString();\n const newImport = `import { ${specifiers} } from '${from}';\\n`;\n if (contents.startsWith('#!')) {\n const newlineIndex = contents.indexOf('\\n');\n tree.write(\n filePath,\n `${contents.substring(0, newlineIndex + 1)}${newImport}${contents.substring(newlineIndex + 1)}`,\n );\n } else {\n tree.write(filePath, `${newImport}${contents}`);\n }\n }\n};\n\n/**\n * Ensure a Python `from <module> import <name1>, <name2>, ...` statement\n * exists in the given file. If the module already has a `from <module> import`\n * line, any missing names are appended; otherwise a fresh import statement is\n * prepended to the file (ruff's import sorter will reorder it into place on\n * the next format pass).\n *\n * We try to append first (which only succeeds if an import-from for the\n * module already exists AND the name isn't yet in the list). If nothing\n * was rewritten after trying every requested name, we fall back to checking\n * whether the module is imported at all; if not, we prepend a fresh\n * `from <module> import ...` line.\n */\nexport const addPythonDestructuredImport = async (\n tree: Tree,\n filePath: string,\n variableNames: string[],\n from: string,\n) => {\n assertFilePath(tree, filePath);\n\n // Determine whether there's any `from <module> import ...` line in the file.\n // We detect this by attempting a no-op transformation — the file is unchanged,\n // but a successful match tells us the line exists.\n const beforeContents = tree.read(filePath)!.toString();\n let moduleAlreadyImported = false;\n for (const variableName of variableNames) {\n // Appends the name if the import-from exists and the name isn't already there.\n const appended = await applyGritQL(\n tree,\n filePath,\n `language python\\n\\`from ${from} import $names\\` where { $names <: not contains \\`${variableName}\\`, $names += \\`, ${variableName}\\` }`,\n );\n if (appended) {\n moduleAlreadyImported = true;\n }\n }\n\n // If the append succeeded for at least one name, we know the module line\n // exists; we're done. If nothing was appended we need to distinguish\n // between \"module not imported\" (need to add a new line) and \"module\n // imported but all names already present\" (no-op).\n if (moduleAlreadyImported) {\n return;\n }\n\n const allAlreadyPresent = await matchGritQL(\n tree,\n filePath,\n `language python\\n\\`from ${from} import $names\\` where { ${variableNames\n .map((n) => `$names <: contains \\`${n}\\``)\n .join(', ')} }`,\n );\n if (allAlreadyPresent) {\n return;\n }\n\n // No existing `from <module> import` line — prepend one. Ruff's import\n // sorter will place it in the right group on the next formatter pass.\n const specifiers = variableNames.join(', ');\n tree.write(filePath, `from ${from} import ${specifiers}\\n${beforeContents}`);\n};\n\n/**\n * Adds an `import <variableName> from '<from>'; statement to the beginning of the file,\n * if it doesn't already exist\n */\nexport const addSingleImport = async (\n tree: Tree,\n filePath: string,\n variableName: string,\n from: string,\n) => {\n assertFilePath(tree, filePath);\n from = normalizeModuleSpecifier(tree, from);\n\n // Check if default import already exists using GritQL\n const alreadyImported = await matchGritQL(\n tree,\n filePath,\n `\\`import ${variableName} from '${from}'\\``,\n );\n if (alreadyImported) {\n return;\n }\n\n // Prepend new import to file\n const contents = tree.read(filePath)!.toString();\n tree.write(filePath, `import ${variableName} from '${from}';\\n${contents}`);\n};\n\n/**\n * Adds an `export * from '<from>'; statement to the given TypeScript file.\n * Note that this will create the file if it does not exist in the tree.\n */\nexport const addStarExport = async (\n tree: Tree,\n filePath: string,\n from: string,\n) => {\n from = normalizeModuleSpecifier(tree, from);\n const contents = tree.read(filePath)?.toString() ?? '';\n\n // For empty/non-existent files, just write the export\n if (!contents.trim()) {\n tree.write(filePath, `export * from '${from}';\\n`);\n return;\n }\n\n // Check if already exported using GritQL\n const alreadyExported = await matchGritQL(\n tree,\n filePath,\n `\\`export * from '${from}'\\``,\n );\n if (alreadyExported) {\n return;\n }\n\n // Prepend new export to file\n tree.write(filePath, `export * from '${from}';\\n${contents}`);\n};\n\n/**\n * Return whether or not the given identifier is exported in the source file.\n * Checks for both `export { Identifier }` and `export type Identifier = ...`.\n */\nexport const hasExportDeclaration = async (\n tree: Tree,\n source: string,\n identifierName: string,\n): Promise<boolean> => {\n ensureGritDir(tree);\n const patterns = [\n `\\`export type ${identifierName} = $_\\``,\n `\\`export { ${identifierName} }\\``,\n `\\`export type { ${identifierName} } from $_\\``,\n ];\n\n for (const pattern of patterns) {\n try {\n const q = new QueryBuilder(`$p => $p where { $p <: ${pattern} }`);\n const result = await q.applyToFile({\n path: 'check.ts',\n content: source,\n });\n if (result !== null) return true;\n } catch {\n // Pattern didn't match, try next\n }\n }\n\n return false;\n};\n\n/**\n * Apply a GritQL pattern to a file in the Nx tree.\n */\nexport const applyGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<boolean> => {\n if (!tree.exists(filePath)) throw new Error(`No file at ${filePath}`);\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n const query = new QueryBuilder(pattern);\n const result = await query.applyToFile({ path: filePath, content: source });\n if (result && result.content !== source) {\n tree.write(filePath, result.content);\n return true;\n }\n return false;\n};\n\n/**\n * Check whether a GritQL pattern matches anywhere in a file.\n * Returns true if the pattern matches at least once.\n *\n * Accepts raw GritQL, including patterns that start with a `language <name>`\n * header (e.g. `language python\\n\\`print($_)\\``) — the pattern is passed\n * straight through to QueryBuilder without any wrapping rewrite.\n */\nexport const matchGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<boolean> => {\n if (!tree.exists(filePath)) return false;\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n let matched = false;\n try {\n const query = new QueryBuilder(pattern);\n query.filter(() => {\n matched = true;\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return false;\n }\n return matched;\n};\n\n/**\n * Capture the source text of the first match of a GritQL pattern.\n *\n * For example `` `dependencyCheck: $_` `` returns the whole\n * `dependencyCheck: { ... }` property text. Returns undefined if nothing\n * matches. The file is not modified.\n */\nexport const captureGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<string | undefined> => {\n if (!tree.exists(filePath)) return undefined;\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n let captured: string | undefined;\n try {\n const query = new QueryBuilder(pattern);\n query.filter((node) => {\n captured ??= node.text();\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return undefined;\n }\n return captured;\n};\n\n/**\n * Like `captureGritQL`, but returns one metavariable's binding (e.g. `mod`\n * for `$mod`) instead of the whole matched node.\n */\nexport const captureGritQLVariable = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n variableName: string,\n): Promise<string | undefined> => {\n if (!tree.exists(filePath)) return undefined;\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n // The underlying binding lookup requires the `$` sigil.\n const name = variableName.startsWith('$') ? variableName : `$${variableName}`;\n let captured: string | undefined;\n try {\n const query = new QueryBuilder(pattern);\n query.filter((node) => {\n captured ??= node.var(name) ?? undefined;\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return undefined;\n }\n return captured;\n};\n\n/**\n * Return the text of every node matching the GritQL pattern, in document order.\n */\nexport const captureAllGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<string[]> => {\n if (!tree.exists(filePath)) return [];\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n const captured: string[] = [];\n try {\n const query = new QueryBuilder(pattern);\n query.filter((node) => {\n captured.push(node.text());\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return [];\n }\n return captured;\n};\n\n/**\n * A unique token that is a valid identifier, so it can stand in for an array\n * element or object property inside a GritQL rewrite.\n */\nexport const GRIT_INSERT_PLACEHOLDER = '__GRIT_INSERT_PLACEHOLDER__';\n\n/**\n * Apply a GritQL rewrite that inserts {@link GRIT_INSERT_PLACEHOLDER}, then\n * replace the placeholder with `text`. Routing user-provided text through a\n * placeholder keeps it out of the GritQL pattern, where quotes, backticks or\n * `${...}` would otherwise break parsing.\n *\n * Any separator (e.g. a leading `, ` when appending to an array) must be part\n * of the GritQL pattern, not `text`. The one adjustment made here: if the\n * placeholder lands after a trailing line comment (`// ...`) — which would\n * comment out the text — the text is moved to its own line.\n *\n * The caller formats afterwards. Returns true if the pattern matched and the\n * file changed.\n */\nexport const insertViaGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n text: string,\n): Promise<boolean> => {\n if (!(await applyGritQL(tree, filePath, pattern))) return false;\n const content = tree.read(filePath)!.toString();\n const at = content.indexOf(GRIT_INSERT_PLACEHOLDER);\n const lineSoFar = content.slice(content.lastIndexOf('\\n', at) + 1, at);\n const replacement = lineSoFar.includes('//') ? `\\n${text}` : text;\n\n // Function replacer so `$` in `text` isn't treated as a replacement pattern.\n tree.write(\n filePath,\n content.replace(GRIT_INSERT_PLACEHOLDER, () => replacement),\n );\n return true;\n};\n"],"names":["QueryBuilder","path","updateGitIgnore","isEsmWorkspace","GRIT_DIR","normalizeModuleSpecifier","tree","from","isRelative","startsWith","endsWith","slice","length","ensureGritDir","process","env","GRIT_GLOBAL_DIR","join","root","patterns","assertFilePath","filePath","exists","Error","addDestructuredImport","variableNames","hasExistingImport","matchGritQL","variableName","localName","includes","split","applyGritQL","specifiers","contents","read","toString","newImport","newlineIndex","indexOf","write","substring","addPythonDestructuredImport","beforeContents","moduleAlreadyImported","appended","allAlreadyPresent","map","n","addSingleImport","alreadyImported","addStarExport","trim","alreadyExported","hasExportDeclaration","source","identifierName","pattern","q","result","applyToFile","content","query","matched","filter","captureGritQL","undefined","captured","node","text","captureGritQLVariable","name","var","captureAllGritQL","push","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","at","lineSoFar","lastIndexOf","replacement","replace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/ast.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { QueryBuilder } from '@getgrit/gritql';\nimport type { Tree } from '@nx/devkit';\nimport * as path from 'path';\nimport { updateGitIgnore } from './git';\nimport { isEsmWorkspace } from './module-format';\n\nconst GRIT_DIR = '.grit';\n\n/**\n * Normalize a relative module specifier for the workspace's module format.\n * ESM (`nodenext`) requires an explicit `.js` extension on relative imports;\n * CommonJS (`node16`) resolves them extensionless. Callers always pass the ESM\n * form (with `.js`); this strips the extension for CommonJS workspaces.\n */\nconst normalizeModuleSpecifier = (tree: Tree, from: string): string => {\n if (isEsmWorkspace(tree)) {\n return from;\n }\n const isRelative = from.startsWith('./') || from.startsWith('../');\n return isRelative && from.endsWith('.js')\n ? from.slice(0, -'.js'.length)\n : from;\n};\n\n// Pin the gritql native library's \"global\" stdlib directory to <workspace>/.grit so it doesn't try to write to /usr/local/.grit (or wherever node's grandparent resolves to).\nconst ensureGritDir = (tree: Tree) => {\n process.env.GRIT_GLOBAL_DIR ??= path.join(tree.root, GRIT_DIR);\n updateGitIgnore(tree, '.', (patterns) => [...patterns, GRIT_DIR]);\n};\n\nconst assertFilePath = (tree: Tree, filePath: string) => {\n if (!tree.exists(filePath)) {\n throw new Error(`No file located at ${filePath}`);\n }\n};\n\nexport const addDestructuredImport = async (\n tree: Tree,\n filePath: string,\n variableNames: string[],\n from: string,\n) => {\n assertFilePath(tree, filePath);\n from = normalizeModuleSpecifier(tree, from);\n\n // Check if there's an existing import from this module\n const hasExistingImport = await matchGritQL(\n tree,\n filePath,\n `\\`import { $_ } from '${from}'\\``,\n );\n\n if (hasExistingImport) {\n // For each new variable, use GritQL rewrite to add it if not already present\n for (const variableName of variableNames) {\n const localName = variableName.includes(' as ')\n ? variableName.split(' as ')[1]\n : variableName;\n // Use rewrite (=>) which correctly handles comma-separated import specifier lists\n await applyGritQL(\n tree,\n filePath,\n `\\`import { $imports } from '${from}'\\` => \\`import { $imports, ${variableName} } from '${from}'\\` where { $imports <: not contains \\`${localName}\\` }`,\n );\n }\n } else {\n // No existing import — prepend a new one (after shebang if present)\n const specifiers = variableNames.join(', ');\n const contents = tree.read(filePath)!.toString();\n const newImport = `import { ${specifiers} } from '${from}';\\n`;\n if (contents.startsWith('#!')) {\n const newlineIndex = contents.indexOf('\\n');\n tree.write(\n filePath,\n `${contents.substring(0, newlineIndex + 1)}${newImport}${contents.substring(newlineIndex + 1)}`,\n );\n } else {\n tree.write(filePath, `${newImport}${contents}`);\n }\n }\n};\n\n/**\n * Ensure a Python `from <module> import <name1>, <name2>, ...` statement\n * exists in the given file. If the module already has a `from <module> import`\n * line, any missing names are appended; otherwise a fresh import statement is\n * prepended to the file (ruff's import sorter will reorder it into place on\n * the next format pass).\n *\n * We try to append first (which only succeeds if an import-from for the\n * module already exists AND the name isn't yet in the list). If nothing\n * was rewritten after trying every requested name, we fall back to checking\n * whether the module is imported at all; if not, we prepend a fresh\n * `from <module> import ...` line.\n */\nexport const addPythonDestructuredImport = async (\n tree: Tree,\n filePath: string,\n variableNames: string[],\n from: string,\n) => {\n assertFilePath(tree, filePath);\n\n // Determine whether there's any `from <module> import ...` line in the file.\n // We detect this by attempting a no-op transformation — the file is unchanged,\n // but a successful match tells us the line exists.\n const beforeContents = tree.read(filePath)!.toString();\n let moduleAlreadyImported = false;\n for (const variableName of variableNames) {\n // Appends the name if the import-from exists and the name isn't already there.\n const appended = await applyGritQL(\n tree,\n filePath,\n `language python\\n\\`from ${from} import $names\\` where { $names <: not contains \\`${variableName}\\`, $names += \\`, ${variableName}\\` }`,\n );\n if (appended) {\n moduleAlreadyImported = true;\n }\n }\n\n // If the append succeeded for at least one name, we know the module line\n // exists; we're done. If nothing was appended we need to distinguish\n // between \"module not imported\" (need to add a new line) and \"module\n // imported but all names already present\" (no-op).\n if (moduleAlreadyImported) {\n return;\n }\n\n const allAlreadyPresent = await matchGritQL(\n tree,\n filePath,\n `language python\\n\\`from ${from} import $names\\` where { ${variableNames\n .map((n) => `$names <: contains \\`${n}\\``)\n .join(', ')} }`,\n );\n if (allAlreadyPresent) {\n return;\n }\n\n // No existing `from <module> import` line — prepend one. Ruff's import\n // sorter will place it in the right group on the next formatter pass.\n const specifiers = variableNames.join(', ');\n tree.write(filePath, `from ${from} import ${specifiers}\\n${beforeContents}`);\n};\n\n/**\n * Adds an `import <variableName> from '<from>'; statement to the beginning of the file,\n * if it doesn't already exist\n */\nexport const addSingleImport = async (\n tree: Tree,\n filePath: string,\n variableName: string,\n from: string,\n) => {\n assertFilePath(tree, filePath);\n from = normalizeModuleSpecifier(tree, from);\n\n // Check if default import already exists using GritQL\n const alreadyImported = await matchGritQL(\n tree,\n filePath,\n `\\`import ${variableName} from '${from}'\\``,\n );\n if (alreadyImported) {\n return;\n }\n\n // Prepend new import to file\n const contents = tree.read(filePath)!.toString();\n tree.write(filePath, `import ${variableName} from '${from}';\\n${contents}`);\n};\n\n/**\n * Adds an `export * from '<from>'; statement to the given TypeScript file.\n * Note that this will create the file if it does not exist in the tree.\n */\nexport const addStarExport = async (\n tree: Tree,\n filePath: string,\n from: string,\n) => {\n from = normalizeModuleSpecifier(tree, from);\n const contents = tree.read(filePath)?.toString() ?? '';\n\n // For empty/non-existent files, just write the export\n if (!contents.trim()) {\n tree.write(filePath, `export * from '${from}';\\n`);\n return;\n }\n\n // Check if already exported using GritQL\n const alreadyExported = await matchGritQL(\n tree,\n filePath,\n `\\`export * from '${from}'\\``,\n );\n if (alreadyExported) {\n return;\n }\n\n // Prepend new export to file\n tree.write(filePath, `export * from '${from}';\\n${contents}`);\n};\n\n/**\n * Return whether or not the given identifier is exported in the source file.\n * Checks for both `export { Identifier }` and `export type Identifier = ...`.\n */\nexport const hasExportDeclaration = async (\n tree: Tree,\n source: string,\n identifierName: string,\n): Promise<boolean> => {\n ensureGritDir(tree);\n const patterns = [\n `\\`export type ${identifierName} = $_\\``,\n `\\`export { ${identifierName} }\\``,\n `\\`export type { ${identifierName} } from $_\\``,\n ];\n\n for (const pattern of patterns) {\n try {\n const q = new QueryBuilder(`$p => $p where { $p <: ${pattern} }`);\n const result = await q.applyToFile({\n path: 'check.ts',\n content: source,\n });\n if (result !== null) return true;\n } catch {\n // Pattern didn't match, try next\n }\n }\n\n return false;\n};\n\n/**\n * Apply a GritQL pattern to a file in the Nx tree.\n */\nexport const applyGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<boolean> => {\n if (!tree.exists(filePath)) throw new Error(`No file at ${filePath}`);\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n const query = new QueryBuilder(pattern);\n const result = await query.applyToFile({ path: filePath, content: source });\n if (result && result.content !== source) {\n tree.write(filePath, result.content);\n return true;\n }\n return false;\n};\n\n/**\n * Check whether a GritQL pattern matches anywhere in a file.\n * Returns true if the pattern matches at least once.\n *\n * Accepts raw GritQL, including patterns that start with a `language <name>`\n * header (e.g. `language python\\n\\`print($_)\\``) — the pattern is passed\n * straight through to QueryBuilder without any wrapping rewrite.\n */\nexport const matchGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<boolean> => {\n if (!tree.exists(filePath)) return false;\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n let matched = false;\n try {\n const query = new QueryBuilder(pattern);\n query.filter(() => {\n matched = true;\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return false;\n }\n return matched;\n};\n\n/**\n * Capture the source text of the first match of a GritQL pattern.\n *\n * For example `` `dependencyCheck: $_` `` returns the whole\n * `dependencyCheck: { ... }` property text. Returns undefined if nothing\n * matches. The file is not modified.\n */\nexport const captureGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<string | undefined> => {\n if (!tree.exists(filePath)) return undefined;\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n let captured: string | undefined;\n try {\n const query = new QueryBuilder(pattern);\n query.filter((node) => {\n captured ??= node.text();\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return undefined;\n }\n return captured;\n};\n\n/**\n * Like `captureGritQL`, but returns one metavariable's binding (e.g. `mod`\n * for `$mod`) instead of the whole matched node.\n */\nexport const captureGritQLVariable = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n variableName: string,\n): Promise<string | undefined> => {\n if (!tree.exists(filePath)) return undefined;\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n // The underlying binding lookup requires the `$` sigil.\n const name = variableName.startsWith('$') ? variableName : `$${variableName}`;\n let captured: string | undefined;\n try {\n const query = new QueryBuilder(pattern);\n query.filter((node) => {\n captured ??= node.var(name) ?? undefined;\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return undefined;\n }\n return captured;\n};\n\n/**\n * Return the text of every node matching the GritQL pattern, in document order.\n */\nexport const captureAllGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n): Promise<string[]> => {\n if (!tree.exists(filePath)) return [];\n ensureGritDir(tree);\n const source = tree.read(filePath)!.toString();\n const captured: string[] = [];\n try {\n const query = new QueryBuilder(pattern);\n query.filter((node) => {\n captured.push(node.text());\n return true;\n });\n await query.applyToFile({ path: filePath, content: source });\n } catch {\n return [];\n }\n return captured;\n};\n\n/**\n * A unique token that is a valid identifier, so it can stand in for an array\n * element or object property inside a GritQL rewrite.\n */\nexport const GRIT_INSERT_PLACEHOLDER = '__GRIT_INSERT_PLACEHOLDER__';\n\n/**\n * Apply a GritQL rewrite that inserts {@link GRIT_INSERT_PLACEHOLDER}, then\n * replace the placeholder with `text`. Routing user-provided text through a\n * placeholder keeps it out of the GritQL pattern, where quotes, backticks or\n * `${...}` would otherwise break parsing.\n *\n * Any separator (e.g. a leading `, ` when appending to an array) must be part\n * of the GritQL pattern, not `text`. The one adjustment made here: if the\n * placeholder lands after a trailing line comment (`// ...`) — which would\n * comment out the text — the text is moved to its own line.\n *\n * The caller formats afterwards. Returns true if the pattern matched and the\n * file changed.\n */\nexport const insertViaGritQL = async (\n tree: Tree,\n filePath: string,\n pattern: string,\n text: string,\n): Promise<boolean> => {\n if (!(await applyGritQL(tree, filePath, pattern))) return false;\n const content = tree.read(filePath)!.toString();\n const at = content.indexOf(GRIT_INSERT_PLACEHOLDER);\n const lineSoFar = content.slice(content.lastIndexOf('\\n', at) + 1, at);\n const replacement = lineSoFar.includes('//') ? `\\n${text}` : text;\n\n // Function replacer so `$` in `text` isn't treated as a replacement pattern.\n tree.write(\n filePath,\n content.replace(GRIT_INSERT_PLACEHOLDER, () => replacement),\n );\n return true;\n};\n\n/**\n * Append `entry` to an array, skipping arrays that already contain it, e.g.\n * `` appendToArrayViaGritQL(tree, file, 'tools:', 'myTool', { scope: 'new Agent($_)' }) ``\n * turns `tools: [a, b]` into `tools: [a, b, myTool]`.\n *\n * `arrayPrefix` is whatever precedes the array literal, including its separator —\n * `'tools:'` for an object property, `'tools='` for a keyword argument or\n * assignment, `'\"tools\":'` for a quoted key. Which separator applies depends on\n * the construct rather than the language, so the caller states it.\n *\n * Appends after the last *element* rather than rewriting the element list with\n * `=>`. GritQL binds a `[$items]` list to its trailing comma too, so\n * `` `[$items]` => `[$items, entry]` `` emits the sparse array `[a, b, , entry]`\n * once the formatter has wrapped the array — a hole that is `undefined` at\n * runtime. (Accumulating with `$items += \", entry\"` is safe by comparison, since\n * it appends within the list rather than re-emitting it; both forms appear in\n * this repo.)\n *\n * `scope` limits the match to an enclosing pattern, so same-named arrays\n * elsewhere in the file are left alone. `skipIfContains` overrides what the\n * append dedupes on, for when the rendered entry is not the identifier to match.\n *\n * The caller formats afterwards. Returns true if the entry was appended.\n */\nexport const appendToArrayViaGritQL = async (\n tree: Tree,\n filePath: string,\n arrayPrefix: string,\n entry: string,\n options: { scope?: string; skipIfContains?: string; language?: 'py' } = {},\n): Promise<boolean> => {\n const { scope, skipIfContains, language } = options;\n const within = (variable: string) =>\n scope ? `${variable} <: within \\`${scope}\\`, ` : '';\n const pattern =\n `or {` +\n // An empty array has no last element to append after.\n ` \\`${arrayPrefix}$arr\\` where { $arr <: \\`[]\\`, ${within('$arr')}` +\n ` $arr => \\`[${GRIT_INSERT_PLACEHOLDER}]\\` },` +\n // Dedupe on the list, append after its last element.\n ` \\`${arrayPrefix}[$items]\\` where {` +\n ` $items <: not contains \\`${skipIfContains ?? entry}\\`, ${within('$items')}` +\n ` $items <: [$..., $last], $last += \\`, ${GRIT_INSERT_PLACEHOLDER}\\` }` +\n ` }`;\n\n return insertViaGritQL(\n tree,\n filePath,\n language === 'py' ? `language python\\n${pattern}` : pattern,\n entry,\n );\n};\n"],"names":["QueryBuilder","path","updateGitIgnore","isEsmWorkspace","GRIT_DIR","normalizeModuleSpecifier","tree","from","isRelative","startsWith","endsWith","slice","length","ensureGritDir","process","env","GRIT_GLOBAL_DIR","join","root","patterns","assertFilePath","filePath","exists","Error","addDestructuredImport","variableNames","hasExistingImport","matchGritQL","variableName","localName","includes","split","applyGritQL","specifiers","contents","read","toString","newImport","newlineIndex","indexOf","write","substring","addPythonDestructuredImport","beforeContents","moduleAlreadyImported","appended","allAlreadyPresent","map","n","addSingleImport","alreadyImported","addStarExport","trim","alreadyExported","hasExportDeclaration","source","identifierName","pattern","q","result","applyToFile","content","query","matched","filter","captureGritQL","undefined","captured","node","text","captureGritQLVariable","name","var","captureAllGritQL","push","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","at","lineSoFar","lastIndexOf","replacement","replace","appendToArrayViaGritQL","arrayPrefix","entry","options","scope","skipIfContains","language","within","variable"],"mappings":"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"}
|
package/src/utils/metrics.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/ import { joinPathFragments } from "@nx/devkit";
|
|
5
|
-
import { applyGritQL, captureAllGritQL } from "./ast.js";
|
|
5
|
+
import { appendToArrayViaGritQL, applyGritQL, captureAllGritQL } from "./ast.js";
|
|
6
6
|
import { formatFilesInSubtree } from "./format.js";
|
|
7
7
|
import { getPackageVersion } from "./nx.js";
|
|
8
8
|
import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "./shared-constructs-constants.js";
|
|
@@ -73,7 +73,7 @@ const readTerraformMetricTags = async (tree)=>(await captureAllGritQL(tree, TERR
|
|
|
73
73
|
await applyGritQL(tree, TERRAFORM_METRICS_FILE_PATH, `\`metric_version = $old\` => \`metric_version = "${getPackageVersion()}"\``);
|
|
74
74
|
// Add each tag, leaving existing tags in place to keep re-runs stable
|
|
75
75
|
for (const tag of tags){
|
|
76
|
-
await
|
|
76
|
+
await appendToArrayViaGritQL(tree, TERRAFORM_METRICS_FILE_PATH, 'metric_tags =', `"${tag}"`);
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
79
|
|
package/src/utils/metrics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/metrics.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { joinPathFragments, type Tree } from '@nx/devkit';\nimport { applyGritQL, captureAllGritQL } from './ast';\nimport { formatFilesInSubtree } from './format';\nimport { getPackageVersion, type NxGeneratorInfo } from './nx';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from './shared-constructs-constants';\n\n// Used to identify @aws/nx-plugin in AWS metrics\nexport const METRIC_ID = 'uksb-4wk0bqpg5s';\n\n// File in which the MetricsAspect may exist (CDK)\nexport const METRICS_ASPECT_FILE_PATH = joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'core',\n 'app.ts',\n);\n\n// File in which the Terraform metrics CloudFormation stack may exist\nexport const TERRAFORM_METRICS_FILE_PATH = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'metrics',\n 'metrics.tf',\n);\n\n// GritQL `within` clause to scope matches to the MetricsAspect class body\nconst WITHIN_METRICS_ASPECT =\n '$old <: within `class MetricsAspect implements $_ { $_ }`';\n\n// Strip the surrounding quotes from a captured string literal (eg 'g1' or \"g1\")\nconst unquote = (literal: string): string => literal.slice(1, -1);\n\n// Extracts the existing metric tags from the CDK MetricsAspect tags array\nconst readCdkMetricTags = async (tree: Tree): Promise<string[]> =>\n (\n await captureAllGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`$tag\\` where { $tag <: string(), $tag <: within \\`const tags: string[] = $_\\` }`,\n )\n ).map(unquote);\n\n// Extracts the existing metric tags from the Terraform metric_tags array\nconst readTerraformMetricTags = async (tree: Tree): Promise<string[]> =>\n (\n await captureAllGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n '`\"$tag\"` where { $tag <: within `metric_tags = $_` }',\n )\n ).map(unquote);\n\n/**\n * Instruments metrics by updating the MetricsAspect in common/constructs/src/core/app.ts if the file exists,\n * and/or updating the Terraform metrics CloudFormation stack if it exists.\n *\n * Both files are converged to the same tag set (the union of tags already\n * present in either file plus the incoming generators). This keeps the tag set\n * deterministic regardless of the order in which metrics files are created\n * across a multi-generator run, so re-running the same set of generators does\n * not grow the tags.\n */\nexport const addGeneratorMetricsIfApplicable = async (\n tree: Tree,\n generatorInfo: NxGeneratorInfo[],\n) => {\n const cdkExists = tree.exists(METRICS_ASPECT_FILE_PATH);\n const terraformExists = tree.exists(TERRAFORM_METRICS_FILE_PATH);\n\n if (!cdkExists && !terraformExists) {\n return;\n }\n\n // Converge both files to the union of all known tags so the set is stable\n // regardless of when each metrics file was created.\n const tags = [\n ...new Set([\n ...(await readCdkMetricTags(tree)),\n ...(await readTerraformMetricTags(tree)),\n ...generatorInfo.map((info) => info.metric),\n ]),\n ];\n\n if (cdkExists) {\n await updateCdkMetrics(tree, tags);\n }\n\n if (terraformExists) {\n await updateTerraformMetrics(tree, tags);\n }\n};\n\n/**\n * Updates the CDK MetricsAspect with the metric id, version and tags\n */\nconst updateCdkMetrics = async (tree: Tree, tags: string[]) => {\n // Update the id\n await applyGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`const id = $old\\` => \\`const id = '${METRIC_ID}'\\`` +\n ` where { ${WITHIN_METRICS_ASPECT} }`,\n );\n\n // Update the version\n await applyGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`const version = $old\\` => \\`const version = '${getPackageVersion()}'\\`` +\n ` where { ${WITHIN_METRICS_ASPECT} }`,\n );\n\n // Add each tag, leaving existing tags in place to keep re-runs stable\n for (const tag of tags) {\n await applyGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`const tags: string[] = $old\\`` +\n ` where { ${WITHIN_METRICS_ASPECT},` +\n ` if ($old <: \\`[]\\`) { $old => \\`['${tag}']\\` }` +\n ` else { $old <: \\`[$items]\\` where { $items += \\`, '${tag}'\\` } },` +\n ` $old <: not contains \\`'${tag}'\\` }`,\n );\n }\n\n await formatFilesInSubtree(tree, METRICS_ASPECT_FILE_PATH);\n};\n\n/**\n * Updates the Terraform metrics CloudFormation stack with the metric id, version and tags\n */\nconst updateTerraformMetrics = async (tree: Tree, tags: string[]) => {\n // Update metric_id\n await applyGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n `\\`metric_id = $old\\` => \\`metric_id = \"${METRIC_ID}\"\\``,\n );\n\n // Update metric_version\n await applyGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n `\\`metric_version = $old\\` => \\`metric_version = \"${getPackageVersion()}\"\\``,\n );\n\n // Add each tag, leaving existing tags in place to keep re-runs stable\n for (const tag of tags) {\n await
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/utils/metrics.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { joinPathFragments, type Tree } from '@nx/devkit';\nimport { appendToArrayViaGritQL, applyGritQL, captureAllGritQL } from './ast';\nimport { formatFilesInSubtree } from './format';\nimport { getPackageVersion, type NxGeneratorInfo } from './nx';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from './shared-constructs-constants';\n\n// Used to identify @aws/nx-plugin in AWS metrics\nexport const METRIC_ID = 'uksb-4wk0bqpg5s';\n\n// File in which the MetricsAspect may exist (CDK)\nexport const METRICS_ASPECT_FILE_PATH = joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'core',\n 'app.ts',\n);\n\n// File in which the Terraform metrics CloudFormation stack may exist\nexport const TERRAFORM_METRICS_FILE_PATH = joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'metrics',\n 'metrics.tf',\n);\n\n// GritQL `within` clause to scope matches to the MetricsAspect class body\nconst WITHIN_METRICS_ASPECT =\n '$old <: within `class MetricsAspect implements $_ { $_ }`';\n\n// Strip the surrounding quotes from a captured string literal (eg 'g1' or \"g1\")\nconst unquote = (literal: string): string => literal.slice(1, -1);\n\n// Extracts the existing metric tags from the CDK MetricsAspect tags array\nconst readCdkMetricTags = async (tree: Tree): Promise<string[]> =>\n (\n await captureAllGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`$tag\\` where { $tag <: string(), $tag <: within \\`const tags: string[] = $_\\` }`,\n )\n ).map(unquote);\n\n// Extracts the existing metric tags from the Terraform metric_tags array\nconst readTerraformMetricTags = async (tree: Tree): Promise<string[]> =>\n (\n await captureAllGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n '`\"$tag\"` where { $tag <: within `metric_tags = $_` }',\n )\n ).map(unquote);\n\n/**\n * Instruments metrics by updating the MetricsAspect in common/constructs/src/core/app.ts if the file exists,\n * and/or updating the Terraform metrics CloudFormation stack if it exists.\n *\n * Both files are converged to the same tag set (the union of tags already\n * present in either file plus the incoming generators). This keeps the tag set\n * deterministic regardless of the order in which metrics files are created\n * across a multi-generator run, so re-running the same set of generators does\n * not grow the tags.\n */\nexport const addGeneratorMetricsIfApplicable = async (\n tree: Tree,\n generatorInfo: NxGeneratorInfo[],\n) => {\n const cdkExists = tree.exists(METRICS_ASPECT_FILE_PATH);\n const terraformExists = tree.exists(TERRAFORM_METRICS_FILE_PATH);\n\n if (!cdkExists && !terraformExists) {\n return;\n }\n\n // Converge both files to the union of all known tags so the set is stable\n // regardless of when each metrics file was created.\n const tags = [\n ...new Set([\n ...(await readCdkMetricTags(tree)),\n ...(await readTerraformMetricTags(tree)),\n ...generatorInfo.map((info) => info.metric),\n ]),\n ];\n\n if (cdkExists) {\n await updateCdkMetrics(tree, tags);\n }\n\n if (terraformExists) {\n await updateTerraformMetrics(tree, tags);\n }\n};\n\n/**\n * Updates the CDK MetricsAspect with the metric id, version and tags\n */\nconst updateCdkMetrics = async (tree: Tree, tags: string[]) => {\n // Update the id\n await applyGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`const id = $old\\` => \\`const id = '${METRIC_ID}'\\`` +\n ` where { ${WITHIN_METRICS_ASPECT} }`,\n );\n\n // Update the version\n await applyGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`const version = $old\\` => \\`const version = '${getPackageVersion()}'\\`` +\n ` where { ${WITHIN_METRICS_ASPECT} }`,\n );\n\n // Add each tag, leaving existing tags in place to keep re-runs stable\n for (const tag of tags) {\n await applyGritQL(\n tree,\n METRICS_ASPECT_FILE_PATH,\n `\\`const tags: string[] = $old\\`` +\n ` where { ${WITHIN_METRICS_ASPECT},` +\n ` if ($old <: \\`[]\\`) { $old => \\`['${tag}']\\` }` +\n ` else { $old <: \\`[$items]\\` where { $items += \\`, '${tag}'\\` } },` +\n ` $old <: not contains \\`'${tag}'\\` }`,\n );\n }\n\n await formatFilesInSubtree(tree, METRICS_ASPECT_FILE_PATH);\n};\n\n/**\n * Updates the Terraform metrics CloudFormation stack with the metric id, version and tags\n */\nconst updateTerraformMetrics = async (tree: Tree, tags: string[]) => {\n // Update metric_id\n await applyGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n `\\`metric_id = $old\\` => \\`metric_id = \"${METRIC_ID}\"\\``,\n );\n\n // Update metric_version\n await applyGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n `\\`metric_version = $old\\` => \\`metric_version = \"${getPackageVersion()}\"\\``,\n );\n\n // Add each tag, leaving existing tags in place to keep re-runs stable\n for (const tag of tags) {\n await appendToArrayViaGritQL(\n tree,\n TERRAFORM_METRICS_FILE_PATH,\n 'metric_tags =',\n `\"${tag}\"`,\n );\n }\n};\n"],"names":["joinPathFragments","appendToArrayViaGritQL","applyGritQL","captureAllGritQL","formatFilesInSubtree","getPackageVersion","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","METRIC_ID","METRICS_ASPECT_FILE_PATH","TERRAFORM_METRICS_FILE_PATH","WITHIN_METRICS_ASPECT","unquote","literal","slice","readCdkMetricTags","tree","map","readTerraformMetricTags","addGeneratorMetricsIfApplicable","generatorInfo","cdkExists","exists","terraformExists","tags","Set","info","metric","updateCdkMetrics","updateTerraformMetrics","tag"],"mappings":"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"}
|