@vscode/component-explorer-cli 0.2.1-18 → 0.2.1-181
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 +130 -12
- package/dist/WorktreePool.d.ts +5 -4
- package/dist/WorktreePool.d.ts.map +1 -1
- package/dist/WorktreePool.js +3 -3
- package/dist/WorktreePool.js.map +1 -1
- package/dist/_virtual/_build-info.js +1 -1
- package/dist/browserPage.d.ts +55 -1
- package/dist/browserPage.d.ts.map +1 -1
- package/dist/browserPage.js +103 -7
- package/dist/browserPage.js.map +1 -1
- package/dist/browserPage.test.d.ts +2 -0
- package/dist/browserPage.test.d.ts.map +1 -0
- package/dist/commands/acceptCommand.d.ts +1 -1
- package/dist/commands/acceptCommand.d.ts.map +1 -1
- package/dist/commands/acceptCommand.js +21 -14
- package/dist/commands/acceptCommand.js.map +1 -1
- package/dist/commands/checkStabilityCommand.d.ts +2 -0
- package/dist/commands/checkStabilityCommand.d.ts.map +1 -1
- package/dist/commands/checkStabilityCommand.js +25 -8
- package/dist/commands/checkStabilityCommand.js.map +1 -1
- package/dist/commands/compareCommand.d.ts +1 -1
- package/dist/commands/compareCommand.d.ts.map +1 -1
- package/dist/commands/compareCommand.js +31 -22
- package/dist/commands/compareCommand.js.map +1 -1
- package/dist/commands/inputArg.d.ts +33 -0
- package/dist/commands/inputArg.d.ts.map +1 -0
- package/dist/commands/inputArg.js +103 -0
- package/dist/commands/inputArg.js.map +1 -0
- package/dist/commands/mcpCommand.d.ts.map +1 -1
- package/dist/commands/mcpCommand.js +18 -12
- package/dist/commands/mcpCommand.js.map +1 -1
- package/dist/commands/renderCommand.d.ts +10 -1
- package/dist/commands/renderCommand.d.ts.map +1 -1
- package/dist/commands/renderCommand.js +390 -61
- package/dist/commands/renderCommand.js.map +1 -1
- package/dist/commands/renderCommand.test.d.ts +2 -0
- package/dist/commands/renderCommand.test.d.ts.map +1 -0
- package/dist/commands/serveCommand.d.ts +4 -1
- package/dist/commands/serveCommand.d.ts.map +1 -1
- package/dist/commands/serveCommand.js +105 -36
- package/dist/commands/serveCommand.js.map +1 -1
- package/dist/commands/serviceDiffCommitsCommand.d.ts.map +1 -1
- package/dist/commands/serviceDiffCommitsCommand.js +10 -9
- package/dist/commands/serviceDiffCommitsCommand.js.map +1 -1
- package/dist/commands/watchCommand.d.ts +1 -0
- package/dist/commands/watchCommand.d.ts.map +1 -1
- package/dist/commands/watchCommand.js +31 -20
- package/dist/commands/watchCommand.js.map +1 -1
- package/dist/comparison.d.ts +4 -3
- package/dist/comparison.d.ts.map +1 -1
- package/dist/comparison.js +17 -14
- package/dist/comparison.js.map +1 -1
- package/dist/component-explorer-config.schema.json +55 -2
- package/dist/componentExplorer.d.ts +110 -12
- package/dist/componentExplorer.d.ts.map +1 -1
- package/dist/componentExplorer.js +177 -24
- package/dist/componentExplorer.js.map +1 -1
- package/dist/config.d.ts +33 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +64 -27
- package/dist/config.js.map +1 -1
- package/dist/coverage.d.ts +64 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +212 -0
- package/dist/coverage.js.map +1 -0
- package/dist/daemon/DaemonService.d.ts +5 -199
- package/dist/daemon/DaemonService.d.ts.map +1 -1
- package/dist/daemon/DaemonService.js +248 -300
- package/dist/daemon/DaemonService.js.map +1 -1
- package/dist/daemon/approvalStore.d.ts +2 -1
- package/dist/daemon/approvalStore.d.ts.map +1 -1
- package/dist/daemon/approvalStore.js +4 -3
- package/dist/daemon/approvalStore.js.map +1 -1
- package/dist/daemon/client.d.ts +76 -5
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/daemonRpc.test.d.ts +2 -0
- package/dist/daemon/daemonRpc.test.d.ts.map +1 -0
- package/dist/daemon/endpointServer.d.ts +4 -0
- package/dist/daemon/endpointServer.d.ts.map +1 -0
- package/dist/daemon/endpointServer.js +24 -0
- package/dist/daemon/endpointServer.js.map +1 -0
- package/dist/daemon/inProcessClient.d.ts +3 -9
- package/dist/daemon/inProcessClient.d.ts.map +1 -1
- package/dist/daemon/inProcessClient.js +29 -30
- package/dist/daemon/inProcessClient.js.map +1 -1
- package/dist/daemon/lifecycle.d.ts +5 -5
- package/dist/daemon/lifecycle.d.ts.map +1 -1
- package/dist/daemon/lifecycle.js +4 -3
- package/dist/daemon/lifecycle.js.map +1 -1
- package/dist/daemon/pipeClient.d.ts +2 -3
- package/dist/daemon/pipeClient.d.ts.map +1 -1
- package/dist/daemon/pipeClient.js +93 -194
- package/dist/daemon/pipeClient.js.map +1 -1
- package/dist/daemon/pipeName.d.ts +3 -3
- package/dist/daemon/pipeName.d.ts.map +1 -1
- package/dist/daemon/pipeName.js +6 -5
- package/dist/daemon/pipeName.js.map +1 -1
- package/dist/daemon/pipeServer.d.ts +2 -2
- package/dist/daemon/pipeServer.d.ts.map +1 -1
- package/dist/daemon/pipeServer.js +79 -70
- package/dist/daemon/pipeServer.js.map +1 -1
- package/dist/daemon/registerDaemonConnection.d.ts +7 -0
- package/dist/daemon/registerDaemonConnection.d.ts.map +1 -0
- package/dist/daemon/registerDaemonConnection.js +37 -0
- package/dist/daemon/registerDaemonConnection.js.map +1 -0
- package/dist/daemon/transports.test.d.ts +2 -0
- package/dist/daemon/transports.test.d.ts.map +1 -0
- package/dist/daemon/types.d.ts +1655 -8
- package/dist/daemon/types.d.ts.map +1 -1
- package/dist/daemon/types.js +4 -0
- package/dist/daemon/types.js.map +1 -0
- package/dist/daemon/version.d.ts +1 -5
- package/dist/daemon/version.d.ts.map +1 -1
- package/dist/daemon/version.js +3 -7
- package/dist/daemon/version.js.map +1 -1
- package/dist/dependencyInstaller.d.ts +2 -1
- package/dist/dependencyInstaller.d.ts.map +1 -1
- package/dist/dependencyInstaller.js +4 -4
- package/dist/dependencyInstaller.js.map +1 -1
- package/dist/explorerSession.d.ts +5 -1
- package/dist/explorerSession.d.ts.map +1 -1
- package/dist/explorerSession.js +24 -7
- package/dist/explorerSession.js.map +1 -1
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/chunks/inspectionHost-BA6ZmC_P.js +2523 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/chunks/inspectionHost-BA6ZmC_P.js.map +1 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/chunks/src-DYVSK_tz.js +1871 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/chunks/src-DYVSK_tz.js.map +1 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/chunks/webSocketTransport-Ct4OIdG9.js +220 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/chunks/webSocketTransport-Ct4OIdG9.js.map +1 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/node.js +721 -0
- package/dist/external/linkrpc/typescript/packages/linkrpc/dist/node.js.map +1 -0
- package/dist/git/gitCommitResolver.d.ts +2 -1
- package/dist/git/gitCommitResolver.d.ts.map +1 -1
- package/dist/git/gitCommitResolver.js +8 -8
- package/dist/git/gitCommitResolver.js.map +1 -1
- package/dist/git/gitIndexResolver.d.ts +2 -1
- package/dist/git/gitIndexResolver.d.ts.map +1 -1
- package/dist/git/gitIndexResolver.js +7 -7
- package/dist/git/gitIndexResolver.js.map +1 -1
- package/dist/git/gitService.d.ts +5 -4
- package/dist/git/gitService.d.ts.map +1 -1
- package/dist/git/gitService.js.map +1 -1
- package/dist/git/gitUtils.d.ts +6 -5
- package/dist/git/gitUtils.d.ts.map +1 -1
- package/dist/git/gitUtils.js +5 -4
- package/dist/git/gitUtils.js.map +1 -1
- package/dist/git/gitWorktreeManager.d.ts +15 -14
- package/dist/git/gitWorktreeManager.d.ts.map +1 -1
- package/dist/git/gitWorktreeManager.js +10 -11
- package/dist/git/gitWorktreeManager.js.map +1 -1
- package/dist/git/testUtils.d.ts +2 -1
- package/dist/git/testUtils.d.ts.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/js-component-explorer/packages/common/dist/daemonRpc.js +165 -0
- package/dist/js-component-explorer/packages/common/dist/daemonRpc.js.map +1 -0
- package/dist/js-component-explorer/packages/common/dist/explorerUrl.js +21 -0
- package/dist/js-component-explorer/packages/common/dist/explorerUrl.js.map +1 -0
- package/dist/js-component-explorer/packages/common/dist/renderManifest.js +68 -0
- package/dist/js-component-explorer/packages/common/dist/renderManifest.js.map +1 -0
- package/dist/js-component-explorer/packages/semver/dist/index.js.map +1 -0
- package/dist/manifest.schema.json +44 -16
- package/dist/mcp/DaemonAccessor.d.ts +2 -3
- package/dist/mcp/DaemonAccessor.d.ts.map +1 -1
- package/dist/mcp/McpServer.d.ts +1 -1
- package/dist/mcp/McpServer.d.ts.map +1 -1
- package/dist/mcp/McpServer.js +252 -159
- package/dist/mcp/McpServer.js.map +1 -1
- package/dist/path.d.ts +30 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +78 -0
- package/dist/path.js.map +1 -0
- package/dist/processTree.d.ts +6 -1
- package/dist/processTree.d.ts.map +1 -1
- package/dist/processTree.js +48 -5
- package/dist/processTree.js.map +1 -1
- package/dist/renderManifest.d.ts +70 -5
- package/dist/renderManifest.js +1 -1
- package/dist/server/httpServer.d.ts +1 -1
- package/dist/server/httpServer.d.ts.map +1 -1
- package/dist/server/httpServer.js +4 -8
- package/dist/server/httpServer.js.map +1 -1
- package/dist/server/serverConfig.d.ts +23 -8
- package/dist/server/serverConfig.d.ts.map +1 -1
- package/dist/server/serverConfig.js +11 -18
- package/dist/server/serverConfig.js.map +1 -1
- package/dist/server/viteServer.d.ts +16 -3
- package/dist/server/viteServer.d.ts.map +1 -1
- package/dist/server/viteServer.js +86 -5
- package/dist/server/viteServer.js.map +1 -1
- package/dist/storage.d.ts +2 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/storage.js.map +1 -1
- package/dist/utils.d.ts +3 -30
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -24
- package/dist/utils.js.map +1 -1
- package/dist/visualCache.d.ts +2 -1
- package/dist/visualCache.d.ts.map +1 -1
- package/dist/visualCache.js +4 -3
- package/dist/visualCache.js.map +1 -1
- package/dist/viteProjectRef.d.ts +4 -12
- package/dist/viteProjectRef.d.ts.map +1 -1
- package/dist/viteProjectRef.js +9 -17
- package/dist/viteProjectRef.js.map +1 -1
- package/dist/vscode-observables/observables/dist/disposables.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/base.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/commonFacade/deps.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/debugLocation.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/debugName.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/index.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/logging/consoleObservableLogger.js.map +1 -0
- package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/logging/debugGetDependencyGraph.js +1 -1
- package/dist/vscode-observables/observables/dist/observableInternal/logging/debugGetDependencyGraph.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/logging/logging.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/observables/baseObservable.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/observables/derived.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/observables/derivedImpl.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/observables/observableFromEvent.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/observables/observableValue.js.map +1 -0
- package/dist/{external/vscode-observables/observables/dist/observableInternal/reactions/autorun.js → vscode-observables/observables/dist/observableInternal/reactions/createEffect.js} +9 -5
- package/dist/vscode-observables/observables/dist/observableInternal/reactions/createEffect.js.map +1 -0
- package/dist/{external/vscode-observables/observables/dist/observableInternal/reactions/autorunImpl.js → vscode-observables/observables/dist/observableInternal/reactions/createEffectImpl.js} +1 -1
- package/dist/vscode-observables/observables/dist/observableInternal/reactions/createEffectImpl.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/transaction.js.map +1 -0
- package/dist/vscode-observables/observables/dist/observableInternal/utils/utils.js.map +1 -0
- package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/utils/utilsCancellation.js +2 -2
- package/dist/vscode-observables/observables/dist/observableInternal/utils/utilsCancellation.js.map +1 -0
- package/package.json +18 -15
- package/dist/commands/artifactCommand.d.ts +0 -14
- package/dist/commands/artifactCommand.d.ts.map +0 -1
- package/dist/external/vscode-observables/observables/dist/disposables.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/base.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/commonFacade/deps.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/debugLocation.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/debugName.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/index.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/logging/consoleObservableLogger.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/logging/debugGetDependencyGraph.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/logging/logging.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/observables/baseObservable.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/observables/derived.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/observables/derivedImpl.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/observables/observableFromEvent.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/observables/observableValue.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/reactions/autorun.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/reactions/autorunImpl.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/transaction.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/utils/utils.js.map +0 -1
- package/dist/external/vscode-observables/observables/dist/observableInternal/utils/utilsCancellation.js.map +0 -1
- package/dist/formatValue.js +0 -96
- package/dist/formatValue.js.map +0 -1
- package/dist/packages/common/dist/renderManifest.js +0 -46
- package/dist/packages/common/dist/renderManifest.js.map +0 -1
- package/dist/packages/semver/dist/index.js.map +0 -1
- package/dist/packages/simple-api/dist/chunk-FJ7AVNQE.js +0 -138
- package/dist/packages/simple-api/dist/chunk-FJ7AVNQE.js.map +0 -1
- package/dist/packages/simple-api/dist/chunk-TTRCY65Z.js +0 -27
- package/dist/packages/simple-api/dist/chunk-TTRCY65Z.js.map +0 -1
- package/dist/packages/simple-api/dist/chunk-WNXMRXWV.js +0 -28
- package/dist/packages/simple-api/dist/chunk-WNXMRXWV.js.map +0 -1
- package/dist/packages/simple-api/dist/express.js +0 -104
- package/dist/packages/simple-api/dist/express.js.map +0 -1
- /package/dist/{packages → js-component-explorer/packages}/semver/dist/index.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/disposables.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/base.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/commonFacade/deps.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/debugLocation.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/debugName.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/index.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/logging/consoleObservableLogger.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/logging/logging.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/observables/baseObservable.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/observables/derived.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/observables/derivedImpl.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/observables/observableFromEvent.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/observables/observableValue.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/transaction.js +0 -0
- /package/dist/{external/vscode-observables → vscode-observables}/observables/dist/observableInternal/utils/utils.js +0 -0
package/dist/mcp/McpServer.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import '../
|
|
5
|
-
import { Disposable } from '../
|
|
6
|
-
import '../
|
|
7
|
-
import { autorun } from '../
|
|
8
|
-
import '../
|
|
9
|
-
import '../
|
|
10
|
-
import '../
|
|
11
|
-
import
|
|
4
|
+
import '../vscode-observables/observables/dist/observableInternal/index.js';
|
|
5
|
+
import { Disposable } from '../vscode-observables/observables/dist/disposables.js';
|
|
6
|
+
import '../vscode-observables/observables/dist/observableInternal/debugLocation.js';
|
|
7
|
+
import { autorun } from '../vscode-observables/observables/dist/observableInternal/reactions/createEffect.js';
|
|
8
|
+
import '../vscode-observables/observables/dist/observableInternal/observables/derived.js';
|
|
9
|
+
import '../vscode-observables/observables/dist/observableInternal/utils/utils.js';
|
|
10
|
+
import '../vscode-observables/observables/dist/observableInternal/observables/observableFromEvent.js';
|
|
11
|
+
import '../js-component-explorer/packages/common/dist/renderManifest.js';
|
|
12
|
+
import { buildExplorerUrl } from '../js-component-explorer/packages/common/dist/explorerUrl.js';
|
|
13
|
+
import { EXPLORER_ROUTE } from '../utils.js';
|
|
12
14
|
import { TaskManager } from './TaskManager.js';
|
|
13
15
|
import { isPipeConnectionError } from '../daemon/pipeClient.js';
|
|
14
16
|
|
|
@@ -30,7 +32,7 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
30
32
|
_taskLastReportedIndex = new Map();
|
|
31
33
|
_multiSessionTools = [];
|
|
32
34
|
_sessions = [];
|
|
33
|
-
|
|
35
|
+
_stopEventListener;
|
|
34
36
|
constructor(_daemon, options) {
|
|
35
37
|
super();
|
|
36
38
|
this._daemon = _daemon;
|
|
@@ -40,21 +42,26 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
40
42
|
version: '0.1.0',
|
|
41
43
|
});
|
|
42
44
|
this._registerTools();
|
|
45
|
+
this._store.add({ dispose: () => this._stopEventListener?.() });
|
|
43
46
|
this._store.add(autorun(async (reader) => {
|
|
44
47
|
const client = this._daemon.connection.read(reader);
|
|
45
48
|
await this._onDaemonChanged(client);
|
|
46
49
|
}));
|
|
47
50
|
}
|
|
48
51
|
async _onDaemonChanged(daemon) {
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
52
|
+
this._stopEventListener?.();
|
|
53
|
+
this._stopEventListener = undefined;
|
|
51
54
|
if (!daemon) {
|
|
52
55
|
this._sessions = [];
|
|
53
56
|
this._log('info', { type: 'daemon-disconnected' });
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
59
|
try {
|
|
57
|
-
|
|
60
|
+
const sessions = await daemon.methods.sessions({});
|
|
61
|
+
if (this._daemon.connection.get() !== daemon) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this._sessions = sessions;
|
|
58
65
|
this._log('debug', { type: 'daemon-connected', sessions: this._sessions.length });
|
|
59
66
|
this._updateMultiSessionToolVisibility();
|
|
60
67
|
this._startEventListener(daemon);
|
|
@@ -100,30 +107,26 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
100
107
|
this._mcp.sendLoggingMessage({ level: mcpLevel, logger: 'daemon', data }).catch(() => { });
|
|
101
108
|
}
|
|
102
109
|
_startEventListener(daemon) {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
const stream = await daemon.methods.events();
|
|
108
|
-
for await (const raw of stream) {
|
|
109
|
-
if (controller.signal.aborted)
|
|
110
|
-
break;
|
|
111
|
-
const event = raw;
|
|
112
|
-
if (event.type === 'source-change' && event.sessionName && event.sourceTreeId) {
|
|
113
|
-
this._updateSessionSourceTreeId(event.sessionName, event.sourceTreeId);
|
|
114
|
-
}
|
|
115
|
-
if (event.type === 'ref-change' || event.type === 'session-change') {
|
|
116
|
-
await this._refreshSessions(daemon);
|
|
117
|
-
}
|
|
118
|
-
this._log(event.type === 'log' && event.level === 'debug' ? 'debug' : 'info', event);
|
|
110
|
+
const call = daemon.methods.events({}, { onMessage: event => {
|
|
111
|
+
if (this._daemon.connection.get() !== daemon) {
|
|
112
|
+
return;
|
|
119
113
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
if (event.type === 'source-change') {
|
|
115
|
+
this._updateSessionSourceTreeId(event.sessionName, event.sourceTreeId);
|
|
116
|
+
}
|
|
117
|
+
if (event.type === 'ref-change' || event.type === 'session-change') {
|
|
118
|
+
void this._refreshSessions(daemon).catch(e => this._log('info', { type: 'session-refresh-error', error: String(e) }));
|
|
124
119
|
}
|
|
120
|
+
this._log(event.type === 'log' && event.level === 'debug' ? 'debug' : 'info', event);
|
|
121
|
+
} });
|
|
122
|
+
this._stopEventListener = () => {
|
|
123
|
+
void call.cancel('listener disposed').finally(() => call.dispose?.('listener disposed')).catch(() => { });
|
|
124
|
+
};
|
|
125
|
+
void call.catch(e => {
|
|
126
|
+
if (this._daemon.connection.get() === daemon) {
|
|
127
|
+
this._log('info', { type: 'event-stream-error', error: String(e) });
|
|
125
128
|
}
|
|
126
|
-
})
|
|
129
|
+
});
|
|
127
130
|
}
|
|
128
131
|
// -- Helpers --------------------------------------------------------------
|
|
129
132
|
_defaultSessionName() {
|
|
@@ -149,7 +152,11 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
149
152
|
}
|
|
150
153
|
async _refreshSessions(daemon) {
|
|
151
154
|
const prevCount = this._sessions.length;
|
|
152
|
-
|
|
155
|
+
const sessions = await daemon.methods.sessions({});
|
|
156
|
+
if (this._daemon.connection.get() !== daemon) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this._sessions = sessions;
|
|
153
160
|
if (this._sessions.length !== prevCount) {
|
|
154
161
|
this._updateMultiSessionToolVisibility();
|
|
155
162
|
}
|
|
@@ -179,23 +186,23 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
179
186
|
}
|
|
180
187
|
}
|
|
181
188
|
// -- Tool registration ---------------------------------------------------
|
|
182
|
-
_filterFixtures(allFixtures,
|
|
189
|
+
_filterFixtures(allFixtures, fixtureIdRegexStr, labelRegexStr) {
|
|
183
190
|
let fixtureIdRegex;
|
|
184
|
-
if (
|
|
191
|
+
if (fixtureIdRegexStr) {
|
|
185
192
|
try {
|
|
186
|
-
fixtureIdRegex = new RegExp(
|
|
193
|
+
fixtureIdRegex = new RegExp(fixtureIdRegexStr);
|
|
187
194
|
}
|
|
188
195
|
catch {
|
|
189
|
-
return { error: `Error: Invalid
|
|
196
|
+
return { error: `Error: Invalid fixtureIdRegex: ${fixtureIdRegexStr}` };
|
|
190
197
|
}
|
|
191
198
|
}
|
|
192
199
|
let labelRegex;
|
|
193
|
-
if (
|
|
200
|
+
if (labelRegexStr) {
|
|
194
201
|
try {
|
|
195
|
-
labelRegex = new RegExp(
|
|
202
|
+
labelRegex = new RegExp(labelRegexStr);
|
|
196
203
|
}
|
|
197
204
|
catch {
|
|
198
|
-
return { error: `Error: Invalid
|
|
205
|
+
return { error: `Error: Invalid labelRegex: ${labelRegexStr}` };
|
|
199
206
|
}
|
|
200
207
|
}
|
|
201
208
|
return {
|
|
@@ -233,12 +240,12 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
233
240
|
_registerListFixtures() {
|
|
234
241
|
this._mcp.registerTool('list_fixtures', {
|
|
235
242
|
description: 'List all fixtures from a session',
|
|
236
|
-
inputSchema: {
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
inputSchema: z.strictObject({
|
|
244
|
+
fixtureIdRegex: z.string().optional().describe('RegExp to filter fixtures by fixture ID'),
|
|
245
|
+
labelRegex: z.string().optional().describe('RegExp to filter fixtures by label (matched against inherited labels)'),
|
|
239
246
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
240
247
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
241
|
-
},
|
|
248
|
+
}),
|
|
242
249
|
annotations: { readOnlyHint: true },
|
|
243
250
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
244
251
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -246,7 +253,7 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
246
253
|
return this._withSourceTreeRetry(daemon, async () => {
|
|
247
254
|
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
248
255
|
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
249
|
-
const filtered = this._filterFixtures(listResult.fixtures, args.
|
|
256
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdRegex, args.labelRegex);
|
|
250
257
|
if ('error' in filtered) {
|
|
251
258
|
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
252
259
|
}
|
|
@@ -262,13 +269,22 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
262
269
|
_registerScreenshot() {
|
|
263
270
|
this._mcp.registerTool('screenshot', {
|
|
264
271
|
description: 'Take a screenshot of a single fixture. ' +
|
|
265
|
-
'When stabilityCheck is true, the fixture is unmounted and re-mounted, then three screenshots are taken. '
|
|
266
|
-
|
|
272
|
+
'When stabilityCheck is true, the fixture is unmounted and re-mounted, then three screenshots are taken. ' +
|
|
273
|
+
'By default the fixture stays mounted after the screenshot (so a follow-up `evaluate_js` call can still operate on it); ' +
|
|
274
|
+
'pass `disposeAfter: true` to dispose immediately and surface any teardown errors as `currentDispose` in the result. ' +
|
|
275
|
+
'Errors / events captured while disposing the previously-mounted fixture (the implicit dispose at the start of every render) are reported as `previousDispose`.',
|
|
276
|
+
inputSchema: z.strictObject({
|
|
267
277
|
fixtureId: z.string().describe('The fixture ID'),
|
|
268
278
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
269
279
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
270
280
|
stabilityCheck: z.boolean().optional().describe('If true, takes three screenshots (at render time, ~500ms, ~3000ms) to check visual stability. Expensive (~3s extra) — only use it when you need to verify rendering stability..'),
|
|
271
|
-
|
|
281
|
+
includeConsoleLog: z.boolean().optional().describe('If true, include console.log events in the returned events array. Defaults to false — console.log entries are filtered out to reduce noise.'),
|
|
282
|
+
input: z.unknown().optional().describe('Arbitrary JSON-serializable data passed to the fixture as `RenderContext.input`. ' +
|
|
283
|
+
'The fixture decides how to interpret it (e.g. theme switch, scenario selection, mock data).'),
|
|
284
|
+
disposeAfter: z.boolean().optional().describe('If true, dispose the fixture after taking the screenshot. ' +
|
|
285
|
+
'Default: false — the fixture stays mounted until the next render, so `evaluate_js` can still operate on it. ' +
|
|
286
|
+
'Use this when validating fixture teardown: dispose errors / events appear in `currentDispose`.'),
|
|
287
|
+
}),
|
|
272
288
|
annotations: { readOnlyHint: true },
|
|
273
289
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
274
290
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -282,6 +298,8 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
282
298
|
sourceTreeId,
|
|
283
299
|
includeImage: true,
|
|
284
300
|
stabilityCheck: args.stabilityCheck,
|
|
301
|
+
input: args.input,
|
|
302
|
+
disposeAfter: args.disposeAfter,
|
|
285
303
|
});
|
|
286
304
|
const r = result;
|
|
287
305
|
this._updateSessionSourceTreeId(sessionName, r.sourceTreeId);
|
|
@@ -289,6 +307,7 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
289
307
|
if (r.hash && r.image) {
|
|
290
308
|
this._imageLru.put(r.hash, r.image);
|
|
291
309
|
}
|
|
310
|
+
const filterEvents = (events) => args.includeConsoleLog ? events : events.filter(e => e.type !== 'console.log');
|
|
292
311
|
const info = {
|
|
293
312
|
hash: r.hash,
|
|
294
313
|
sourceTreeId: r.sourceTreeId,
|
|
@@ -300,14 +319,37 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
300
319
|
info.error = r.error;
|
|
301
320
|
}
|
|
302
321
|
if (r.events && r.events.length > 0) {
|
|
303
|
-
|
|
322
|
+
const filtered = filterEvents(r.events);
|
|
323
|
+
if (filtered.length > 0) {
|
|
324
|
+
info.events = filtered;
|
|
325
|
+
}
|
|
304
326
|
}
|
|
305
|
-
if (r.
|
|
306
|
-
info.
|
|
327
|
+
if (r.output !== undefined) {
|
|
328
|
+
info.output = r.output;
|
|
307
329
|
}
|
|
308
330
|
if (r.isStable !== undefined) {
|
|
309
331
|
info.isStable = r.isStable;
|
|
310
332
|
}
|
|
333
|
+
if (r.previousDispose) {
|
|
334
|
+
const filtered = filterEvents(r.previousDispose.events);
|
|
335
|
+
if (r.previousDispose.hasError || r.previousDispose.errors.length > 0 || filtered.length > 0) {
|
|
336
|
+
info.previousDispose = {
|
|
337
|
+
hasError: r.previousDispose.hasError,
|
|
338
|
+
errors: r.previousDispose.errors,
|
|
339
|
+
events: filtered,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (r.currentDispose) {
|
|
344
|
+
const filtered = filterEvents(r.currentDispose.events);
|
|
345
|
+
if (r.currentDispose.hasError || r.currentDispose.errors.length > 0 || filtered.length > 0) {
|
|
346
|
+
info.currentDispose = {
|
|
347
|
+
hasError: r.currentDispose.hasError,
|
|
348
|
+
errors: r.currentDispose.errors,
|
|
349
|
+
events: filtered,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
311
353
|
// Visual review status
|
|
312
354
|
if (r.hash) {
|
|
313
355
|
try {
|
|
@@ -351,13 +393,13 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
351
393
|
_registerCompareScreenshot() {
|
|
352
394
|
const tool = this._mcp.registerTool('compare_screenshot', {
|
|
353
395
|
description: 'Compare a fixture\'s screenshot across two sessions (e.g. baseline vs current)',
|
|
354
|
-
inputSchema: {
|
|
396
|
+
inputSchema: z.strictObject({
|
|
355
397
|
fixtureId: z.string().describe('The fixture ID'),
|
|
356
398
|
baselineSessionName: z.string().optional().describe('Baseline session name (defaults to worktree session)'),
|
|
357
399
|
currentSessionName: z.string().optional().describe('Current session name (defaults to current session)'),
|
|
358
400
|
baselineSourceTreeId: z.string().optional().describe('Baseline source tree ID (defaults to latest known)'),
|
|
359
401
|
currentSourceTreeId: z.string().optional().describe('Current source tree ID (defaults to latest known)'),
|
|
360
|
-
},
|
|
402
|
+
}),
|
|
361
403
|
annotations: { readOnlyHint: true },
|
|
362
404
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
363
405
|
const baselineSessionName = args.baselineSessionName ?? this._defaultBaselineSessionName();
|
|
@@ -391,8 +433,8 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
391
433
|
if (r.baselineEvents && r.baselineEvents.length > 0) {
|
|
392
434
|
info.baselineEvents = r.baselineEvents;
|
|
393
435
|
}
|
|
394
|
-
if (r.
|
|
395
|
-
info.
|
|
436
|
+
if (r.baselineOutput !== undefined) {
|
|
437
|
+
info.baselineOutput = r.baselineOutput;
|
|
396
438
|
}
|
|
397
439
|
if (r.currentHasError) {
|
|
398
440
|
info.currentHasError = true;
|
|
@@ -403,8 +445,8 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
403
445
|
if (r.currentEvents && r.currentEvents.length > 0) {
|
|
404
446
|
info.currentEvents = r.currentEvents;
|
|
405
447
|
}
|
|
406
|
-
if (r.
|
|
407
|
-
info.
|
|
448
|
+
if (r.currentOutput !== undefined) {
|
|
449
|
+
info.currentOutput = r.currentOutput;
|
|
408
450
|
}
|
|
409
451
|
if (r.approval) {
|
|
410
452
|
info.approval = r.approval;
|
|
@@ -427,12 +469,12 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
427
469
|
_registerApproveDiff() {
|
|
428
470
|
const tool = this._mcp.registerTool('approve_diff', {
|
|
429
471
|
description: 'Approve a visual diff so it won\'t require re-inspection next time',
|
|
430
|
-
inputSchema: {
|
|
472
|
+
inputSchema: z.strictObject({
|
|
431
473
|
fixtureId: z.string(),
|
|
432
474
|
originalHash: z.string(),
|
|
433
475
|
modifiedHash: z.string(),
|
|
434
476
|
comment: z.string().describe('Reason for approving this diff'),
|
|
435
|
-
},
|
|
477
|
+
}),
|
|
436
478
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
437
479
|
await daemon.methods.approvals.approve(args);
|
|
438
480
|
return {
|
|
@@ -450,14 +492,14 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
450
492
|
description: 'Approve or reject a fixture\'s screenshot based on its expectedVisualDescriptions. ' +
|
|
451
493
|
'You must take a screenshot first and pass the resulting hash. ' +
|
|
452
494
|
'On approve, caches (expectedVisualDescriptions, screenshotHash) so future runs auto-approve.',
|
|
453
|
-
inputSchema: {
|
|
495
|
+
inputSchema: z.strictObject({
|
|
454
496
|
fixtureId: z.string().describe('The fixture ID'),
|
|
455
497
|
screenshotHash: z.string().describe('The screenshot hash (from a prior screenshot tool call)'),
|
|
456
498
|
verdict: z.enum(['approve', 'reject']).describe('Whether the visual matches expectations'),
|
|
457
499
|
comment: z.string().describe('Reason for the verdict'),
|
|
458
500
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
459
501
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
460
|
-
},
|
|
502
|
+
}),
|
|
461
503
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
462
504
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
463
505
|
this._log('debug', { type: 'tool-call', tool: 'review_visual', fixtureId: args.fixtureId, verdict: args.verdict });
|
|
@@ -485,13 +527,15 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
485
527
|
}
|
|
486
528
|
else {
|
|
487
529
|
return {
|
|
488
|
-
content: [{
|
|
530
|
+
content: [{
|
|
531
|
+
type: 'text', text: JSON.stringify({
|
|
489
532
|
fixtureId: args.fixtureId,
|
|
490
533
|
verdict: 'rejected',
|
|
491
534
|
comment: args.comment,
|
|
492
535
|
screenshotHash: args.screenshotHash,
|
|
493
536
|
expectedVisualDescriptions: fixture.expectedVisualDescriptions,
|
|
494
|
-
}, null, 2)
|
|
537
|
+
}, null, 2)
|
|
538
|
+
}],
|
|
495
539
|
};
|
|
496
540
|
}
|
|
497
541
|
});
|
|
@@ -501,12 +545,12 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
501
545
|
this._mcp.registerTool('check_visuals', {
|
|
502
546
|
description: 'Batch check visual review status for fixtures that have expectedVisualDescription. ' +
|
|
503
547
|
'Returns lists of approved, needs-review, and no-expectation fixtures.',
|
|
504
|
-
inputSchema: {
|
|
505
|
-
|
|
506
|
-
|
|
548
|
+
inputSchema: z.strictObject({
|
|
549
|
+
fixtureIdRegex: z.string().optional().describe('RegExp to filter fixtures by fixture ID'),
|
|
550
|
+
labelRegex: z.string().optional().describe('RegExp to filter fixtures by label'),
|
|
507
551
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
508
552
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
509
|
-
},
|
|
553
|
+
}),
|
|
510
554
|
annotations: { readOnlyHint: true },
|
|
511
555
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
512
556
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -517,7 +561,7 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
517
561
|
if (listResult.loadError) {
|
|
518
562
|
return { content: [{ type: 'text', text: `Error: Fixture loading failed: ${listResult.loadError}\nThe fixture list may be incomplete.` }], isError: true };
|
|
519
563
|
}
|
|
520
|
-
const filtered = this._filterFixtures(listResult.fixtures, args.
|
|
564
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdRegex, args.labelRegex);
|
|
521
565
|
if ('error' in filtered) {
|
|
522
566
|
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
523
567
|
}
|
|
@@ -575,12 +619,12 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
575
619
|
'Returns the expression result as JSON. The expression can return a Promise (it will be awaited). ' +
|
|
576
620
|
'Use this to inspect DOM state, computed styles, element dimensions, or component output. ' +
|
|
577
621
|
'Do NOT use this to modify the DOM — this tool is for read-only inspection and debugging only.',
|
|
578
|
-
inputSchema: {
|
|
622
|
+
inputSchema: z.strictObject({
|
|
579
623
|
expression: z.string().describe('JavaScript expression to evaluate. Can return a Promise. The result must be JSON-serializable.'),
|
|
580
624
|
fixtureId: z.string().optional().describe('If provided, renders this fixture before evaluating the expression'),
|
|
581
625
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
582
626
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
583
|
-
},
|
|
627
|
+
}),
|
|
584
628
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
585
629
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
586
630
|
this._log('debug', { type: 'tool-call', tool: 'evaluate_js', sessionName, hasFixtureId: !!args.fixtureId });
|
|
@@ -612,9 +656,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
612
656
|
description: 'Force-reload the browser page used for rendering fixtures. ' +
|
|
613
657
|
'Only use this as a last resort if screenshots or evaluate_js return stale/broken results ' +
|
|
614
658
|
'that persist after source changes. Normal HMR updates should handle most cases automatically.',
|
|
615
|
-
inputSchema: {
|
|
659
|
+
inputSchema: z.strictObject({
|
|
616
660
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
617
|
-
},
|
|
661
|
+
}),
|
|
618
662
|
annotations: { destructiveHint: true },
|
|
619
663
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
620
664
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -632,9 +676,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
632
676
|
_registerWatchAdd() {
|
|
633
677
|
this._mcp.registerTool('watch_add', {
|
|
634
678
|
description: 'Add fixtures to the watch list. Watched fixtures are automatically re-screenshotted when source changes.',
|
|
635
|
-
inputSchema: {
|
|
679
|
+
inputSchema: z.strictObject({
|
|
636
680
|
fixtureIds: z.array(z.string()).describe('Fixture IDs to add'),
|
|
637
|
-
},
|
|
681
|
+
}),
|
|
638
682
|
}, async (args) => {
|
|
639
683
|
this._watchList.add(args.fixtureIds);
|
|
640
684
|
return {
|
|
@@ -648,9 +692,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
648
692
|
_registerWatchRemove() {
|
|
649
693
|
this._mcp.registerTool('watch_remove', {
|
|
650
694
|
description: 'Remove fixtures from the watch list',
|
|
651
|
-
inputSchema: {
|
|
695
|
+
inputSchema: z.strictObject({
|
|
652
696
|
fixtureIds: z.array(z.string()).describe('Fixture IDs to remove'),
|
|
653
|
-
},
|
|
697
|
+
}),
|
|
654
698
|
}, async (args) => {
|
|
655
699
|
this._watchList.remove(args.fixtureIds);
|
|
656
700
|
return {
|
|
@@ -664,9 +708,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
664
708
|
_registerWatchSet() {
|
|
665
709
|
this._mcp.registerTool('watch_set', {
|
|
666
710
|
description: 'Replace the watch list entirely',
|
|
667
|
-
inputSchema: {
|
|
711
|
+
inputSchema: z.strictObject({
|
|
668
712
|
fixtureIds: z.array(z.string()).describe('Fixture IDs to watch'),
|
|
669
|
-
},
|
|
713
|
+
}),
|
|
670
714
|
}, async (args) => {
|
|
671
715
|
this._watchList.set(args.fixtureIds);
|
|
672
716
|
return {
|
|
@@ -680,12 +724,12 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
680
724
|
_registerWatchCompare() {
|
|
681
725
|
const tool = this._mcp.registerTool('watch_compare', {
|
|
682
726
|
description: 'Compare all watched fixtures across two sessions. Takes fresh screenshots from both sessions and reports which fixtures differ.',
|
|
683
|
-
inputSchema: {
|
|
727
|
+
inputSchema: z.strictObject({
|
|
684
728
|
baselineSessionName: z.string().optional().describe('Baseline session name (defaults to worktree session)'),
|
|
685
729
|
currentSessionName: z.string().optional().describe('Current session name (defaults to current session)'),
|
|
686
730
|
baselineSourceTreeId: z.string().optional().describe('Baseline source tree ID (defaults to latest known)'),
|
|
687
731
|
currentSourceTreeId: z.string().optional().describe('Current source tree ID (defaults to latest known)'),
|
|
688
|
-
},
|
|
732
|
+
}),
|
|
689
733
|
annotations: { readOnlyHint: true },
|
|
690
734
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
691
735
|
const ids = [...this._watchList.fixtureIds];
|
|
@@ -750,10 +794,10 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
750
794
|
'Pass the sourceTreeId you already observed — resolves immediately if it already differs, ' +
|
|
751
795
|
'otherwise waits for a source-change or ref-change event. ' +
|
|
752
796
|
'If fixtures are on the watch list, automatically re-screenshots them and reports which changed.',
|
|
753
|
-
inputSchema: {
|
|
797
|
+
inputSchema: z.strictObject({
|
|
754
798
|
sourceTreeId: z.string().describe('The sourceTreeId the client currently knows about. The call resolves once the source tree differs from this value.'),
|
|
755
799
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
756
|
-
},
|
|
800
|
+
}),
|
|
757
801
|
annotations: { readOnlyHint: true },
|
|
758
802
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
759
803
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -763,43 +807,49 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
763
807
|
return this._waitForUpdateResult(daemon, sessionName, currentSourceTreeId);
|
|
764
808
|
}
|
|
765
809
|
// Wait for an event that changes the source tree (max 5s)
|
|
766
|
-
|
|
767
|
-
const
|
|
810
|
+
let onEvent = () => { };
|
|
811
|
+
const call = daemon.methods.events({}, { onMessage: event => onEvent(event) });
|
|
812
|
+
let timer;
|
|
768
813
|
try {
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
const timeout = new Promise(resolve => setTimeout(() => resolve('timeout'), remaining));
|
|
776
|
-
const next = iterator.next();
|
|
777
|
-
const result = await Promise.race([next, timeout]);
|
|
778
|
-
if (result === 'timeout') {
|
|
779
|
-
return { content: [{ type: 'text', text: JSON.stringify({ timeout: true, sessionName, sourceTreeId: knownSourceTreeId }, null, 2) }] };
|
|
780
|
-
}
|
|
781
|
-
const { value: event, done } = result;
|
|
782
|
-
if (done || !event) {
|
|
783
|
-
return { content: [{ type: 'text', text: 'Event stream ended.' }] };
|
|
784
|
-
}
|
|
785
|
-
const ev = event;
|
|
786
|
-
if (ev.type === 'source-change' && ev.sourceTreeId) {
|
|
787
|
-
this._updateSessionSourceTreeId(ev.sessionName, ev.sourceTreeId);
|
|
788
|
-
}
|
|
789
|
-
if (ev.type === 'ref-change') {
|
|
790
|
-
const refreshResult = await Promise.race([this._refreshSessions(daemon), timeout]);
|
|
791
|
-
if (refreshResult === 'timeout') {
|
|
792
|
-
return { content: [{ type: 'text', text: JSON.stringify({ timeout: true, sessionName, sourceTreeId: knownSourceTreeId }, null, 2) }] };
|
|
814
|
+
const changed = new Promise((resolve, reject) => {
|
|
815
|
+
let refreshing = false;
|
|
816
|
+
onEvent = event => {
|
|
817
|
+
if (event.type === 'source-change') {
|
|
818
|
+
this._updateSessionSourceTreeId(event.sessionName, event.sourceTreeId);
|
|
793
819
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
820
|
+
if (event.type === 'ref-change' && !refreshing) {
|
|
821
|
+
refreshing = true;
|
|
822
|
+
void this._refreshSessions(daemon).then(() => {
|
|
823
|
+
refreshing = false;
|
|
824
|
+
const newId = this._sourceTreeId(sessionName);
|
|
825
|
+
if (newId && newId !== knownSourceTreeId) {
|
|
826
|
+
resolve(newId);
|
|
827
|
+
}
|
|
828
|
+
}, reject);
|
|
829
|
+
}
|
|
830
|
+
const newId = this._sourceTreeId(sessionName);
|
|
831
|
+
if (newId && newId !== knownSourceTreeId) {
|
|
832
|
+
resolve(newId);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
timer = setTimeout(() => resolve('timeout'), 5000);
|
|
836
|
+
void call.then(() => resolve('ended'), reject);
|
|
837
|
+
});
|
|
838
|
+
const result = await changed;
|
|
839
|
+
if (result === 'timeout') {
|
|
840
|
+
return { content: [{ type: 'text', text: JSON.stringify({ timeout: true, sessionName, sourceTreeId: knownSourceTreeId }, null, 2) }] };
|
|
799
841
|
}
|
|
842
|
+
if (result === 'ended') {
|
|
843
|
+
return { content: [{ type: 'text', text: 'Event stream ended.' }] };
|
|
844
|
+
}
|
|
845
|
+
return this._waitForUpdateResult(daemon, sessionName, result);
|
|
800
846
|
}
|
|
801
847
|
finally {
|
|
802
|
-
|
|
848
|
+
if (timer) {
|
|
849
|
+
clearTimeout(timer);
|
|
850
|
+
}
|
|
851
|
+
void call.cancel('wait completed').finally(() => call.dispose?.('wait completed')).catch(() => { });
|
|
852
|
+
void call.catch(() => { });
|
|
803
853
|
}
|
|
804
854
|
}));
|
|
805
855
|
}
|
|
@@ -855,9 +905,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
855
905
|
this._mcp.registerTool('restart_session', {
|
|
856
906
|
description: 'Restart a session by disposing its browser page and dev server, then recreating them. ' +
|
|
857
907
|
'Use this when a session appears stuck (e.g. after a timeout).',
|
|
858
|
-
inputSchema: {
|
|
908
|
+
inputSchema: z.strictObject({
|
|
859
909
|
sessionName: z.string().optional().describe('Session name to restart (defaults to first session)'),
|
|
860
|
-
},
|
|
910
|
+
}),
|
|
861
911
|
annotations: { destructiveHint: true },
|
|
862
912
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
863
913
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -875,10 +925,10 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
875
925
|
'The ref can be a branch name, tag, commit SHA, or the special value "INDEX" to snapshot staged changes. ' +
|
|
876
926
|
'The daemon allocates a reusable worktree slot from a fixed pool (max configured in component-explorer.json). ' +
|
|
877
927
|
'Returns the updated session list on success.',
|
|
878
|
-
inputSchema: {
|
|
928
|
+
inputSchema: z.strictObject({
|
|
879
929
|
name: z.string().describe('Unique session name (e.g. "baseline", "bisect")'),
|
|
880
930
|
ref: z.string().describe('Git ref: branch, tag, commit SHA, or "INDEX" for staged changes'),
|
|
881
|
-
},
|
|
931
|
+
}),
|
|
882
932
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
883
933
|
this._log('info', { type: 'tool-call', tool: 'open_session', name: args.name, ref: args.ref });
|
|
884
934
|
const result = await daemon.methods.openSession({ name: args.name, ref: args.ref });
|
|
@@ -896,9 +946,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
896
946
|
this._mcp.registerTool('close_session', {
|
|
897
947
|
description: 'Close a dynamic worktree session and release its worktree slot back to the pool. ' +
|
|
898
948
|
'Cannot close static sessions configured in component-explorer.json.',
|
|
899
|
-
inputSchema: {
|
|
949
|
+
inputSchema: z.strictObject({
|
|
900
950
|
name: z.string().describe('Session name to close'),
|
|
901
|
-
},
|
|
951
|
+
}),
|
|
902
952
|
annotations: { destructiveHint: true },
|
|
903
953
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
904
954
|
this._log('info', { type: 'tool-call', tool: 'close_session', name: args.name });
|
|
@@ -919,10 +969,10 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
919
969
|
'The worktree is checked out to the new ref and Vite\'s HMR handles the incremental update (no server restart). ' +
|
|
920
970
|
'Fails if the worktree has uncommitted changes — the error will list the dirty files. ' +
|
|
921
971
|
'The ref can be a branch, tag, commit SHA, or "INDEX" for staged changes.',
|
|
922
|
-
inputSchema: {
|
|
972
|
+
inputSchema: z.strictObject({
|
|
923
973
|
name: z.string().describe('Session name to update'),
|
|
924
974
|
ref: z.string().describe('New git ref: branch, tag, commit SHA, or "INDEX"'),
|
|
925
|
-
},
|
|
975
|
+
}),
|
|
926
976
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
927
977
|
this._log('info', { type: 'tool-call', tool: 'update_session_ref', name: args.name, ref: args.ref });
|
|
928
978
|
const result = await daemon.methods.updateSessionRef({ name: args.name, ref: args.ref });
|
|
@@ -939,11 +989,11 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
939
989
|
this._mcp.registerTool('get_url', {
|
|
940
990
|
description: 'Get URL for viewing fixtures. Returns the full Component Explorer UI by default. ' +
|
|
941
991
|
'Use `embedded: true` for a minimal single-fixture view (requires fixtureId).',
|
|
942
|
-
inputSchema: {
|
|
992
|
+
inputSchema: z.strictObject({
|
|
943
993
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
944
994
|
fixtureId: z.string().optional().describe('Specific fixture ID to view. In explorer mode, pre-selects this fixture. In embedded mode (required), shows only this fixture.'),
|
|
945
995
|
embedded: z.boolean().optional().describe('If true, returns an embedded single-fixture URL (minimal UI, requires fixtureId). Default: false (full explorer UI).'),
|
|
946
|
-
},
|
|
996
|
+
}),
|
|
947
997
|
annotations: { readOnlyHint: true },
|
|
948
998
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
949
999
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
@@ -984,6 +1034,7 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
984
1034
|
const mode = args.embedded ? 'embedded' : undefined;
|
|
985
1035
|
const url = buildExplorerUrl({
|
|
986
1036
|
baseUrl,
|
|
1037
|
+
pathname: EXPLORER_ROUTE,
|
|
987
1038
|
mode,
|
|
988
1039
|
fixtureId: args.fixtureId,
|
|
989
1040
|
});
|
|
@@ -1002,32 +1053,45 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1002
1053
|
}
|
|
1003
1054
|
_registerCheckFixtureErrors() {
|
|
1004
1055
|
this._mcp.registerTool('check_fixture_errors', {
|
|
1005
|
-
description: 'Render fixtures and check for errors.
|
|
1056
|
+
description: 'Render fixtures and check for errors. Each fixture is rendered and then disposed; ' +
|
|
1057
|
+
'render exceptions appear as `error`, dispose exceptions as `disposeError`, and console / window ' +
|
|
1058
|
+
'events from both phases are collected in `events` (each tagged with `phase: "render" | "dispose"`). ' +
|
|
1059
|
+
'A fixture is considered errored if either the render or the dispose phase reports `hasError` ' +
|
|
1060
|
+
'(i.e. an exception was thrown or an event of type `console.error` / `window.error` / `window.unhandledrejection` was captured). ' +
|
|
1061
|
+
'Only errored fixtures are listed; successful fixtures are summarised via the `summary` counts. ' +
|
|
1006
1062
|
'Returns results directly if finished within ~10s, otherwise returns a taskId for polling via check_task.',
|
|
1007
|
-
inputSchema: {
|
|
1008
|
-
|
|
1009
|
-
|
|
1063
|
+
inputSchema: z.strictObject({
|
|
1064
|
+
fixtureIdRegex: z.string().optional().describe('RegExp to filter fixtures by fixture ID'),
|
|
1065
|
+
labelRegex: z.string().optional().describe('RegExp to filter fixtures by label (matched against inherited labels)'),
|
|
1010
1066
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
1011
1067
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
1012
|
-
|
|
1068
|
+
reloadBetweenFixtures: z.boolean().optional().describe('If true, reload the browser page between fixtures. Provides cleaner isolation but is slower. ' +
|
|
1069
|
+
'Default: false (the page is only reloaded after a fixture errors).'),
|
|
1070
|
+
input: z.unknown().optional().describe('Arbitrary JSON object passed to every fixture as `RenderContext.input`. ' +
|
|
1071
|
+
'See `screenshot` for details.'),
|
|
1072
|
+
}),
|
|
1013
1073
|
annotations: { readOnlyHint: true },
|
|
1014
1074
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
1015
1075
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
1016
|
-
this._log('debug', { type: 'tool-call', tool: 'check_fixture_errors', sessionName,
|
|
1076
|
+
this._log('debug', { type: 'tool-call', tool: 'check_fixture_errors', sessionName, fixtureIdRegex: args.fixtureIdRegex, labelRegex: args.labelRegex });
|
|
1017
1077
|
return this._withSourceTreeRetry(daemon, async () => {
|
|
1018
1078
|
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
1019
1079
|
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
1020
1080
|
if (listResult.loadError) {
|
|
1021
1081
|
return { content: [{ type: 'text', text: `Error: Fixture loading failed: ${listResult.loadError}\nThe fixture list may be incomplete.` }], isError: true };
|
|
1022
1082
|
}
|
|
1023
|
-
const filtered = this._filterFixtures(listResult.fixtures, args.
|
|
1083
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdRegex, args.labelRegex);
|
|
1024
1084
|
if ('error' in filtered) {
|
|
1025
1085
|
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
1026
1086
|
}
|
|
1027
1087
|
const fixtures = filtered.fixtures;
|
|
1028
1088
|
const task = this._taskManager.startTask(async (report, signal) => {
|
|
1029
|
-
|
|
1030
|
-
|
|
1089
|
+
// Only errored fixtures are stored/reported; successful fixtures would just bloat the response.
|
|
1090
|
+
// The summary below still surfaces the total/ok/errored counts.
|
|
1091
|
+
const erroredResults = [];
|
|
1092
|
+
let okCount = 0;
|
|
1093
|
+
let processed = 0;
|
|
1094
|
+
report({ completed: 0, total: fixtures.length, partialResult: erroredResults });
|
|
1031
1095
|
for (let i = 0; i < fixtures.length; i++) {
|
|
1032
1096
|
if (signal.aborted) {
|
|
1033
1097
|
break;
|
|
@@ -1039,30 +1103,52 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1039
1103
|
sessionName,
|
|
1040
1104
|
sourceTreeId,
|
|
1041
1105
|
includeImage: false,
|
|
1106
|
+
// Skip the reload before the very first fixture — the page is already fresh.
|
|
1107
|
+
reloadBeforeRender: args.reloadBetweenFixtures && i > 0,
|
|
1108
|
+
input: args.input,
|
|
1109
|
+
// Always dispose after rendering so teardown errors / events are observable for every fixture
|
|
1110
|
+
// (including the last one). Because we explicitly dispose here, the implicit
|
|
1111
|
+
// `previousDispose` at the start of the next render is a no-op and need not be inspected.
|
|
1112
|
+
disposeAfter: true,
|
|
1042
1113
|
});
|
|
1043
1114
|
const r = result;
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1115
|
+
const renderEvents = (r.events ?? []).map(e => ({ phase: 'render', ...e }));
|
|
1116
|
+
const disposeEvents = (r.currentDispose?.events ?? []).map(e => ({ phase: 'dispose', ...e }));
|
|
1117
|
+
const events = [...renderEvents, ...disposeEvents];
|
|
1118
|
+
const disposeError = r.currentDispose?.errors[0];
|
|
1119
|
+
// Trust the upstream `hasError` flags so this stays in sync with the render-report rules
|
|
1120
|
+
// (exception OR an event of type console.error / window.error / window.unhandledrejection).
|
|
1121
|
+
const hasError = r.hasError || !!r.currentDispose?.hasError;
|
|
1122
|
+
if (hasError) {
|
|
1123
|
+
const entry = { fixtureId: fixture.fixtureId, hasError: true };
|
|
1124
|
+
if (r.error) {
|
|
1125
|
+
entry.error = r.error;
|
|
1126
|
+
}
|
|
1127
|
+
if (disposeError) {
|
|
1128
|
+
entry.disposeError = disposeError;
|
|
1129
|
+
}
|
|
1130
|
+
if (events.length > 0) {
|
|
1131
|
+
entry.events = events;
|
|
1132
|
+
}
|
|
1133
|
+
erroredResults.push(entry);
|
|
1047
1134
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1135
|
+
else {
|
|
1136
|
+
okCount++;
|
|
1050
1137
|
}
|
|
1051
|
-
results.push(entry);
|
|
1052
1138
|
}
|
|
1053
1139
|
catch (e) {
|
|
1054
|
-
|
|
1140
|
+
erroredResults.push({
|
|
1055
1141
|
fixtureId: fixture.fixtureId,
|
|
1056
1142
|
hasError: true,
|
|
1057
1143
|
error: { message: e instanceof Error ? e.message : String(e) },
|
|
1058
1144
|
});
|
|
1059
1145
|
}
|
|
1060
|
-
|
|
1146
|
+
processed++;
|
|
1147
|
+
report({ completed: i + 1, total: fixtures.length, partialResult: erroredResults });
|
|
1061
1148
|
}
|
|
1062
|
-
const withErrors = results.filter(r => r.hasError);
|
|
1063
1149
|
return {
|
|
1064
|
-
fixtures:
|
|
1065
|
-
summary: { total:
|
|
1150
|
+
fixtures: erroredResults,
|
|
1151
|
+
summary: { total: processed, ok: okCount, errored: erroredResults.length },
|
|
1066
1152
|
};
|
|
1067
1153
|
});
|
|
1068
1154
|
const waited = await this._taskManager.waitForTask(task.id, 10_000);
|
|
@@ -1096,23 +1182,27 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1096
1182
|
description: 'Check rendering stability of fixtures. Each fixture is unmounted, re-mounted, and screenshotted 3 times (~3s per fixture). ' +
|
|
1097
1183
|
'Returns results directly if finished within ~10s, otherwise returns a taskId for polling via check_task. ' +
|
|
1098
1184
|
'When returning a taskId, includes partial results collected so far.',
|
|
1099
|
-
inputSchema: {
|
|
1100
|
-
|
|
1101
|
-
|
|
1185
|
+
inputSchema: z.strictObject({
|
|
1186
|
+
fixtureIdRegex: z.string().optional().describe('RegExp to filter fixtures by fixture ID'),
|
|
1187
|
+
labelRegex: z.string().optional().describe('RegExp to filter fixtures by label (matched against inherited labels)'),
|
|
1102
1188
|
sessionName: z.string().optional().describe('Session name (defaults to first session)'),
|
|
1103
1189
|
sourceTreeId: z.string().optional().describe('Source tree ID (defaults to latest known)'),
|
|
1104
|
-
|
|
1190
|
+
reloadBetweenFixtures: z.boolean().optional().describe('If true, reload the browser page between fixtures. Provides cleaner isolation but is slower. ' +
|
|
1191
|
+
'Default: false (the page is only reloaded after a fixture errors).'),
|
|
1192
|
+
input: z.unknown().optional().describe('Arbitrary JSON object passed to every fixture as `RenderContext.input`. ' +
|
|
1193
|
+
'See `screenshot` for details.'),
|
|
1194
|
+
}),
|
|
1105
1195
|
annotations: { readOnlyHint: true },
|
|
1106
1196
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
1107
1197
|
const sessionName = args.sessionName ?? this._defaultSessionName();
|
|
1108
|
-
this._log('debug', { type: 'tool-call', tool: 'check_stability', sessionName,
|
|
1198
|
+
this._log('debug', { type: 'tool-call', tool: 'check_stability', sessionName, fixtureIdRegex: args.fixtureIdRegex, labelRegex: args.labelRegex });
|
|
1109
1199
|
return this._withSourceTreeRetry(daemon, async () => {
|
|
1110
1200
|
const sourceTreeId = args.sourceTreeId ?? this._sourceTreeId(sessionName);
|
|
1111
1201
|
const listResult = await daemon.methods.fixtures.list({ sessionName, sourceTreeId });
|
|
1112
1202
|
if (listResult.loadError) {
|
|
1113
1203
|
return { content: [{ type: 'text', text: `Error: Fixture loading failed: ${listResult.loadError}\nThe fixture list may be incomplete.` }], isError: true };
|
|
1114
1204
|
}
|
|
1115
|
-
const filtered = this._filterFixtures(listResult.fixtures, args.
|
|
1205
|
+
const filtered = this._filterFixtures(listResult.fixtures, args.fixtureIdRegex, args.labelRegex);
|
|
1116
1206
|
if ('error' in filtered) {
|
|
1117
1207
|
return { content: [{ type: 'text', text: filtered.error }], isError: true };
|
|
1118
1208
|
}
|
|
@@ -1133,6 +1223,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1133
1223
|
sourceTreeId,
|
|
1134
1224
|
includeImage: false,
|
|
1135
1225
|
stabilityCheck: true,
|
|
1226
|
+
// Skip the reload before the very first fixture — the page is already fresh.
|
|
1227
|
+
reloadBeforeRender: args.reloadBetweenFixtures && i > 0,
|
|
1228
|
+
input: args.input,
|
|
1136
1229
|
});
|
|
1137
1230
|
const r = result;
|
|
1138
1231
|
results.push({
|
|
@@ -1177,9 +1270,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1177
1270
|
_registerCheckTask() {
|
|
1178
1271
|
this._mcp.registerTool('check_task', {
|
|
1179
1272
|
description: 'Check on a running task. Waits up to ~2s for completion; if still running, returns progress and new results since last check.',
|
|
1180
|
-
inputSchema: {
|
|
1273
|
+
inputSchema: z.strictObject({
|
|
1181
1274
|
taskId: z.string().describe('The task ID returned by a previous tool call'),
|
|
1182
|
-
},
|
|
1275
|
+
}),
|
|
1183
1276
|
annotations: { readOnlyHint: true },
|
|
1184
1277
|
}, async (args) => {
|
|
1185
1278
|
const waited = await this._taskManager.waitForTask(args.taskId, 2_000);
|
|
@@ -1227,9 +1320,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1227
1320
|
_registerCancelTask() {
|
|
1228
1321
|
this._mcp.registerTool('cancel_task', {
|
|
1229
1322
|
description: 'Cancel a running task',
|
|
1230
|
-
inputSchema: {
|
|
1323
|
+
inputSchema: z.strictObject({
|
|
1231
1324
|
taskId: z.string().describe('The task ID to cancel'),
|
|
1232
|
-
},
|
|
1325
|
+
}),
|
|
1233
1326
|
}, async (args) => {
|
|
1234
1327
|
const task = this._taskManager.getTask(args.taskId);
|
|
1235
1328
|
if (!task) {
|
|
@@ -1249,9 +1342,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1249
1342
|
description: 'Retrieve a recently-taken screenshot image by its hash. ' +
|
|
1250
1343
|
'Keeps the last ~10 images in an LRU cache. ' +
|
|
1251
1344
|
'Useful for debugging when screenshot hashes behave unexpectedly.',
|
|
1252
|
-
inputSchema: {
|
|
1345
|
+
inputSchema: z.strictObject({
|
|
1253
1346
|
hash: z.string().describe('The screenshot hash to look up'),
|
|
1254
|
-
},
|
|
1347
|
+
}),
|
|
1255
1348
|
annotations: { readOnlyHint: true },
|
|
1256
1349
|
}, async (args) => {
|
|
1257
1350
|
const image = this._imageLru.get(args.hash);
|
|
@@ -1275,9 +1368,9 @@ class ComponentExplorerMcpServer extends Disposable {
|
|
|
1275
1368
|
'Only use this tool when the user explicitly asks to show or hide the browser. ' +
|
|
1276
1369
|
'Do not call this tool automatically or as part of other workflows. ' +
|
|
1277
1370
|
'Note: changing visibility closes the current browser instance, so the next screenshot or evaluate_js call will relaunch it.',
|
|
1278
|
-
inputSchema: {
|
|
1371
|
+
inputSchema: z.strictObject({
|
|
1279
1372
|
visible: z.boolean().describe('true to show the browser window (headed mode), false to hide it (headless mode)'),
|
|
1280
|
-
},
|
|
1373
|
+
}),
|
|
1281
1374
|
annotations: { destructiveHint: true },
|
|
1282
1375
|
}, async (args) => this._withDaemon(async (daemon) => {
|
|
1283
1376
|
await daemon.methods.setBrowserVisibility({ visible: args.visible });
|