@rubytech/create-maxy-code 0.1.118 → 0.1.120
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/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +95 -4
- package/payload/platform/package-lock.json +141 -125
- package/payload/platform/plugins/aeo/PLUGIN.md +1 -1
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js +121 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/audit-heuristics.test.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js +129 -0
- package/payload/platform/plugins/aeo/mcp/dist/__tests__/schema-mapping.test.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.d.ts +2 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js +171 -0
- package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts +27 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js +274 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/audit-heuristics.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts +5 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js +38 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts +48 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js +254 -0
- package/payload/platform/plugins/aeo/mcp/dist/lib/schema-mapping.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts +25 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js +78 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-audit-page.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts +18 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js +56 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-emit-jsonld.js.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts +37 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.d.ts.map +1 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js +94 -0
- package/payload/platform/plugins/aeo/mcp/dist/tools/aeo-write-llms-txt.js.map +1 -0
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts +12 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +34 -1
- package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
- package/payload/platform/plugins/docs/references/aeo.md +2 -2
- package/payload/platform/plugins/docs/references/visitor-graph.md +82 -0
- package/payload/platform/plugins/memory/references/schema-base.md +44 -0
- package/payload/server/{chunk-FJCI6X3H.js → chunk-HCYM5FLU.js} +2 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/privacy.html +66 -0
- package/payload/server/public/v.js +191 -0
- package/payload/server/server.js +473 -85
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { auditHtml } from "../lib/audit-heuristics.js";
|
|
3
|
+
function pick(result, name) {
|
|
4
|
+
const r = result.heuristics.find((h) => h.name === name);
|
|
5
|
+
if (!r)
|
|
6
|
+
throw new Error(`heuristic ${name} not found`);
|
|
7
|
+
return r;
|
|
8
|
+
}
|
|
9
|
+
const GOOD_PAGE = `<!doctype html>
|
|
10
|
+
<html><head>
|
|
11
|
+
<title>How much does a boiler service cost? — Acme</title>
|
|
12
|
+
<meta name="description" content="A standard boiler service in Brighton costs £85 to £120, takes about one hour, and includes a written safety report.">
|
|
13
|
+
<link rel="canonical" href="https://acme.test/boiler-service-cost">
|
|
14
|
+
<meta property="og:title" content="How much does a boiler service cost?">
|
|
15
|
+
<meta property="og:description" content="A standard boiler service in Brighton costs £85 to £120, takes about one hour, and includes a written safety report.">
|
|
16
|
+
<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Cost?","acceptedAnswer":{"@type":"Answer","text":"£85–£120."}}]}</script>
|
|
17
|
+
</head><body>
|
|
18
|
+
<h1>How much does a boiler service cost?</h1>
|
|
19
|
+
<p>A standard boiler service in Brighton costs £85 to £120 and takes about one hour.</p>
|
|
20
|
+
<h2>What's included</h2>
|
|
21
|
+
<p>Safety checks, flue gas analysis, pressure adjustment, written report.</p>
|
|
22
|
+
<h2>Frequently asked questions</h2>
|
|
23
|
+
<p>Common questions are answered above.</p>
|
|
24
|
+
</body></html>`;
|
|
25
|
+
const BAD_PAGE = `<!doctype html>
|
|
26
|
+
<html><head><title>Acme</title></head>
|
|
27
|
+
<body>
|
|
28
|
+
<div>Some words.</div>
|
|
29
|
+
</body></html>`;
|
|
30
|
+
describe("auditHtml — golden good page", () => {
|
|
31
|
+
const result = auditHtml(GOOD_PAGE);
|
|
32
|
+
it("scores 100", () => {
|
|
33
|
+
expect(result.score).toBe(100);
|
|
34
|
+
});
|
|
35
|
+
it("passes every heuristic", () => {
|
|
36
|
+
for (const h of result.heuristics) {
|
|
37
|
+
expect(h.status, `${h.name}: ${h.detail}`).toBe("pass");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("auditHtml — bad page exercises every fail path", () => {
|
|
42
|
+
const result = auditHtml(BAD_PAGE);
|
|
43
|
+
it("scores 0 or near-zero", () => {
|
|
44
|
+
expect(result.score).toBeLessThanOrEqual(10);
|
|
45
|
+
});
|
|
46
|
+
it("fails h1-present", () => {
|
|
47
|
+
expect(pick(result, "h1-present").status).toBe("fail");
|
|
48
|
+
});
|
|
49
|
+
it("fails jsonld-present", () => {
|
|
50
|
+
expect(pick(result, "jsonld-present").status).toBe("fail");
|
|
51
|
+
});
|
|
52
|
+
it("fails meta-description", () => {
|
|
53
|
+
expect(pick(result, "meta-description").status).toBe("fail");
|
|
54
|
+
});
|
|
55
|
+
it("fails canonical-url", () => {
|
|
56
|
+
expect(pick(result, "canonical-url").status).toBe("fail");
|
|
57
|
+
});
|
|
58
|
+
it("fails og-tags with both missing", () => {
|
|
59
|
+
const r = pick(result, "og-tags");
|
|
60
|
+
expect(r.status).toBe("fail");
|
|
61
|
+
expect(r.detail).toContain("og:title");
|
|
62
|
+
expect(r.detail).toContain("og:description");
|
|
63
|
+
});
|
|
64
|
+
it("emits a rewrite suggestion for every failure", () => {
|
|
65
|
+
for (const h of result.heuristics) {
|
|
66
|
+
if (h.status === "fail") {
|
|
67
|
+
expect(h.suggestion, `${h.name} missing suggestion`).toBeTruthy();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("auditHtml — warn paths", () => {
|
|
73
|
+
it("warns when first <p> after <h1> exceeds 280 chars", () => {
|
|
74
|
+
const long = "X".repeat(300);
|
|
75
|
+
const html = `<html><head>
|
|
76
|
+
<title>t</title>
|
|
77
|
+
<meta name="description" content="${"d".repeat(120)}">
|
|
78
|
+
<link rel="canonical" href="https://a.test">
|
|
79
|
+
<meta property="og:title" content="t">
|
|
80
|
+
<meta property="og:description" content="t">
|
|
81
|
+
<script type="application/ld+json">{"@type":"FAQPage"}</script>
|
|
82
|
+
</head><body>
|
|
83
|
+
<h1>Topic</h1><p>${long}</p>
|
|
84
|
+
<h2>More</h2><p>x</p>
|
|
85
|
+
</body></html>`;
|
|
86
|
+
const result = auditHtml(html);
|
|
87
|
+
expect(pick(result, "structured-answer").status).toBe("warn");
|
|
88
|
+
});
|
|
89
|
+
it("warns when meta-description length is outside 80–160", () => {
|
|
90
|
+
const html = `<html><head>
|
|
91
|
+
<title>t</title>
|
|
92
|
+
<meta name="description" content="too short">
|
|
93
|
+
<link rel="canonical" href="https://a.test">
|
|
94
|
+
<meta property="og:title" content="t">
|
|
95
|
+
<meta property="og:description" content="t">
|
|
96
|
+
<script type="application/ld+json">{"@type":"FAQPage"}</script>
|
|
97
|
+
</head><body>
|
|
98
|
+
<h1>Topic</h1><p>short answer</p>
|
|
99
|
+
</body></html>`;
|
|
100
|
+
expect(pick(auditHtml(html), "meta-description").status).toBe("warn");
|
|
101
|
+
});
|
|
102
|
+
it("warns when there are multiple <h1> elements", () => {
|
|
103
|
+
const html = `<html><body><h1>A</h1><h1>B</h1><p>x</p></body></html>`;
|
|
104
|
+
expect(pick(auditHtml(html), "h1-present").status).toBe("warn");
|
|
105
|
+
});
|
|
106
|
+
it("fails heading-hierarchy on h1→h3 skip", () => {
|
|
107
|
+
const html = `<html><body><h1>A</h1><p>x</p><h3>Skipped</h3></body></html>`;
|
|
108
|
+
const r = pick(auditHtml(html), "heading-hierarchy");
|
|
109
|
+
expect(r.status).toBe("fail");
|
|
110
|
+
expect(r.detail).toContain("h1");
|
|
111
|
+
expect(r.detail).toContain("h3");
|
|
112
|
+
});
|
|
113
|
+
it("warns on unparseable JSON-LD when other blocks parse", () => {
|
|
114
|
+
const html = `<html><head>
|
|
115
|
+
<script type="application/ld+json">{"@type":"FAQPage"}</script>
|
|
116
|
+
<script type="application/ld+json">not json</script>
|
|
117
|
+
</head><body><h1>T</h1><p>a</p></body></html>`;
|
|
118
|
+
expect(pick(auditHtml(html), "jsonld-present").status).toBe("warn");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=audit-heuristics.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-heuristics.test.js","sourceRoot":"","sources":["../../src/__tests__/audit-heuristics.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-mapping.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema-mapping.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { buildJsonLd, renderJsonLdScript, resolveSchemaType, MissingPropertyError, } from "../lib/schema-mapping.js";
|
|
3
|
+
describe("resolveSchemaType", () => {
|
|
4
|
+
it("maps known graph labels to schema.org types", () => {
|
|
5
|
+
expect(resolveSchemaType("LocalBusiness")).toBe("LocalBusiness");
|
|
6
|
+
expect(resolveSchemaType("Organization")).toBe("Organization");
|
|
7
|
+
expect(resolveSchemaType("Person")).toBe("Person");
|
|
8
|
+
expect(resolveSchemaType("Service")).toBe("Service");
|
|
9
|
+
expect(resolveSchemaType("Product")).toBe("Product");
|
|
10
|
+
expect(resolveSchemaType("CreativeWork")).toBe("Article");
|
|
11
|
+
expect(resolveSchemaType("Article")).toBe("Article");
|
|
12
|
+
expect(resolveSchemaType("FAQPage")).toBe("FAQPage");
|
|
13
|
+
expect(resolveSchemaType("Listing")).toBe("RealEstateListing");
|
|
14
|
+
expect(resolveSchemaType("RealEstateListing")).toBe("RealEstateListing");
|
|
15
|
+
expect(resolveSchemaType("Event")).toBe("Event");
|
|
16
|
+
});
|
|
17
|
+
it("falls back to Thing for unknown labels", () => {
|
|
18
|
+
expect(resolveSchemaType("UnknownLabel")).toBe("Thing");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("buildJsonLd", () => {
|
|
22
|
+
it("renders an Organization with required name", () => {
|
|
23
|
+
const obj = buildJsonLd({
|
|
24
|
+
label: "Organization",
|
|
25
|
+
properties: { name: "Acme", website: "https://acme.test" },
|
|
26
|
+
});
|
|
27
|
+
expect(obj["@context"]).toBe("https://schema.org");
|
|
28
|
+
expect(obj["@type"]).toBe("Organization");
|
|
29
|
+
expect(obj.name).toBe("Acme");
|
|
30
|
+
expect(obj.url).toBe("https://acme.test");
|
|
31
|
+
});
|
|
32
|
+
it("renders a LocalBusiness with nested PostalAddress", () => {
|
|
33
|
+
const obj = buildJsonLd({
|
|
34
|
+
label: "LocalBusiness",
|
|
35
|
+
elementId: "4:abc:1",
|
|
36
|
+
properties: {
|
|
37
|
+
name: "Acme Plumbing",
|
|
38
|
+
streetAddress: "1 High St",
|
|
39
|
+
addressLocality: "Brighton",
|
|
40
|
+
postalCode: "BN1 1AA",
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
expect(obj["@id"]).toBe("4:abc:1");
|
|
44
|
+
expect(obj.address).toEqual({
|
|
45
|
+
"@type": "PostalAddress",
|
|
46
|
+
streetAddress: "1 High St",
|
|
47
|
+
addressLocality: "Brighton",
|
|
48
|
+
postalCode: "BN1 1AA",
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
it("renders a Person and synthesises name from givenName + familyName", () => {
|
|
52
|
+
const obj = buildJsonLd({
|
|
53
|
+
label: "Person",
|
|
54
|
+
properties: {
|
|
55
|
+
givenName: "Joel",
|
|
56
|
+
familyName: "Smalley",
|
|
57
|
+
email: "j@example.com",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
expect(obj["@type"]).toBe("Person");
|
|
61
|
+
expect(obj.name).toBe("Joel Smalley");
|
|
62
|
+
expect(obj.email).toBe("j@example.com");
|
|
63
|
+
});
|
|
64
|
+
it("renders a Service with an Offer when price is set", () => {
|
|
65
|
+
const obj = buildJsonLd({
|
|
66
|
+
label: "Service",
|
|
67
|
+
properties: { name: "Boiler service", price: 120, priceCurrency: "GBP" },
|
|
68
|
+
});
|
|
69
|
+
expect(obj.offers).toEqual({
|
|
70
|
+
"@type": "Offer",
|
|
71
|
+
price: 120,
|
|
72
|
+
priceCurrency: "GBP",
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it("renders an Article remapping title→headline and abstract→description", () => {
|
|
76
|
+
const obj = buildJsonLd({
|
|
77
|
+
label: "Article",
|
|
78
|
+
properties: { title: "How boilers work", abstract: "Short summary" },
|
|
79
|
+
});
|
|
80
|
+
expect(obj.headline).toBe("How boilers work");
|
|
81
|
+
expect(obj.description).toBe("Short summary");
|
|
82
|
+
});
|
|
83
|
+
it("renders an FAQPage with mainEntity Question/Answer pairs", () => {
|
|
84
|
+
const obj = buildJsonLd({
|
|
85
|
+
label: "FAQPage",
|
|
86
|
+
properties: {
|
|
87
|
+
name: "Boiler FAQ",
|
|
88
|
+
questions: [
|
|
89
|
+
{ name: "How long does it take?", acceptedAnswer: "About an hour." },
|
|
90
|
+
{ name: "Do you charge call-out?", acceptedAnswer: "No." },
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
expect(Array.isArray(obj.mainEntity)).toBe(true);
|
|
95
|
+
const main = obj.mainEntity;
|
|
96
|
+
expect(main).toHaveLength(2);
|
|
97
|
+
});
|
|
98
|
+
it("renders an Event with required name + startDate", () => {
|
|
99
|
+
const obj = buildJsonLd({
|
|
100
|
+
label: "Event",
|
|
101
|
+
properties: { name: "Open day", startDate: "2026-06-01T10:00:00Z" },
|
|
102
|
+
});
|
|
103
|
+
expect(obj.startDate).toBe("2026-06-01T10:00:00Z");
|
|
104
|
+
});
|
|
105
|
+
it("throws MissingPropertyError when required property is absent", () => {
|
|
106
|
+
expect(() => buildJsonLd({ label: "Event", properties: { name: "Open day" } })).toThrow(MissingPropertyError);
|
|
107
|
+
});
|
|
108
|
+
it("renders unknown labels as schema:Thing without throwing", () => {
|
|
109
|
+
const obj = buildJsonLd({
|
|
110
|
+
label: "Mystery",
|
|
111
|
+
properties: { name: "Anonymous", description: "Unknown" },
|
|
112
|
+
});
|
|
113
|
+
expect(obj["@type"]).toBe("Thing");
|
|
114
|
+
expect(obj.name).toBe("Anonymous");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe("renderJsonLdScript", () => {
|
|
118
|
+
it("wraps the JSON in a script block", () => {
|
|
119
|
+
const obj = buildJsonLd({
|
|
120
|
+
label: "Organization",
|
|
121
|
+
properties: { name: "Acme" },
|
|
122
|
+
});
|
|
123
|
+
const block = renderJsonLdScript(obj);
|
|
124
|
+
expect(block).toMatch(/^<script type="application\/ld\+json">/);
|
|
125
|
+
expect(block).toMatch(/<\/script>$/);
|
|
126
|
+
expect(block).toContain('"Acme"');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=schema-mapping.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-mapping.test.js","sourceRoot":"","sources":["../../src/__tests__/schema-mapping.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
|
|
2
|
+
initStderrTee("aeo");
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { eagerTool } from "../../../../lib/mcp-eager/dist/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { emitJsonLd } from "./tools/aeo-emit-jsonld.js";
|
|
8
|
+
import { writeLlmsTxt } from "./tools/aeo-write-llms-txt.js";
|
|
9
|
+
import { auditPage } from "./tools/aeo-audit-page.js";
|
|
10
|
+
import { closeDriver } from "./lib/neo4j.js";
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
const server = new McpServer({
|
|
13
|
+
name: "maxy-aeo",
|
|
14
|
+
version: "0.1.0",
|
|
15
|
+
});
|
|
16
|
+
// Plugin-system boot tolerance — see Task 202. Module-init must never
|
|
17
|
+
// throw; plugin-system spawn enumerates tools/list before ACCOUNT_ID
|
|
18
|
+
// is set. Per-call refusal preserves account isolation.
|
|
19
|
+
const accountId = process.env.ACCOUNT_ID ?? null;
|
|
20
|
+
process.stderr.write(`[aeo] boot accountId=${accountId ?? "null"}\n`);
|
|
21
|
+
function refuseNoAccount(toolName) {
|
|
22
|
+
process.stderr.write(`[aeo] tool=${toolName} refuse reason=no-account-context\n`);
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: `${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID (plugin-system metadata-only mode). Per-account AEO operations require ACCOUNT_ID at spawn.`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const agentSlug = process.env.AGENT_SLUG || undefined;
|
|
34
|
+
const sessionId = process.env.SESSION_ID || undefined;
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// aeo-emit-jsonld
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
eagerTool(server, "aeo-emit-jsonld", "Emit a schema.org JSON-LD block for a typed graph entity. Pass entityId for a Neo4j-sourced render, or label + properties for an inline render. Returns the parsed JSON-LD object and a ready-to-inline <script> block string.", {
|
|
39
|
+
entityId: z
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Neo4j elementId of a typed node (Organization, Person, Service, …)"),
|
|
43
|
+
label: z
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe("Source label when rendering inline (no entityId)"),
|
|
47
|
+
properties: z
|
|
48
|
+
.record(z.unknown())
|
|
49
|
+
.optional()
|
|
50
|
+
.describe("Property bag for inline render"),
|
|
51
|
+
}, async (args) => {
|
|
52
|
+
if (!accountId)
|
|
53
|
+
return refuseNoAccount("aeo-emit-jsonld");
|
|
54
|
+
try {
|
|
55
|
+
const result = await emitJsonLd({ accountId, ...args });
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: "text",
|
|
60
|
+
text: JSON.stringify(result, null, 2),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
67
|
+
process.stderr.write(`[aeo-emit-jsonld] error: ${message}\n`);
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// aeo-write-llms-txt
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
eagerTool(server, "aeo-write-llms-txt", "Generate /llms.txt and /llms-full.txt for the account's published KnowledgeDocument pages. Index lists every page with title, url, summary. Full file concatenates each page's body, demoting any in-body H1s to H2 to keep one heading hierarchy.", {
|
|
78
|
+
siteName: z.string().describe("Display name of the site (top H1 of llms.txt)"),
|
|
79
|
+
siteDescription: z
|
|
80
|
+
.string()
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("One-line site description (emitted as a blockquote)"),
|
|
83
|
+
}, async (args) => {
|
|
84
|
+
if (!accountId)
|
|
85
|
+
return refuseNoAccount("aeo-write-llms-txt");
|
|
86
|
+
try {
|
|
87
|
+
const result = await writeLlmsTxt({ accountId, ...args });
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: JSON.stringify({
|
|
93
|
+
pageCount: result.pageCount,
|
|
94
|
+
skippedNoUrl: result.skippedNoUrl,
|
|
95
|
+
sizeBytes: result.sizeBytes,
|
|
96
|
+
llmsTxt: result.llmsTxt,
|
|
97
|
+
llmsFullTxt: result.llmsFullTxt,
|
|
98
|
+
}, null, 2),
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
105
|
+
process.stderr.write(`[aeo-llms-txt] error: ${message}\n`);
|
|
106
|
+
return {
|
|
107
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
108
|
+
isError: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// aeo-audit-page
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
eagerTool(server, "aeo-audit-page", "Run the eight-heuristic AEO audit against a URL or inline HTML. Returns a 0–100 score, per-heuristic pass/warn/fail, and one rewrite suggestion per fail. Pass persist=true with targetKnowledgeDocumentId to record an :AEOAudit node linked from the document.", {
|
|
116
|
+
url: z.string().url().optional().describe("Page URL to fetch and audit"),
|
|
117
|
+
html: z.string().optional().describe("Raw HTML to audit (alternative to url)"),
|
|
118
|
+
persist: z
|
|
119
|
+
.boolean()
|
|
120
|
+
.optional()
|
|
121
|
+
.describe("If true, write an :AEOAudit node and HAS_AEO_AUDIT edge"),
|
|
122
|
+
targetKnowledgeDocumentId: z
|
|
123
|
+
.string()
|
|
124
|
+
.optional()
|
|
125
|
+
.describe("Neo4j elementId of the KnowledgeDocument to link the audit to"),
|
|
126
|
+
}, async (args) => {
|
|
127
|
+
if (!accountId)
|
|
128
|
+
return refuseNoAccount("aeo-audit-page");
|
|
129
|
+
try {
|
|
130
|
+
const result = await auditPage({
|
|
131
|
+
accountId,
|
|
132
|
+
agentSlug,
|
|
133
|
+
sessionId,
|
|
134
|
+
...args,
|
|
135
|
+
});
|
|
136
|
+
return {
|
|
137
|
+
content: [
|
|
138
|
+
{ type: "text", text: JSON.stringify(result, null, 2) },
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
144
|
+
process.stderr.write(`[aeo-audit] error: ${message}\n`);
|
|
145
|
+
return {
|
|
146
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
147
|
+
isError: true,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Boot
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
async function shutdown() {
|
|
155
|
+
try {
|
|
156
|
+
await closeDriver();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// already-closed driver during shutdown is benign
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
process.on("SIGTERM", () => {
|
|
163
|
+
void shutdown().finally(() => process.exit(0));
|
|
164
|
+
});
|
|
165
|
+
process.on("SIGINT", () => {
|
|
166
|
+
void shutdown().finally(() => process.exit(0));
|
|
167
|
+
});
|
|
168
|
+
const transport = new StdioServerTransport();
|
|
169
|
+
await server.connect(transport);
|
|
170
|
+
process.stderr.write("[aeo] ready\n");
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-level AEO (Answer Engine Optimisation) heuristics. Eight checks
|
|
3
|
+
* run against raw HTML; each returns a status and, when the check fails,
|
|
4
|
+
* one concrete rewrite suggestion the operator can apply.
|
|
5
|
+
*
|
|
6
|
+
* The HTML parser is a deliberate single-pass regex scanner: AEO audits
|
|
7
|
+
* run on pages the platform itself renders, so we know they are
|
|
8
|
+
* server-side-generated, well-formed enough for regex inspection, and
|
|
9
|
+
* not gated on script-injected content. Bringing in a full DOM parser
|
|
10
|
+
* (jsdom, cheerio) would dwarf the rest of the plugin and pull in
|
|
11
|
+
* Node-version-coupled native deps. If we later need to audit
|
|
12
|
+
* third-party arbitrary HTML, swap the parser then.
|
|
13
|
+
*/
|
|
14
|
+
export type HeuristicStatus = "pass" | "warn" | "fail";
|
|
15
|
+
export interface HeuristicResult {
|
|
16
|
+
name: HeuristicName;
|
|
17
|
+
status: HeuristicStatus;
|
|
18
|
+
detail: string;
|
|
19
|
+
suggestion?: string;
|
|
20
|
+
}
|
|
21
|
+
export type HeuristicName = "h1-present" | "jsonld-present" | "structured-answer" | "faq-section" | "meta-description" | "canonical-url" | "og-tags" | "heading-hierarchy";
|
|
22
|
+
export interface AuditResult {
|
|
23
|
+
score: number;
|
|
24
|
+
heuristics: HeuristicResult[];
|
|
25
|
+
}
|
|
26
|
+
export declare function auditHtml(html: string): AuditResult;
|
|
27
|
+
//# sourceMappingURL=audit-heuristics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-heuristics.d.ts","sourceRoot":"","sources":["../../src/lib/audit-heuristics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,gBAAgB,GAChB,mBAAmB,GACnB,aAAa,GACb,kBAAkB,GAClB,eAAe,GACf,SAAS,GACT,mBAAmB,CAAC;AAExB,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B;AAoRD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAOnD"}
|