diff --git a/.github/workflows/sync-agt-marketplace.yml b/.github/workflows/sync-agt-marketplace.yml new file mode 100644 index 0000000..51d6a6c --- /dev/null +++ b/.github/workflows/sync-agt-marketplace.yml @@ -0,0 +1,46 @@ +name: Sync AGT Marketplace snapshot +on: + schedule: + - cron: "17 7 * * *" + workflow_dispatch: +permissions: + contents: write + pull-requests: write +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: true + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: microsoft/agent-governance-toolkit + ref: main + fetch-depth: 1 + path: .agt-source + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + - name: Generate and verify snapshot + run: | + python scripts/generate_agt_marketplace_catalog.py --agt-root .agt-source + python -m pytest tests/test_generate_agt_marketplace_catalog.py -q + - name: Open refresh pull request + env: + GH_TOKEN: ${{ github.token }} + run: | + if git diff --quiet -- marketplace/agt-catalog.json; then + echo "AGT snapshot is current" + exit 0 + fi + branch=automation/agt-marketplace-snapshot + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git fetch origin "$branch:refs/remotes/origin/$branch" || true + git switch -C "$branch" + git add marketplace/agt-catalog.json + git commit -s -m "chore: refresh AGT marketplace snapshot" + git push --force-with-lease origin "$branch" + gh pr view "$branch" >/dev/null 2>&1 || gh pr create --base main --head "$branch" --title "chore: refresh AGT marketplace snapshot" --body "Automated, commit-pinned refresh of the Microsoft AGT integration inventory. Generated from package metadata and README headings; no AGT repository change is required." diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 10de540..9291a3e 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -42,3 +42,5 @@ jobs: run: | python -m pytest tests/test_generate_marketplace_catalog.py -q python scripts/generate_marketplace_catalog.py --check + - name: Test AGT Marketplace snapshot generation + run: python -m pytest tests/test_generate_agt_marketplace_catalog.py -q diff --git a/marketplace/agt-catalog.json b/marketplace/agt-catalog.json new file mode 100644 index 0000000..90cf957 --- /dev/null +++ b/marketplace/agt-catalog.json @@ -0,0 +1,514 @@ +{ + "catalog_version": 1, + "source": "https://github.com/microsoft/agent-governance-toolkit", + "source_commit": "b5705588883fac48b88cbe6fd0bd7d48c798453e", + "count": 22, + "integrations": [ + { + "name": "A2A AgentMesh", + "package_name": "a2a_agentmesh", + "vendor": "Microsoft", + "description": "A2A protocol bridge for AgentMesh — trust-verified agent-to-agent communication via the A2A standard", + "path": "agent-governance-python/agentmesh-integrations/a2a-protocol", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/a2a-protocol", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "AA", + "featured": null, + "keywords": [ + "a2a-protocol", + "a2a_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "ADK AgentMesh", + "package_name": "adk_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as adk-agentmesh. Install agent-governance-toolkit-integrations[adk] instead.", + "path": "agent-governance-python/agentmesh-integrations/adk-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/adk-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "AA", + "featured": null, + "keywords": [ + "adk-agentmesh", + "adk_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Agent Veil Protocol Integration for AgentMesh", + "package_name": "avp_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as avp-agentmesh. Install agent-governance-toolkit-integrations[avp] instead.", + "path": "agent-governance-python/agentmesh-integrations/agentmesh-avp", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/agentmesh-avp", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "AV", + "featured": null, + "keywords": [ + "agentmesh-avp", + "avp_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "AgentMesh Trust Extension for Dify", + "package_name": "dify", + "vendor": "Microsoft", + "description": "Cryptographic identity and trust verification for Dify agents and workflows.", + "path": "agent-governance-python/agentmesh-integrations/dify", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/dify", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "AT", + "featured": null, + "keywords": [ + "dify", + "dify", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Audit Accountability Export", + "package_name": "agentmesh_audit_export", + "vendor": "Microsoft", + "description": "Example adapter from AgentMesh AuditEntry output to an external accountability export shape", + "path": "agent-governance-python/agentmesh-integrations/audit-accountability-export", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/audit-accountability-export", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "AA", + "featured": null, + "keywords": [ + "audit-accountability-export", + "agentmesh_audit_export", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Copilot Governance", + "package_name": "@microsoft/agentmesh-copilot-governance", + "vendor": "Microsoft", + "description": "Public Preview — GitHub Copilot Extension for agent governance code review: detects missing policy checks, unguarded tool calls, and audit logging gaps", + "path": "agent-governance-python/agentmesh-integrations/copilot-governance", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/copilot-governance", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "CG", + "featured": null, + "keywords": [ + "copilot-governance", + "@microsoft/agentmesh-copilot-governance", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "CrewAI AgentMesh", + "package_name": "crewai_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as crewai-agentmesh. Install agent-governance-toolkit-integrations[crewai] instead.", + "path": "agent-governance-python/agentmesh-integrations/crewai-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/crewai-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "CA", + "featured": null, + "keywords": [ + "crewai-agentmesh", + "crewai_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Flowise", + "package_name": "flowise_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as flowise-agentmesh. Install agent-governance-toolkit-integrations[flowise] instead.", + "path": "agent-governance-python/agentmesh-integrations/flowise-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/flowise-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "F", + "featured": null, + "keywords": [ + "flowise-agentmesh", + "flowise_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Haystack", + "package_name": "haystack_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as haystack-agentmesh. Install agent-governance-toolkit-integrations[haystack] instead.", + "path": "agent-governance-python/agentmesh-integrations/haystack-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/haystack-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "H", + "featured": null, + "keywords": [ + "haystack-agentmesh", + "haystack_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "LangChain AgentMesh Integration", + "package_name": "agentmesh_langchain", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as agentmesh-langchain. Install agent-governance-toolkit-integrations[langchain] instead.", + "path": "agent-governance-python/agentmesh-integrations/langchain-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/langchain-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "LA", + "featured": null, + "keywords": [ + "langchain-agentmesh", + "agentmesh_langchain", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Langflow", + "package_name": "langflow_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as langflow-agentmesh. Install agent-governance-toolkit-integrations[langflow] instead.", + "path": "agent-governance-python/agentmesh-integrations/langflow-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/langflow-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "L", + "featured": null, + "keywords": [ + "langflow-agentmesh", + "langflow_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Langgraph Trust", + "package_name": "langgraph_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as langgraph-agentmesh. Install agent-governance-toolkit-integrations[langgraph] instead.", + "path": "agent-governance-python/agentmesh-integrations/langgraph-trust", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/langgraph-trust", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "LT", + "featured": null, + "keywords": [ + "langgraph-trust", + "langgraph_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "LlamaIndex", + "package_name": "llamaindex_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as llamaindex-agentmesh. Install agent-governance-toolkit-integrations[llamaindex] instead.", + "path": "agent-governance-python/agentmesh-integrations/llamaindex-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/llamaindex-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "L", + "featured": null, + "keywords": [ + "llamaindex-agentmesh", + "llamaindex_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Mastra", + "package_name": "@microsoft/agentmesh-mastra", + "vendor": "Microsoft", + "description": "Public Preview — Governance, trust verification, and audit middleware for Mastra AI agents", + "path": "agent-governance-python/agentmesh-integrations/mastra-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/mastra-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "M", + "featured": null, + "keywords": [ + "mastra-agentmesh", + "@microsoft/agentmesh-mastra", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "MCP Receipt Governed", + "package_name": "agentmesh_mcp_receipts", + "vendor": "Microsoft", + "description": "MCP tool-call receipt signing — Cedar policy decisions linked to governance receipts", + "path": "agent-governance-python/agentmesh-integrations/mcp-receipt-governed", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/mcp-receipt-governed", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "MR", + "featured": null, + "keywords": [ + "mcp-receipt-governed", + "agentmesh_mcp_receipts", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "MCP Trust Proxy", + "package_name": "agentmesh_mcp_proxy", + "vendor": "Microsoft", + "description": "MCP proxy that wraps any MCP tool with AgentMesh trust verification", + "path": "agent-governance-python/agentmesh-integrations/mcp-trust-proxy", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/mcp-trust-proxy", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "MT", + "featured": null, + "keywords": [ + "mcp-trust-proxy", + "agentmesh_mcp_proxy", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Nostr Web of Trust Integration for AgentMesh", + "package_name": "nostr_wot_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as nostr-wot-agentmesh. Install agent-governance-toolkit-integrations[nostr-wot] instead.", + "path": "agent-governance-python/agentmesh-integrations/nostr-wot", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/nostr-wot", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "NW", + "featured": null, + "keywords": [ + "nostr-wot", + "nostr_wot_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "OpenAI Agents AgentMesh", + "package_name": "openai_agents_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as openai-agents-agentmesh. Install agent-governance-toolkit-integrations[openai-agents] instead.", + "path": "agent-governance-python/agentmesh-integrations/openai-agents-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/openai-agents-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "OA", + "featured": null, + "keywords": [ + "openai-agents-agentmesh", + "openai_agents_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "OpenAI Agents Trust", + "package_name": "agentmesh_openai_agents_trust", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as agentmesh-openai-agents-trust. Install agent-governance-toolkit-integrations[openai-agents] instead.", + "path": "agent-governance-python/agentmesh-integrations/openai-agents-trust", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/openai-agents-trust", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "OA", + "featured": null, + "keywords": [ + "openai-agents-trust", + "agentmesh_openai_agents_trust", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Openshell", + "package_name": "openshell-agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as openshell-agentmesh. Install agent-governance-toolkit-integrations[openshell] instead.", + "path": "agent-governance-python/agentmesh-integrations/openshell-skill", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/openshell-skill", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "O", + "featured": null, + "keywords": [ + "openshell-skill", + "openshell-agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Pydantic AI Governance", + "package_name": "pydantic_ai_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as pydantic-ai-agentmesh. Install agent-governance-toolkit-integrations[pydantic-ai] instead.", + "path": "agent-governance-python/agentmesh-integrations/pydantic-ai-governance", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/pydantic-ai-governance", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "PA", + "featured": null, + "keywords": [ + "pydantic-ai-governance", + "pydantic_ai_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + }, + { + "name": "Structural Authorization Gates", + "package_name": "structural_authz_agentmesh", + "vendor": "Microsoft", + "description": "Deprecated. Previously published as structural-authz-agentmesh. Install agent-governance-toolkit-integrations[structural-authz] instead.", + "path": "agent-governance-python/agentmesh-integrations/structural-authz-agentmesh", + "url": "https://github.com/microsoft/agent-governance-toolkit/tree/b5705588883fac48b88cbe6fd0bd7d48c798453e/agent-governance-python/agentmesh-integrations/structural-authz-agentmesh", + "homepage": null, + "repository": "https://github.com/microsoft/agent-governance-toolkit", + "tier": "project", + "stack": [ + "AGT" + ], + "category": "AGT integrations", + "mark": "SA", + "featured": null, + "keywords": [ + "structural-authz-agentmesh", + "structural_authz_agentmesh", + "Agent Governance Toolkit", + "AgentMesh" + ] + } + ] +} diff --git a/scripts/generate_agt_marketplace_catalog.py b/scripts/generate_agt_marketplace_catalog.py new file mode 100644 index 0000000..8ee2ba8 --- /dev/null +++ b/scripts/generate_agt_marketplace_catalog.py @@ -0,0 +1,119 @@ +"""Generate a deterministic, commit-pinned snapshot of AGT integrations.""" +from __future__ import annotations +import argparse +import json +import re +import subprocess +import tomllib +from pathlib import Path + +CATALOG_VERSION = 1 +AGT_REPOSITORY = "https://github.com/microsoft/agent-governance-toolkit" +INTEGRATIONS_PATH = Path("agent-governance-python/agentmesh-integrations") +EXCLUDED_DIRECTORIES = {"template-agentmesh"} + +def source_commit(root: Path) -> str: + result = subprocess.run(["git", "-C", str(root), "rev-parse", "HEAD"], check=True, capture_output=True, text=True) + commit = result.stdout.strip() + if not re.fullmatch(r"[0-9a-f]{40}", commit): + raise ValueError(f"invalid AGT source commit: {commit!r}") + return commit + +def project_metadata(directory: Path) -> tuple[str, str]: + pyproject, package_json = directory / "pyproject.toml", directory / "package.json" + if pyproject.is_file(): + project = tomllib.loads(pyproject.read_text(encoding="utf-8")).get("project", {}) + elif package_json.is_file(): + project = json.loads(package_json.read_text(encoding="utf-8")) + else: + readme = (directory / "README.md").read_text(encoding="utf-8") + paragraphs = re.split(r"\n\s*\n", readme) + description = next((part.strip() for part in paragraphs[1:] if part.strip() and not part.lstrip().startswith(("[![", "##", "```"))), "") + project = {"name": directory.name, "description": re.sub(r"\s+", " ", description)} + name, description = project.get("name"), project.get("description") + if not isinstance(name, str) or not name.strip() or not isinstance(description, str) or not description.strip(): + raise ValueError(f"{directory.name}: package name and description are required") + return name.strip(), description.strip() + +def display_name(directory: Path) -> str: + readme = directory / "README.md" + if not readme.is_file(): + raise ValueError(f"{directory.name}: missing README.md") + match = re.search(r"^#\s+(.+?)(?=##|\r?$)", readme.read_text(encoding="utf-8"), re.MULTILINE) + if not match: + raise ValueError(f"{directory.name}: README.md needs an H1 display name") + heading = re.sub(r"\s*(?:—|\|)\s+.*$", "", match.group(1)).strip() + if len(heading) > 100: + heading = directory.name + if heading.startswith("@") or (" " not in heading and ("-" in heading or "_" in heading)): + heading = heading.rsplit("/", 1)[-1] + heading = re.sub(r"^(?:agentmesh[-_])|(?:[-_]agentmesh)$", "", heading, flags=re.IGNORECASE) + heading = " ".join(word.capitalize() for word in re.split(r"[-_]", heading)) + replacements = {"Ai": "AI", "A2a": "A2A", "Adk": "ADK", "Api": "API", "Avp": "AVP", "Llamaindex": "LlamaIndex", "Mcp": "MCP", "Openai": "OpenAI"} + heading = " ".join(replacements.get(word, word) for word in heading.split()) + return heading + +def mark(name: str) -> str: + words = re.findall(r"[A-Za-z0-9]+", name) + return "".join(word[0] for word in words[:2]).upper() + +def build_catalog(agt_root: Path, commit: str | None = None) -> dict: + commit = commit or source_commit(agt_root) + if not re.fullmatch(r"[0-9a-f]{40}", commit): + raise ValueError("source commit must be a full lowercase Git SHA") + collection = agt_root / INTEGRATIONS_PATH + if not collection.is_dir(): + raise ValueError(f"AGT integration collection not found at {collection}") + integrations = [] + for directory in sorted(collection.iterdir(), key=lambda path: path.name.casefold()): + if not directory.is_dir() or directory.name.startswith(".") or directory.name in EXCLUDED_DIRECTORIES: + continue + package_name, description = project_metadata(directory) + name = display_name(directory) + path = (INTEGRATIONS_PATH / directory.name).as_posix() + integrations.append({ + "name": name, "package_name": package_name, "vendor": "Microsoft", "description": description, + "path": path, "url": f"{AGT_REPOSITORY}/tree/{commit}/{path}", "homepage": None, + "repository": AGT_REPOSITORY, "tier": "project", "stack": ["AGT"], + "category": "AGT integrations", "mark": mark(name), "featured": None, + "keywords": [directory.name, package_name, "Agent Governance Toolkit", "AgentMesh"], + }) + if not integrations: + raise ValueError("no AGT integrations discovered") + integrations.sort(key=lambda item: item["name"].casefold()) + return {"catalog_version": CATALOG_VERSION, "source": AGT_REPOSITORY, "source_commit": commit, "count": len(integrations), "integrations": integrations} + +def validate_catalog(catalog: dict) -> None: + entries = catalog.get("integrations") + if catalog.get("catalog_version") != CATALOG_VERSION or not isinstance(entries, list): + raise ValueError("unsupported AGT catalog") + if catalog.get("count") != len(entries): + raise ValueError("AGT catalog count does not match its entries") + commit = catalog.get("source_commit", "") + if not re.fullmatch(r"[0-9a-f]{40}", commit): + raise ValueError("AGT catalog has an invalid source commit") + for field in ("name", "package_name", "url"): + values = [entry.get(field) for entry in entries] + if any(not isinstance(value, str) or not value for value in values) or len(values) != len(set(values)): + raise ValueError(f"AGT catalog has missing or duplicate {field}") + if any(f"/tree/{commit}/" not in entry["url"] for entry in entries): + raise ValueError("every AGT integration URL must be pinned to source_commit") + +def render(agt_root: Path, commit: str | None = None) -> str: + catalog = build_catalog(agt_root, commit) + validate_catalog(catalog) + return json.dumps(catalog, indent=2, ensure_ascii=False) + "\n" + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--agt-root", type=Path, required=True) + parser.add_argument("--output", type=Path, default=Path(__file__).resolve().parents[1] / "marketplace" / "agt-catalog.json") + args = parser.parse_args() + output = args.output.resolve() + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(render(args.agt_root.resolve()).encode("utf-8")) + print(f"wrote {output}") + return 0 + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_generate_agt_marketplace_catalog.py b/tests/test_generate_agt_marketplace_catalog.py new file mode 100644 index 0000000..7d17a50 --- /dev/null +++ b/tests/test_generate_agt_marketplace_catalog.py @@ -0,0 +1,52 @@ +import json +from pathlib import Path +import pytest +from scripts.generate_agt_marketplace_catalog import build_catalog, render, validate_catalog +SHA = "a" * 40 + +def integration(root: Path, slug: str, heading: str, name: str, description: str) -> None: + directory = root / "agent-governance-python" / "agentmesh-integrations" / slug + directory.mkdir(parents=True) + (directory / "README.md").write_text(f"# {heading}\n", encoding="utf-8") + (directory / "pyproject.toml").write_text(f'[project]\nname = "{name}"\ndescription = "{description}"\n', encoding="utf-8") + +def test_catalog_is_complete_sorted_and_commit_pinned(tmp_path): + integration(tmp_path, "zulu", "Zulu Adapter — extra", "zulu-package", "Zulu description") + integration(tmp_path, "alpha", "Alpha Adapter", "alpha-package", "Alpha description") + integration(tmp_path, "template-agentmesh", "Template", "template", "Not a listing") + catalog = build_catalog(tmp_path, SHA) + assert catalog["count"] == 2 + assert [item["name"] for item in catalog["integrations"]] == ["Alpha Adapter", "Zulu Adapter"] + assert all(f"/tree/{SHA}/" in item["url"] for item in catalog["integrations"]) + assert json.loads(render(tmp_path, SHA)) == catalog + +def test_package_json_metadata_is_supported(tmp_path): + directory = tmp_path / "agent-governance-python" / "agentmesh-integrations" / "typescript" + directory.mkdir(parents=True) + (directory / "README.md").write_text("# TypeScript Adapter\n", encoding="utf-8") + (directory / "package.json").write_text(json.dumps({"name": "@agentmesh/typescript", "description": "TypeScript description"}), encoding="utf-8") + assert build_catalog(tmp_path, SHA)["integrations"][0]["package_name"] == "@agentmesh/typescript" + +def test_scoped_package_heading_is_presented_as_a_marketplace_name(tmp_path): + integration(tmp_path, "copilot", "@microsoft/agentmesh-copilot-governance", "package", "Description") + assert build_catalog(tmp_path, SHA)["integrations"][0]["name"] == "Copilot Governance" + +def test_readme_metadata_fallback_supports_unpackaged_integration(tmp_path): + directory = tmp_path / "agent-governance-python" / "agentmesh-integrations" / "readme-only" + directory.mkdir(parents=True) + (directory / "README.md").write_text("# README Only\n\nA useful integration.\n", encoding="utf-8") + item = build_catalog(tmp_path, SHA)["integrations"][0] + assert item["package_name"] == "readme-only" + assert item["description"] == "A useful integration." + +def test_validator_rejects_duplicate_and_unpinned_entries(tmp_path): + integration(tmp_path, "alpha", "Alpha", "alpha", "Description") + catalog = build_catalog(tmp_path, SHA) + catalog["integrations"].append(dict(catalog["integrations"][0])) + catalog["count"] = 2 + with pytest.raises(ValueError, match="duplicate name"): + validate_catalog(catalog) + catalog = build_catalog(tmp_path, SHA) + catalog["integrations"][0]["url"] = "https://example.com/main/alpha" + with pytest.raises(ValueError, match="pinned"): + validate_catalog(catalog)