@agentmarkup/astro 0.4.0 → 0.5.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @agentmarkup/astro
2
2
 
3
- Build-time `llms.txt`, optional `llms-full.txt`, JSON-LD, markdown mirrors, AI crawler `robots.txt`, headers, and validation for Astro websites.
3
+ Build-time `llms.txt`, optional `llms-full.txt`, optional A2A Agent Cards, JSON-LD, markdown mirrors, AI crawler `robots.txt`, headers, and validation for Astro websites.
4
4
 
5
5
  `@agentmarkup/astro` is the Astro adapter in the `agentmarkup` package family. Framework-agnostic helpers live in `@agentmarkup/core`, Vite sites use `@agentmarkup/vite`, and Next.js sites use `@agentmarkup/next`.
6
6
 
@@ -91,6 +91,7 @@ export default defineConfig({
91
91
 
92
92
  ## What It Does
93
93
 
94
+ - Generates optional `/.well-known/agent-card.json` for existing A2A services
94
95
  - Injects JSON-LD into built HTML pages during the Astro build
95
96
  - Generates `/llms.txt` from config
96
97
  - Generates optional `/llms-full.txt` with inlined same-site markdown content
@@ -114,6 +115,8 @@ When markdown mirrors are enabled, same-site page entries in `llms.txt` automati
114
115
 
115
116
  Enable `llmsFullTxt` when you want a richer companion file for agents that can consume more than the compact `llms.txt` manifest. The generated `llms-full.txt` keeps the same section structure but inlines same-site markdown mirror content when those mirrors exist.
116
117
 
118
+ Enable `agentCard` when you already run a real A2A-compatible agent service and want Astro builds to publish its discovery file at `/.well-known/agent-card.json`. agentmarkup only emits and validates the static Agent Card. It does not implement the A2A runtime server or transport endpoints for you. When enabled, provide a `version`, at least one `supportedInterfaces` entry, and a non-empty description through either the top-level `description` or `agentCard.description`.
119
+
117
120
  ## Maintainer
118
121
 
119
122
  Copyright (c) 2026 [Sebastian Cochinescu](https://www.cochinescu.com). MIT License.
package/dist/index.js CHANGED
@@ -1,11 +1,13 @@
1
1
  // src/index.ts
2
2
  import { existsSync } from "fs";
3
- import { readdir, readFile, writeFile } from "fs/promises";
4
- import { join, relative } from "path";
3
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
4
+ import { dirname, join, relative } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import {
7
+ A2A_AGENT_CARD_FILE_NAME,
7
8
  collectSchemasForPage,
8
9
  filterJsonLdByExistingTypes,
10
+ generateAgentCard,
9
11
  generateLlmsFullTxt,
10
12
  generateLlmsTxtDiscoveryLink,
11
13
  generateMarkdownAlternateLink,
@@ -24,6 +26,8 @@ import {
24
26
  presetToJsonLd,
25
27
  printReport,
26
28
  resolveLlmsTxtSections,
29
+ validateAgentCardConfig,
30
+ validateAgentCardJson,
27
31
  validateExistingJsonLd,
28
32
  validateHtmlContent,
29
33
  validateLlmsTxt,
@@ -36,6 +40,7 @@ import {
36
40
  export * from "@agentmarkup/core";
37
41
  function agentmarkup(config) {
38
42
  const validationResults = [];
43
+ let agentCardStatus = "none";
39
44
  let llmsTxtEntries = 0;
40
45
  let llmsTxtSections = 0;
41
46
  let llmsTxtStatus = "none";
@@ -63,6 +68,7 @@ function agentmarkup(config) {
63
68
  const markdownByUrl = {};
64
69
  const availableMarkdownUrls = /* @__PURE__ */ new Set();
65
70
  const finalHtmlByFile = /* @__PURE__ */ new Map();
71
+ const shouldManageAgentCard = Boolean(config.agentCard) && config.agentCard?.enabled !== false;
66
72
  const advertiseLlmsTxt = Boolean(config.llmsTxt) || Boolean(publicDir && existsSync(join(publicDir, "llms.txt")));
67
73
  for (const htmlFile of htmlFiles) {
68
74
  const pagePath = pagePathFromOutputFile(outDir, htmlFile);
@@ -195,6 +201,39 @@ function agentmarkup(config) {
195
201
  );
196
202
  }
197
203
  }
204
+ if (shouldManageAgentCard) {
205
+ const outputAgentCardPath = join(outDir, A2A_AGENT_CARD_FILE_NAME);
206
+ const existingOutputAgentCard = await readTextFileIfExists(outputAgentCardPath);
207
+ const existingAgentCard = existingOutputAgentCard ?? (publicDir ? await readTextFileIfExists(join(publicDir, A2A_AGENT_CARD_FILE_NAME)) : null);
208
+ if (existingAgentCard && !config.agentCard?.replaceExisting) {
209
+ agentCardStatus = "preserved";
210
+ if (!existingOutputAgentCard) {
211
+ await writeTextFile(outputAgentCardPath, existingAgentCard);
212
+ }
213
+ if (!config.validation?.disabled) {
214
+ validationResults.push(...validateAgentCardJson(existingAgentCard));
215
+ }
216
+ } else {
217
+ const agentCardConfigIssues = validateAgentCardConfig(
218
+ config,
219
+ `/${A2A_AGENT_CARD_FILE_NAME}`
220
+ );
221
+ if (!config.validation?.disabled) {
222
+ validationResults.push(...agentCardConfigIssues);
223
+ }
224
+ if (!agentCardConfigIssues.some((result) => result.severity === "error")) {
225
+ const agentCardContent = generateAgentCard(config);
226
+ if (!agentCardContent) {
227
+ throw new Error("Agent Card generation returned no output for a valid config");
228
+ }
229
+ agentCardStatus = "generated";
230
+ await writeTextFile(outputAgentCardPath, agentCardContent);
231
+ if (!config.validation?.disabled) {
232
+ validationResults.push(...validateAgentCardJson(agentCardContent));
233
+ }
234
+ }
235
+ }
236
+ }
198
237
  const llmsTxtContent = generateLlmsTxt(config);
199
238
  if (llmsTxtContent) {
200
239
  const outputLlmsPath = join(outDir, "llms.txt");
@@ -289,6 +328,7 @@ function agentmarkup(config) {
289
328
  }
290
329
  printReport({
291
330
  label: "@agentmarkup/astro",
331
+ agentCardStatus,
292
332
  llmsTxtEntries,
293
333
  llmsTxtSections,
294
334
  llmsTxtStatus,
@@ -318,6 +358,10 @@ async function readTextFileIfExists(filePath) {
318
358
  throw error;
319
359
  }
320
360
  }
361
+ async function writeTextFile(filePath, content) {
362
+ await mkdir(dirname(filePath), { recursive: true });
363
+ await writeFile(filePath, ensureTrailingNewline(content), "utf8");
364
+ }
321
365
  async function findHtmlFiles(rootDir) {
322
366
  const entries = await readdir(rootDir, { withFileTypes: true });
323
367
  const htmlFiles = [];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentmarkup/astro",
3
- "version": "0.4.0",
4
- "description": "Build-time llms.txt, llms-full.txt, JSON-LD, markdown mirrors, headers, AI crawler controls, and validation for Astro",
3
+ "version": "0.5.0",
4
+ "description": "Build-time llms.txt, llms-full.txt, A2A Agent Cards, JSON-LD, markdown mirrors, headers, AI crawler controls, and validation for Astro",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Sebastian Cochinescu <hello@animafelix.com> (https://animafelix.com)",
@@ -18,6 +18,8 @@
18
18
  "astro",
19
19
  "llms-txt",
20
20
  "llms-full",
21
+ "a2a",
22
+ "agent-card",
21
23
  "json-ld",
22
24
  "markdown",
23
25
  "schema-org",
@@ -27,6 +29,7 @@
27
29
  "headers",
28
30
  "ai-crawler",
29
31
  "machine-readable",
32
+ "seo",
30
33
  "validation",
31
34
  "geo"
32
35
  ],
@@ -42,7 +45,7 @@
42
45
  "dist"
43
46
  ],
44
47
  "dependencies": {
45
- "@agentmarkup/core": "0.4.0"
48
+ "@agentmarkup/core": "0.5.0"
46
49
  },
47
50
  "peerDependencies": {
48
51
  "astro": ">=4.0.0"