@vscode/component-explorer-cli 0.2.0 → 0.2.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/_build-info.js +1 -1
- package/dist/browserPage.d.ts +11 -2
- package/dist/browserPage.d.ts.map +1 -1
- package/dist/browserPage.js +26 -8
- package/dist/browserPage.js.map +1 -1
- package/dist/commands/checkStabilityCommand.d.ts.map +1 -1
- package/dist/commands/checkStabilityCommand.js +7 -4
- package/dist/commands/checkStabilityCommand.js.map +1 -1
- package/dist/commands/mcpCommand.d.ts.map +1 -1
- package/dist/commands/mcpCommand.js +34 -23
- package/dist/commands/mcpCommand.js.map +1 -1
- package/dist/commands/screenshotCommand.d.ts.map +1 -1
- package/dist/commands/screenshotCommand.js +10 -3
- package/dist/commands/screenshotCommand.js.map +1 -1
- package/dist/commands/serveCommand.d.ts +2 -0
- package/dist/commands/serveCommand.d.ts.map +1 -1
- package/dist/commands/serveCommand.js +75 -24
- package/dist/commands/serveCommand.js.map +1 -1
- package/dist/commands/watchCommand.js +11 -6
- package/dist/commands/watchCommand.js.map +1 -1
- package/dist/component-explorer-config.schema.json +166 -0
- package/dist/componentExplorer.d.ts +14 -3
- package/dist/componentExplorer.d.ts.map +1 -1
- package/dist/componentExplorer.js +42 -6
- package/dist/componentExplorer.js.map +1 -1
- package/dist/daemon/DaemonService.d.ts +26 -5
- package/dist/daemon/DaemonService.d.ts.map +1 -1
- package/dist/daemon/DaemonService.js +61 -20
- package/dist/daemon/DaemonService.js.map +1 -1
- package/dist/daemon/lifecycle.d.ts +2 -1
- package/dist/daemon/lifecycle.d.ts.map +1 -1
- package/dist/daemon/lifecycle.js +41 -6
- package/dist/daemon/lifecycle.js.map +1 -1
- package/dist/daemon/pipeClient.d.ts.map +1 -1
- package/dist/daemon/pipeClient.js +2 -0
- package/dist/daemon/pipeClient.js.map +1 -1
- package/dist/explorerSession.d.ts +3 -3
- package/dist/explorerSession.d.ts.map +1 -1
- package/dist/explorerSession.js +26 -9
- package/dist/explorerSession.js.map +1 -1
- package/dist/httpServer.d.ts +12 -6
- package/dist/httpServer.d.ts.map +1 -1
- package/dist/httpServer.js +104 -18
- package/dist/httpServer.js.map +1 -1
- package/dist/httpServer.test.d.ts +2 -0
- package/dist/httpServer.test.d.ts.map +1 -0
- package/dist/mcp/McpServer.d.ts +5 -0
- package/dist/mcp/McpServer.d.ts.map +1 -1
- package/dist/mcp/McpServer.js +235 -8
- package/dist/mcp/McpServer.js.map +1 -1
- package/dist/visualCache.d.ts +34 -0
- package/dist/visualCache.d.ts.map +1 -0
- package/dist/visualCache.js +90 -0
- package/dist/visualCache.js.map +1 -0
- package/dist/watchConfig.d.ts +50 -4
- package/dist/watchConfig.d.ts.map +1 -1
- package/dist/watchConfig.js +72 -23
- package/dist/watchConfig.js.map +1 -1
- package/package.json +1 -1
package/dist/httpServer.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { IObservable } from '@vscode/observables';
|
|
2
1
|
import { type ILogger } from './logger.js';
|
|
3
|
-
import { SourceTreeId } from './sourceTreeId.js';
|
|
4
2
|
import { ViteProjectRef } from './viteProjectRef.js';
|
|
5
3
|
export interface ViteServerOptions {
|
|
6
4
|
readonly resolveViteFrom?: string;
|
|
@@ -13,18 +11,26 @@ export interface ViteServerOptions {
|
|
|
13
11
|
readonly pluginProtocolVersion: string;
|
|
14
12
|
};
|
|
15
13
|
}
|
|
14
|
+
export interface HttpServerOptions {
|
|
15
|
+
readonly cmd?: string;
|
|
16
|
+
readonly cwd?: string;
|
|
17
|
+
readonly logger?: ILogger;
|
|
18
|
+
readonly wait?: {
|
|
19
|
+
readonly stdout?: RegExp;
|
|
20
|
+
readonly stderr?: RegExp;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
16
23
|
export interface ComponentExplorerHttpServerFactory {
|
|
17
24
|
createViteServer(project: ViteProjectRef, options?: ViteServerOptions): Promise<ComponentExplorerHttpServer>;
|
|
18
|
-
|
|
25
|
+
createHttpServer(url: string, options?: HttpServerOptions): Promise<ComponentExplorerHttpServer>;
|
|
19
26
|
}
|
|
20
27
|
export interface ComponentExplorerHttpServer {
|
|
21
28
|
readonly url: string;
|
|
22
|
-
readonly
|
|
23
|
-
readonly sourceTreeId: IObservable<SourceTreeId>;
|
|
29
|
+
readonly onSourceChange?: (callback: () => void) => void;
|
|
24
30
|
dispose(): Promise<void>;
|
|
25
31
|
}
|
|
26
32
|
export declare class DefaultComponentExplorerHttpServerFactory implements ComponentExplorerHttpServerFactory {
|
|
27
33
|
createViteServer(project: ViteProjectRef, options?: ViteServerOptions): Promise<ComponentExplorerHttpServer>;
|
|
28
|
-
|
|
34
|
+
createHttpServer(url: string, options?: HttpServerOptions): Promise<ComponentExplorerHttpServer>;
|
|
29
35
|
}
|
|
30
36
|
//# sourceMappingURL=httpServer.d.ts.map
|
package/dist/httpServer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpServer.d.ts","sourceRoot":"","sources":["../src/httpServer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"httpServer.d.ts","sourceRoot":"","sources":["../src/httpServer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAc,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,YAAY,CAAC,EAAE;QACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;QAClC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;KACvC,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE;QACf,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACF;AAED,MAAM,WAAW,kCAAkC;IAClD,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC7G,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;CACjG;AAED,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED,qBAAa,yCAA0C,YAAW,kCAAkC;IAC7F,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAI5G,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,2BAA2B,CAAC;CAGtG"}
|
package/dist/httpServer.js
CHANGED
|
@@ -1,29 +1,116 @@
|
|
|
1
|
-
import './external/vscode-observables/observables/dist/observableInternal/index.js';
|
|
2
|
-
import { observableValue } from './external/vscode-observables/observables/dist/observableInternal/observables/observableValue.js';
|
|
3
|
-
import './external/vscode-observables/observables/dist/observableInternal/debugLocation.js';
|
|
4
|
-
import './external/vscode-observables/observables/dist/observableInternal/observables/derived.js';
|
|
5
|
-
import './external/vscode-observables/observables/dist/observableInternal/utils/utils.js';
|
|
6
|
-
import './external/vscode-observables/observables/dist/observableInternal/observables/observableFromEvent.js';
|
|
7
1
|
import { nullLogger } from './logger.js';
|
|
8
|
-
import { SourceTreeId } from './sourceTreeId.js';
|
|
9
2
|
|
|
10
3
|
class DefaultComponentExplorerHttpServerFactory {
|
|
11
4
|
async createViteServer(project, options) {
|
|
12
5
|
return ViteComponentExplorerHttpServer.create(project, options);
|
|
13
6
|
}
|
|
14
|
-
async
|
|
15
|
-
|
|
7
|
+
async createHttpServer(url, options) {
|
|
8
|
+
return HttpComponentExplorerServer.create(url, options);
|
|
16
9
|
}
|
|
17
10
|
}
|
|
11
|
+
class HttpComponentExplorerServer {
|
|
12
|
+
url;
|
|
13
|
+
_process;
|
|
14
|
+
static async create(urlTemplate, options) {
|
|
15
|
+
let proc;
|
|
16
|
+
let vars = {};
|
|
17
|
+
if (options?.cmd) {
|
|
18
|
+
const log = options.logger ?? nullLogger;
|
|
19
|
+
log.debug(`Starting HTTP server: ${options.cmd}`);
|
|
20
|
+
log.debug(`HTTP server cwd: ${options.cwd ?? process.cwd()}`);
|
|
21
|
+
const { spawn } = await import('node:child_process');
|
|
22
|
+
proc = spawn(options.cmd, { shell: true, stdio: 'pipe', cwd: options.cwd });
|
|
23
|
+
proc.on('error', (e) => log.log(`HTTP server process error: ${e.message}`));
|
|
24
|
+
proc.on('exit', (code) => log.debug(`HTTP server process exited (code=${code})`));
|
|
25
|
+
if (options.wait?.stdout || options.wait?.stderr) {
|
|
26
|
+
vars = await waitForOutputPattern(proc, options.wait, log);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
proc.stdout?.on('data', (d) => log.trace(d.toString().trimEnd()));
|
|
30
|
+
proc.stderr?.on('data', (d) => log.trace(d.toString().trimEnd()));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const url = resolveUrlTemplate(urlTemplate, vars);
|
|
34
|
+
return new HttpComponentExplorerServer(url, proc);
|
|
35
|
+
}
|
|
36
|
+
constructor(url, _process) {
|
|
37
|
+
this.url = url;
|
|
38
|
+
this._process = _process;
|
|
39
|
+
}
|
|
40
|
+
async dispose() {
|
|
41
|
+
if (this._process && !this._process.killed) {
|
|
42
|
+
this._process.kill();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function resolveUrlTemplate(template, vars) {
|
|
47
|
+
return template.replace(/\$\{var:([^}]+)\}/g, (match, name) => {
|
|
48
|
+
if (!(name in vars)) {
|
|
49
|
+
throw new Error(`Unresolved variable \${var:${name}} in URL. Available: ${Object.keys(vars).join(', ') || '(none)'}`);
|
|
50
|
+
}
|
|
51
|
+
return vars[name];
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const _stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '').replace(/\x1b\].*?\x07/g, '');
|
|
55
|
+
function waitForOutputPattern(proc, wait, log) {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
let resolved = false;
|
|
58
|
+
const vars = {};
|
|
59
|
+
let pendingCount = (wait.stdout ? 1 : 0) + (wait.stderr ? 1 : 0);
|
|
60
|
+
const buffers = { stdout: '', stderr: '' };
|
|
61
|
+
const tryResolve = () => {
|
|
62
|
+
if (resolved || pendingCount > 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
resolved = true;
|
|
66
|
+
proc.stdout?.on('data', (d) => log.trace(d.toString().trimEnd()));
|
|
67
|
+
proc.stderr?.on('data', (d) => log.trace(d.toString().trimEnd()));
|
|
68
|
+
resolve(vars);
|
|
69
|
+
};
|
|
70
|
+
const makeHandler = (stream, pattern) => {
|
|
71
|
+
const handler = (d) => {
|
|
72
|
+
const text = d.toString();
|
|
73
|
+
log.trace(text.trimEnd());
|
|
74
|
+
buffers[stream] += _stripAnsi(text);
|
|
75
|
+
const m = pattern.exec(buffers[stream]);
|
|
76
|
+
if (m) {
|
|
77
|
+
log.debug(`${stream} pattern matched: ${JSON.stringify(m.groups ?? {})}`);
|
|
78
|
+
proc[stream]?.off('data', handler);
|
|
79
|
+
Object.assign(vars, m.groups ?? {});
|
|
80
|
+
pendingCount--;
|
|
81
|
+
tryResolve();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return handler;
|
|
85
|
+
};
|
|
86
|
+
if (wait.stdout) {
|
|
87
|
+
proc.stdout?.on('data', makeHandler('stdout', wait.stdout));
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
proc.stdout?.on('data', (d) => log.trace(d.toString().trimEnd()));
|
|
91
|
+
}
|
|
92
|
+
if (wait.stderr) {
|
|
93
|
+
proc.stderr?.on('data', makeHandler('stderr', wait.stderr));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
proc.stderr?.on('data', (d) => log.trace(d.toString().trimEnd()));
|
|
97
|
+
}
|
|
98
|
+
proc.on('exit', (code) => {
|
|
99
|
+
if (!resolved) {
|
|
100
|
+
const details = [wait.stdout ? `stdout buffer:\n${buffers.stdout}` : '', wait.stderr ? `stderr buffer:\n${buffers.stderr}` : ''].filter(Boolean).join('\n');
|
|
101
|
+
reject(new Error(`HTTP server process exited (code=${code}) before output matched wait patterns.\n${details}`));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
18
106
|
class ViteComponentExplorerHttpServer {
|
|
19
107
|
_server;
|
|
20
108
|
url;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
constructor(_server, url, sourceTreeId) {
|
|
109
|
+
onSourceChange;
|
|
110
|
+
constructor(_server, url, onSourceChange) {
|
|
24
111
|
this._server = _server;
|
|
25
112
|
this.url = url;
|
|
26
|
-
this.
|
|
113
|
+
this.onSourceChange = onSourceChange;
|
|
27
114
|
}
|
|
28
115
|
static async create(project, options) {
|
|
29
116
|
const log = options?.logger ?? nullLogger;
|
|
@@ -116,11 +203,10 @@ class ViteComponentExplorerHttpServer {
|
|
|
116
203
|
const resolvedUrls = server.resolvedUrls;
|
|
117
204
|
const url = resolvedUrls?.local[0] ?? `http://localhost:${server.httpServer?.address()?.port}`;
|
|
118
205
|
log.debug(`Vite server started: ${url}`);
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return new ViteComponentExplorerHttpServer(server, url.replace(/\/$/, ''), treeId);
|
|
206
|
+
const onSourceChange = (callback) => {
|
|
207
|
+
server.watcher.on('change', callback);
|
|
208
|
+
};
|
|
209
|
+
return new ViteComponentExplorerHttpServer(server, url.replace(/\/$/, ''), onSourceChange);
|
|
124
210
|
}
|
|
125
211
|
async dispose() {
|
|
126
212
|
await this._server.close();
|
package/dist/httpServer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpServer.js","sources":["../src/httpServer.ts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"httpServer.js","sources":["../src/httpServer.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpServer.test.d.ts","sourceRoot":"","sources":["../src/httpServer.test.ts"],"names":[],"mappings":""}
|
package/dist/mcp/McpServer.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare class ComponentExplorerMcpServer extends Disposable {
|
|
|
18
18
|
static create(daemon: IObservable<DaemonConnection | undefined>, options?: McpServerOptions): Promise<ComponentExplorerMcpServer>;
|
|
19
19
|
private readonly _mcp;
|
|
20
20
|
private readonly _watchList;
|
|
21
|
+
private readonly _imageLru;
|
|
21
22
|
private readonly _taskManager;
|
|
22
23
|
private readonly _taskLastReportedIndex;
|
|
23
24
|
private readonly _pollFn?;
|
|
@@ -50,6 +51,8 @@ export declare class ComponentExplorerMcpServer extends Disposable {
|
|
|
50
51
|
private _registerScreenshot;
|
|
51
52
|
private _registerCompareScreenshot;
|
|
52
53
|
private _registerApproveDiff;
|
|
54
|
+
private _registerReviewVisual;
|
|
55
|
+
private _registerCheckVisuals;
|
|
53
56
|
private _registerEvaluateJs;
|
|
54
57
|
private _registerDebugReloadPage;
|
|
55
58
|
private _registerWatchAdd;
|
|
@@ -67,5 +70,7 @@ export declare class ComponentExplorerMcpServer extends Disposable {
|
|
|
67
70
|
private _registerCheckStability;
|
|
68
71
|
private _registerCheckTask;
|
|
69
72
|
private _registerCancelTask;
|
|
73
|
+
private _registerDebugGetImageByHash;
|
|
74
|
+
private _registerDebugSetBrowserVisibility;
|
|
70
75
|
}
|
|
71
76
|
//# sourceMappingURL=McpServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServer.d.ts","sourceRoot":"","sources":["../../src/mcp/McpServer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAW,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"McpServer.d.ts","sourceRoot":"","sources":["../../src/mcp/McpServer.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/McpServer.js
CHANGED
|
@@ -14,6 +14,35 @@ import { TaskManager } from './TaskManager.js';
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
// Client-local state
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
|
+
class ImageLruCache {
|
|
18
|
+
_maxSize;
|
|
19
|
+
_entries = [];
|
|
20
|
+
constructor(_maxSize = 10) {
|
|
21
|
+
this._maxSize = _maxSize;
|
|
22
|
+
}
|
|
23
|
+
put(hash, image) {
|
|
24
|
+
const idx = this._entries.findIndex(e => e.hash === hash);
|
|
25
|
+
if (idx !== -1) {
|
|
26
|
+
this._entries.splice(idx, 1);
|
|
27
|
+
}
|
|
28
|
+
this._entries.unshift({ hash, image });
|
|
29
|
+
if (this._entries.length > this._maxSize) {
|
|
30
|
+
this._entries.length = this._maxSize;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
get(hash) {
|
|
34
|
+
const idx = this._entries.findIndex(e => e.hash === hash);
|
|
35
|
+
if (idx === -1) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const [entry] = this._entries.splice(idx, 1);
|
|
39
|
+
this._entries.unshift(entry);
|
|
40
|
+
return entry.image;
|
|
41
|
+
}
|
|
42
|
+
keys() {
|
|
43
|
+
return this._entries.map(e => e.hash);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
17
46
|
class WatchList {
|
|
18
47
|
_fixtureIds = new Set();
|
|
19
48
|
_hashes = new Map();
|
|
@@ -101,6 +130,7 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
101
130
|
}
|
|
102
131
|
_mcp;
|
|
103
132
|
_watchList = new WatchList();
|
|
133
|
+
_imageLru = new ImageLruCache(10);
|
|
104
134
|
_taskManager = new TaskManager();
|
|
105
135
|
_taskLastReportedIndex = new Map();
|
|
106
136
|
_pollFn;
|
|
@@ -337,6 +367,8 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
337
367
|
this._registerScreenshot();
|
|
338
368
|
this._registerCompareScreenshot();
|
|
339
369
|
this._registerApproveDiff();
|
|
370
|
+
this._registerReviewVisual();
|
|
371
|
+
this._registerCheckVisuals();
|
|
340
372
|
this._registerEvaluateJs();
|
|
341
373
|
this._registerDebugReloadPage();
|
|
342
374
|
this._registerWatchAdd();
|
|
@@ -353,6 +385,8 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
353
385
|
this._registerCheckStability();
|
|
354
386
|
this._registerCheckTask();
|
|
355
387
|
this._registerCancelTask();
|
|
388
|
+
this._registerDebugGetImageByHash();
|
|
389
|
+
this._registerDebugSetBrowserVisibility();
|
|
356
390
|
}
|
|
357
391
|
_registerListFixtures() {
|
|
358
392
|
this._mcp.registerTool('list_fixtures', {
|
|
@@ -369,14 +403,17 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
369
403
|
this._log('debug', { type: 'tool-call', tool: 'list_fixtures', sessionName });
|
|
370
404
|
return this._withSourceTreeRetry(async () => {
|
|
371
405
|
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
372
|
-
const
|
|
373
|
-
const filtered = this._filterFixtures(
|
|
406
|
+
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
407
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdPattern, args.labelPattern);
|
|
374
408
|
if ('error' in filtered) {
|
|
375
409
|
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
376
410
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
411
|
+
const content = [];
|
|
412
|
+
if (listResult.loadError) {
|
|
413
|
+
content.push({ type: 'text', text: `Warning: Fixture loading encountered an error: ${listResult.loadError}\nThe fixture list may be incomplete.` });
|
|
414
|
+
}
|
|
415
|
+
content.push({ type: 'text', text: JSON.stringify(filtered.fixtures, null, 2) });
|
|
416
|
+
return { content };
|
|
380
417
|
});
|
|
381
418
|
}));
|
|
382
419
|
}
|
|
@@ -406,6 +443,10 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
406
443
|
});
|
|
407
444
|
const r = result;
|
|
408
445
|
this._updateSessionSourceTreeId(sessionName, r.sourceTreeId);
|
|
446
|
+
// Cache image for debug_get_image_by_hash
|
|
447
|
+
if (r.hash && r.image) {
|
|
448
|
+
this._imageLru.put(r.hash, r.image);
|
|
449
|
+
}
|
|
409
450
|
const info = {
|
|
410
451
|
hash: r.hash,
|
|
411
452
|
sourceTreeId: r.sourceTreeId,
|
|
@@ -425,6 +466,23 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
425
466
|
if (r.isStable !== undefined) {
|
|
426
467
|
info.isStable = r.isStable;
|
|
427
468
|
}
|
|
469
|
+
// Visual review status
|
|
470
|
+
if (r.hash) {
|
|
471
|
+
try {
|
|
472
|
+
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
473
|
+
const fixture = listResult.fixtures.find(f => f.fixtureId === args.fixtureId);
|
|
474
|
+
const descriptions = fixture?.expectedVisualDescriptions ?? [];
|
|
475
|
+
const review = await daemon.methods.visualReview.getStatus({
|
|
476
|
+
fixtureId: args.fixtureId,
|
|
477
|
+
expectedVisualDescriptions: [...descriptions],
|
|
478
|
+
screenshotHash: r.hash,
|
|
479
|
+
});
|
|
480
|
+
info.review = review;
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
// Visual review not available — ignore
|
|
484
|
+
}
|
|
485
|
+
}
|
|
428
486
|
const content = [];
|
|
429
487
|
if (r.isStable === false && r.stabilityScreenshots) {
|
|
430
488
|
// Not stable: return all distinct screenshots
|
|
@@ -545,6 +603,130 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
545
603
|
tool.disable();
|
|
546
604
|
this._multiSessionTools.push(tool);
|
|
547
605
|
}
|
|
606
|
+
_registerReviewVisual() {
|
|
607
|
+
this._mcp.registerTool('review_visual', {
|
|
608
|
+
description: 'Approve or reject a fixture\'s screenshot based on its expectedVisualDescriptions. ' +
|
|
609
|
+
'You must take a screenshot first and pass the resulting hash. ' +
|
|
610
|
+
'On approve, caches (expectedVisualDescriptions, screenshotHash) so future runs auto-approve.',
|
|
611
|
+
inputSchema: {
|
|
612
|
+
fixtureId: z.string().describe('The fixture ID'),
|
|
613
|
+
screenshotHash: z.string().describe('The screenshot hash (from a prior screenshot tool call)'),
|
|
614
|
+
verdict: z.enum(['approve', 'reject']).describe('Whether the visual matches expectations'),
|
|
615
|
+
comment: z.string().describe('Reason for the verdict'),
|
|
616
|
+
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
617
|
+
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
618
|
+
},
|
|
619
|
+
}, async (args) => this._withDaemon(async (daemon) => {
|
|
620
|
+
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
621
|
+
this._log('debug', { type: 'tool-call', tool: 'review_visual', fixtureId: args.fixtureId, verdict: args.verdict });
|
|
622
|
+
return this._withSourceTreeRetry(async () => {
|
|
623
|
+
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
624
|
+
// Get fixture descriptions
|
|
625
|
+
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
626
|
+
const fixture = listResult.fixtures.find(f => f.fixtureId === args.fixtureId);
|
|
627
|
+
if (!fixture) {
|
|
628
|
+
return { content: [{ type: 'text', text: `Fixture not found: ${args.fixtureId}` }], isError: true };
|
|
629
|
+
}
|
|
630
|
+
if (fixture.expectedVisualDescriptions.length === 0) {
|
|
631
|
+
return { content: [{ type: 'text', text: `Fixture ${args.fixtureId} has no expectedVisualDescriptions — nothing to review.` }], isError: true };
|
|
632
|
+
}
|
|
633
|
+
if (args.verdict === 'approve') {
|
|
634
|
+
await daemon.methods.visualReview.approve({
|
|
635
|
+
fixtureId: args.fixtureId,
|
|
636
|
+
expectedVisualDescriptions: [...fixture.expectedVisualDescriptions],
|
|
637
|
+
screenshotHash: args.screenshotHash,
|
|
638
|
+
comment: args.comment,
|
|
639
|
+
});
|
|
640
|
+
return {
|
|
641
|
+
content: [{ type: 'text', text: `Approved: ${args.fixtureId} (hash: ${args.screenshotHash})` }],
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
return {
|
|
646
|
+
content: [{ type: 'text', text: JSON.stringify({
|
|
647
|
+
fixtureId: args.fixtureId,
|
|
648
|
+
verdict: 'rejected',
|
|
649
|
+
comment: args.comment,
|
|
650
|
+
screenshotHash: args.screenshotHash,
|
|
651
|
+
expectedVisualDescriptions: fixture.expectedVisualDescriptions,
|
|
652
|
+
}, null, 2) }],
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}));
|
|
657
|
+
}
|
|
658
|
+
_registerCheckVisuals() {
|
|
659
|
+
this._mcp.registerTool('check_visuals', {
|
|
660
|
+
description: 'Batch check visual review status for fixtures that have expectedVisualDescription. ' +
|
|
661
|
+
'Returns lists of approved, needs-review, and no-expectation fixtures.',
|
|
662
|
+
inputSchema: {
|
|
663
|
+
fixtureIdPattern: z.string().optional().describe('RegExp to filter fixtures by fixture ID'),
|
|
664
|
+
labelPattern: z.string().optional().describe('RegExp to filter fixtures by label'),
|
|
665
|
+
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
666
|
+
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
667
|
+
},
|
|
668
|
+
annotations: { readOnlyHint: true },
|
|
669
|
+
}, async (args) => this._withDaemon(async (daemon) => {
|
|
670
|
+
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
671
|
+
this._log('debug', { type: 'tool-call', tool: 'check_visuals', sessionName });
|
|
672
|
+
return this._withSourceTreeRetry(async () => {
|
|
673
|
+
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
674
|
+
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
675
|
+
if (listResult.loadError) {
|
|
676
|
+
return { content: [{ type: 'text', text: `Error: Fixture loading failed: ${listResult.loadError}\nThe fixture list may be incomplete.` }], isError: true };
|
|
677
|
+
}
|
|
678
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdPattern, args.labelPattern);
|
|
679
|
+
if ('error' in filtered) {
|
|
680
|
+
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
681
|
+
}
|
|
682
|
+
const approved = [];
|
|
683
|
+
const needsReview = [];
|
|
684
|
+
const noExpectation = [];
|
|
685
|
+
// Take screenshots for all fixtures with expectations
|
|
686
|
+
const withExpectations = filtered.fixtures.filter(f => f.expectedVisualDescriptions.length > 0);
|
|
687
|
+
const withoutExpectations = filtered.fixtures.filter(f => f.expectedVisualDescriptions.length === 0);
|
|
688
|
+
for (const f of withoutExpectations) {
|
|
689
|
+
noExpectation.push(f.fixtureId);
|
|
690
|
+
}
|
|
691
|
+
for (const f of withExpectations) {
|
|
692
|
+
try {
|
|
693
|
+
const screenshotResult = await daemon.methods.screenshots.take({
|
|
694
|
+
fixtureId: f.fixtureId, sessionName, sourceTreeId, includeImage: true,
|
|
695
|
+
});
|
|
696
|
+
const hash = screenshotResult.hash;
|
|
697
|
+
const image = screenshotResult.image;
|
|
698
|
+
if (hash && image) {
|
|
699
|
+
this._imageLru.put(hash, image);
|
|
700
|
+
}
|
|
701
|
+
if (!hash) {
|
|
702
|
+
needsReview.push({ fixtureId: f.fixtureId, reason: 'screenshot-failed' });
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
const review = await daemon.methods.visualReview.getStatus({
|
|
706
|
+
fixtureId: f.fixtureId,
|
|
707
|
+
expectedVisualDescriptions: [...f.expectedVisualDescriptions],
|
|
708
|
+
screenshotHash: hash,
|
|
709
|
+
});
|
|
710
|
+
if (review === 'no-expectations') {
|
|
711
|
+
noExpectation.push(f.fixtureId);
|
|
712
|
+
}
|
|
713
|
+
else if (typeof review === 'object' && review.status === 'approved') {
|
|
714
|
+
approved.push({ fixtureId: f.fixtureId, screenshotHash: hash });
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
needsReview.push({ fixtureId: f.fixtureId, screenshotHash: hash, reason: 'needs-review' });
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
catch {
|
|
721
|
+
needsReview.push({ fixtureId: f.fixtureId, reason: 'error' });
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
content: [{ type: 'text', text: JSON.stringify({ approved, needsReview, noExpectation }, null, 2) }],
|
|
726
|
+
};
|
|
727
|
+
});
|
|
728
|
+
}));
|
|
729
|
+
}
|
|
548
730
|
_registerEvaluateJs() {
|
|
549
731
|
this._mcp.registerTool('evaluate_js', {
|
|
550
732
|
description: 'Evaluate a JavaScript expression in the browser page where fixtures are rendered, for debugging purposes. ' +
|
|
@@ -1005,13 +1187,16 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1005
1187
|
this._log('debug', { type: 'tool-call', tool: 'check_stability', sessionName, fixtureIdPattern: args.fixtureIdPattern, labelPattern: args.labelPattern });
|
|
1006
1188
|
return this._withSourceTreeRetry(async () => {
|
|
1007
1189
|
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1190
|
+
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
1191
|
+
if (listResult.loadError) {
|
|
1192
|
+
return { content: [{ type: 'text', text: `Error: Fixture loading failed: ${listResult.loadError}\nThe fixture list may be incomplete.` }], isError: true };
|
|
1193
|
+
}
|
|
1194
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdPattern, args.labelPattern);
|
|
1010
1195
|
if ('error' in filtered) {
|
|
1011
1196
|
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
1012
1197
|
}
|
|
1013
1198
|
const fixtures = filtered.fixtures;
|
|
1014
|
-
this._log('info', { type: 'check-stability-start', total: fixtures.length, filtered:
|
|
1199
|
+
this._log('info', { type: 'check-stability-start', total: fixtures.length, filtered: listResult.fixtures.length - fixtures.length });
|
|
1015
1200
|
const task = this._taskManager.startTask(async (report, signal) => {
|
|
1016
1201
|
const results = [];
|
|
1017
1202
|
report({ completed: 0, total: fixtures.length, partialResult: results });
|
|
@@ -1138,6 +1323,48 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1138
1323
|
};
|
|
1139
1324
|
});
|
|
1140
1325
|
}
|
|
1326
|
+
_registerDebugGetImageByHash() {
|
|
1327
|
+
this._mcp.registerTool('debug_get_image_by_hash', {
|
|
1328
|
+
description: 'Retrieve a recently-taken screenshot image by its hash. ' +
|
|
1329
|
+
'Keeps the last ~10 images in an LRU cache. ' +
|
|
1330
|
+
'Useful for debugging when screenshot hashes behave unexpectedly.',
|
|
1331
|
+
inputSchema: {
|
|
1332
|
+
hash: z.string().describe('The screenshot hash to look up'),
|
|
1333
|
+
},
|
|
1334
|
+
annotations: { readOnlyHint: true },
|
|
1335
|
+
}, async (args) => {
|
|
1336
|
+
const image = this._imageLru.get(args.hash);
|
|
1337
|
+
if (!image) {
|
|
1338
|
+
return {
|
|
1339
|
+
content: [{ type: 'text', text: `No cached image for hash '${args.hash}'. Available hashes: ${this._imageLru.keys().join(', ') || '(none)'}` }],
|
|
1340
|
+
isError: true,
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
content: [
|
|
1345
|
+
{ type: 'text', text: `Image for hash: ${args.hash}` },
|
|
1346
|
+
{ type: 'image', data: image, mimeType: 'image/png' },
|
|
1347
|
+
],
|
|
1348
|
+
};
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
_registerDebugSetBrowserVisibility() {
|
|
1352
|
+
this._mcp.registerTool('debug_set_browser_visibility', {
|
|
1353
|
+
description: 'Show or hide the browser window used for rendering fixtures. ' +
|
|
1354
|
+
'Only use this tool when the user explicitly asks to show or hide the browser. ' +
|
|
1355
|
+
'Do not call this tool automatically or as part of other workflows. ' +
|
|
1356
|
+
'Note: changing visibility closes the current browser instance, so the next screenshot or evaluate_js call will relaunch it.',
|
|
1357
|
+
inputSchema: {
|
|
1358
|
+
visible: z.boolean().describe('true to show the browser window (headed mode), false to hide it (headless mode)'),
|
|
1359
|
+
},
|
|
1360
|
+
annotations: { destructiveHint: true },
|
|
1361
|
+
}, async (args) => this._withDaemon(async (daemon) => {
|
|
1362
|
+
await daemon.methods.setBrowserVisibility({ visible: args.visible });
|
|
1363
|
+
return {
|
|
1364
|
+
content: [{ type: 'text', text: `Browser is now ${args.visible ? 'visible (headed)' : 'hidden (headless)'}.` }],
|
|
1365
|
+
};
|
|
1366
|
+
}));
|
|
1367
|
+
}
|
|
1141
1368
|
}
|
|
1142
1369
|
|
|
1143
1370
|
export { ComponentExplorerMcpServer, DaemonConnection };
|