@gtkx/cli 1.0.0-rc.1 → 1.0.0-rc.2
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/builder.d.ts +3 -2
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -1
- package/dist/builder.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +21 -6
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts +5 -5
- package/dist/codegen/freshness.d.ts.map +1 -1
- package/dist/codegen/freshness.js +26 -23
- package/dist/codegen/freshness.js.map +1 -1
- package/dist/codegen/report.d.ts +2 -1
- package/dist/codegen/report.d.ts.map +1 -1
- package/dist/codegen/report.js +4 -2
- package/dist/codegen/report.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +8 -8
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +72 -43
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/codegen/store-resolver.d.ts +8 -5
- package/dist/codegen/store-resolver.d.ts.map +1 -1
- package/dist/codegen/store-resolver.js +26 -6
- package/dist/codegen/store-resolver.js.map +1 -1
- package/dist/commands/build.d.ts +4 -3
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +2 -1
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/codegen.d.ts +2 -1
- package/dist/commands/codegen.d.ts.map +1 -1
- package/dist/commands/codegen.js +6 -4
- package/dist/commands/codegen.js.map +1 -1
- package/dist/commands/dev.d.ts +2 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +2 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs.d.ts +2 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +22 -7
- package/dist/commands/docs.js.map +1 -1
- package/dist/dev/icon-dir.d.ts +2 -1
- package/dist/dev/icon-dir.d.ts.map +1 -1
- package/dist/dev/icon-dir.js +6 -3
- package/dist/dev/icon-dir.js.map +1 -1
- package/dist/dev/refresh-tracker.d.ts +5 -4
- package/dist/dev/refresh-tracker.d.ts.map +1 -1
- package/dist/dev/refresh-tracker.js +6 -5
- package/dist/dev/refresh-tracker.js.map +1 -1
- package/dist/dev/runner-deps.d.ts +2 -1
- package/dist/dev/runner-deps.d.ts.map +1 -1
- package/dist/dev/runner-deps.js +20 -4
- package/dist/dev/runner-deps.js.map +1 -1
- package/dist/dev/runner-main.d.ts +2 -1
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +3 -2
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/runner.d.ts +6 -5
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +69 -38
- package/dist/dev/runner.js.map +1 -1
- package/dist/dev/schema-dir.d.ts +2 -1
- package/dist/dev/schema-dir.d.ts.map +1 -1
- package/dist/dev/schema-dir.js +4 -2
- package/dist/dev/schema-dir.js.map +1 -1
- package/dist/dev/supervisor.d.ts +7 -6
- package/dist/dev/supervisor.d.ts.map +1 -1
- package/dist/dev/supervisor.js +107 -67
- package/dist/dev/supervisor.js.map +1 -1
- package/dist/dev/vite-dev-server.d.ts +3 -3
- package/dist/dev/vite-dev-server.d.ts.map +1 -1
- package/dist/dev/vite-dev-server.js +2 -1
- package/dist/dev/vite-dev-server.js.map +1 -1
- package/dist/internal/banner.d.ts +2 -1
- package/dist/internal/banner.d.ts.map +1 -1
- package/dist/internal/banner.js +2 -1
- package/dist/internal/banner.js.map +1 -1
- package/dist/internal/data-dir.d.ts +3 -2
- package/dist/internal/data-dir.d.ts.map +1 -1
- package/dist/internal/data-dir.js +29 -14
- package/dist/internal/data-dir.js.map +1 -1
- package/dist/internal/entry-arg.d.ts +5 -4
- package/dist/internal/entry-arg.d.ts.map +1 -1
- package/dist/internal/entry-arg.js +10 -7
- package/dist/internal/entry-arg.js.map +1 -1
- package/dist/internal/errors.d.ts +3 -2
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +7 -5
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/list-files.d.ts +3 -2
- package/dist/internal/list-files.d.ts.map +1 -1
- package/dist/internal/list-files.js +5 -3
- package/dist/internal/list-files.js.map +1 -1
- package/dist/internal/resolve-cli-tool.d.ts +2 -1
- package/dist/internal/resolve-cli-tool.d.ts.map +1 -1
- package/dist/internal/resolve-cli-tool.js +17 -10
- package/dist/internal/resolve-cli-tool.js.map +1 -1
- package/dist/internal/staging-dir.d.ts +3 -2
- package/dist/internal/staging-dir.d.ts.map +1 -1
- package/dist/internal/staging-dir.js +3 -2
- package/dist/internal/staging-dir.js.map +1 -1
- package/dist/internal/xdg-data-dirs.d.ts +2 -1
- package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
- package/dist/internal/xdg-data-dirs.js +4 -2
- package/dist/internal/xdg-data-dirs.js.map +1 -1
- package/dist/mcp/client.d.ts +9 -4
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +106 -81
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/handlers.d.ts +3 -2
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js +89 -61
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +30 -13
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/serialize-widget.d.ts +3 -3
- package/dist/mcp/serialize-widget.d.ts.map +1 -1
- package/dist/mcp/serialize-widget.js +9 -6
- package/dist/mcp/serialize-widget.js.map +1 -1
- package/dist/mcp/testing-loader.d.ts +3 -4
- package/dist/mcp/testing-loader.d.ts.map +1 -1
- package/dist/mcp/testing-loader.js +43 -21
- package/dist/mcp/testing-loader.js.map +1 -1
- package/dist/mcp/widget-registry.d.ts +3 -2
- package/dist/mcp/widget-registry.d.ts.map +1 -1
- package/dist/mcp/widget-registry.js +4 -3
- package/dist/mcp/widget-registry.js.map +1 -1
- package/dist/refresh-globals.d.ts +2 -0
- package/dist/refresh-globals.d.ts.map +1 -0
- package/dist/refresh-globals.js +8 -0
- package/dist/refresh-globals.js.map +1 -0
- package/dist/refresh-runtime.d.ts +5 -11
- package/dist/refresh-runtime.d.ts.map +1 -1
- package/dist/refresh-runtime.js +16 -14
- package/dist/refresh-runtime.js.map +1 -1
- package/dist/settings/compile.d.ts +2 -1
- package/dist/settings/compile.d.ts.map +1 -1
- package/dist/settings/compile.js +6 -4
- package/dist/settings/compile.js.map +1 -1
- package/dist/settings/parser.d.ts +6 -5
- package/dist/settings/parser.d.ts.map +1 -1
- package/dist/settings/parser.js +26 -22
- package/dist/settings/parser.js.map +1 -1
- package/dist/settings/render.d.ts +3 -2
- package/dist/settings/render.d.ts.map +1 -1
- package/dist/settings/render.js +38 -64
- package/dist/settings/render.js.map +1 -1
- package/dist/settings/schema.d.ts +8 -8
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +61 -43
- package/dist/settings/schema.js.map +1 -1
- package/dist/vite-plugins/asset-extensions.d.ts +4 -3
- package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
- package/dist/vite-plugins/asset-extensions.js +4 -3
- package/dist/vite-plugins/asset-extensions.js.map +1 -1
- package/dist/vite-plugins/built-url.d.ts +2 -1
- package/dist/vite-plugins/built-url.d.ts.map +1 -1
- package/dist/vite-plugins/built-url.js +19 -13
- package/dist/vite-plugins/built-url.js.map +1 -1
- package/dist/vite-plugins/css.d.ts +2 -1
- package/dist/vite-plugins/css.d.ts.map +1 -1
- package/dist/vite-plugins/css.js +23 -15
- package/dist/vite-plugins/css.js.map +1 -1
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +5 -4
- package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -1
- package/dist/vite-plugins/fast-refresh/refresh-filter.js +11 -7
- package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -1
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +4 -3
- package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -1
- package/dist/vite-plugins/fast-refresh/swc-refresh.js +45 -41
- package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -1
- package/dist/vite-plugins/icons.d.ts +2 -1
- package/dist/vite-plugins/icons.d.ts.map +1 -1
- package/dist/vite-plugins/icons.js +24 -20
- package/dist/vite-plugins/icons.js.map +1 -1
- package/dist/vite-plugins/index.d.ts +2 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +3 -2
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/native.d.ts +2 -1
- package/dist/vite-plugins/native.d.ts.map +1 -1
- package/dist/vite-plugins/native.js +6 -4
- package/dist/vite-plugins/native.js.map +1 -1
- package/dist/vite-plugins/react-compiler.d.ts +3 -2
- package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
- package/dist/vite-plugins/react-compiler.js +26 -20
- package/dist/vite-plugins/react-compiler.js.map +1 -1
- package/dist/vite-plugins/react-dom-prebundle.d.ts +2 -1
- package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -1
- package/dist/vite-plugins/react-dom-prebundle.js +2 -1
- package/dist/vite-plugins/react-dom-prebundle.js.map +1 -1
- package/dist/vite-plugins/resource-init-module.d.ts +2 -1
- package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
- package/dist/vite-plugins/resource-init-module.js +39 -38
- package/dist/vite-plugins/resource-init-module.js.map +1 -1
- package/dist/vite-plugins/resource-shared.d.ts +7 -5
- package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
- package/dist/vite-plugins/resource-shared.js +17 -10
- package/dist/vite-plugins/resource-shared.js.map +1 -1
- package/dist/vite-plugins/resources.d.ts +3 -2
- package/dist/vite-plugins/resources.d.ts.map +1 -1
- package/dist/vite-plugins/resources.js +94 -54
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.d.ts +2 -1
- package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +4 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/settings.d.ts +2 -1
- package/dist/vite-plugins/settings.d.ts.map +1 -1
- package/dist/vite-plugins/settings.js +56 -32
- package/dist/vite-plugins/settings.js.map +1 -1
- package/dist/vite-plugins/strip-query.d.ts +3 -0
- package/dist/vite-plugins/strip-query.d.ts.map +1 -0
- package/dist/vite-plugins/strip-query.js +6 -0
- package/dist/vite-plugins/strip-query.js.map +1 -0
- package/dist/vite-plugins/undeclared-library.d.ts +2 -1
- package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
- package/dist/vite-plugins/undeclared-library.js +8 -5
- package/dist/vite-plugins/undeclared-library.js.map +1 -1
- package/dist/vite-plugins/virtual-module.d.ts +2 -2
- package/dist/vite-plugins/virtual-module.d.ts.map +1 -1
- package/dist/vite-plugins/virtual-module.js +5 -3
- package/dist/vite-plugins/virtual-module.js.map +1 -1
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js.map +1 -1
- package/env.d.ts +21 -61
- package/package.json +12 -14
- package/src/babel-modules.d.ts +0 -2
- package/src/builder.ts +7 -6
- package/src/cli.ts +26 -6
- package/src/codegen/freshness.ts +38 -25
- package/src/codegen/report.ts +10 -2
- package/src/codegen/run-codegen.ts +135 -60
- package/src/codegen/store-resolver.ts +56 -15
- package/src/commands/build.ts +3 -2
- package/src/commands/codegen.ts +9 -4
- package/src/commands/dev.ts +3 -4
- package/src/commands/docs.ts +36 -7
- package/src/dev/icon-dir.ts +13 -3
- package/src/dev/refresh-tracker.ts +12 -8
- package/src/dev/runner-deps.ts +30 -7
- package/src/dev/runner-main.ts +4 -3
- package/src/dev/runner.ts +100 -41
- package/src/dev/schema-dir.ts +7 -2
- package/src/dev/supervisor.ts +169 -71
- package/src/dev/vite-dev-server.ts +4 -2
- package/src/internal/banner.ts +5 -1
- package/src/internal/data-dir.ts +47 -16
- package/src/internal/entry-arg.ts +16 -8
- package/src/internal/errors.ts +15 -7
- package/src/internal/list-files.ts +10 -4
- package/src/internal/resolve-cli-tool.ts +28 -12
- package/src/internal/staging-dir.ts +5 -2
- package/src/internal/xdg-data-dirs.ts +8 -2
- package/src/mcp/client.ts +143 -85
- package/src/mcp/handlers.ts +126 -69
- package/src/mcp/index.ts +38 -14
- package/src/mcp/serialize-widget.ts +15 -8
- package/src/mcp/testing-loader.ts +64 -23
- package/src/mcp/widget-registry.ts +11 -5
- package/src/refresh-globals.ts +10 -0
- package/src/refresh-runtime.ts +20 -16
- package/src/settings/compile.ts +7 -4
- package/src/settings/parser.ts +69 -30
- package/src/settings/render.ts +66 -66
- package/src/settings/schema.ts +100 -46
- package/src/vite-plugins/asset-extensions.ts +4 -3
- package/src/vite-plugins/built-url.ts +30 -15
- package/src/vite-plugins/css.ts +35 -20
- package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -10
- package/src/vite-plugins/fast-refresh/swc-refresh.ts +60 -47
- package/src/vite-plugins/icons.ts +40 -24
- package/src/vite-plugins/index.ts +6 -3
- package/src/vite-plugins/native.ts +14 -5
- package/src/vite-plugins/react-compiler.ts +44 -30
- package/src/vite-plugins/react-dom-prebundle.ts +4 -1
- package/src/vite-plugins/resource-init-module.ts +41 -38
- package/src/vite-plugins/resource-shared.ts +27 -14
- package/src/vite-plugins/resources.ts +153 -59
- package/src/vite-plugins/settings-worker-env.ts +7 -2
- package/src/vite-plugins/settings.ts +107 -42
- package/src/vite-plugins/strip-query.ts +7 -0
- package/src/vite-plugins/undeclared-library.ts +21 -12
- package/src/vite-plugins/virtual-module.ts +9 -2
- package/src/vitest-plugin.ts +1 -2
package/dist/mcp/client.js
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
|
-
import * as net from "node:net";
|
|
2
1
|
import * as Gio from "@gtkx/gi/gio";
|
|
3
2
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
4
3
|
import { DEFAULT_SOCKET_PATH, ErrorCode, ProtocolConnection, ProtocolError } from "@gtkx/mcp/internal";
|
|
5
4
|
import { error, errorMessage, info, normalizeError, warn } from "@gtkx/utils";
|
|
5
|
+
import * as net from "node:net";
|
|
6
6
|
import { dispatch } from "./handlers.js";
|
|
7
7
|
import { WidgetRegistry } from "./widget-registry.js";
|
|
8
8
|
const RECONNECT_DELAY_MS = 2000;
|
|
9
|
-
const REGISTER_TIMEOUT_MS =
|
|
10
|
-
|
|
9
|
+
const REGISTER_TIMEOUT_MS = 30_000;
|
|
10
|
+
const DISCONNECT_ERROR_CODES = new Set(["ENOENT", "ECONNREFUSED", "EPIPE", "ECONNRESET"]);
|
|
11
|
+
const toResponseError = (error) => error instanceof ProtocolError
|
|
12
|
+
? error.toErrorObject()
|
|
13
|
+
: { code: ErrorCode.INTERNAL_ERROR, message: errorMessage(error) };
|
|
14
|
+
const connectSettler = (callbacks) => {
|
|
15
|
+
let isSettled = false;
|
|
16
|
+
const settle = (notify) => {
|
|
17
|
+
if (isSettled) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
isSettled = true;
|
|
21
|
+
notify();
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
succeed: () => {
|
|
25
|
+
settle(() => callbacks.onSuccess?.());
|
|
26
|
+
},
|
|
27
|
+
fail: (failure) => {
|
|
28
|
+
settle(() => callbacks.onError?.(failure));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
class McpClient {
|
|
11
33
|
socket = null;
|
|
12
34
|
connection = null;
|
|
13
35
|
socketPath;
|
|
@@ -21,84 +43,53 @@ export class McpClient {
|
|
|
21
43
|
this.socketPath = options.socketPath ?? DEFAULT_SOCKET_PATH;
|
|
22
44
|
this.applicationId = options.applicationId;
|
|
23
45
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
reject(error);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
46
|
+
handleClose() {
|
|
47
|
+
if (this.hasConnected) {
|
|
48
|
+
info("Disconnected from MCP server");
|
|
49
|
+
this.hasConnected = false;
|
|
50
|
+
}
|
|
51
|
+
this.socket = null;
|
|
52
|
+
this.connection = null;
|
|
53
|
+
this.scheduleReconnect();
|
|
35
54
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
this.
|
|
40
|
-
this.pendingConnectReject = null;
|
|
55
|
+
handleSocketError(socketError) {
|
|
56
|
+
const code = socketError.code;
|
|
57
|
+
if (code !== undefined && DISCONNECT_ERROR_CODES.has(code)) {
|
|
58
|
+
this.scheduleReconnect();
|
|
41
59
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.reconnectTimer = null;
|
|
60
|
+
else {
|
|
61
|
+
error("Socket error:", socketError.message);
|
|
45
62
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.
|
|
50
|
-
|
|
63
|
+
}
|
|
64
|
+
async registerWithServer(settle) {
|
|
65
|
+
try {
|
|
66
|
+
await this.register();
|
|
67
|
+
info("Registered with MCP server");
|
|
68
|
+
settle.succeed();
|
|
69
|
+
}
|
|
70
|
+
catch (registerError) {
|
|
71
|
+
error("Failed to register with MCP server:", errorMessage(registerError));
|
|
72
|
+
settle.fail(normalizeError(registerError));
|
|
51
73
|
}
|
|
52
|
-
this.connection = null;
|
|
53
|
-
this.hasConnected = false;
|
|
54
74
|
}
|
|
55
|
-
attemptConnect(
|
|
56
|
-
|
|
57
|
-
const settle = (callback, ...args) => {
|
|
58
|
-
if (settled)
|
|
59
|
-
return;
|
|
60
|
-
settled = true;
|
|
61
|
-
callback?.(...args);
|
|
62
|
-
};
|
|
75
|
+
attemptConnect(callbacks = {}) {
|
|
76
|
+
const settle = connectSettler(callbacks);
|
|
63
77
|
const socket = net.createConnection(this.socketPath, () => {
|
|
64
78
|
info(`Connected to MCP server at ${this.socketPath}`);
|
|
65
79
|
this.hasConnected = true;
|
|
66
|
-
this.
|
|
67
|
-
.then(() => {
|
|
68
|
-
info("Registered with MCP server");
|
|
69
|
-
settle(onSuccess);
|
|
70
|
-
})
|
|
71
|
-
.catch((cause) => {
|
|
72
|
-
error("Failed to register with MCP server:", cause.message);
|
|
73
|
-
settle(onError, normalizeError(cause));
|
|
74
|
-
});
|
|
80
|
+
void this.registerWithServer(settle);
|
|
75
81
|
});
|
|
76
82
|
const connection = ProtocolConnection.fromSocket(socket, {
|
|
77
83
|
onClose: () => {
|
|
78
|
-
|
|
79
|
-
info("Disconnected from MCP server");
|
|
80
|
-
this.hasConnected = false;
|
|
81
|
-
}
|
|
82
|
-
this.socket = null;
|
|
83
|
-
this.connection = null;
|
|
84
|
-
this.scheduleReconnect();
|
|
84
|
+
this.handleClose();
|
|
85
85
|
},
|
|
86
86
|
onError: (socketError) => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (isDisconnectError) {
|
|
90
|
-
this.scheduleReconnect();
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
error("Socket error:", socketError.message);
|
|
94
|
-
}
|
|
95
|
-
settle(onError, socketError);
|
|
87
|
+
this.handleSocketError(socketError);
|
|
88
|
+
settle.fail(socketError);
|
|
96
89
|
},
|
|
97
90
|
});
|
|
98
91
|
connection.on("request", (request) => {
|
|
99
|
-
this.handleRequest(request)
|
|
100
|
-
error("Error handling request:", cause);
|
|
101
|
-
});
|
|
92
|
+
void this.handleRequest(request);
|
|
102
93
|
});
|
|
103
94
|
connection.on("invalid", ({ error: parseError }) => {
|
|
104
95
|
warn(`Received invalid JSON from MCP server: ${parseError.message}`);
|
|
@@ -107,8 +98,9 @@ export class McpClient {
|
|
|
107
98
|
this.connection = connection;
|
|
108
99
|
}
|
|
109
100
|
scheduleReconnect() {
|
|
110
|
-
if (this.reconnectTimer || this.isStopping)
|
|
101
|
+
if (this.reconnectTimer || this.isStopping) {
|
|
111
102
|
return;
|
|
103
|
+
}
|
|
112
104
|
this.reconnectTimer = setTimeout(() => {
|
|
113
105
|
this.reconnectTimer = null;
|
|
114
106
|
this.attemptConnect();
|
|
@@ -124,11 +116,12 @@ export class McpClient {
|
|
|
124
116
|
projectRoot: process.cwd(),
|
|
125
117
|
}, REGISTER_TIMEOUT_MS);
|
|
126
118
|
}
|
|
127
|
-
async
|
|
119
|
+
async respondToRequest(request) {
|
|
128
120
|
const { id, method, params } = request;
|
|
129
121
|
const connection = this.connection;
|
|
130
|
-
if (!connection)
|
|
122
|
+
if (!connection) {
|
|
131
123
|
return;
|
|
124
|
+
}
|
|
132
125
|
try {
|
|
133
126
|
const defaultApp = Gio.Application.getDefault();
|
|
134
127
|
if (!(defaultApp instanceof Gtk.Application)) {
|
|
@@ -138,20 +131,52 @@ export class McpClient {
|
|
|
138
131
|
const result = await dispatch(method, params, { app: defaultApp, registry: this.registry });
|
|
139
132
|
connection.write({ id, result });
|
|
140
133
|
}
|
|
141
|
-
catch (
|
|
142
|
-
|
|
143
|
-
connection.write({ id, error: error.toErrorObject() });
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
connection.write({
|
|
147
|
-
id,
|
|
148
|
-
error: {
|
|
149
|
-
code: ErrorCode.INTERNAL_ERROR,
|
|
150
|
-
message: errorMessage(error),
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
}
|
|
134
|
+
catch (dispatchError) {
|
|
135
|
+
connection.write({ id, error: toResponseError(dispatchError) });
|
|
154
136
|
}
|
|
155
137
|
}
|
|
138
|
+
async handleRequest(request) {
|
|
139
|
+
try {
|
|
140
|
+
await this.respondToRequest(request);
|
|
141
|
+
}
|
|
142
|
+
catch (requestError) {
|
|
143
|
+
error("Error handling request:", requestError);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async connect() {
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
this.pendingConnectReject = reject;
|
|
149
|
+
this.attemptConnect({
|
|
150
|
+
onSuccess: () => {
|
|
151
|
+
this.pendingConnectReject = null;
|
|
152
|
+
resolve();
|
|
153
|
+
},
|
|
154
|
+
onError: (connectError) => {
|
|
155
|
+
this.pendingConnectReject = null;
|
|
156
|
+
reject(connectError);
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
disconnect() {
|
|
162
|
+
this.isStopping = true;
|
|
163
|
+
if (this.pendingConnectReject) {
|
|
164
|
+
this.pendingConnectReject(new Error("Client disconnected before connection registered"));
|
|
165
|
+
this.pendingConnectReject = null;
|
|
166
|
+
}
|
|
167
|
+
if (this.reconnectTimer) {
|
|
168
|
+
clearTimeout(this.reconnectTimer);
|
|
169
|
+
this.reconnectTimer = null;
|
|
170
|
+
}
|
|
171
|
+
this.connection?.rejectPending(new Error("Client disconnected"));
|
|
172
|
+
if (this.socket) {
|
|
173
|
+
this.connection?.write({ id: crypto.randomUUID(), method: "app.unregister" });
|
|
174
|
+
this.socket.destroy();
|
|
175
|
+
this.socket = null;
|
|
176
|
+
}
|
|
177
|
+
this.connection = null;
|
|
178
|
+
this.hasConnected = false;
|
|
179
|
+
}
|
|
156
180
|
}
|
|
181
|
+
export { McpClient };
|
|
157
182
|
//# sourceMappingURL=client.js.map
|
package/dist/mcp/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mcp/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,kBAAkB,EAAE,aAAa,EAAgB,MAAM,oBAAoB,CAAC;AACrH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAOtD,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAElC,MAAM,OAAO,SAAS;IACV,MAAM,GAAsB,IAAI,CAAC;IACjC,UAAU,GAA8B,IAAI,CAAC;IAC7C,UAAU,CAAS;IACnB,aAAa,CAAS;IACtB,cAAc,GAA0B,IAAI,CAAC;IAC7C,YAAY,GAAG,KAAK,CAAC;IACrB,UAAU,GAAG,KAAK,CAAC;IACnB,oBAAoB,GAAoC,IAAI,CAAC;IAC7D,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;IAExC,YAAY,OAAyB;QACjC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;QAC5D,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAAO;QACT,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACzC,IAAI,CAAC,oBAAoB,GAAG,MAAM,CAAC;YACnC,IAAI,CAAC,cAAc,CACf,GAAG,EAAE;gBACD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,OAAO,EAAE,CAAC;YACd,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;gBACN,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;gBACjC,MAAM,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC,CACJ,CAAC;QACN,CAAC,CAAC,CAAC;IACP,CAAC;IAED,UAAU;QACN,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC5B,IAAI,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC,CAAC;YACzF,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACrC,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;QACjE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;YAC9E,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC9B,CAAC;IAEO,cAAc,CAAC,SAAsB,EAAE,OAAgC;QAC3E,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,MAAM,GAAG,CAAsB,QAA4C,EAAE,GAAG,IAAO,EAAE,EAAE;YAC7F,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACxB,CAAC,CAAC;QAEF,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE;YACtD,IAAI,CAAC,8BAA8B,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YACtD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,CAAC,QAAQ,EAAE;iBACV,IAAI,CAAC,GAAG,EAAE;gBACP,IAAI,CAAC,4BAA4B,CAAC,CAAC;gBACnC,MAAM,CAAC,SAAS,CAAC,CAAC;YACtB,CAAC,CAAC;iBACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACb,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC5D,MAAM,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3C,CAAC,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,MAAM,EAAE;YACrD,OAAO,EAAE,GAAG,EAAE;gBACV,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;oBACpB,IAAI,CAAC,8BAA8B,CAAC,CAAC;oBACrC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;gBAC9B,CAAC;gBACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;gBACnB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;gBACvB,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC7B,CAAC;YACD,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE;gBACrB,MAAM,IAAI,GAAI,WAAqC,CAAC,IAAI,CAAC;gBACzD,MAAM,iBAAiB,GACnB,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,YAAY,CAAC;gBAC9F,IAAI,iBAAiB,EAAE,CAAC;oBACpB,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACJ,KAAK,CAAC,eAAe,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;gBAChD,CAAC;gBACD,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YACjC,CAAC;SACJ,CAAC,CAAC;QACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE;YACjC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;gBACxC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE;YAC/C,IAAI,CAAC,0CAA0C,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,CAAC;IAEO,iBAAiB;QACrB,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QACnD,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1B,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC3B,CAAC;IAEO,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACnB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,CAAC;QACnE,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CACvB,cAAc,EACd;YACI,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE;SAC7B,EACD,mBAAmB,CACtB,CAAC;IACN,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,OAAgB;QACxC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,UAAU;YAAE,OAAO;QAExB,IAAI,CAAC;YACD,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;YAChD,IAAI,CAAC,CAAC,UAAU,YAAY,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,SAAS,CAAC,6BAA6B,CAAC,CAAC;YACvD,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC5F,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;gBACjC,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;YAC3D,CAAC;iBAAM,CAAC;gBACJ,UAAU,CAAC,KAAK,CAAC;oBACb,EAAE;oBACF,KAAK,EAAE;wBACH,IAAI,EAAE,SAAS,CAAC,cAAc;wBAC9B,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;qBAC/B;iBACJ,CAAC,CAAC;YACP,CAAC;QACL,CAAC;IACL,CAAC;CACJ","sourcesContent":["import * as net from \"node:net\";\nimport * as Gio from \"@gtkx/gi/gio\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { DEFAULT_SOCKET_PATH, ErrorCode, ProtocolConnection, ProtocolError, type Request } from \"@gtkx/mcp/internal\";\nimport { error, errorMessage, info, normalizeError, warn } from \"@gtkx/utils\";\nimport { dispatch } from \"./handlers.js\";\nimport { WidgetRegistry } from \"./widget-registry.js\";\n\nexport type McpClientOptions = {\n socketPath?: string;\n applicationId: string;\n};\n\nconst RECONNECT_DELAY_MS = 2000;\nconst REGISTER_TIMEOUT_MS = 30000;\n\nexport class McpClient {\n private socket: net.Socket | null = null;\n private connection: ProtocolConnection | null = null;\n private socketPath: string;\n private applicationId: string;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private hasConnected = false;\n private isStopping = false;\n private pendingConnectReject: ((error: Error) => void) | null = null;\n private registry = new WidgetRegistry();\n\n constructor(options: McpClientOptions) {\n this.socketPath = options.socketPath ?? DEFAULT_SOCKET_PATH;\n this.applicationId = options.applicationId;\n }\n\n async connect(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.pendingConnectReject = reject;\n this.attemptConnect(\n () => {\n this.pendingConnectReject = null;\n resolve();\n },\n (error) => {\n this.pendingConnectReject = null;\n reject(error);\n },\n );\n });\n }\n\n disconnect(): void {\n this.isStopping = true;\n if (this.pendingConnectReject) {\n this.pendingConnectReject(new Error(\"Client disconnected before connection registered\"));\n this.pendingConnectReject = null;\n }\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.connection?.rejectPending(new Error(\"Client disconnected\"));\n if (this.socket) {\n this.connection?.write({ id: crypto.randomUUID(), method: \"app.unregister\" });\n this.socket.destroy();\n this.socket = null;\n }\n this.connection = null;\n this.hasConnected = false;\n }\n\n private attemptConnect(onSuccess?: () => void, onError?: (error: Error) => void): void {\n let settled = false;\n\n const settle = <T extends unknown[]>(callback: ((...args: T) => void) | undefined, ...args: T) => {\n if (settled) return;\n settled = true;\n callback?.(...args);\n };\n\n const socket = net.createConnection(this.socketPath, () => {\n info(`Connected to MCP server at ${this.socketPath}`);\n this.hasConnected = true;\n this.register()\n .then(() => {\n info(\"Registered with MCP server\");\n settle(onSuccess);\n })\n .catch((cause) => {\n error(\"Failed to register with MCP server:\", cause.message);\n settle(onError, normalizeError(cause));\n });\n });\n\n const connection = ProtocolConnection.fromSocket(socket, {\n onClose: () => {\n if (this.hasConnected) {\n info(\"Disconnected from MCP server\");\n this.hasConnected = false;\n }\n this.socket = null;\n this.connection = null;\n this.scheduleReconnect();\n },\n onError: (socketError) => {\n const code = (socketError as NodeJS.ErrnoException).code;\n const isDisconnectError =\n code === \"ENOENT\" || code === \"ECONNREFUSED\" || code === \"EPIPE\" || code === \"ECONNRESET\";\n if (isDisconnectError) {\n this.scheduleReconnect();\n } else {\n error(\"Socket error:\", socketError.message);\n }\n settle(onError, socketError);\n },\n });\n connection.on(\"request\", (request) => {\n this.handleRequest(request).catch((cause) => {\n error(\"Error handling request:\", cause);\n });\n });\n connection.on(\"invalid\", ({ error: parseError }) => {\n warn(`Received invalid JSON from MCP server: ${parseError.message}`);\n });\n\n this.socket = socket;\n this.connection = connection;\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer || this.isStopping) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.attemptConnect();\n }, RECONNECT_DELAY_MS);\n }\n\n private register(): Promise<unknown> {\n if (!this.connection) {\n return Promise.reject(new Error(\"Connection not initialized\"));\n }\n return this.connection.send(\n \"app.register\",\n {\n applicationId: this.applicationId,\n pid: process.pid,\n projectRoot: process.cwd(),\n },\n REGISTER_TIMEOUT_MS,\n );\n }\n\n private async handleRequest(request: Request): Promise<void> {\n const { id, method, params } = request;\n const connection = this.connection;\n if (!connection) return;\n\n try {\n const defaultApp = Gio.Application.getDefault();\n if (!(defaultApp instanceof Gtk.Application)) {\n throw new TypeError(\"Application not initialized\");\n }\n this.registry.refresh();\n const result = await dispatch(method, params, { app: defaultApp, registry: this.registry });\n connection.write({ id, result });\n } catch (error) {\n if (error instanceof ProtocolError) {\n connection.write({ id, error: error.toErrorObject() });\n } else {\n connection.write({\n id,\n error: {\n code: ErrorCode.INTERNAL_ERROR,\n message: errorMessage(error),\n },\n });\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mcp/client.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gio from \"@gtkx/gi/gio\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport { DEFAULT_SOCKET_PATH, ErrorCode, ProtocolConnection, ProtocolError, type Request } from \"@gtkx/mcp/internal\";\nimport { error, errorMessage, info, normalizeError, warn } from \"@gtkx/utils\";\nimport * as net from \"node:net\";\nimport { dispatch } from \"./handlers.js\";\nimport { WidgetRegistry } from \"./widget-registry.js\";\n\ntype McpClientOptions = {\n socketPath?: string;\n applicationId: string;\n};\n\ntype ConnectCallbacks = {\n onSuccess?: (() => void) | undefined;\n onError?: ((error: Error) => void) | undefined;\n};\n\ntype ConnectSettler = {\n succeed: () => void;\n fail: (error: Error) => void;\n};\n\nconst RECONNECT_DELAY_MS = 2000;\nconst REGISTER_TIMEOUT_MS = 30_000;\nconst DISCONNECT_ERROR_CODES: Set<string> = new Set([\"ENOENT\", \"ECONNREFUSED\", \"EPIPE\", \"ECONNRESET\"]);\n\nconst toResponseError = (error: unknown): { code: number; message: string; data?: unknown } =>\n error instanceof ProtocolError\n ? error.toErrorObject()\n : { code: ErrorCode.INTERNAL_ERROR, message: errorMessage(error) };\n\nconst connectSettler = (callbacks: ConnectCallbacks): ConnectSettler => {\n let isSettled = false;\n\n const settle = (notify: () => void): void => {\n if (isSettled) {\n return;\n }\n\n isSettled = true;\n notify();\n };\n\n return {\n succeed: () => {\n settle(() => callbacks.onSuccess?.());\n },\n fail: (failure) => {\n settle(() => callbacks.onError?.(failure));\n },\n };\n};\n\nclass McpClient {\n private socket: net.Socket | null = null;\n private connection: ProtocolConnection | null = null;\n private socketPath: string;\n private applicationId: string;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private hasConnected = false;\n private isStopping = false;\n private pendingConnectReject: ((error: Error) => void) | null = null;\n private registry = new WidgetRegistry();\n\n constructor(options: McpClientOptions) {\n this.socketPath = options.socketPath ?? DEFAULT_SOCKET_PATH;\n this.applicationId = options.applicationId;\n }\n\n private handleClose(): void {\n if (this.hasConnected) {\n info(\"Disconnected from MCP server\");\n this.hasConnected = false;\n }\n\n this.socket = null;\n this.connection = null;\n this.scheduleReconnect();\n }\n\n private handleSocketError(socketError: Error): void {\n const code = (socketError as NodeJS.ErrnoException).code;\n\n if (code !== undefined && DISCONNECT_ERROR_CODES.has(code)) {\n this.scheduleReconnect();\n } else {\n error(\"Socket error:\", socketError.message);\n }\n }\n\n private async registerWithServer(settle: ConnectSettler): Promise<void> {\n try {\n await this.register();\n info(\"Registered with MCP server\");\n settle.succeed();\n } catch (registerError) {\n error(\"Failed to register with MCP server:\", errorMessage(registerError));\n settle.fail(normalizeError(registerError));\n }\n }\n\n private attemptConnect(callbacks: ConnectCallbacks = {}): void {\n const settle = connectSettler(callbacks);\n\n const socket = net.createConnection(this.socketPath, () => {\n info(`Connected to MCP server at ${this.socketPath}`);\n this.hasConnected = true;\n void this.registerWithServer(settle);\n });\n\n const connection = ProtocolConnection.fromSocket(socket, {\n onClose: () => {\n this.handleClose();\n },\n onError: (socketError) => {\n this.handleSocketError(socketError);\n settle.fail(socketError);\n },\n });\n\n connection.on(\"request\", (request) => {\n void this.handleRequest(request);\n });\n\n connection.on(\"invalid\", ({ error: parseError }) => {\n warn(`Received invalid JSON from MCP server: ${parseError.message}`);\n });\n\n this.socket = socket;\n this.connection = connection;\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer || this.isStopping) {\n return;\n }\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.attemptConnect();\n }, RECONNECT_DELAY_MS);\n }\n\n private register(): Promise<unknown> {\n if (!this.connection) {\n return Promise.reject(new Error(\"Connection not initialized\"));\n }\n\n return this.connection.send(\n \"app.register\",\n {\n applicationId: this.applicationId,\n pid: process.pid,\n projectRoot: process.cwd(),\n },\n REGISTER_TIMEOUT_MS,\n );\n }\n\n private async respondToRequest(request: Request): Promise<void> {\n const { id, method, params } = request;\n const connection = this.connection;\n\n if (!connection) {\n return;\n }\n\n try {\n const defaultApp = Gio.Application.getDefault();\n\n if (!(defaultApp instanceof Gtk.Application)) {\n throw new TypeError(\"Application not initialized\");\n }\n\n this.registry.refresh();\n const result = await dispatch(method, params, { app: defaultApp, registry: this.registry });\n connection.write({ id, result });\n } catch (dispatchError) {\n connection.write({ id, error: toResponseError(dispatchError) });\n }\n }\n\n private async handleRequest(request: Request): Promise<void> {\n try {\n await this.respondToRequest(request);\n } catch (requestError) {\n error(\"Error handling request:\", requestError);\n }\n }\n\n async connect(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.pendingConnectReject = reject;\n\n this.attemptConnect({\n onSuccess: () => {\n this.pendingConnectReject = null;\n resolve();\n },\n onError: (connectError) => {\n this.pendingConnectReject = null;\n reject(connectError);\n },\n });\n });\n }\n\n disconnect(): void {\n this.isStopping = true;\n\n if (this.pendingConnectReject) {\n this.pendingConnectReject(new Error(\"Client disconnected before connection registered\"));\n this.pendingConnectReject = null;\n }\n\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n\n this.connection?.rejectPending(new Error(\"Client disconnected\"));\n\n if (this.socket) {\n this.connection?.write({ id: crypto.randomUUID(), method: \"app.unregister\" });\n this.socket.destroy();\n this.socket = null;\n }\n\n this.connection = null;\n this.hasConnected = false;\n }\n}\n\nexport { McpClient, type McpClientOptions };\n"]}
|
package/dist/mcp/handlers.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
2
2
|
import type { WidgetRegistry } from "./widget-registry.js";
|
|
3
|
-
|
|
3
|
+
type HandlerContext = {
|
|
4
4
|
app: Gtk.Application;
|
|
5
5
|
registry: WidgetRegistry;
|
|
6
6
|
};
|
|
7
|
-
|
|
7
|
+
declare const dispatch: (method: string, params: unknown, ctx: HandlerContext) => Promise<unknown>;
|
|
8
|
+
export { dispatch, type HandlerContext };
|
|
8
9
|
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/mcp/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAYpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,KAAK,cAAc,GAAG;IAClB,GAAG,EAAE,GAAG,CAAC,WAAW,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;CAC5B,CAAC;AAyLF,QAAA,MAAM,QAAQ,GAAU,QAAQ,MAAM,EAAE,QAAQ,OAAO,EAAE,KAAK,cAAc,KAAG,OAAO,CAAC,OAAO,CAM7F,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,CAAC"}
|
package/dist/mcp/handlers.js
CHANGED
|
@@ -1,10 +1,54 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
1
|
import * as Gtk from "@gtkx/gi/gtk";
|
|
4
2
|
import { invalidRequestError, methodNotFoundError, ServerRequestParamsSchemas, widgetNotFoundError, } from "@gtkx/mcp/internal";
|
|
3
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
5
|
import { serializeWidget } from "./serialize-widget.js";
|
|
6
6
|
import { loadTestingModule } from "./testing-loader.js";
|
|
7
|
-
const
|
|
7
|
+
const HANDLERS = {
|
|
8
|
+
"app.getWindows": validated(ServerRequestParamsSchemas["app.getWindows"], ({ registry }) => Promise.resolve({
|
|
9
|
+
windows: registry.toplevels().map((window) => ({
|
|
10
|
+
id: registry.getOrCreateId(window),
|
|
11
|
+
title: window.getTitle(),
|
|
12
|
+
})),
|
|
13
|
+
})),
|
|
14
|
+
"widget.getTree": validated(ServerRequestParamsSchemas["widget.getTree"], async ({ app, registry }, params) => {
|
|
15
|
+
const testing = await loadTestingModule();
|
|
16
|
+
const container = params.rootId === undefined ? app : requireWidget(registry, params.rootId);
|
|
17
|
+
return {
|
|
18
|
+
tree: testing.prettyWidget(container, {
|
|
19
|
+
getId: (w) => registry.getOrCreateId(w),
|
|
20
|
+
highlight: false,
|
|
21
|
+
...((params.maxDepth !== undefined) && { maxDepth: params.maxDepth }),
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
}),
|
|
25
|
+
"widget.query": validated(ServerRequestParamsSchemas["widget.query"], handleQuery),
|
|
26
|
+
"widget.getProps": validated(ServerRequestParamsSchemas["widget.getProps"], async ({ registry }, params) => {
|
|
27
|
+
const { testing, widget } = await widgetTarget(registry, params.widgetId);
|
|
28
|
+
return serializeWidget(widget, (target) => registry.getOrCreateId(target), testing);
|
|
29
|
+
}),
|
|
30
|
+
"widget.click": validated(ServerRequestParamsSchemas["widget.click"], async ({ registry }, params) => {
|
|
31
|
+
const { testing, widget } = await widgetTarget(registry, params.widgetId);
|
|
32
|
+
await testing.userEvent.click(widget);
|
|
33
|
+
return { success: true };
|
|
34
|
+
}),
|
|
35
|
+
"widget.type": validated(ServerRequestParamsSchemas["widget.type"], async ({ registry }, params) => {
|
|
36
|
+
const { testing, widget } = await widgetTarget(registry, params.widgetId);
|
|
37
|
+
if (params.clear) {
|
|
38
|
+
await testing.userEvent.clear(widget);
|
|
39
|
+
}
|
|
40
|
+
await testing.userEvent.type(widget, params.text);
|
|
41
|
+
return { success: true };
|
|
42
|
+
}),
|
|
43
|
+
"widget.fireEvent": validated(ServerRequestParamsSchemas["widget.fireEvent"], async ({ registry }, params) => {
|
|
44
|
+
const { testing, widget } = await widgetTarget(registry, params.widgetId);
|
|
45
|
+
const signalArgs = (params.args ?? []).map((arg) => extractSignalArg(arg));
|
|
46
|
+
await testing.fireEvent(widget, params.signal, ...signalArgs);
|
|
47
|
+
return { success: true };
|
|
48
|
+
}),
|
|
49
|
+
"widget.screenshot": validated(ServerRequestParamsSchemas["widget.screenshot"], handleScreenshot),
|
|
50
|
+
};
|
|
51
|
+
function validated(schema, handler) {
|
|
8
52
|
return (ctx, params) => {
|
|
9
53
|
const parsed = schema.safeParse(params ?? {});
|
|
10
54
|
if (!parsed.success) {
|
|
@@ -12,7 +56,11 @@ const validated = (schema, handler) => {
|
|
|
12
56
|
}
|
|
13
57
|
return handler(ctx, parsed.data);
|
|
14
58
|
};
|
|
15
|
-
}
|
|
59
|
+
}
|
|
60
|
+
const widgetTarget = async (registry, widgetId) => ({
|
|
61
|
+
testing: await loadTestingModule(),
|
|
62
|
+
widget: requireWidget(registry, widgetId),
|
|
63
|
+
});
|
|
16
64
|
const requireWidget = (registry, widgetId) => {
|
|
17
65
|
if (widgetId === undefined) {
|
|
18
66
|
throw widgetNotFoundError("undefined");
|
|
@@ -27,29 +75,49 @@ const extractSignalArg = (arg) => {
|
|
|
27
75
|
const isTypedArg = typeof arg === "object" && arg !== null && "type" in arg && "value" in arg;
|
|
28
76
|
return isTypedArg ? arg.value : arg;
|
|
29
77
|
};
|
|
30
|
-
const
|
|
78
|
+
const resolveRole = (value) => {
|
|
79
|
+
if (typeof value === "number") {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
const resolved = Gtk.AccessibleRole[value.toUpperCase()];
|
|
83
|
+
return typeof resolved === "number" ? resolved : undefined;
|
|
84
|
+
};
|
|
85
|
+
const matchesOrEmpty = async (find) => {
|
|
86
|
+
try {
|
|
87
|
+
return await find();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
async function handleQuery({ app, registry }, params) {
|
|
31
94
|
const testing = await loadTestingModule();
|
|
32
95
|
let widgets = [];
|
|
33
96
|
switch (params.by) {
|
|
34
97
|
case "role": {
|
|
35
|
-
const roleValue =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
98
|
+
const roleValue = resolveRole(params.value);
|
|
99
|
+
if (roleValue === undefined) {
|
|
100
|
+
throw invalidRequestError(`Unknown accessible role "${String(params.value)}"; use the lowercase role shown in the ` +
|
|
101
|
+
"widget tree, e.g. \"button\", \"list\", \"list_item\", or \"checkbox\".");
|
|
102
|
+
}
|
|
103
|
+
widgets = await matchesOrEmpty(() => testing.findAllByRole(app, roleValue, params.options));
|
|
39
104
|
break;
|
|
40
105
|
}
|
|
41
|
-
case "text":
|
|
42
|
-
widgets = await testing.findAllByText(app, String(params.value), params.options);
|
|
106
|
+
case "text": {
|
|
107
|
+
widgets = await matchesOrEmpty(() => testing.findAllByText(app, String(params.value), params.options));
|
|
43
108
|
break;
|
|
44
|
-
|
|
45
|
-
|
|
109
|
+
}
|
|
110
|
+
case "name": {
|
|
111
|
+
widgets = await matchesOrEmpty(() => testing.findAllByName(app, String(params.value), params.options));
|
|
46
112
|
break;
|
|
47
|
-
|
|
48
|
-
|
|
113
|
+
}
|
|
114
|
+
case "labelText": {
|
|
115
|
+
widgets = await matchesOrEmpty(() => testing.findAllByLabelText(app, String(params.value), params.options));
|
|
49
116
|
break;
|
|
117
|
+
}
|
|
50
118
|
}
|
|
51
|
-
return { widgets: widgets.map((w) => serializeWidget(w, (widget) => registry.
|
|
52
|
-
}
|
|
119
|
+
return { widgets: widgets.map((w) => serializeWidget(w, (widget) => registry.getOrCreateId(widget), testing, 0)) };
|
|
120
|
+
}
|
|
53
121
|
const defaultScreenshotTarget = (registry) => {
|
|
54
122
|
const [toplevel] = registry.toplevels();
|
|
55
123
|
if (!toplevel) {
|
|
@@ -57,7 +125,7 @@ const defaultScreenshotTarget = (registry) => {
|
|
|
57
125
|
}
|
|
58
126
|
return toplevel;
|
|
59
127
|
};
|
|
60
|
-
|
|
128
|
+
async function handleScreenshot({ registry }, params) {
|
|
61
129
|
const testing = await loadTestingModule();
|
|
62
130
|
const target = params.windowId ? requireWidget(registry, params.windowId) : defaultScreenshotTarget(registry);
|
|
63
131
|
const result = await testing.screenshot(target);
|
|
@@ -67,53 +135,13 @@ const handleScreenshot = async ({ registry }, params) => {
|
|
|
67
135
|
return { data: result.data, mimeType: result.mimeType, savedPath: params.path };
|
|
68
136
|
}
|
|
69
137
|
return { data: result.data, mimeType: result.mimeType };
|
|
70
|
-
}
|
|
71
|
-
const HANDLERS = {
|
|
72
|
-
"app.getWindows": validated(ServerRequestParamsSchemas["app.getWindows"], async ({ registry }) => ({
|
|
73
|
-
windows: registry.toplevels().map((window) => ({
|
|
74
|
-
id: registry.idFor(window),
|
|
75
|
-
title: window.getTitle(),
|
|
76
|
-
})),
|
|
77
|
-
})),
|
|
78
|
-
"widget.getTree": validated(ServerRequestParamsSchemas["widget.getTree"], async ({ app, registry }) => {
|
|
79
|
-
const testing = await loadTestingModule();
|
|
80
|
-
return { tree: testing.prettyWidget(app, { getId: (w) => registry.idFor(w), highlight: false }) };
|
|
81
|
-
}),
|
|
82
|
-
"widget.query": validated(ServerRequestParamsSchemas["widget.query"], handleQuery),
|
|
83
|
-
"widget.getProps": validated(ServerRequestParamsSchemas["widget.getProps"], async ({ registry }, params) => {
|
|
84
|
-
const testing = await loadTestingModule();
|
|
85
|
-
const widget = requireWidget(registry, params.widgetId);
|
|
86
|
-
return serializeWidget(widget, (target) => registry.idFor(target), testing);
|
|
87
|
-
}),
|
|
88
|
-
"widget.click": validated(ServerRequestParamsSchemas["widget.click"], async ({ registry }, params) => {
|
|
89
|
-
const testing = await loadTestingModule();
|
|
90
|
-
const widget = requireWidget(registry, params.widgetId);
|
|
91
|
-
await testing.userEvent.click(widget);
|
|
92
|
-
return { success: true };
|
|
93
|
-
}),
|
|
94
|
-
"widget.type": validated(ServerRequestParamsSchemas["widget.type"], async ({ registry }, params) => {
|
|
95
|
-
const testing = await loadTestingModule();
|
|
96
|
-
const widget = requireWidget(registry, params.widgetId);
|
|
97
|
-
if (params.clear) {
|
|
98
|
-
await testing.userEvent.clear(widget);
|
|
99
|
-
}
|
|
100
|
-
await testing.userEvent.type(widget, params.text);
|
|
101
|
-
return { success: true };
|
|
102
|
-
}),
|
|
103
|
-
"widget.fireEvent": validated(ServerRequestParamsSchemas["widget.fireEvent"], async ({ registry }, params) => {
|
|
104
|
-
const testing = await loadTestingModule();
|
|
105
|
-
const widget = requireWidget(registry, params.widgetId);
|
|
106
|
-
const signalArgs = (params.args ?? []).map(extractSignalArg);
|
|
107
|
-
await testing.fireEvent(widget, params.signal, ...signalArgs);
|
|
108
|
-
return { success: true };
|
|
109
|
-
}),
|
|
110
|
-
"widget.screenshot": validated(ServerRequestParamsSchemas["widget.screenshot"], handleScreenshot),
|
|
111
|
-
};
|
|
138
|
+
}
|
|
112
139
|
const isServerInitiatedMethod = (method) => Object.hasOwn(ServerRequestParamsSchemas, method);
|
|
113
|
-
|
|
140
|
+
const dispatch = async (method, params, ctx) => {
|
|
114
141
|
if (!isServerInitiatedMethod(method)) {
|
|
115
142
|
throw methodNotFoundError(method);
|
|
116
143
|
}
|
|
117
144
|
return HANDLERS[method](ctx, params);
|
|
118
145
|
};
|
|
146
|
+
export { dispatch };
|
|
119
147
|
//# sourceMappingURL=handlers.js.map
|
package/dist/mcp/handlers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/mcp/handlers.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport * as Gtk from \"@gtkx/gi/gtk\";\nimport {\n invalidRequestError,\n methodNotFoundError,\n type ParamsSchema,\n type ServerInitiatedMethod,\n type ServerRequestParams,\n ServerRequestParamsSchemas,\n widgetNotFoundError,\n} from \"@gtkx/mcp/internal\";\nimport { serializeWidget } from \"./serialize-widget.js\";\nimport { loadTestingModule } from \"./testing-loader.js\";\nimport type { WidgetRegistry } from \"./widget-registry.js\";\n\nexport type HandlerContext = {\n app: Gtk.Application;\n registry: WidgetRegistry;\n};\n\ntype ValidatedHandler = (ctx: HandlerContext, params: unknown) => Promise<unknown>;\n\nconst validated = <Params>(\n schema: ParamsSchema<Params>,\n handler: (ctx: HandlerContext, params: Params) => Promise<unknown>,\n): ValidatedHandler => {\n return (ctx, params) => {\n const parsed = schema.safeParse(params ?? {});\n if (!parsed.success) {\n throw invalidRequestError(parsed.error.message);\n }\n return handler(ctx, parsed.data);\n };\n};\n\nconst requireWidget = (registry: WidgetRegistry, widgetId: string | undefined): Gtk.Widget => {\n if (widgetId === undefined) {\n throw widgetNotFoundError(\"undefined\");\n }\n const widget = registry.get(widgetId);\n if (!widget) {\n throw widgetNotFoundError(widgetId);\n }\n return widget;\n};\n\nconst extractSignalArg = (arg: unknown): unknown => {\n const isTypedArg = typeof arg === \"object\" && arg !== null && \"type\" in arg && \"value\" in arg;\n return isTypedArg ? (arg as { value: unknown }).value : arg;\n};\n\nconst handleQuery = async (\n { app, registry }: HandlerContext,\n params: ServerRequestParams<\"widget.query\">,\n): Promise<unknown> => {\n const testing = await loadTestingModule();\n let widgets: Gtk.Widget[] = [];\n\n switch (params.by) {\n case \"role\": {\n const roleValue =\n typeof params.value === \"string\"\n ? Gtk.AccessibleRole[params.value as keyof typeof Gtk.AccessibleRole]\n : params.value;\n widgets = await testing.findAllByRole(app, roleValue as Gtk.AccessibleRole, params.options);\n break;\n }\n case \"text\":\n widgets = await testing.findAllByText(app, String(params.value), params.options);\n break;\n case \"name\":\n widgets = await testing.findAllByName(app, String(params.value), params.options);\n break;\n case \"labelText\":\n widgets = await testing.findAllByLabelText(app, String(params.value), params.options);\n break;\n }\n\n return { widgets: widgets.map((w) => serializeWidget(w, (widget) => registry.idFor(widget), testing)) };\n};\n\nconst defaultScreenshotTarget = (registry: WidgetRegistry): Gtk.Widget => {\n const [toplevel] = registry.toplevels();\n if (!toplevel) {\n throw new Error(\"No windows available for screenshot\");\n }\n return toplevel;\n};\n\nconst handleScreenshot = async (\n { registry }: HandlerContext,\n params: ServerRequestParams<\"widget.screenshot\">,\n): Promise<unknown> => {\n const testing = await loadTestingModule();\n const target = params.windowId ? requireWidget(registry, params.windowId) : defaultScreenshotTarget(registry);\n const result = await testing.screenshot(target);\n if (params.path) {\n mkdirSync(dirname(params.path), { recursive: true });\n writeFileSync(params.path, Buffer.from(result.data, \"base64\"));\n return { data: result.data, mimeType: result.mimeType, savedPath: params.path };\n }\n return { data: result.data, mimeType: result.mimeType };\n};\n\nconst HANDLERS: Record<ServerInitiatedMethod, ValidatedHandler> = {\n \"app.getWindows\": validated(ServerRequestParamsSchemas[\"app.getWindows\"], async ({ registry }) => ({\n windows: registry.toplevels().map((window) => ({\n id: registry.idFor(window),\n title: window.getTitle(),\n })),\n })),\n \"widget.getTree\": validated(ServerRequestParamsSchemas[\"widget.getTree\"], async ({ app, registry }) => {\n const testing = await loadTestingModule();\n return { tree: testing.prettyWidget(app, { getId: (w) => registry.idFor(w), highlight: false }) };\n }),\n \"widget.query\": validated(ServerRequestParamsSchemas[\"widget.query\"], handleQuery),\n \"widget.getProps\": validated(ServerRequestParamsSchemas[\"widget.getProps\"], async ({ registry }, params) => {\n const testing = await loadTestingModule();\n const widget = requireWidget(registry, params.widgetId);\n return serializeWidget(widget, (target) => registry.idFor(target), testing);\n }),\n \"widget.click\": validated(ServerRequestParamsSchemas[\"widget.click\"], async ({ registry }, params) => {\n const testing = await loadTestingModule();\n const widget = requireWidget(registry, params.widgetId);\n await testing.userEvent.click(widget);\n return { success: true };\n }),\n \"widget.type\": validated(ServerRequestParamsSchemas[\"widget.type\"], async ({ registry }, params) => {\n const testing = await loadTestingModule();\n const widget = requireWidget(registry, params.widgetId);\n if (params.clear) {\n await testing.userEvent.clear(widget);\n }\n await testing.userEvent.type(widget, params.text);\n return { success: true };\n }),\n \"widget.fireEvent\": validated(ServerRequestParamsSchemas[\"widget.fireEvent\"], async ({ registry }, params) => {\n const testing = await loadTestingModule();\n const widget = requireWidget(registry, params.widgetId);\n const signalArgs = (params.args ?? []).map(extractSignalArg);\n await testing.fireEvent(widget, params.signal, ...signalArgs);\n return { success: true };\n }),\n \"widget.screenshot\": validated(ServerRequestParamsSchemas[\"widget.screenshot\"], handleScreenshot),\n};\n\nconst isServerInitiatedMethod = (method: string): method is ServerInitiatedMethod =>\n Object.hasOwn(ServerRequestParamsSchemas, method);\n\nexport const dispatch = async (method: string, params: unknown, ctx: HandlerContext): Promise<unknown> => {\n if (!isServerInitiatedMethod(method)) {\n throw methodNotFoundError(method);\n }\n return HANDLERS[method](ctx, params);\n};\n"]}
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/mcp/handlers.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Gtk from \"@gtkx/gi/gtk\";\nimport {\n invalidRequestError,\n methodNotFoundError,\n type ParamsSchema,\n type ServerInitiatedMethod,\n type ServerRequestParams,\n ServerRequestParamsSchemas,\n widgetNotFoundError,\n} from \"@gtkx/mcp/internal\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport type { WidgetRegistry } from \"./widget-registry.js\";\nimport { serializeWidget } from \"./serialize-widget.js\";\nimport { loadTestingModule, type TestingModule } from \"./testing-loader.js\";\n\ntype HandlerContext = {\n app: Gtk.Application;\n registry: WidgetRegistry;\n};\n\ntype ValidatedHandler = (ctx: HandlerContext, params: unknown) => Promise<unknown>;\ntype WidgetTarget = { testing: TestingModule; widget: Gtk.Widget };\n\nconst HANDLERS: Record<ServerInitiatedMethod, ValidatedHandler> = {\n \"app.getWindows\": validated(ServerRequestParamsSchemas[\"app.getWindows\"], ({ registry }) =>\n Promise.resolve({\n windows: registry.toplevels().map((window) => ({\n id: registry.getOrCreateId(window),\n title: window.getTitle(),\n })),\n })),\n \"widget.getTree\": validated(ServerRequestParamsSchemas[\"widget.getTree\"], async ({ app, registry }, params) => {\n const testing = await loadTestingModule();\n const container = params.rootId === undefined ? app : requireWidget(registry, params.rootId);\n\n return {\n tree: testing.prettyWidget(container, {\n getId: (w) => registry.getOrCreateId(w),\n highlight: false,\n ...((params.maxDepth !== undefined) && { maxDepth: params.maxDepth }),\n }),\n };\n }),\n \"widget.query\": validated(ServerRequestParamsSchemas[\"widget.query\"], handleQuery),\n \"widget.getProps\": validated(ServerRequestParamsSchemas[\"widget.getProps\"], async ({ registry }, params) => {\n const { testing, widget } = await widgetTarget(registry, params.widgetId);\n\n return serializeWidget(widget, (target) => registry.getOrCreateId(target), testing);\n }),\n \"widget.click\": validated(ServerRequestParamsSchemas[\"widget.click\"], async ({ registry }, params) => {\n const { testing, widget } = await widgetTarget(registry, params.widgetId);\n await testing.userEvent.click(widget);\n\n return { success: true };\n }),\n \"widget.type\": validated(ServerRequestParamsSchemas[\"widget.type\"], async ({ registry }, params) => {\n const { testing, widget } = await widgetTarget(registry, params.widgetId);\n\n if (params.clear) {\n await testing.userEvent.clear(widget);\n }\n\n await testing.userEvent.type(widget, params.text);\n\n return { success: true };\n }),\n \"widget.fireEvent\": validated(ServerRequestParamsSchemas[\"widget.fireEvent\"], async ({ registry }, params) => {\n const { testing, widget } = await widgetTarget(registry, params.widgetId);\n const signalArgs = (params.args ?? []).map((arg) => extractSignalArg(arg));\n await testing.fireEvent(widget, params.signal, ...signalArgs);\n\n return { success: true };\n }),\n \"widget.screenshot\": validated(ServerRequestParamsSchemas[\"widget.screenshot\"], handleScreenshot),\n};\n\nfunction validated<Params>(\n schema: ParamsSchema<Params>,\n handler: (ctx: HandlerContext, params: Params) => Promise<unknown>,\n): ValidatedHandler {\n return (ctx, params) => {\n const parsed = schema.safeParse(params ?? {});\n\n if (!parsed.success) {\n throw invalidRequestError(parsed.error.message);\n }\n\n return handler(ctx, parsed.data);\n };\n}\n\nconst widgetTarget = async (registry: WidgetRegistry, widgetId: string | undefined): Promise<WidgetTarget> => ({\n testing: await loadTestingModule(),\n widget: requireWidget(registry, widgetId),\n});\n\nconst requireWidget = (registry: WidgetRegistry, widgetId: string | undefined): Gtk.Widget => {\n if (widgetId === undefined) {\n throw widgetNotFoundError(\"undefined\");\n }\n\n const widget = registry.get(widgetId);\n\n if (!widget) {\n throw widgetNotFoundError(widgetId);\n }\n\n return widget;\n};\n\nconst extractSignalArg = (arg: unknown): unknown => {\n const isTypedArg = typeof arg === \"object\" && arg !== null && \"type\" in arg && \"value\" in arg;\n\n return isTypedArg ? (arg as { value: unknown }).value : arg;\n};\n\nconst resolveRole = (value: string | number): Gtk.AccessibleRole | undefined => {\n if (typeof value === \"number\") {\n return value;\n }\n\n const resolved = Gtk.AccessibleRole[value.toUpperCase() as keyof typeof Gtk.AccessibleRole];\n\n return typeof resolved === \"number\" ? resolved : undefined;\n};\n\nconst matchesOrEmpty = async (find: () => Promise<Gtk.Widget[]>): Promise<Gtk.Widget[]> => {\n try {\n return await find();\n } catch {\n return [];\n }\n};\n\nasync function handleQuery(\n { app, registry }: HandlerContext,\n params: ServerRequestParams<\"widget.query\">,\n): Promise<unknown> {\n const testing = await loadTestingModule();\n let widgets: Gtk.Widget[] = [];\n\n switch (params.by) {\n case \"role\": {\n const roleValue = resolveRole(params.value);\n\n if (roleValue === undefined) {\n throw invalidRequestError(\n `Unknown accessible role \"${String(params.value)}\"; use the lowercase role shown in the ` +\n \"widget tree, e.g. \\\"button\\\", \\\"list\\\", \\\"list_item\\\", or \\\"checkbox\\\".\",\n );\n }\n\n widgets = await matchesOrEmpty(() => testing.findAllByRole(app, roleValue, params.options));\n break;\n }\n case \"text\": {\n widgets = await matchesOrEmpty(() => testing.findAllByText(app, String(params.value), params.options));\n break;\n }\n case \"name\": {\n widgets = await matchesOrEmpty(() => testing.findAllByName(app, String(params.value), params.options));\n break;\n }\n case \"labelText\": {\n widgets = await matchesOrEmpty(() => testing.findAllByLabelText(app, String(params.value), params.options));\n break;\n }\n }\n\n return { widgets: widgets.map((w) => serializeWidget(w, (widget) => registry.getOrCreateId(widget), testing, 0)) };\n}\n\nconst defaultScreenshotTarget = (registry: WidgetRegistry): Gtk.Widget => {\n const [toplevel] = registry.toplevels();\n\n if (!toplevel) {\n throw new Error(\"No windows available for screenshot\");\n }\n\n return toplevel;\n};\n\nasync function handleScreenshot(\n { registry }: HandlerContext,\n params: ServerRequestParams<\"widget.screenshot\">,\n): Promise<unknown> {\n const testing = await loadTestingModule();\n const target = params.windowId ? requireWidget(registry, params.windowId) : defaultScreenshotTarget(registry);\n const result = await testing.screenshot(target);\n\n if (params.path) {\n mkdirSync(dirname(params.path), { recursive: true });\n writeFileSync(params.path, Buffer.from(result.data, \"base64\"));\n\n return { data: result.data, mimeType: result.mimeType, savedPath: params.path };\n }\n\n return { data: result.data, mimeType: result.mimeType };\n}\n\nconst isServerInitiatedMethod = (method: string): method is ServerInitiatedMethod =>\n Object.hasOwn(ServerRequestParamsSchemas, method);\n\nconst dispatch = async (method: string, params: unknown, ctx: HandlerContext): Promise<unknown> => {\n if (!isServerInitiatedMethod(method)) {\n throw methodNotFoundError(method);\n }\n\n return HANDLERS[method](ctx, params);\n};\n\nexport { dispatch, type HandlerContext };\n"]}
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { McpClient } from "./client.js";
|
|
2
|
-
|
|
3
|
-
export
|
|
2
|
+
declare const startMcpClient: (applicationId: string) => Promise<McpClient>, stopMcpClient: () => void;
|
|
3
|
+
export { startMcpClient, stopMcpClient };
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAOxC,QAAA,MAAe,cAAc,kBAJF,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,EAInB,aAAa,QAHlC,IAGkE,CAAC;AAsCnF,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC"}
|