vite-plugin-ai-annotator 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/README.md +98 -0
- package/dist/annotator/detectors.d.ts +33 -0
- package/dist/annotator/inspection.d.ts +10 -0
- package/dist/annotator/selection.d.ts +24 -0
- package/dist/annotator-toolbar.d.ts +67 -0
- package/dist/annotator-toolbar.js +8066 -0
- package/dist/index.cjs +448 -0
- package/dist/index.d.ts +2 -0
- package/dist/inspector/ai.d.ts +20 -0
- package/dist/inspector/console.d.ts +20 -0
- package/dist/inspector/detectors.d.ts +33 -0
- package/dist/inspector/inspection.d.ts +10 -0
- package/dist/inspector/logger.d.ts +11 -0
- package/dist/inspector/selection.d.ts +24 -0
- package/dist/inspector/style.d.ts +13 -0
- package/dist/inspector-toolbar.js +6705 -0
- package/dist/mcp/index.d.ts +8 -0
- package/dist/rpc/client.generated.d.ts +82 -0
- package/dist/rpc/define.d.ts +81 -0
- package/dist/rpc/server.generated.d.ts +82 -0
- package/dist/rpc/types.generated.d.ts +21 -0
- package/dist/shared/schemas.d.ts +119 -0
- package/dist/shared/types.d.ts +1 -0
- package/dist/trpc/context.d.ts +28 -0
- package/dist/trpc/router.d.ts +195 -0
- package/dist/trpc-server.d.ts +14 -0
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/sourcemap.d.ts +72 -0
- package/dist/utils/xpath.d.ts +54 -0
- package/dist/vite-plugin.d.ts +25 -0
- package/dist/vite-plugin.js +219 -0
- package/dist/vite-plugin.js.map +7 -0
- package/dist/ws-server.d.ts +26 -0
- package/package.json +86 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source Map utilities for mapping runtime elements back to source code
|
|
3
|
+
*/
|
|
4
|
+
export interface SourceMapPosition {
|
|
5
|
+
line: number;
|
|
6
|
+
column: number;
|
|
7
|
+
source?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SourceMapInfo {
|
|
11
|
+
file: string;
|
|
12
|
+
line: number;
|
|
13
|
+
column: number;
|
|
14
|
+
endLine?: number;
|
|
15
|
+
endColumn?: number;
|
|
16
|
+
originalSource?: string;
|
|
17
|
+
sourcesContent?: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Basic source map consumer for parsing VLQ-encoded mappings
|
|
21
|
+
* Note: This is a simplified implementation. For production use,
|
|
22
|
+
* consider using the 'source-map' library for full compatibility.
|
|
23
|
+
*/
|
|
24
|
+
export declare class BasicSourceMapConsumer {
|
|
25
|
+
private sources;
|
|
26
|
+
private sourcesContent?;
|
|
27
|
+
constructor(sourceMap: any);
|
|
28
|
+
/**
|
|
29
|
+
* Find original position for generated position
|
|
30
|
+
* This is a simplified implementation that looks for approximate mappings
|
|
31
|
+
*/
|
|
32
|
+
originalPositionFor(line: number, column: number): SourceMapPosition | null;
|
|
33
|
+
/**
|
|
34
|
+
* Get source content for a given source file
|
|
35
|
+
*/
|
|
36
|
+
sourceContentFor(source: string): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Get all available sources
|
|
39
|
+
*/
|
|
40
|
+
getSources(): string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Attempt to find and parse source maps from various sources
|
|
44
|
+
*/
|
|
45
|
+
export declare class SourceMapResolver {
|
|
46
|
+
private sourceMapCache;
|
|
47
|
+
/**
|
|
48
|
+
* Try to find source map for a given file URL
|
|
49
|
+
*/
|
|
50
|
+
findSourceMapForFile(fileUrl: string): Promise<BasicSourceMapConsumer | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve original position from generated position using source maps
|
|
53
|
+
*/
|
|
54
|
+
resolvePosition(fileUrl: string, line: number, column: number): Promise<SourceMapPosition | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Get enhanced location info by combining runtime data with source maps
|
|
57
|
+
*/
|
|
58
|
+
getEnhancedLocationInfo(fileUrl: string, line: number, column: number): Promise<SourceMapInfo | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Clear the source map cache
|
|
61
|
+
*/
|
|
62
|
+
clearCache(): void;
|
|
63
|
+
}
|
|
64
|
+
export declare const sourceMapResolver: SourceMapResolver;
|
|
65
|
+
/**
|
|
66
|
+
* Helper function to extract source location from browser stack traces
|
|
67
|
+
*/
|
|
68
|
+
export declare function extractLocationFromStackTrace(error: Error): {
|
|
69
|
+
file: string;
|
|
70
|
+
line: number;
|
|
71
|
+
column: number;
|
|
72
|
+
} | null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced XPath and CSS selector utilities for browser environment
|
|
3
|
+
*/
|
|
4
|
+
export interface ElementSelector {
|
|
5
|
+
xpath: string;
|
|
6
|
+
cssSelector: string;
|
|
7
|
+
uniqueId?: string;
|
|
8
|
+
position?: {
|
|
9
|
+
index: number;
|
|
10
|
+
total: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare class XPathUtils {
|
|
14
|
+
static generateXPath(element: Element): string;
|
|
15
|
+
private static getXPathStep;
|
|
16
|
+
private static getXPathIndex;
|
|
17
|
+
private static isValidId;
|
|
18
|
+
/**
|
|
19
|
+
* Generate optimized CSS selector using optimal-select library
|
|
20
|
+
*/
|
|
21
|
+
static generateOptimalSelector(element: Element): string;
|
|
22
|
+
/**
|
|
23
|
+
* Generate enhanced CSS selector optimized for browser environment
|
|
24
|
+
*/
|
|
25
|
+
static generateEnhancedCSSSelector(element: Element): string;
|
|
26
|
+
/**
|
|
27
|
+
* Validate XPath by testing if it uniquely identifies the element
|
|
28
|
+
*/
|
|
29
|
+
static validateXPath(xpath: string, targetElement: Element): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Validate CSS selector by testing if it uniquely identifies the element
|
|
32
|
+
*/
|
|
33
|
+
static validateCSSSelector(selector: string, targetElement: Element): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Find element using XPath
|
|
36
|
+
*/
|
|
37
|
+
static findElementByXPath(xpath: string): Element | null;
|
|
38
|
+
/**
|
|
39
|
+
* Generate comprehensive selector information for an element
|
|
40
|
+
*/
|
|
41
|
+
static generateElementSelector(element: Element): ElementSelector;
|
|
42
|
+
/**
|
|
43
|
+
* Check if an ID is unique in the document
|
|
44
|
+
*/
|
|
45
|
+
private static isUniqueId;
|
|
46
|
+
/**
|
|
47
|
+
* Generate multiple selector strategies for robust element identification
|
|
48
|
+
*/
|
|
49
|
+
static generateRobustSelectors(element: Element): {
|
|
50
|
+
primary: ElementSelector;
|
|
51
|
+
fallbacks: string[];
|
|
52
|
+
confidence: 'high' | 'medium' | 'low';
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
export interface AiAnnotatorOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Port to run the server on
|
|
5
|
+
* @default 7318
|
|
6
|
+
*/
|
|
7
|
+
port?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Address for the server to listen on
|
|
10
|
+
* @default 'localhost'
|
|
11
|
+
*/
|
|
12
|
+
listenAddress?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Public URL for reverse proxy scenarios
|
|
15
|
+
* @default Automatically determined from listenAddress and port
|
|
16
|
+
*/
|
|
17
|
+
publicAddress?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Verbose logging
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
verbose?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function aiAnnotator(options?: AiAnnotatorOptions): Plugin;
|
|
25
|
+
export default aiAnnotator;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// src/vite-plugin.ts
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
var AiAnnotatorServer = class {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.serverProcess = null;
|
|
9
|
+
const port = options.port ?? 7318;
|
|
10
|
+
const listenAddress = options.listenAddress ?? "localhost";
|
|
11
|
+
this.options = {
|
|
12
|
+
port,
|
|
13
|
+
listenAddress,
|
|
14
|
+
publicAddress: options.publicAddress ?? `http://${listenAddress}:${port}`,
|
|
15
|
+
verbose: options.verbose ?? false
|
|
16
|
+
};
|
|
17
|
+
const currentFileDir = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
this.isDevelopment = currentFileDir.endsWith("/src") || currentFileDir.endsWith("\\src");
|
|
19
|
+
if (this.isDevelopment) {
|
|
20
|
+
this.packageDir = dirname(currentFileDir);
|
|
21
|
+
} else {
|
|
22
|
+
this.packageDir = dirname(currentFileDir);
|
|
23
|
+
}
|
|
24
|
+
this.log(`Package directory: ${this.packageDir}`);
|
|
25
|
+
this.log(`Running in ${this.isDevelopment ? "development" : "production"} mode`);
|
|
26
|
+
}
|
|
27
|
+
async start() {
|
|
28
|
+
if (this.serverProcess) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const isRunning = await this.isServerRunning();
|
|
32
|
+
if (isRunning) {
|
|
33
|
+
this.log(`Server already running on port ${this.options.port}, skipping spawn`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let serverFile;
|
|
37
|
+
let cmd;
|
|
38
|
+
let args;
|
|
39
|
+
if (this.isDevelopment) {
|
|
40
|
+
serverFile = join(this.packageDir, "src", "index.ts");
|
|
41
|
+
cmd = "bun";
|
|
42
|
+
args = [serverFile];
|
|
43
|
+
} else {
|
|
44
|
+
serverFile = join(this.packageDir, "dist", "index.cjs");
|
|
45
|
+
if (!existsSync(serverFile)) {
|
|
46
|
+
const fallbackFile = join(this.packageDir, "src", "index.ts");
|
|
47
|
+
if (existsSync(fallbackFile)) {
|
|
48
|
+
this.log("dist/index.cjs not found, falling back to src/index.ts");
|
|
49
|
+
serverFile = fallbackFile;
|
|
50
|
+
cmd = "bun";
|
|
51
|
+
args = [serverFile];
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error(`Annotator server file not found at ${serverFile} or ${fallbackFile}`);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
cmd = "node";
|
|
57
|
+
args = [serverFile];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
args.push("--port", String(this.options.port));
|
|
61
|
+
args.push("--listen", this.options.listenAddress);
|
|
62
|
+
args.push("--public-address", this.options.publicAddress);
|
|
63
|
+
if (this.options.verbose) {
|
|
64
|
+
args.push("--verbose");
|
|
65
|
+
}
|
|
66
|
+
this.log(`Starting annotator server: ${cmd} ${args.join(" ")}`);
|
|
67
|
+
this.log(`Working directory: ${this.packageDir}`);
|
|
68
|
+
this.serverProcess = spawn(cmd, args, {
|
|
69
|
+
cwd: this.packageDir,
|
|
70
|
+
env: process.env,
|
|
71
|
+
stdio: this.options.verbose ? "inherit" : "pipe"
|
|
72
|
+
});
|
|
73
|
+
if (!this.options.verbose && this.serverProcess.stdout) {
|
|
74
|
+
this.serverProcess.stdout.on("data", (_data) => {
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (!this.options.verbose && this.serverProcess.stderr) {
|
|
78
|
+
this.serverProcess.stderr.on("data", (data) => {
|
|
79
|
+
console.error(`[ai-annotator-server] Error: ${data}`);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
this.serverProcess.on("error", (error) => {
|
|
83
|
+
console.error("[ai-annotator-server] Failed to start:", error);
|
|
84
|
+
});
|
|
85
|
+
this.serverProcess.on("exit", (code) => {
|
|
86
|
+
if (code !== 0 && code !== null) {
|
|
87
|
+
console.error(`[ai-annotator-server] Process exited with code ${code}`);
|
|
88
|
+
}
|
|
89
|
+
this.serverProcess = null;
|
|
90
|
+
});
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
92
|
+
}
|
|
93
|
+
async stop() {
|
|
94
|
+
if (this.serverProcess) {
|
|
95
|
+
this.log("Stopping annotator server...");
|
|
96
|
+
this.serverProcess.kill("SIGTERM");
|
|
97
|
+
await new Promise((resolve) => {
|
|
98
|
+
if (!this.serverProcess) {
|
|
99
|
+
resolve();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const timeout = setTimeout(() => {
|
|
103
|
+
if (this.serverProcess) {
|
|
104
|
+
this.log("Force killing annotator server...");
|
|
105
|
+
this.serverProcess.kill("SIGKILL");
|
|
106
|
+
}
|
|
107
|
+
resolve();
|
|
108
|
+
}, 5e3);
|
|
109
|
+
this.serverProcess.on("exit", () => {
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
resolve();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
this.serverProcess = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async isServerRunning() {
|
|
118
|
+
try {
|
|
119
|
+
const response = await fetch(`http://${this.options.listenAddress}:${this.options.port}/health`, {
|
|
120
|
+
signal: AbortSignal.timeout(1e3)
|
|
121
|
+
});
|
|
122
|
+
return response.ok;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
log(message) {
|
|
128
|
+
if (this.options.verbose) {
|
|
129
|
+
console.log(`[ai-annotator] ${message}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
getInjectScript() {
|
|
133
|
+
return `<script src="${this.options.publicAddress}/annotator-toolbar.js" type="module" async></script>`;
|
|
134
|
+
}
|
|
135
|
+
shouldInject() {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
function injectScriptIntoHtml(html, scriptTag) {
|
|
140
|
+
if (html.includes("</body>")) {
|
|
141
|
+
return html.replace("</body>", `${scriptTag}
|
|
142
|
+
</body>`);
|
|
143
|
+
} else if (html.includes("</html>")) {
|
|
144
|
+
return html.replace("</html>", `${scriptTag}
|
|
145
|
+
</html>`);
|
|
146
|
+
}
|
|
147
|
+
return html + scriptTag;
|
|
148
|
+
}
|
|
149
|
+
function aiAnnotator(options = {}) {
|
|
150
|
+
let serverManager;
|
|
151
|
+
return {
|
|
152
|
+
name: "vite-plugin-ai-annotator",
|
|
153
|
+
// Only apply plugin during development (serve command)
|
|
154
|
+
apply: "serve",
|
|
155
|
+
configResolved() {
|
|
156
|
+
serverManager = new AiAnnotatorServer(options);
|
|
157
|
+
},
|
|
158
|
+
async buildStart() {
|
|
159
|
+
await serverManager.start();
|
|
160
|
+
},
|
|
161
|
+
// For regular Vite apps (SPA)
|
|
162
|
+
transformIndexHtml(html) {
|
|
163
|
+
if (!serverManager || !serverManager.shouldInject()) {
|
|
164
|
+
return html;
|
|
165
|
+
}
|
|
166
|
+
return injectScriptIntoHtml(html, serverManager.getInjectScript());
|
|
167
|
+
},
|
|
168
|
+
// For SSR frameworks like Nuxt - intercept HTML responses
|
|
169
|
+
configureServer(server) {
|
|
170
|
+
server.middlewares.use((_req, res, next) => {
|
|
171
|
+
if (!serverManager || !serverManager.shouldInject()) {
|
|
172
|
+
return next();
|
|
173
|
+
}
|
|
174
|
+
const originalWrite = res.write.bind(res);
|
|
175
|
+
const originalEnd = res.end.bind(res);
|
|
176
|
+
let chunks = [];
|
|
177
|
+
let isHtml = false;
|
|
178
|
+
res.write = function(chunk, ...args) {
|
|
179
|
+
const contentType = res.getHeader("content-type");
|
|
180
|
+
if (typeof contentType === "string" && contentType.includes("text/html")) {
|
|
181
|
+
isHtml = true;
|
|
182
|
+
}
|
|
183
|
+
if (isHtml && chunk) {
|
|
184
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
return originalWrite(chunk, ...args);
|
|
188
|
+
};
|
|
189
|
+
res.end = function(chunk, ...args) {
|
|
190
|
+
if (chunk) {
|
|
191
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
192
|
+
}
|
|
193
|
+
if (isHtml && chunks.length > 0) {
|
|
194
|
+
const html = Buffer.concat(chunks).toString("utf-8");
|
|
195
|
+
const injectedHtml = injectScriptIntoHtml(html, serverManager.getInjectScript());
|
|
196
|
+
res.setHeader("content-length", Buffer.byteLength(injectedHtml));
|
|
197
|
+
return originalEnd(injectedHtml, ...args);
|
|
198
|
+
}
|
|
199
|
+
return originalEnd(chunk, ...args);
|
|
200
|
+
};
|
|
201
|
+
next();
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
async closeBundle() {
|
|
205
|
+
await serverManager.stop();
|
|
206
|
+
},
|
|
207
|
+
async buildEnd() {
|
|
208
|
+
if (this.meta.watchMode === false) {
|
|
209
|
+
await serverManager.stop();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
var vite_plugin_default = aiAnnotator;
|
|
215
|
+
export {
|
|
216
|
+
aiAnnotator,
|
|
217
|
+
vite_plugin_default as default
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=vite-plugin.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/vite-plugin.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Plugin, ViteDevServer } from 'vite';\nimport { spawn, ChildProcess } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { existsSync } from 'node:fs';\n\nexport interface AiAnnotatorOptions {\n /**\n * Port to run the server on\n * @default 7318\n */\n port?: number;\n /**\n * Address for the server to listen on\n * @default 'localhost'\n */\n listenAddress?: string;\n /**\n * Public URL for reverse proxy scenarios\n * @default Automatically determined from listenAddress and port\n */\n publicAddress?: string;\n /**\n * Verbose logging\n * @default false\n */\n verbose?: boolean;\n}\n\nclass AiAnnotatorServer {\n private serverProcess: ChildProcess | null = null;\n private options: Required<AiAnnotatorOptions>;\n private packageDir: string;\n private isDevelopment: boolean;\n\n constructor(options: AiAnnotatorOptions = {}) {\n const port = options.port ?? 7318;\n const listenAddress = options.listenAddress ?? 'localhost';\n \n this.options = {\n port,\n listenAddress,\n publicAddress: options.publicAddress ?? `http://${listenAddress}:${port}`,\n verbose: options.verbose ?? false,\n };\n \n\n // Detect if we're running from source or from installed package\n const currentFileDir = dirname(fileURLToPath(import.meta.url));\n\n // Check if we're in src directory (development) or dist directory (production)\n this.isDevelopment = currentFileDir.endsWith('/src') || currentFileDir.endsWith('\\\\src');\n\n // Get the package root directory\n if (this.isDevelopment) {\n // In development: current file is in src/, package root is one level up\n this.packageDir = dirname(currentFileDir);\n } else {\n // In production: current file is in dist/, package root is one level up\n this.packageDir = dirname(currentFileDir);\n }\n\n this.log(`Package directory: ${this.packageDir}`);\n this.log(`Running in ${this.isDevelopment ? 'development' : 'production'} mode`);\n }\n\n async start(): Promise<void> {\n if (this.serverProcess) {\n return;\n }\n\n // Check if server is already running on the port\n const isRunning = await this.isServerRunning();\n if (isRunning) {\n this.log(`Server already running on port ${this.options.port}, skipping spawn`);\n return;\n }\n\n // Determine the server file to run\n let serverFile: string;\n let cmd: string;\n let args: string[];\n\n if (this.isDevelopment) {\n // Development: run TypeScript file directly with bun\n serverFile = join(this.packageDir, 'src', 'index.ts');\n cmd = 'bun';\n args = [serverFile];\n } else {\n // Production: run compiled JavaScript file\n serverFile = join(this.packageDir, 'dist', 'index.cjs');\n\n // Check if dist/index.cjs exists, if not try to use bun with src/index.ts as fallback\n if (!existsSync(serverFile)) {\n const fallbackFile = join(this.packageDir, 'src', 'index.ts');\n if (existsSync(fallbackFile)) {\n this.log('dist/index.cjs not found, falling back to src/index.ts');\n serverFile = fallbackFile;\n cmd = 'bun';\n args = [serverFile];\n } else {\n throw new Error(`Annotator server file not found at ${serverFile} or ${fallbackFile}`);\n }\n } else {\n // Use node for compiled CJS in production\n cmd = 'node';\n args = [serverFile];\n }\n }\n\n // Add CLI arguments\n args.push('--port', String(this.options.port));\n args.push('--listen', this.options.listenAddress);\n args.push('--public-address', this.options.publicAddress);\n if (this.options.verbose) {\n args.push('--verbose');\n }\n\n this.log(`Starting annotator server: ${cmd} ${args.join(' ')}`);\n this.log(`Working directory: ${this.packageDir}`);\n\n // Start the server process\n this.serverProcess = spawn(cmd, args, {\n cwd: this.packageDir,\n env: process.env,\n stdio: this.options.verbose ? 'inherit' : 'pipe',\n });\n\n if (!this.options.verbose && this.serverProcess.stdout) {\n this.serverProcess.stdout.on('data', (_data) => {\n // Pipe output for debugging if needed\n });\n }\n\n if (!this.options.verbose && this.serverProcess.stderr) {\n this.serverProcess.stderr.on('data', (data) => {\n console.error(`[ai-annotator-server] Error: ${data}`);\n });\n }\n\n this.serverProcess.on('error', (error) => {\n console.error('[ai-annotator-server] Failed to start:', error);\n });\n\n this.serverProcess.on('exit', (code) => {\n if (code !== 0 && code !== null) {\n console.error(`[ai-annotator-server] Process exited with code ${code}`);\n }\n this.serverProcess = null;\n });\n\n // Give server a moment to start\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n\n async stop(): Promise<void> {\n if (this.serverProcess) {\n this.log('Stopping annotator server...');\n\n // Send SIGTERM for graceful shutdown\n this.serverProcess.kill('SIGTERM');\n\n // Wait for process to exit\n await new Promise<void>((resolve) => {\n if (!this.serverProcess) {\n resolve();\n return;\n }\n\n const timeout = setTimeout(() => {\n // Force kill if not exited after 5 seconds\n if (this.serverProcess) {\n this.log('Force killing annotator server...');\n this.serverProcess.kill('SIGKILL');\n }\n resolve();\n }, 5000);\n\n this.serverProcess.on('exit', () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n\n this.serverProcess = null;\n }\n }\n\n\n private async isServerRunning(): Promise<boolean> {\n try {\n const response = await fetch(`http://${this.options.listenAddress}:${this.options.port}/health`, {\n signal: AbortSignal.timeout(1000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n private log(message: string): void {\n if (this.options.verbose) {\n console.log(`[ai-annotator] ${message}`);\n }\n }\n\n getInjectScript(): string {\n return `<script src=\"${this.options.publicAddress}/annotator-toolbar.js\" type=\"module\" async></script>`;\n }\n\n shouldInject(): boolean {\n return true;\n }\n\n}\n\nfunction injectScriptIntoHtml(html: string, scriptTag: string): string {\n if (html.includes('</body>')) {\n return html.replace('</body>', `${scriptTag}\\n</body>`);\n } else if (html.includes('</html>')) {\n return html.replace('</html>', `${scriptTag}\\n</html>`);\n }\n return html + scriptTag;\n}\n\nexport function aiAnnotator(options: AiAnnotatorOptions = {}): Plugin {\n let serverManager: AiAnnotatorServer;\n\n return {\n name: 'vite-plugin-ai-annotator',\n // Only apply plugin during development (serve command)\n apply: 'serve',\n\n configResolved() {\n serverManager = new AiAnnotatorServer(options);\n },\n\n async buildStart() {\n await serverManager.start();\n },\n\n // For regular Vite apps (SPA)\n transformIndexHtml(html: string) {\n if (!serverManager || !serverManager.shouldInject()) {\n return html;\n }\n return injectScriptIntoHtml(html, serverManager.getInjectScript());\n },\n\n // For SSR frameworks like Nuxt - intercept HTML responses\n configureServer(server: ViteDevServer) {\n server.middlewares.use((_req, res, next) => {\n if (!serverManager || !serverManager.shouldInject()) {\n return next();\n }\n\n // Only intercept HTML responses\n const originalWrite = res.write.bind(res);\n const originalEnd = res.end.bind(res);\n let chunks: Buffer[] = [];\n let isHtml = false;\n\n res.write = function(chunk: any, ...args: any[]) {\n // Check content-type on first write\n const contentType = res.getHeader('content-type');\n if (typeof contentType === 'string' && contentType.includes('text/html')) {\n isHtml = true;\n }\n\n if (isHtml && chunk) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n return true;\n }\n return originalWrite(chunk, ...args);\n } as any;\n\n res.end = function(chunk?: any, ...args: any[]) {\n if (chunk) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n if (isHtml && chunks.length > 0) {\n const html = Buffer.concat(chunks).toString('utf-8');\n const injectedHtml = injectScriptIntoHtml(html, serverManager.getInjectScript());\n\n // Update content-length header\n res.setHeader('content-length', Buffer.byteLength(injectedHtml));\n return originalEnd(injectedHtml, ...args);\n }\n\n return originalEnd(chunk, ...args);\n } as any;\n\n next();\n });\n },\n\n async closeBundle() {\n await serverManager.stop();\n },\n\n async buildEnd() {\n // Stop server when build ends (in build mode)\n if (this.meta.watchMode === false) {\n await serverManager.stop();\n }\n },\n };\n}\n\n// Default export for convenience\nexport default aiAnnotator;"],
|
|
5
|
+
"mappings": ";AACA,SAAS,aAA2B;AACpC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,YAAY;AAC9B,SAAS,kBAAkB;AAyB3B,IAAM,oBAAN,MAAwB;AAAA,EAMtB,YAAY,UAA8B,CAAC,GAAG;AAL9C,SAAQ,gBAAqC;AAM3C,UAAM,OAAO,QAAQ,QAAQ;AAC7B,UAAM,gBAAgB,QAAQ,iBAAiB;AAE/C,SAAK,UAAU;AAAA,MACb;AAAA,MACA;AAAA,MACA,eAAe,QAAQ,iBAAiB,UAAU,aAAa,IAAI,IAAI;AAAA,MACvE,SAAS,QAAQ,WAAW;AAAA,IAC9B;AAIA,UAAM,iBAAiB,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG7D,SAAK,gBAAgB,eAAe,SAAS,MAAM,KAAK,eAAe,SAAS,OAAO;AAGvF,QAAI,KAAK,eAAe;AAEtB,WAAK,aAAa,QAAQ,cAAc;AAAA,IAC1C,OAAO;AAEL,WAAK,aAAa,QAAQ,cAAc;AAAA,IAC1C;AAEA,SAAK,IAAI,sBAAsB,KAAK,UAAU,EAAE;AAChD,SAAK,IAAI,cAAc,KAAK,gBAAgB,gBAAgB,YAAY,OAAO;AAAA,EACjF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,eAAe;AACtB;AAAA,IACF;AAGA,UAAM,YAAY,MAAM,KAAK,gBAAgB;AAC7C,QAAI,WAAW;AACb,WAAK,IAAI,kCAAkC,KAAK,QAAQ,IAAI,kBAAkB;AAC9E;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,KAAK,eAAe;AAEtB,mBAAa,KAAK,KAAK,YAAY,OAAO,UAAU;AACpD,YAAM;AACN,aAAO,CAAC,UAAU;AAAA,IACpB,OAAO;AAEL,mBAAa,KAAK,KAAK,YAAY,QAAQ,WAAW;AAGtD,UAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,cAAM,eAAe,KAAK,KAAK,YAAY,OAAO,UAAU;AAC5D,YAAI,WAAW,YAAY,GAAG;AAC5B,eAAK,IAAI,wDAAwD;AACjE,uBAAa;AACb,gBAAM;AACN,iBAAO,CAAC,UAAU;AAAA,QACpB,OAAO;AACL,gBAAM,IAAI,MAAM,sCAAsC,UAAU,OAAO,YAAY,EAAE;AAAA,QACvF;AAAA,MACF,OAAO;AAEL,cAAM;AACN,eAAO,CAAC,UAAU;AAAA,MACpB;AAAA,IACF;AAGA,SAAK,KAAK,UAAU,OAAO,KAAK,QAAQ,IAAI,CAAC;AAC7C,SAAK,KAAK,YAAY,KAAK,QAAQ,aAAa;AAChD,SAAK,KAAK,oBAAoB,KAAK,QAAQ,aAAa;AACxD,QAAI,KAAK,QAAQ,SAAS;AACxB,WAAK,KAAK,WAAW;AAAA,IACvB;AAEA,SAAK,IAAI,8BAA8B,GAAG,IAAI,KAAK,KAAK,GAAG,CAAC,EAAE;AAC9D,SAAK,IAAI,sBAAsB,KAAK,UAAU,EAAE;AAGhD,SAAK,gBAAgB,MAAM,KAAK,MAAM;AAAA,MACpC,KAAK,KAAK;AAAA,MACV,KAAK,QAAQ;AAAA,MACb,OAAO,KAAK,QAAQ,UAAU,YAAY;AAAA,IAC5C,CAAC;AAED,QAAI,CAAC,KAAK,QAAQ,WAAW,KAAK,cAAc,QAAQ;AACtD,WAAK,cAAc,OAAO,GAAG,QAAQ,CAAC,UAAU;AAAA,MAEhD,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,KAAK,QAAQ,WAAW,KAAK,cAAc,QAAQ;AACtD,WAAK,cAAc,OAAO,GAAG,QAAQ,CAAC,SAAS;AAC7C,gBAAQ,MAAM,gCAAgC,IAAI,EAAE;AAAA,MACtD,CAAC;AAAA,IACH;AAEA,SAAK,cAAc,GAAG,SAAS,CAAC,UAAU;AACxC,cAAQ,MAAM,0CAA0C,KAAK;AAAA,IAC/D,CAAC;AAED,SAAK,cAAc,GAAG,QAAQ,CAAC,SAAS;AACtC,UAAI,SAAS,KAAK,SAAS,MAAM;AAC/B,gBAAQ,MAAM,kDAAkD,IAAI,EAAE;AAAA,MACxE;AACA,WAAK,gBAAgB;AAAA,IACvB,CAAC;AAGD,UAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,GAAI,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,OAAsB;AAC1B,QAAI,KAAK,eAAe;AACtB,WAAK,IAAI,8BAA8B;AAGvC,WAAK,cAAc,KAAK,SAAS;AAGjC,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,YAAI,CAAC,KAAK,eAAe;AACvB,kBAAQ;AACR;AAAA,QACF;AAEA,cAAM,UAAU,WAAW,MAAM;AAE/B,cAAI,KAAK,eAAe;AACtB,iBAAK,IAAI,mCAAmC;AAC5C,iBAAK,cAAc,KAAK,SAAS;AAAA,UACnC;AACA,kBAAQ;AAAA,QACV,GAAG,GAAI;AAEP,aAAK,cAAc,GAAG,QAAQ,MAAM;AAClC,uBAAa,OAAO;AACpB,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AAED,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA,EAGA,MAAc,kBAAoC;AAChD,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,UAAU,KAAK,QAAQ,aAAa,IAAI,KAAK,QAAQ,IAAI,WAAW;AAAA,QAC/F,QAAQ,YAAY,QAAQ,GAAI;AAAA,MAClC,CAAC;AACD,aAAO,SAAS;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,IAAI,SAAuB;AACjC,QAAI,KAAK,QAAQ,SAAS;AACxB,cAAQ,IAAI,kBAAkB,OAAO,EAAE;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,kBAA0B;AACxB,WAAO,gBAAgB,KAAK,QAAQ,aAAa;AAAA,EACnD;AAAA,EAEA,eAAwB;AACtB,WAAO;AAAA,EACT;AAEF;AAEA,SAAS,qBAAqB,MAAc,WAA2B;AACrE,MAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,WAAO,KAAK,QAAQ,WAAW,GAAG,SAAS;AAAA,QAAW;AAAA,EACxD,WAAW,KAAK,SAAS,SAAS,GAAG;AACnC,WAAO,KAAK,QAAQ,WAAW,GAAG,SAAS;AAAA,QAAW;AAAA,EACxD;AACA,SAAO,OAAO;AAChB;AAEO,SAAS,YAAY,UAA8B,CAAC,GAAW;AACpE,MAAI;AAEJ,SAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAEN,OAAO;AAAA,IAEP,iBAAiB;AACf,sBAAgB,IAAI,kBAAkB,OAAO;AAAA,IAC/C;AAAA,IAEA,MAAM,aAAa;AACjB,YAAM,cAAc,MAAM;AAAA,IAC5B;AAAA;AAAA,IAGA,mBAAmB,MAAc;AAC/B,UAAI,CAAC,iBAAiB,CAAC,cAAc,aAAa,GAAG;AACnD,eAAO;AAAA,MACT;AACA,aAAO,qBAAqB,MAAM,cAAc,gBAAgB,CAAC;AAAA,IACnE;AAAA;AAAA,IAGA,gBAAgB,QAAuB;AACrC,aAAO,YAAY,IAAI,CAAC,MAAM,KAAK,SAAS;AAC1C,YAAI,CAAC,iBAAiB,CAAC,cAAc,aAAa,GAAG;AACnD,iBAAO,KAAK;AAAA,QACd;AAGA,cAAM,gBAAgB,IAAI,MAAM,KAAK,GAAG;AACxC,cAAM,cAAc,IAAI,IAAI,KAAK,GAAG;AACpC,YAAI,SAAmB,CAAC;AACxB,YAAI,SAAS;AAEb,YAAI,QAAQ,SAAS,UAAe,MAAa;AAE/C,gBAAM,cAAc,IAAI,UAAU,cAAc;AAChD,cAAI,OAAO,gBAAgB,YAAY,YAAY,SAAS,WAAW,GAAG;AACxE,qBAAS;AAAA,UACX;AAEA,cAAI,UAAU,OAAO;AACnB,mBAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAC/D,mBAAO;AAAA,UACT;AACA,iBAAO,cAAc,OAAO,GAAG,IAAI;AAAA,QACrC;AAEA,YAAI,MAAM,SAAS,UAAgB,MAAa;AAC9C,cAAI,OAAO;AACT,mBAAO,KAAK,OAAO,SAAS,KAAK,IAAI,QAAQ,OAAO,KAAK,KAAK,CAAC;AAAA,UACjE;AAEA,cAAI,UAAU,OAAO,SAAS,GAAG;AAC/B,kBAAM,OAAO,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AACnD,kBAAM,eAAe,qBAAqB,MAAM,cAAc,gBAAgB,CAAC;AAG/E,gBAAI,UAAU,kBAAkB,OAAO,WAAW,YAAY,CAAC;AAC/D,mBAAO,YAAY,cAAc,GAAG,IAAI;AAAA,UAC1C;AAEA,iBAAO,YAAY,OAAO,GAAG,IAAI;AAAA,QACnC;AAEA,aAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,cAAc;AAClB,YAAM,cAAc,KAAK;AAAA,IAC3B;AAAA,IAEA,MAAM,WAAW;AAEf,UAAI,KAAK,KAAK,cAAc,OAAO;AACjC,cAAM,cAAc,KAAK;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AACF;AAGA,IAAO,sBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Express } from 'express';
|
|
2
|
+
import type { Server } from 'node:http';
|
|
3
|
+
import { Server as SocketIOServer, Socket } from 'socket.io';
|
|
4
|
+
import { type RpcServer } from './rpc/server.generated';
|
|
5
|
+
import type { BrowserSession } from './rpc/define';
|
|
6
|
+
export interface ServerInstance {
|
|
7
|
+
app: Express;
|
|
8
|
+
server: Server;
|
|
9
|
+
io: SocketIOServer;
|
|
10
|
+
port: number;
|
|
11
|
+
listenAddress: string;
|
|
12
|
+
publicAddress: string;
|
|
13
|
+
verbose: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface BrowserConnection {
|
|
16
|
+
socket: Socket;
|
|
17
|
+
rpc: RpcServer;
|
|
18
|
+
session: BrowserSession;
|
|
19
|
+
}
|
|
20
|
+
export declare function getAllSessions(): BrowserSession[];
|
|
21
|
+
export declare function getRpc(sessionId?: string): {
|
|
22
|
+
rpc: RpcServer;
|
|
23
|
+
sessionId: string;
|
|
24
|
+
} | null;
|
|
25
|
+
export declare function startServer(port: number, listenAddress: string, publicAddress: string, verbose?: boolean): Promise<ServerInstance>;
|
|
26
|
+
export declare function stopServer(serverInstance: ServerInstance): Promise<void>;
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-ai-annotator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered element annotator for Vite - Pick elements and get instant AI code modifications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/vite-plugin.js",
|
|
7
|
+
"types": "dist/vite-plugin.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ai-annotator": "./dist/index.cjs"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/vite-plugin.d.ts",
|
|
14
|
+
"import": "./dist/vite-plugin.js"
|
|
15
|
+
},
|
|
16
|
+
"./vite-plugin": {
|
|
17
|
+
"types": "./dist/vite-plugin.d.ts",
|
|
18
|
+
"import": "./dist/vite-plugin.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "bun src/index.ts",
|
|
23
|
+
"build:server": "bun scripts/build-server.js",
|
|
24
|
+
"build:annotator": "bun scripts/build-annotator.js",
|
|
25
|
+
"build:vite-plugin": "bun scripts/build-vite-plugin.js",
|
|
26
|
+
"build:types": "bun scripts/build-types.js",
|
|
27
|
+
"build": "bun run build:server && bun run build:annotator && bun run build:vite-plugin && bun run build:types",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "bun run build"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/**/*",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"keywords": [
|
|
36
|
+
"vite",
|
|
37
|
+
"vite-plugin",
|
|
38
|
+
"ai",
|
|
39
|
+
"annotator",
|
|
40
|
+
"element-picker",
|
|
41
|
+
"claude",
|
|
42
|
+
"inspector",
|
|
43
|
+
"web-development",
|
|
44
|
+
"frontend"
|
|
45
|
+
],
|
|
46
|
+
"author": "Nguyen Van Duoc <nguyenvanduocit@gmail.com>",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/nguyenvanduocit/vite-plugin-ai-annotator.git"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/nguyenvanduocit/vite-plugin-ai-annotator#readme",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/nguyenvanduocit/vite-plugin-ai-annotator/issues"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@floating-ui/dom": "^1.7.4",
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
59
|
+
"@types/js-yaml": "^4.0.9",
|
|
60
|
+
"cors": "^2.8.5",
|
|
61
|
+
"express": "^5.1.0",
|
|
62
|
+
"html-to-image": "^1.11.13",
|
|
63
|
+
"js-yaml": "^4.1.0",
|
|
64
|
+
"lit": "^3.3.1",
|
|
65
|
+
"optimal-select": "^4.0.1",
|
|
66
|
+
"socket.io": "^4.8.1",
|
|
67
|
+
"socket.io-client": "^4.8.1",
|
|
68
|
+
"zod": "^4.0.17"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/bun": "^1.2.20",
|
|
72
|
+
"@types/cors": "^2.8.17",
|
|
73
|
+
"@types/express": "^5.0.2",
|
|
74
|
+
"@types/node": "^22.13.4",
|
|
75
|
+
"esbuild": "^0.25.9",
|
|
76
|
+
"tsx": "^4.7.0",
|
|
77
|
+
"typescript": "^5.7.3",
|
|
78
|
+
"vite": "^6.0.7"
|
|
79
|
+
},
|
|
80
|
+
"engines": {
|
|
81
|
+
"node": ">=18.0.0"
|
|
82
|
+
},
|
|
83
|
+
"peerDependencies": {
|
|
84
|
+
"vite": ">=2.0.0"
|
|
85
|
+
}
|
|
86
|
+
}
|