@anubhav-choubey/web-for-agents 0.2.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -60,12 +60,23 @@ type JsonLdNode = {
60
60
  "@type": string;
61
61
  [k: string]: unknown;
62
62
  };
63
+ /** A single `@graph` document: one `@context`, many nodes. Built by `schema.graph`. */
64
+ type JsonLdGraph = {
65
+ "@context": "https://schema.org";
66
+ "@graph": JsonLdNode[];
67
+ };
63
68
  /** JSON-LD as a string that is safe inside a `<script>` element (`<`, `>`, `&`, U+2028 and U+2029 are escaped). */
64
- declare function serializeJsonLd(data: JsonLdNode | JsonLdNode[]): string;
69
+ declare function serializeJsonLd(data: JsonLdNode | JsonLdNode[] | JsonLdGraph): string;
65
70
  type Author = {
66
71
  name: string;
67
72
  url?: string;
68
73
  };
74
+ type Offer = {
75
+ price: number | string;
76
+ priceCurrency: string;
77
+ availability?: "InStock" | "OutOfStock" | "PreOrder";
78
+ url?: string;
79
+ };
69
80
  /** Builders for typed schema.org JSON-LD nodes. Undefined fields are omitted. */
70
81
  declare const schema: {
71
82
  article(i: {
@@ -83,12 +94,7 @@ declare const schema: {
83
94
  image?: string | string[];
84
95
  sku?: string;
85
96
  brand?: string;
86
- offers?: {
87
- price: number | string;
88
- priceCurrency: string;
89
- availability?: "InStock" | "OutOfStock" | "PreOrder";
90
- url?: string;
91
- };
97
+ offers?: Offer;
92
98
  }): JsonLdNode;
93
99
  faqPage(items: Array<{
94
100
  question: string;
@@ -109,11 +115,64 @@ declare const schema: {
109
115
  url: string;
110
116
  searchUrlTemplate?: string;
111
117
  }): JsonLdNode;
118
+ person(i: {
119
+ name: string;
120
+ url?: string;
121
+ image?: string;
122
+ jobTitle?: string;
123
+ description?: string;
124
+ email?: string;
125
+ sameAs?: string[];
126
+ worksFor?: string | {
127
+ name: string;
128
+ url?: string;
129
+ };
130
+ }): JsonLdNode;
131
+ softwareApplication(i: {
132
+ name: string;
133
+ description?: string;
134
+ applicationCategory?: string;
135
+ operatingSystem?: string;
136
+ url?: string;
137
+ image?: string | string[];
138
+ offers?: Offer;
139
+ aggregateRating?: {
140
+ ratingValue: number | string;
141
+ ratingCount?: number;
142
+ reviewCount?: number;
143
+ bestRating?: number;
144
+ };
145
+ author?: Author;
146
+ }): JsonLdNode;
147
+ profilePage(i: {
148
+ mainEntity: JsonLdNode;
149
+ url?: string;
150
+ name?: string;
151
+ dateCreated?: string;
152
+ dateModified?: string;
153
+ }): JsonLdNode;
154
+ creativeWork(i: {
155
+ name: string;
156
+ description?: string;
157
+ url?: string;
158
+ image?: string | string[];
159
+ author?: Author;
160
+ dateCreated?: string;
161
+ keywords?: string | string[];
162
+ }): JsonLdNode;
163
+ webPage(i: {
164
+ name: string;
165
+ url: string;
166
+ description?: string;
167
+ breadcrumb?: JsonLdNode;
168
+ }): JsonLdNode;
169
+ /** One `@graph` document from several nodes; each node's own `@context` is dropped in favour of a single top-level one. */
170
+ graph(nodes: JsonLdNode[]): JsonLdGraph;
112
171
  };
113
172
 
114
173
  /** Renders a `<script type="application/ld+json">` with `data`, escaped for safe embedding in HTML. */
115
174
  declare function JsonLd({ data }: {
116
- data: JsonLdNode | JsonLdNode[];
175
+ data: JsonLdNode | JsonLdNode[] | JsonLdGraph;
117
176
  }): react.JSX.Element;
118
177
 
119
178
  type MarkerProps = {
@@ -171,7 +230,8 @@ type JsonSchema = {
171
230
  required?: string[];
172
231
  additionalProperties?: boolean;
173
232
  };
174
- type ActionKind = "handler" | "form" | "click";
233
+ /** "link": a plain `<a href>` an agent can open without JavaScript (see markupActions). */
234
+ type ActionKind = "handler" | "form" | "click" | "link";
175
235
  interface AgentActionDescriptor {
176
236
  name: string;
177
237
  title?: string;
@@ -180,6 +240,10 @@ interface AgentActionDescriptor {
180
240
  destructive: boolean;
181
241
  readOnly: boolean;
182
242
  kind: ActionKind;
243
+ /** Link actions only: the absolute http(s), mailto: or tel: URL. */
244
+ href?: string;
245
+ /** Link actions with an http(s) href only: open it with a GET request. */
246
+ method?: "GET";
183
247
  }
184
248
  type AgentActionResult = {
185
249
  status: "ok";
@@ -254,6 +318,20 @@ declare function AgentForm<I = Record<string, unknown>, R = unknown>(props: {
254
318
  /** Props that describe a form field to agents. */
255
319
  declare function agentParam(description: string): AgentParamProps;
256
320
 
321
+ interface AgentLinkSpec {
322
+ /** 1 to 128 characters of A-Z a-z 0-9 _ - . Unique per page. */
323
+ name: string;
324
+ description: string;
325
+ /** Marks a link with side effects (for example an unsubscribe URL); it is then not readOnly. */
326
+ destructive?: boolean;
327
+ }
328
+ /**
329
+ * Props that make a plain `<a href>` a link action: listed in agents.json with its absolute href,
330
+ * no JavaScript needed. Only http(s), mailto: and tel: hrefs are listed. Registers nothing and is
331
+ * not exposed through WebMCP; safe to call during SSR and prerendering.
332
+ */
333
+ declare function agentLinkProps(spec: AgentLinkSpec): AgentHintProps;
334
+
257
335
  /** The actions currently exposed on the page, sorted by name. */
258
336
  declare function listAgentActions(): AgentActionDescriptor[];
259
337
  /** Runs an exposed action with `input`. Never throws; failures are reported in the result. */
@@ -266,5 +344,5 @@ declare function executeAgentAction(name: string, input: unknown, signal?: Abort
266
344
  */
267
345
  declare function refreshAgentActions(opts?: RefreshAgentActionsOptions): void;
268
346
 
269
- export { AgentForm, CONTENT_ATTR, ContentRegion, EXCLUDE_ATTR, ExcludeFromContent, JsonLd, PRERENDERED_ATTR, READY_GLOBAL, Seo, SeoProvider, agentParam, executeAgentAction, holdPrerender, hydrateApp, listAgentActions, refreshAgentActions, schema, serializeJsonLd, useAgentAction, useSeo };
270
- export type { AgentActionDescriptor, AgentActionResult, AgentActionSpec, AgentHintProps, AgentParamProps, HeadTag, JsonLdNode, JsonSchema, RefreshAgentActionsOptions, SeoDefaults, SeoProps, SeoProviderProps };
347
+ export { AgentForm, CONTENT_ATTR, ContentRegion, EXCLUDE_ATTR, ExcludeFromContent, JsonLd, PRERENDERED_ATTR, READY_GLOBAL, Seo, SeoProvider, agentLinkProps, agentParam, executeAgentAction, holdPrerender, hydrateApp, listAgentActions, refreshAgentActions, schema, serializeJsonLd, useAgentAction, useSeo };
348
+ export type { AgentActionDescriptor, AgentActionResult, AgentActionSpec, AgentHintProps, AgentLinkSpec, AgentParamProps, HeadTag, JsonLdGraph, JsonLdNode, JsonSchema, RefreshAgentActionsOptions, SeoDefaults, SeoProps, SeoProviderProps };
package/dist/index.js CHANGED
@@ -8,18 +8,20 @@ import {
8
8
  schemaFromForm,
9
9
  validateFormInput,
10
10
  validateInput
11
- } from "./chunk-LCVPXUFN.js";
11
+ } from "./chunk-OKCYZE6C.js";
12
12
  import {
13
- AGENT_GLOBAL_VERSION,
14
13
  DESCRIPTION_MAX_CHARS,
15
14
  DESCRIPTION_MIN_CHARS,
16
- TITLE_MAX_CHARS,
17
- isValidActionName
18
- } from "./chunk-PHTV5NAY.js";
15
+ TITLE_MAX_CHARS
16
+ } from "./chunk-L3AYWCSX.js";
19
17
  import {
20
18
  compareStrings
21
19
  } from "./chunk-AAIYCJD3.js";
22
- import "./chunk-FEAKVNMG.js";
20
+ import {
21
+ ACTION_NAME_RULE,
22
+ AGENT_GLOBAL_VERSION,
23
+ isValidActionName
24
+ } from "./chunk-4STTPCBR.js";
23
25
  import {
24
26
  ACTION_ATTR,
25
27
  ACTION_DESCRIPTION_ATTR,
@@ -35,7 +37,7 @@ import {
35
37
  publicUrl,
36
38
  resolveUrl,
37
39
  routeOf
38
- } from "./chunk-F7PN5R25.js";
40
+ } from "./chunk-2Y3LLYEC.js";
39
41
  import {
40
42
  errorMessage,
41
43
  withPrefix
@@ -338,6 +340,23 @@ function node(type, fields) {
338
340
  for (const [k, v] of Object.entries(fields)) if (v !== void 0) out[k] = v;
339
341
  return out;
340
342
  }
343
+ function definedOnly(o) {
344
+ return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
345
+ }
346
+ function nested(n) {
347
+ const { "@context": _context, ...rest } = n;
348
+ return rest;
349
+ }
350
+ function offer(o) {
351
+ if (o === void 0) return void 0;
352
+ return {
353
+ "@type": "Offer",
354
+ price: o.price,
355
+ priceCurrency: o.priceCurrency,
356
+ availability: o.availability && `https://schema.org/${o.availability}`,
357
+ url: o.url
358
+ };
359
+ }
341
360
  var schema = {
342
361
  article(i) {
343
362
  const { author, ...rest } = i;
@@ -351,13 +370,7 @@ var schema = {
351
370
  return node("Product", {
352
371
  ...rest,
353
372
  brand: brand === void 0 ? void 0 : { "@type": "Brand", name: brand },
354
- offers: offers === void 0 ? void 0 : {
355
- "@type": "Offer",
356
- price: offers.price,
357
- priceCurrency: offers.priceCurrency,
358
- availability: offers.availability && `https://schema.org/${offers.availability}`,
359
- url: offers.url
360
- }
373
+ offers: offer(offers)
361
374
  });
362
375
  },
363
376
  faqPage(items) {
@@ -392,6 +405,41 @@ var schema = {
392
405
  "query-input": "required name=search_term_string"
393
406
  } : void 0
394
407
  });
408
+ },
409
+ person(i) {
410
+ const { worksFor, ...rest } = i;
411
+ return node("Person", {
412
+ ...rest,
413
+ worksFor: worksFor === void 0 ? void 0 : typeof worksFor === "string" ? { "@type": "Organization", name: worksFor } : definedOnly({ "@type": "Organization", name: worksFor.name, url: worksFor.url })
414
+ });
415
+ },
416
+ softwareApplication(i) {
417
+ const { offers, aggregateRating, author, ...rest } = i;
418
+ return node("SoftwareApplication", {
419
+ ...rest,
420
+ offers: offer(offers),
421
+ aggregateRating: aggregateRating === void 0 ? void 0 : { "@type": "AggregateRating", ...definedOnly(aggregateRating) },
422
+ author: author === void 0 ? void 0 : person(author)
423
+ });
424
+ },
425
+ profilePage(i) {
426
+ return node("ProfilePage", { ...i, mainEntity: nested(i.mainEntity) });
427
+ },
428
+ creativeWork(i) {
429
+ const { author, keywords, ...rest } = i;
430
+ return node("CreativeWork", {
431
+ ...rest,
432
+ author: author === void 0 ? void 0 : person(author),
433
+ keywords: Array.isArray(keywords) ? keywords.join(", ") : keywords
434
+ });
435
+ },
436
+ webPage(i) {
437
+ const { breadcrumb, ...rest } = i;
438
+ return node("WebPage", { ...rest, breadcrumb: breadcrumb && nested(breadcrumb) });
439
+ },
440
+ /** One `@graph` document from several nodes; each node's own `@context` is dropped in favour of a single top-level one. */
441
+ graph(nodes) {
442
+ return { "@context": "https://schema.org", "@graph": nodes.map(nested) };
395
443
  }
396
444
  };
397
445
 
@@ -447,6 +495,11 @@ function hydrateApp(container, app, options) {
447
495
  // src/runtime/agent/react.tsx
448
496
  import { useCallback, useEffect as useEffect2, useLayoutEffect as useLayoutEffect2, useRef as useRef2 } from "react";
449
497
 
498
+ // src/shared/actionName.ts
499
+ function assertValidActionName(name) {
500
+ if (!isValidActionName(name)) throw new Error(withPrefix(`invalid action name "${name}": ${ACTION_NAME_RULE}`));
501
+ }
502
+
450
503
  // src/runtime/agent/formAutomation.ts
451
504
  var MESSAGE_SELECTOR = '[role="alert"], [role="status"], [aria-live]';
452
505
  var MAX_MESSAGE = 500;
@@ -600,11 +653,6 @@ var nextId = 1;
600
653
  var syncScheduled = false;
601
654
  var forceScheduled = false;
602
655
  var CONFIRM_BUDGET = 1e3;
603
- function assertValidActionName(name) {
604
- if (!isValidActionName(name)) {
605
- throw new Error(withPrefix(`invalid action name "${name}": use 1-128 chars of A-Z a-z 0-9 _ - .`));
606
- }
607
- }
608
656
  function installGlobal() {
609
657
  if (typeof window === "undefined") return;
610
658
  const w = window;
@@ -919,6 +967,18 @@ function AgentForm(props) {
919
967
  function agentParam(description) {
920
968
  return { [ACTION_PARAM_ATTR]: description };
921
969
  }
970
+
971
+ // src/runtime/agent/link.ts
972
+ function agentLinkProps(spec) {
973
+ assertValidActionName(spec.name);
974
+ const props = {
975
+ [ACTION_ATTR]: spec.name,
976
+ [ACTION_DESCRIPTION_ATTR]: spec.description,
977
+ "aria-description": spec.description
978
+ };
979
+ if (spec.destructive) props[ACTION_DESTRUCTIVE_ATTR] = "true";
980
+ return props;
981
+ }
922
982
  export {
923
983
  AgentForm,
924
984
  CONTENT_ATTR,
@@ -930,6 +990,7 @@ export {
930
990
  READY_GLOBAL,
931
991
  Seo,
932
992
  SeoProvider,
993
+ agentLinkProps,
933
994
  agentParam,
934
995
  executeAgentAction,
935
996
  holdPrerender,
package/dist/overlay.js CHANGED
@@ -1,25 +1,31 @@
1
1
  import {
2
2
  DEFAULT_CONTENT_SELECTOR,
3
3
  DEFAULT_SHORTCUT,
4
+ INVALID_NAME_REASON,
5
+ actionGroups,
4
6
  buildPageMarkdown,
7
+ firstPerName,
5
8
  markupActions,
6
9
  matchesShortcut,
7
10
  parseShortcut,
8
11
  readPageMeta,
12
+ resolveLinkHref,
9
13
  selectContentRegion
10
- } from "./chunk-PSNLS4GL.js";
14
+ } from "./chunk-T42S3WRV.js";
11
15
  import {
12
16
  compareStrings
13
17
  } from "./chunk-AAIYCJD3.js";
14
18
  import {
15
19
  jsonLdTypes
16
- } from "./chunk-LCLDQ3A3.js";
17
- import "./chunk-FEAKVNMG.js";
20
+ } from "./chunk-MIVGE5TM.js";
21
+ import {
22
+ isValidActionName
23
+ } from "./chunk-4STTPCBR.js";
18
24
  import {
19
25
  AGENT_GLOBAL,
20
26
  routeOf,
21
27
  trimTrailingSlash
22
- } from "./chunk-F7PN5R25.js";
28
+ } from "./chunk-2Y3LLYEC.js";
23
29
  import {
24
30
  errorMessage
25
31
  } from "./chunk-JJN6FNJD.js";
@@ -63,18 +69,15 @@ function registryActions(doc) {
63
69
  return [];
64
70
  }
65
71
  }
66
- function collectActions(doc) {
67
- const markup = markupActions(doc).map(
68
- ({ name, description, kind, destructive, source }) => ({ name, description, kind, destructive, source })
69
- );
70
- const seen = /* @__PURE__ */ new Set();
71
- const out = [];
72
- for (const a of [...registryActions(doc), ...markup]) {
73
- if (seen.has(a.name)) continue;
74
- seen.add(a.name);
75
- out.push(a);
76
- }
77
- return out.sort((a, b) => compareStrings(a.name, b.name));
72
+ function collectActions(doc, pageUrl) {
73
+ const markup = markupActions(doc).map(({ name, description, kind, destructive, source, href }) => {
74
+ const a = { name, description, kind, destructive, source };
75
+ if (!isValidActionName(name)) return { ...a, warning: `Not listed in agents.json: ${INVALID_NAME_REASON}` };
76
+ if (kind !== "link") return a;
77
+ const r = resolveLinkHref(href ?? "", pageUrl);
78
+ return r.ok ? { ...a, href: r.href } : { ...a, warning: `Not listed in agents.json: ${r.reason}` };
79
+ });
80
+ return firstPerName(registryActions(doc), ...actionGroups(markup)).sort((a, b) => compareStrings(a.name, b.name));
78
81
  }
79
82
  function collectPageReport(doc = document, opts = {}) {
80
83
  const url = doc.location?.href ?? "";
@@ -110,7 +113,7 @@ function collectPageReport(doc = document, opts = {}) {
110
113
  } catch {
111
114
  }
112
115
  try {
113
- actions = collectActions(doc);
116
+ actions = collectActions(doc, pageUrl);
114
117
  } catch {
115
118
  }
116
119
  return { url, meta, jsonLd, content, actions };
@@ -181,6 +184,8 @@ function renderPanel(root, report) {
181
184
  const line = [a.name, a.kind, a.source, a.destructive ? "destructive" : ""].filter(Boolean).join(" \xB7 ");
182
185
  actions.append(el(doc, "div", "action", el(doc, "strong", void 0, a.name), line.slice(a.name.length)));
183
186
  if (a.description) actions.append(el(doc, "div", "note", a.description));
187
+ if (a.href) actions.append(el(doc, "div", "note", a.href));
188
+ if (a.warning) actions.append(el(doc, "p", "warn", a.warning));
184
189
  }
185
190
  const refresh = el(doc, "button", "btn", "Refresh");
186
191
  refresh.setAttribute("type", "button");
@@ -224,7 +224,7 @@ async function runRenderChild() {
224
224
  const allow = createAllowList();
225
225
  for (const o of [...options.localOrigins ?? [], ...options.allowOrigins ?? []]) allowOrigin(allow, o);
226
226
  containNetwork(allow);
227
- const { createHappyDomRenderer } = await import("./happyDom-WTYQ5UAC.js");
227
+ const { createHappyDomRenderer } = await import("./happyDom-ZEFNMD7B.js");
228
228
  const renderer = createHappyDomRenderer(options);
229
229
  process.on("message", (m) => {
230
230
  if (m.type === "resources") {
package/dist/vite.d.ts CHANGED
@@ -19,7 +19,7 @@ interface WebForAgentsOptions {
19
19
  disallow?: string[];
20
20
  };
21
21
  /** Generate sitemap.xml and the `Sitemap:` line in robots.txt. Default true. */
22
- sitemap?: boolean;
22
+ sitemap?: boolean | SitemapOptions;
23
23
  /** Prerender routes after the build. `false` also turns off markdown, llms and the agent map. */
24
24
  prerender?: PrerenderOptions | false;
25
25
  /** Write a `.md` version of each page. `false` also turns off llms-full.txt. */
@@ -123,6 +123,33 @@ interface LlmsOptions {
123
123
  title?: string;
124
124
  /** llms.txt summary. Default: the description of "/". */
125
125
  description?: string;
126
+ /** Plain text after the summary, in llms.txt and llms-full.txt. Blank lines separate paragraphs. */
127
+ intro?: string;
128
+ /** Sections written before "## Pages" in llms.txt, and in the llms-full.txt header. */
129
+ sections?: LlmsSectionOptions[];
130
+ }
131
+ interface LlmsSectionOptions {
132
+ /** The section heading. */
133
+ title: string;
134
+ /** Plain text under the heading. Blank lines separate paragraphs. */
135
+ text?: string;
136
+ /** Links listed under the text. */
137
+ links?: LlmsLinkOptions[];
138
+ }
139
+ interface LlmsLinkOptions {
140
+ title: string;
141
+ /** http(s) or mailto: URL. A relative URL ("/resume.pdf") is resolved against siteUrl. */
142
+ url: string;
143
+ description?: string;
144
+ }
145
+ /** A route's last modification date: "YYYY-MM-DD", a W3C datetime, a Date, or undefined for none. */
146
+ type SitemapLastmod = (route: string) => string | Date | undefined | Promise<string | Date | undefined>;
147
+ interface SitemapOptions {
148
+ /**
149
+ * `<lastmod>` for each URL. "file": the source HTML file's modification time with `generate`, the
150
+ * build date with the plugin. A function returns the date per route. Default "none".
151
+ */
152
+ lastmod?: "file" | "none" | SitemapLastmod;
126
153
  }
127
154
 
128
155
  interface GenerateOptions {
@@ -135,7 +162,7 @@ interface GenerateOptions {
135
162
  /** Routes to leave out. A string is an exact path, or a prefix when it ends in "/*". */
136
163
  exclude?: Array<string | RegExp>;
137
164
  /** Write sitemap.xml and the `Sitemap:` line in robots.txt. Default true. */
138
- sitemap?: boolean;
165
+ sitemap?: WebForAgentsOptions["sitemap"];
139
166
  /** Write a `.md` version of each page. `false` also turns off llms-full.txt. */
140
167
  markdown?: MarkdownOptions | false;
141
168
  /** Write llms.txt and llms-full.txt. Default on. */
@@ -191,4 +218,4 @@ declare const AI_BOTS: {
191
218
  declare function webForAgents(options: WebForAgentsOptions): Plugin[];
192
219
 
193
220
  export { AI_BOTS, generateAgentFiles, webForAgents };
194
- export type { AgentMapOptions, FileLayout, GenerateOptions, GenerateResult, GeneratedPage, LlmsOptions, MarkdownOptions, MarkdownTransform, MarkdownTransformPage, OverlayPluginOptions, PrerenderOptions, RendererName, Rule, WebForAgentsOptions };
221
+ export type { AgentMapOptions, FileLayout, GenerateOptions, GenerateResult, GeneratedPage, LlmsLinkOptions, LlmsOptions, LlmsSectionOptions, MarkdownOptions, MarkdownTransform, MarkdownTransformPage, OverlayPluginOptions, PrerenderOptions, RendererName, Rule, SitemapLastmod, SitemapOptions, WebForAgentsOptions };