wmcp-annotate 1.0.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/AGENTS.md +108 -0
- package/IMPLEMENTATION_PLAN.md +187 -0
- package/LAUNCH.md +217 -0
- package/PRD.md +199 -0
- package/PROMPT.md +62 -0
- package/README.md +140 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +46 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/scan.d.ts +3 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +24 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/suggest.d.ts +3 -0
- package/dist/commands/suggest.d.ts.map +1 -0
- package/dist/commands/suggest.js +36 -0
- package/dist/commands/suggest.js.map +1 -0
- package/dist/commands/validate.d.ts +3 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +39 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/analyzer.d.ts +10 -0
- package/dist/lib/analyzer.d.ts.map +1 -0
- package/dist/lib/analyzer.js +80 -0
- package/dist/lib/analyzer.js.map +1 -0
- package/dist/lib/generator.d.ts +12 -0
- package/dist/lib/generator.d.ts.map +1 -0
- package/dist/lib/generator.js +136 -0
- package/dist/lib/generator.js.map +1 -0
- package/dist/lib/output.d.ts +6 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +35 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/scanner.d.ts +19 -0
- package/dist/lib/scanner.d.ts.map +1 -0
- package/dist/lib/scanner.js +159 -0
- package/dist/lib/scanner.js.map +1 -0
- package/dist/lib/validator.d.ts +13 -0
- package/dist/lib/validator.d.ts.map +1 -0
- package/dist/lib/validator.js +178 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/docs/index.html +563 -0
- package/marketing/email-outreach.md +183 -0
- package/marketing/landing-page.md +165 -0
- package/marketing/social-posts.md +192 -0
- package/package.json +58 -0
- package/scripts/publish.sh +33 -0
- package/specs/generate-command.md +147 -0
- package/specs/scan-command.md +92 -0
- package/specs/suggest-command.md +120 -0
- package/specs/validate-command.md +108 -0
- package/src/commands/generate.ts +48 -0
- package/src/commands/scan.ts +28 -0
- package/src/commands/suggest.ts +39 -0
- package/src/commands/validate.ts +44 -0
- package/src/index.ts +51 -0
- package/src/lib/analyzer.ts +90 -0
- package/src/lib/generator.ts +149 -0
- package/src/lib/output.ts +40 -0
- package/src/lib/scanner.ts +185 -0
- package/src/lib/validator.ts +192 -0
- package/src/types.ts +124 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import Handlebars from 'handlebars';
|
|
2
|
+
const jsTemplate = Handlebars.compile(`// WebMCP Tool Registration
|
|
3
|
+
// Generated by wmcp-annotate
|
|
4
|
+
// URL: {{url}}
|
|
5
|
+
// Generated: {{generatedAt}}
|
|
6
|
+
|
|
7
|
+
{{#each tools}}
|
|
8
|
+
// Tool: {{name}}
|
|
9
|
+
// {{description}}
|
|
10
|
+
navigator.modelContext.registerTool({
|
|
11
|
+
name: "{{name}}",
|
|
12
|
+
description: "{{description}}",
|
|
13
|
+
readOnly: {{readOnly}},
|
|
14
|
+
inputSchema: {{{schemaJson}}},
|
|
15
|
+
async execute({{#if hasInputs}}{ {{inputNames}} }{{/if}}) {
|
|
16
|
+
// TODO: Implement {{name}}
|
|
17
|
+
// Source element: {{sourceElement.selector}}
|
|
18
|
+
|
|
19
|
+
{{#if isForm}}
|
|
20
|
+
const form = document.querySelector('{{sourceElement.selector}}');
|
|
21
|
+
{{#each inputFields}}
|
|
22
|
+
form.querySelector('[name="{{this}}"]').value = {{this}};
|
|
23
|
+
{{/each}}
|
|
24
|
+
form.submit();
|
|
25
|
+
{{else}}
|
|
26
|
+
const element = document.querySelector('{{sourceElement.selector}}');
|
|
27
|
+
element.click();
|
|
28
|
+
{{/if}}
|
|
29
|
+
|
|
30
|
+
// Return results
|
|
31
|
+
return {
|
|
32
|
+
content: [{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: JSON.stringify({ success: true })
|
|
35
|
+
}]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
{{/each}}
|
|
41
|
+
`);
|
|
42
|
+
const tsTemplate = Handlebars.compile(`// WebMCP Tool Registration
|
|
43
|
+
// Generated by wmcp-annotate
|
|
44
|
+
// URL: {{url}}
|
|
45
|
+
|
|
46
|
+
{{#each tools}}
|
|
47
|
+
interface {{pascalName}}Input {
|
|
48
|
+
{{#each inputFields}}
|
|
49
|
+
{{this}}: string;
|
|
50
|
+
{{/each}}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
{{/each}}
|
|
54
|
+
{{#each tools}}
|
|
55
|
+
navigator.modelContext.registerTool({
|
|
56
|
+
name: "{{name}}",
|
|
57
|
+
description: "{{description}}",
|
|
58
|
+
readOnly: {{readOnly}},
|
|
59
|
+
inputSchema: {{{schemaJson}}},
|
|
60
|
+
async execute(input: {{pascalName}}Input) {
|
|
61
|
+
// TODO: Implement
|
|
62
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
{{/each}}
|
|
67
|
+
`);
|
|
68
|
+
const reactTemplate = Handlebars.compile(`// WebMCP React Hook
|
|
69
|
+
// Generated by wmcp-annotate
|
|
70
|
+
|
|
71
|
+
import { useEffect } from 'react';
|
|
72
|
+
|
|
73
|
+
export function useWebMCPTools() {
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (!navigator.modelContext) {
|
|
76
|
+
console.warn('WebMCP not available');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const cleanups: (() => void)[] = [];
|
|
81
|
+
|
|
82
|
+
{{#each tools}}
|
|
83
|
+
cleanups.push(
|
|
84
|
+
navigator.modelContext.registerTool({
|
|
85
|
+
name: "{{name}}",
|
|
86
|
+
description: "{{description}}",
|
|
87
|
+
readOnly: {{readOnly}},
|
|
88
|
+
inputSchema: {{{schemaJson}}},
|
|
89
|
+
async execute(input) {
|
|
90
|
+
// TODO: Implement
|
|
91
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true }) }] };
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
{{/each}}
|
|
97
|
+
return () => cleanups.forEach(cleanup => cleanup?.());
|
|
98
|
+
}, []);
|
|
99
|
+
}
|
|
100
|
+
`);
|
|
101
|
+
class Generator {
|
|
102
|
+
async generate(suggestions, options) {
|
|
103
|
+
const data = {
|
|
104
|
+
url: suggestions.url,
|
|
105
|
+
generatedAt: new Date().toISOString(),
|
|
106
|
+
tools: suggestions.tools.map(tool => this.prepareToolData(tool)),
|
|
107
|
+
};
|
|
108
|
+
switch (options.format) {
|
|
109
|
+
case 'ts':
|
|
110
|
+
return tsTemplate(data);
|
|
111
|
+
case 'react':
|
|
112
|
+
return reactTemplate(data);
|
|
113
|
+
case 'vue':
|
|
114
|
+
// Vue is similar to React for this use case
|
|
115
|
+
return reactTemplate(data).replace('useWebMCPTools', 'useWebMCPTools');
|
|
116
|
+
default:
|
|
117
|
+
return jsTemplate(data);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
prepareToolData(tool) {
|
|
121
|
+
const inputFields = tool.inputSchema.properties
|
|
122
|
+
? Object.keys(tool.inputSchema.properties)
|
|
123
|
+
: [];
|
|
124
|
+
return {
|
|
125
|
+
...tool,
|
|
126
|
+
schemaJson: JSON.stringify(tool.inputSchema, null, 2),
|
|
127
|
+
hasInputs: inputFields.length > 0,
|
|
128
|
+
inputNames: inputFields.join(', '),
|
|
129
|
+
inputFields,
|
|
130
|
+
isForm: tool.sourceElement.type === 'form',
|
|
131
|
+
pascalName: tool.name.charAt(0).toUpperCase() + tool.name.slice(1),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export const generator = new Generator();
|
|
136
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/lib/generator.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,MAAM,YAAY,CAAC;AAOpC,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCrC,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;CAyBrC,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCxC,CAAC,CAAC;AAEH,MAAM,SAAS;IACb,KAAK,CAAC,QAAQ,CAAC,WAA0B,EAAE,OAAwB;QACjE,MAAM,IAAI,GAAG;YACX,GAAG,EAAE,WAAW,CAAC,GAAG;YACpB,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACrC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;SACjE,CAAC;QAEF,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,KAAK,IAAI;gBACP,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;YAC1B,KAAK,OAAO;gBACV,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;YAC7B,KAAK,KAAK;gBACR,4CAA4C;gBAC5C,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;YACzE;gBACE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,IAAoB;QAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU;YAC7C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;YAC1C,CAAC,CAAC,EAAE,CAAC;QAEP,OAAO;YACL,GAAG,IAAI;YACP,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;YACrD,SAAS,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC;YACjC,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,WAAW;YACX,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,MAAM;YAC1C,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;SACnE,CAAC;IACJ,CAAC;CACF;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAEA,wBAAsB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAU7G;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAG3D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
export async function writeOutput(data, options) {
|
|
3
|
+
const output = options.format === 'yaml'
|
|
4
|
+
? toYaml(data)
|
|
5
|
+
: JSON.stringify(data, null, 2);
|
|
6
|
+
if (options.output) {
|
|
7
|
+
await fs.writeFile(options.output, output, 'utf-8');
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
console.log(output);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function readInput(path) {
|
|
14
|
+
const content = await fs.readFile(path, 'utf-8');
|
|
15
|
+
return JSON.parse(content);
|
|
16
|
+
}
|
|
17
|
+
function toYaml(data, indent = 0) {
|
|
18
|
+
// Simple YAML conversion
|
|
19
|
+
const spaces = ' '.repeat(indent);
|
|
20
|
+
if (Array.isArray(data)) {
|
|
21
|
+
return data.map(item => `${spaces}- ${toYaml(item, indent + 1).trim()}`).join('\\n');
|
|
22
|
+
}
|
|
23
|
+
if (typeof data === 'object' && data !== null) {
|
|
24
|
+
return Object.entries(data)
|
|
25
|
+
.map(([key, value]) => {
|
|
26
|
+
if (typeof value === 'object' && value !== null) {
|
|
27
|
+
return `${spaces}${key}:\\n${toYaml(value, indent + 1)}`;
|
|
28
|
+
}
|
|
29
|
+
return `${spaces}${key}: ${JSON.stringify(value)}`;
|
|
30
|
+
})
|
|
31
|
+
.join('\\n');
|
|
32
|
+
}
|
|
33
|
+
return JSON.stringify(data);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,aAAa,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAa,EAAE,OAA6C;IAC5F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,MAAM;QACtC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;QACd,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAElC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,IAAY;IAC7C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;AAClC,CAAC;AAED,SAAS,MAAM,CAAC,IAAa,EAAE,MAAM,GAAG,CAAC;IACvC,yBAAyB;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEnC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;aACxB,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;YACpB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAChD,OAAO,GAAG,MAAM,GAAG,GAAG,OAAO,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;YAC3D,CAAC;YACD,OAAO,GAAG,MAAM,GAAG,GAAG,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,CAAC,CAAC;aACD,IAAI,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ScanResult } from '../types.js';
|
|
2
|
+
interface ScanOptions {
|
|
3
|
+
depth?: number;
|
|
4
|
+
verbose?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare class Scanner {
|
|
7
|
+
private browser;
|
|
8
|
+
scan(url: string, options?: ScanOptions): Promise<ScanResult>;
|
|
9
|
+
private scanElements;
|
|
10
|
+
private getFormInputs;
|
|
11
|
+
private getInputLabel;
|
|
12
|
+
private getLabel;
|
|
13
|
+
private getSelector;
|
|
14
|
+
private ensureBrowser;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare const scanner: Scanner;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/lib/scanner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAuC,MAAM,aAAa,CAAC;AAEnF,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,cAAM,OAAO;IACX,OAAO,CAAC,OAAO,CAAwB;IAEjC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;YAqCzD,YAAY;YAoDZ,aAAa;YAyBb,aAAa;YAqBb,QAAQ;YAUR,WAAW;YAcX,aAAa;IAMrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B;AAED,eAAO,MAAM,OAAO,SAAgB,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
class Scanner {
|
|
3
|
+
browser = null;
|
|
4
|
+
async scan(url, options = {}) {
|
|
5
|
+
const { depth = 1, verbose = false } = options;
|
|
6
|
+
await this.ensureBrowser();
|
|
7
|
+
const page = await this.browser.newPage();
|
|
8
|
+
try {
|
|
9
|
+
// Navigate and wait for load
|
|
10
|
+
await page.goto(url, { waitUntil: 'networkidle' });
|
|
11
|
+
// Collect API calls
|
|
12
|
+
const apiCalls = [];
|
|
13
|
+
page.on('request', (request) => {
|
|
14
|
+
const reqUrl = request.url();
|
|
15
|
+
if (reqUrl.includes('/api/') || request.resourceType() === 'fetch' || request.resourceType() === 'xhr') {
|
|
16
|
+
apiCalls.push({
|
|
17
|
+
method: request.method(),
|
|
18
|
+
url: reqUrl,
|
|
19
|
+
params: Array.from(new URL(reqUrl).searchParams.keys()),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
// Scan for elements
|
|
24
|
+
const elements = await this.scanElements(page);
|
|
25
|
+
return {
|
|
26
|
+
url,
|
|
27
|
+
scannedAt: new Date().toISOString(),
|
|
28
|
+
elements,
|
|
29
|
+
apiCalls,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await page.close();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async scanElements(page) {
|
|
37
|
+
const elements = [];
|
|
38
|
+
// Scan forms
|
|
39
|
+
const forms = await page.$$('form');
|
|
40
|
+
for (const form of forms) {
|
|
41
|
+
const id = await form.getAttribute('id');
|
|
42
|
+
const inputs = await this.getFormInputs(form);
|
|
43
|
+
const submitBtn = await form.$('button[type="submit"], input[type="submit"]');
|
|
44
|
+
elements.push({
|
|
45
|
+
type: 'form',
|
|
46
|
+
id: id || undefined,
|
|
47
|
+
selector: id ? `#${id}` : 'form',
|
|
48
|
+
label: await this.getLabel(form) || 'Form',
|
|
49
|
+
inputs,
|
|
50
|
+
submitButton: submitBtn ? {
|
|
51
|
+
selector: await this.getSelector(submitBtn),
|
|
52
|
+
label: await submitBtn.textContent() || 'Submit',
|
|
53
|
+
} : undefined,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Scan buttons (not in forms)
|
|
57
|
+
const buttons = await page.$$('button:not(form button), [role="button"]');
|
|
58
|
+
for (const button of buttons) {
|
|
59
|
+
const label = await button.textContent();
|
|
60
|
+
if (label?.trim()) {
|
|
61
|
+
elements.push({
|
|
62
|
+
type: 'button',
|
|
63
|
+
selector: await this.getSelector(button),
|
|
64
|
+
label: label.trim(),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Scan links with actions
|
|
69
|
+
const actionLinks = await page.$$('a[href^="#"], a[href^="javascript:"], a[onclick]');
|
|
70
|
+
for (const link of actionLinks) {
|
|
71
|
+
const label = await link.textContent();
|
|
72
|
+
if (label?.trim()) {
|
|
73
|
+
elements.push({
|
|
74
|
+
type: 'link',
|
|
75
|
+
selector: await this.getSelector(link),
|
|
76
|
+
label: label.trim(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return elements;
|
|
81
|
+
}
|
|
82
|
+
async getFormInputs(form) {
|
|
83
|
+
const inputs = [];
|
|
84
|
+
const inputElements = await form.$$('input, select, textarea');
|
|
85
|
+
for (const input of inputElements) {
|
|
86
|
+
const name = await input.getAttribute('name');
|
|
87
|
+
const type = await input.getAttribute('type') || 'text';
|
|
88
|
+
const required = await input.getAttribute('required') !== null;
|
|
89
|
+
const placeholder = await input.getAttribute('placeholder');
|
|
90
|
+
const label = await this.getInputLabel(input);
|
|
91
|
+
if (name && type !== 'hidden' && type !== 'submit') {
|
|
92
|
+
inputs.push({
|
|
93
|
+
name,
|
|
94
|
+
type,
|
|
95
|
+
label: label || undefined,
|
|
96
|
+
required,
|
|
97
|
+
placeholder: placeholder || undefined,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return inputs;
|
|
102
|
+
}
|
|
103
|
+
async getInputLabel(input) {
|
|
104
|
+
try {
|
|
105
|
+
const id = await input.getAttribute('id');
|
|
106
|
+
if (id) {
|
|
107
|
+
// Use evaluate to find the label in the DOM
|
|
108
|
+
const labelText = await input.evaluate((el) => {
|
|
109
|
+
const id = el.getAttribute('id');
|
|
110
|
+
if (id) {
|
|
111
|
+
const label = document.querySelector(`label[for="${id}"]`);
|
|
112
|
+
return label?.textContent || null;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
});
|
|
116
|
+
return labelText;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Ignore errors
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
async getLabel(element) {
|
|
125
|
+
const ariaLabel = await element.getAttribute('aria-label');
|
|
126
|
+
if (ariaLabel)
|
|
127
|
+
return ariaLabel;
|
|
128
|
+
const title = await element.getAttribute('title');
|
|
129
|
+
if (title)
|
|
130
|
+
return title;
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
async getSelector(element) {
|
|
134
|
+
const id = await element.getAttribute('id');
|
|
135
|
+
if (id)
|
|
136
|
+
return `#${id}`;
|
|
137
|
+
const className = await element.getAttribute('class');
|
|
138
|
+
if (className) {
|
|
139
|
+
const classes = className.split(' ').filter((c) => c && !c.includes(':'));
|
|
140
|
+
if (classes.length > 0)
|
|
141
|
+
return `.${classes[0]}`;
|
|
142
|
+
}
|
|
143
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase());
|
|
144
|
+
return tagName;
|
|
145
|
+
}
|
|
146
|
+
async ensureBrowser() {
|
|
147
|
+
if (!this.browser) {
|
|
148
|
+
this.browser = await chromium.launch({ headless: true });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async close() {
|
|
152
|
+
if (this.browser) {
|
|
153
|
+
await this.browser.close();
|
|
154
|
+
this.browser = null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export const scanner = new Scanner();
|
|
159
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/lib/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ValidationResult } from '../types.js';
|
|
2
|
+
declare class Validator {
|
|
3
|
+
private browser;
|
|
4
|
+
validate(url: string): Promise<ValidationResult>;
|
|
5
|
+
private validateTool;
|
|
6
|
+
private isCamelCase;
|
|
7
|
+
private toCamelCase;
|
|
8
|
+
private ensureBrowser;
|
|
9
|
+
close(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare const validator: Validator;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/lib/validator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAmC,MAAM,aAAa,CAAC;AAKrF,cAAM,SAAS;IACb,OAAO,CAAC,OAAO,CAAwB;IAEjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgDtD,OAAO,CAAC,YAAY;IA6GpB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,WAAW;YAML,aAAa;IAMrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAM7B;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
import AjvModule from 'ajv';
|
|
3
|
+
const Ajv = AjvModule.default || AjvModule;
|
|
4
|
+
const ajv = new Ajv();
|
|
5
|
+
class Validator {
|
|
6
|
+
browser = null;
|
|
7
|
+
async validate(url) {
|
|
8
|
+
await this.ensureBrowser();
|
|
9
|
+
const page = await this.browser.newPage();
|
|
10
|
+
try {
|
|
11
|
+
// Navigate with WebMCP flag (Chrome 146+)
|
|
12
|
+
await page.goto(url, { waitUntil: 'networkidle' });
|
|
13
|
+
// Query registered tools
|
|
14
|
+
const tools = await page.evaluate(() => {
|
|
15
|
+
// @ts-ignore - WebMCP API
|
|
16
|
+
if (typeof navigator.modelContext?.getTools === 'function') {
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
return navigator.modelContext.getTools();
|
|
19
|
+
}
|
|
20
|
+
return [];
|
|
21
|
+
});
|
|
22
|
+
const validations = [];
|
|
23
|
+
let validCount = 0;
|
|
24
|
+
let warningCount = 0;
|
|
25
|
+
let errorCount = 0;
|
|
26
|
+
for (const tool of tools) {
|
|
27
|
+
const validation = this.validateTool(tool);
|
|
28
|
+
validations.push(validation);
|
|
29
|
+
if (validation.status === 'valid')
|
|
30
|
+
validCount++;
|
|
31
|
+
else if (validation.status === 'warning')
|
|
32
|
+
warningCount++;
|
|
33
|
+
else
|
|
34
|
+
errorCount++;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
url,
|
|
38
|
+
validatedAt: new Date().toISOString(),
|
|
39
|
+
summary: {
|
|
40
|
+
total: tools.length,
|
|
41
|
+
valid: validCount,
|
|
42
|
+
warnings: warningCount,
|
|
43
|
+
errors: errorCount,
|
|
44
|
+
},
|
|
45
|
+
tools: validations,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await page.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
validateTool(tool) {
|
|
53
|
+
const checks = {};
|
|
54
|
+
const issues = [];
|
|
55
|
+
// Check name format (camelCase)
|
|
56
|
+
if (this.isCamelCase(tool.name)) {
|
|
57
|
+
checks.nameConvention = 'pass';
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
checks.nameConvention = 'fail';
|
|
61
|
+
issues.push({
|
|
62
|
+
level: 'error',
|
|
63
|
+
code: 'NAME_FORMAT',
|
|
64
|
+
message: 'Tool name must be camelCase',
|
|
65
|
+
suggestion: `Rename to: ${this.toCamelCase(tool.name)}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Check description
|
|
69
|
+
if (!tool.description) {
|
|
70
|
+
checks.descriptionPresent = 'fail';
|
|
71
|
+
issues.push({
|
|
72
|
+
level: 'error',
|
|
73
|
+
code: 'DESCRIPTION_MISSING',
|
|
74
|
+
message: 'Description is required',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
else if (tool.description.length < 20) {
|
|
78
|
+
checks.descriptionPresent = 'warning';
|
|
79
|
+
issues.push({
|
|
80
|
+
level: 'warning',
|
|
81
|
+
code: 'DESCRIPTION_TOO_SHORT',
|
|
82
|
+
message: 'Description should be at least 20 characters',
|
|
83
|
+
suggestion: 'Add more detail about what this tool does',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
checks.descriptionPresent = 'pass';
|
|
88
|
+
}
|
|
89
|
+
// Check schema validity
|
|
90
|
+
if (tool.inputSchema) {
|
|
91
|
+
try {
|
|
92
|
+
ajv.compile(tool.inputSchema);
|
|
93
|
+
checks.schemaValid = 'pass';
|
|
94
|
+
// Check for property descriptions
|
|
95
|
+
const props = tool.inputSchema.properties || {};
|
|
96
|
+
const missingDesc = Object.entries(props).filter(([_, prop]) => !prop.description);
|
|
97
|
+
if (missingDesc.length > 0) {
|
|
98
|
+
checks.schemaDescriptions = 'warning';
|
|
99
|
+
issues.push({
|
|
100
|
+
level: 'warning',
|
|
101
|
+
code: 'SCHEMA_NO_DESCRIPTION',
|
|
102
|
+
message: `Properties missing descriptions: ${missingDesc.map(([k]) => k).join(', ')}`,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
checks.schemaDescriptions = 'pass';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
checks.schemaValid = 'fail';
|
|
111
|
+
issues.push({
|
|
112
|
+
level: 'error',
|
|
113
|
+
code: 'SCHEMA_INVALID',
|
|
114
|
+
message: `Invalid JSON Schema: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Check handler
|
|
119
|
+
if (typeof tool.execute === 'function') {
|
|
120
|
+
checks.handlerPresent = 'pass';
|
|
121
|
+
// Check if async
|
|
122
|
+
if (tool.execute.constructor.name === 'AsyncFunction') {
|
|
123
|
+
checks.handlerAsync = 'pass';
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
checks.handlerAsync = 'fail';
|
|
127
|
+
issues.push({
|
|
128
|
+
level: 'error',
|
|
129
|
+
code: 'HANDLER_NOT_ASYNC',
|
|
130
|
+
message: 'Execute handler must be an async function',
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
checks.handlerPresent = 'fail';
|
|
136
|
+
issues.push({
|
|
137
|
+
level: 'error',
|
|
138
|
+
code: 'HANDLER_MISSING',
|
|
139
|
+
message: 'Execute handler is required',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Determine overall status
|
|
143
|
+
let status = 'valid';
|
|
144
|
+
if (issues.some(i => i.level === 'error')) {
|
|
145
|
+
status = 'error';
|
|
146
|
+
}
|
|
147
|
+
else if (issues.some(i => i.level === 'warning')) {
|
|
148
|
+
status = 'warning';
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
name: tool.name,
|
|
152
|
+
status,
|
|
153
|
+
checks,
|
|
154
|
+
issues: issues.length > 0 ? issues : undefined,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
isCamelCase(str) {
|
|
158
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(str);
|
|
159
|
+
}
|
|
160
|
+
toCamelCase(str) {
|
|
161
|
+
return str
|
|
162
|
+
.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
|
|
163
|
+
.replace(/^./, c => c.toLowerCase());
|
|
164
|
+
}
|
|
165
|
+
async ensureBrowser() {
|
|
166
|
+
if (!this.browser) {
|
|
167
|
+
this.browser = await chromium.launch({ headless: true });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async close() {
|
|
171
|
+
if (this.browser) {
|
|
172
|
+
await this.browser.close();
|
|
173
|
+
this.browser = null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export const validator = new Validator();
|
|
178
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/lib/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;AACpD,OAAO,SAAS,MAAM,KAAK,CAAC;AAG5B,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC;AAC3C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;AAEtB,MAAM,SAAS;IACL,OAAO,GAAmB,IAAI,CAAC;IAEvC,KAAK,CAAC,QAAQ,CAAC,GAAW;QACxB,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAQ,CAAC,OAAO,EAAE,CAAC;QAE3C,IAAI,CAAC;YACH,0CAA0C;YAC1C,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;YAEnD,yBAAyB;YACzB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;gBACrC,0BAA0B;gBAC1B,IAAI,OAAO,SAAS,CAAC,YAAY,EAAE,QAAQ,KAAK,UAAU,EAAE,CAAC;oBAC3D,aAAa;oBACb,OAAO,SAAS,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;gBAC3C,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YAEH,MAAM,WAAW,GAAqB,EAAE,CAAC;YACzC,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,IAAI,UAAU,GAAG,CAAC,CAAC;YAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAC3C,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAE7B,IAAI,UAAU,CAAC,MAAM,KAAK,OAAO;oBAAE,UAAU,EAAE,CAAC;qBAC3C,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;oBAAE,YAAY,EAAE,CAAC;;oBACpD,UAAU,EAAE,CAAC;YACpB,CAAC;YAED,OAAO;gBACL,GAAG;gBACH,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACrC,OAAO,EAAE;oBACP,KAAK,EAAE,KAAK,CAAC,MAAM;oBACnB,KAAK,EAAE,UAAU;oBACjB,QAAQ,EAAE,YAAY;oBACtB,MAAM,EAAE,UAAU;iBACnB;gBACD,KAAK,EAAE,WAAW;aACnB,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,IAAS;QAC5B,MAAM,MAAM,GAAgD,EAAE,CAAC;QAC/D,MAAM,MAAM,GAAsB,EAAE,CAAC;QAErC,gCAAgC;QAChC,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,aAAa;gBACnB,OAAO,EAAE,6BAA6B;gBACtC,UAAU,EAAE,cAAc,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aACxD,CAAC,CAAC;QACL,CAAC;QAED,oBAAoB;QACpB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,CAAC,kBAAkB,GAAG,MAAM,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,qBAAqB;gBAC3B,OAAO,EAAE,yBAAyB;aACnC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,kBAAkB,GAAG,SAAS,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,SAAS;gBAChB,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,8CAA8C;gBACvD,UAAU,EAAE,2CAA2C;aACxD,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,kBAAkB,GAAG,MAAM,CAAC;QACrC,CAAC;QAED,wBAAwB;QACxB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC9B,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC;gBAE5B,kCAAkC;gBAClC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,IAAI,EAAE,CAAC;gBAChD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAC9C,CAAC,CAAC,CAAC,EAAE,IAAI,CAAgB,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAChD,CAAC;gBAEF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC3B,MAAM,CAAC,kBAAkB,GAAG,SAAS,CAAC;oBACtC,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,SAAS;wBAChB,IAAI,EAAE,uBAAuB;wBAC7B,OAAO,EAAE,oCAAoC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;qBACtF,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,kBAAkB,GAAG,MAAM,CAAC;gBACrC,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC;gBAC5B,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,gBAAgB;oBACtB,OAAO,EAAE,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE;iBAC5F,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,gBAAgB;QAChB,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACvC,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC;YAE/B,iBAAiB;YACjB,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;gBACtD,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE,mBAAmB;oBACzB,OAAO,EAAE,2CAA2C;iBACrD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,OAAO;gBACd,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,6BAA6B;aACvC,CAAC,CAAC;QACL,CAAC;QAED,2BAA2B;QAC3B,IAAI,MAAM,GAAkC,OAAO,CAAC;QACpD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,EAAE,CAAC;YAC1C,MAAM,GAAG,OAAO,CAAC;QACnB,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,EAAE,CAAC;YACnD,MAAM,GAAG,SAAS,CAAC;QACrB,CAAC;QAED,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM;YACN,MAAM;YACN,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SAC/C,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,GAAW;QAC7B,OAAO,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC,CAAC;IAEO,WAAW,CAAC,GAAW;QAC7B,OAAO,GAAG;aACP,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aAC7D,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC"}
|