@kirkelabs/agent-readiness-scan 0.1.0 → 0.2.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/src/generators.js CHANGED
@@ -1,174 +1,228 @@
1
- /**
2
- * generators.js
3
- *
4
- * Produces the "customs declaration" — a drop-in set of files ready to
5
- * paste at the site root that close most of the policy gaps the
6
- * scanner identifies. The user reviews and edits before deploy;
7
- * these are conservative scaffolds, not finished output.
8
- *
9
- * Generated artefacts:
10
- * - robots.txt (per-bot policy + Content-Signals)
11
- * - .well-known/security.txt (RFC 9116)
12
- * - .well-known/mcp/server-card.json (MCP server card scaffold)
13
- * - .well-known/acp/manifest.json (ACP manifest scaffold)
14
- */
15
-
16
- const AI_BOTS = {
17
- training: [
18
- 'GPTBot',
19
- 'ClaudeBot',
20
- 'Google-Extended',
21
- 'anthropic-ai',
22
- 'CCBot',
23
- 'Bytespider',
24
- 'Amazonbot',
25
- 'Applebot-Extended',
26
- 'meta-externalagent',
27
- ],
28
- grounding: ['OAI-SearchBot', 'PerplexityBot', 'Claude-Web'],
29
- userDirected: ['ChatGPT-User', 'Claude-User'],
30
- };
31
-
32
- function generateRobotsTxt(origin) {
33
- const lines = [];
34
- lines.push('# robots.txt — Customs Declaration');
35
- lines.push('# Generated by @kirkelabs/agent-readiness-scan.');
36
- lines.push('# Review and edit before deploying. Tighten or loosen rules to match');
37
- lines.push('# your stated bargain with AI agents (training vs grounding vs user-directed).');
38
- lines.push('');
39
- lines.push('# --- Default allow-all baseline ---');
40
- lines.push('User-agent: *');
41
- lines.push('Allow: /');
42
- lines.push('');
43
- lines.push('# --- Training crawlers (use your content to train foundation models) ---');
44
- for (const bot of AI_BOTS.training) {
45
- lines.push(`User-agent: ${bot}`);
46
- lines.push('Disallow: # TODO: set to / to disallow, blank to allow');
47
- lines.push('');
48
- }
49
- lines.push('# --- Grounding crawlers (fetch in real time for answer-engine retrieval) ---');
50
- for (const bot of AI_BOTS.grounding) {
51
- lines.push(`User-agent: ${bot}`);
52
- lines.push('Allow: /');
53
- lines.push('');
54
- }
55
- lines.push('# --- User-directed crawlers (fetched on behalf of a logged-in user) ---');
56
- for (const bot of AI_BOTS.userDirected) {
57
- lines.push(`User-agent: ${bot}`);
58
- lines.push('Allow: /');
59
- lines.push('');
60
- }
61
- lines.push('# --- Cloudflare Content Signals (which uses you permit) ---');
62
- lines.push('# See https://contentsignals.org/');
63
- lines.push('Content-Signal: search=yes, ai-input=yes, ai-train=no');
64
- lines.push('');
65
- lines.push(`Sitemap: ${origin}/sitemap.xml`);
66
- lines.push('');
67
- return lines.join('\n');
68
- }
69
-
70
- function generateSecurityTxt() {
71
- const expires = new Date();
72
- expires.setFullYear(expires.getFullYear() + 1);
73
- const lines = [];
74
- lines.push('# /.well-known/security.txt — RFC 9116');
75
- lines.push('# Generated by @kirkelabs/agent-readiness-scan. Review before deploying.');
76
- lines.push('');
77
- lines.push('Contact: mailto:security@your-domain.example');
78
- lines.push(`Expires: ${expires.toISOString()}`);
79
- lines.push('Preferred-Languages: en');
80
- lines.push('# Canonical: https://your-domain.example/.well-known/security.txt');
81
- lines.push('# Encryption: https://your-domain.example/pgp-key.asc');
82
- lines.push('');
83
- return lines.join('\n');
84
- }
85
-
86
- function generateMcpServerCard(origin, name) {
87
- return JSON.stringify(
88
- {
89
- $schema: 'https://modelcontextprotocol.io/schemas/server-card/v1',
90
- name: name || 'TODO: Your service name',
91
- description: 'TODO: One-sentence description of what this MCP server lets agents do.',
92
- version: '0.1.0',
93
- url: `${origin}/mcp`,
94
- contact: {
95
- name: 'TODO: maintainer',
96
- email: 'mcp@your-domain.example',
97
- },
98
- tools: [
99
- {
100
- name: 'TODO_tool_name',
101
- description: 'TODO: what this tool does',
102
- inputSchema: {
103
- type: 'object',
104
- properties: {},
105
- required: [],
106
- },
107
- },
108
- ],
109
- authentication: {
110
- type: 'oauth2',
111
- authorizationEndpoint: `${origin}/oauth/authorize`,
112
- tokenEndpoint: `${origin}/oauth/token`,
113
- pkce: { codeChallengeMethods: ['S256'] },
114
- },
115
- },
116
- null,
117
- 2,
118
- );
119
- }
120
-
121
- function generateAcpManifest(origin, name) {
122
- return JSON.stringify(
123
- {
124
- version: '2026-04-17',
125
- merchant: {
126
- name: name || 'TODO: Your merchant name',
127
- url: origin,
128
- contact: 'merchant@your-domain.example',
129
- },
130
- capabilities: {
131
- checkout: { endpoint: `${origin}/acp/checkout`, supportedMethods: ['card'] },
132
- productCatalog: { endpoint: `${origin}/acp/products` },
133
- },
134
- keys: [
135
- {
136
- kty: 'TODO',
137
- kid: 'TODO',
138
- alg: 'RS256',
139
- use: 'sig',
140
- n: 'TODO: base64url-encoded RSA modulus',
141
- e: 'AQAB',
142
- },
143
- ],
144
- },
145
- null,
146
- 2,
147
- );
148
- }
149
-
150
- function deriveName($, finalUrl) {
151
- const t = ($('title').first().text() || '').trim();
152
- if (t) return t.split('—')[0].split('|')[0].trim();
153
- try {
154
- return new URL(finalUrl).hostname.replace(/^www\./, '');
155
- } catch {
156
- return 'Your Brand';
157
- }
158
- }
159
-
160
- export function generateCustomsDeclaration(ctx) {
161
- let origin = ctx.finalUrl;
162
- try {
163
- origin = new URL(ctx.finalUrl).origin;
164
- } catch {
165
- /* keep */
166
- }
167
- const name = deriveName(ctx.$, ctx.finalUrl);
168
- return {
169
- 'robots.txt': generateRobotsTxt(origin),
170
- '.well-known/security.txt': generateSecurityTxt(),
171
- '.well-known/mcp/server-card.json': generateMcpServerCard(origin, name),
172
- '.well-known/acp/manifest.json': generateAcpManifest(origin, name),
173
- };
174
- }
1
+ /**
2
+ * generators.js
3
+ *
4
+ * Produces the "customs declaration" — a drop-in set of files ready to
5
+ * paste at the site root that close most of the policy gaps the
6
+ * scanner identifies. The user reviews and edits before deploy;
7
+ * these are conservative scaffolds, not finished output.
8
+ *
9
+ * Generated artefacts:
10
+ * - robots.txt (per-bot policy + Content-Signals)
11
+ * - .well-known/security.txt (RFC 9116)
12
+ * - .well-known/mcp/server-card.json (MCP server card scaffold)
13
+ * - .well-known/acp/manifest.json (ACP manifest scaffold)
14
+ * - .well-known/agent-access.json (Open Agent Access + x402 scaffold)
15
+ *
16
+ * The OAA scaffold is offered alongside ACP/UCP as one of several live
17
+ * agent-commerce/agent-auth declarations. Disclosure: Open Agent Access is
18
+ * implemented by Kirke Labs, the author of this tool; it is scaffolded as a
19
+ * neutral option, not a default the user is steered toward.
20
+ */
21
+
22
+ const AI_BOTS = {
23
+ training: [
24
+ 'GPTBot',
25
+ 'ClaudeBot',
26
+ 'Google-Extended',
27
+ 'anthropic-ai',
28
+ 'CCBot',
29
+ 'Bytespider',
30
+ 'Amazonbot',
31
+ 'Applebot-Extended',
32
+ 'meta-externalagent',
33
+ ],
34
+ grounding: ['OAI-SearchBot', 'PerplexityBot', 'Claude-Web'],
35
+ userDirected: ['ChatGPT-User', 'Claude-User'],
36
+ };
37
+
38
+ function generateRobotsTxt(origin) {
39
+ const lines = [];
40
+ lines.push('# robots.txt — Customs Declaration');
41
+ lines.push('# Generated by @kirkelabs/agent-readiness-scan.');
42
+ lines.push('# Review and edit before deploying. Tighten or loosen rules to match');
43
+ lines.push(
44
+ '# your stated bargain with AI agents (training vs grounding vs user-directed).',
45
+ );
46
+ lines.push('');
47
+ lines.push('# --- Default allow-all baseline ---');
48
+ lines.push('User-agent: *');
49
+ lines.push('Allow: /');
50
+ lines.push('');
51
+ lines.push('# --- Training crawlers (use your content to train foundation models) ---');
52
+ for (const bot of AI_BOTS.training) {
53
+ lines.push(`User-agent: ${bot}`);
54
+ lines.push(
55
+ 'Disallow: # TODO: set to / to disallow, blank to allow',
56
+ );
57
+ lines.push('');
58
+ }
59
+ lines.push(
60
+ '# --- Grounding crawlers (fetch in real time for answer-engine retrieval) ---',
61
+ );
62
+ for (const bot of AI_BOTS.grounding) {
63
+ lines.push(`User-agent: ${bot}`);
64
+ lines.push('Allow: /');
65
+ lines.push('');
66
+ }
67
+ lines.push('# --- User-directed crawlers (fetched on behalf of a logged-in user) ---');
68
+ for (const bot of AI_BOTS.userDirected) {
69
+ lines.push(`User-agent: ${bot}`);
70
+ lines.push('Allow: /');
71
+ lines.push('');
72
+ }
73
+ lines.push('# --- Cloudflare Content Signals (which uses you permit) ---');
74
+ lines.push('# See https://contentsignals.org/');
75
+ lines.push('Content-Signal: search=yes, ai-input=yes, ai-train=no');
76
+ lines.push('');
77
+ lines.push(`Sitemap: ${origin}/sitemap.xml`);
78
+ lines.push('');
79
+ return lines.join('\n');
80
+ }
81
+
82
+ function generateSecurityTxt() {
83
+ const expires = new Date();
84
+ expires.setFullYear(expires.getFullYear() + 1);
85
+ const lines = [];
86
+ lines.push('# /.well-known/security.txt — RFC 9116');
87
+ lines.push('# Generated by @kirkelabs/agent-readiness-scan. Review before deploying.');
88
+ lines.push('');
89
+ lines.push('Contact: mailto:security@your-domain.example');
90
+ lines.push(`Expires: ${expires.toISOString()}`);
91
+ lines.push('Preferred-Languages: en');
92
+ lines.push('# Canonical: https://your-domain.example/.well-known/security.txt');
93
+ lines.push('# Encryption: https://your-domain.example/pgp-key.asc');
94
+ lines.push('');
95
+ return lines.join('\n');
96
+ }
97
+
98
+ function generateMcpServerCard(origin, name) {
99
+ return JSON.stringify(
100
+ {
101
+ $schema: 'https://modelcontextprotocol.io/schemas/server-card/v1',
102
+ name: name || 'TODO: Your service name',
103
+ description:
104
+ 'TODO: One-sentence description of what this MCP server lets agents do.',
105
+ version: '0.1.0',
106
+ url: `${origin}/mcp`,
107
+ contact: {
108
+ name: 'TODO: maintainer',
109
+ email: 'mcp@your-domain.example',
110
+ },
111
+ tools: [
112
+ {
113
+ name: 'TODO_tool_name',
114
+ description: 'TODO: what this tool does',
115
+ inputSchema: {
116
+ type: 'object',
117
+ properties: {},
118
+ required: [],
119
+ },
120
+ },
121
+ ],
122
+ authentication: {
123
+ type: 'oauth2',
124
+ authorizationEndpoint: `${origin}/oauth/authorize`,
125
+ tokenEndpoint: `${origin}/oauth/token`,
126
+ pkce: { codeChallengeMethods: ['S256'] },
127
+ },
128
+ // Scoped per-tool authorization via an Open Agent Access policy. This
129
+ // is an alternative to (or complement of) the OAuth flow above.
130
+ authorization: {
131
+ model: 'open-agent-access',
132
+ policy: `${origin}/.well-known/agent-access.json`,
133
+ },
134
+ },
135
+ null,
136
+ 2,
137
+ );
138
+ }
139
+
140
+ function generateAgentAccess(origin, name) {
141
+ return JSON.stringify(
142
+ {
143
+ $schema: 'https://openagentaccess.org/schema/agent-access/v1',
144
+ version: '0.1',
145
+ service: { name: name || 'TODO: Your service name', url: origin },
146
+ // Require a verifiable agent passport before any gated call.
147
+ defaults: { requireAgentIdentity: true, decision: 'allow' },
148
+ passport: {
149
+ required: true,
150
+ trustedIssuers: ['TODO: agent-passport issuer URL(s)'],
151
+ },
152
+ rules: [
153
+ { match: { purpose: 'read' }, decision: 'allow' },
154
+ { match: { purpose: 'summarize' }, decision: 'allow' },
155
+ { match: { purpose: 'ai-train' }, decision: 'deny' },
156
+ {
157
+ // A paid, agent-transactable capability settled via x402.
158
+ match: { tool: 'TODO_paid_tool' },
159
+ decision: 'charge',
160
+ payment: {
161
+ type: 'x402',
162
+ network: 'algorand',
163
+ settlement: 'TODO: receiver address / ASA id',
164
+ amount: 'TODO: price (e.g. "0.50 USDC")',
165
+ },
166
+ },
167
+ ],
168
+ },
169
+ null,
170
+ 2,
171
+ );
172
+ }
173
+
174
+ function generateAcpManifest(origin, name) {
175
+ return JSON.stringify(
176
+ {
177
+ version: '2026-04-17',
178
+ merchant: {
179
+ name: name || 'TODO: Your merchant name',
180
+ url: origin,
181
+ contact: 'merchant@your-domain.example',
182
+ },
183
+ capabilities: {
184
+ checkout: { endpoint: `${origin}/acp/checkout`, supportedMethods: ['card'] },
185
+ productCatalog: { endpoint: `${origin}/acp/products` },
186
+ },
187
+ keys: [
188
+ {
189
+ kty: 'TODO',
190
+ kid: 'TODO',
191
+ alg: 'RS256',
192
+ use: 'sig',
193
+ n: 'TODO: base64url-encoded RSA modulus',
194
+ e: 'AQAB',
195
+ },
196
+ ],
197
+ },
198
+ null,
199
+ 2,
200
+ );
201
+ }
202
+
203
+ function deriveName($, finalUrl) {
204
+ const t = ($('title').first().text() || '').trim();
205
+ if (t) return t.split('—')[0].split('|')[0].trim();
206
+ try {
207
+ return new URL(finalUrl).hostname.replace(/^www\./, '');
208
+ } catch {
209
+ return 'Your Brand';
210
+ }
211
+ }
212
+
213
+ export function generateCustomsDeclaration(ctx) {
214
+ let origin = ctx.finalUrl;
215
+ try {
216
+ origin = new URL(ctx.finalUrl).origin;
217
+ } catch {
218
+ /* keep */
219
+ }
220
+ const name = deriveName(ctx.$, ctx.finalUrl);
221
+ return {
222
+ 'robots.txt': generateRobotsTxt(origin),
223
+ '.well-known/security.txt': generateSecurityTxt(),
224
+ '.well-known/mcp/server-card.json': generateMcpServerCard(origin, name),
225
+ '.well-known/acp/manifest.json': generateAcpManifest(origin, name),
226
+ '.well-known/agent-access.json': generateAgentAccess(origin, name),
227
+ };
228
+ }