@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.
Files changed (286) hide show
  1. package/dist/builder.d.ts +3 -2
  2. package/dist/builder.d.ts.map +1 -1
  3. package/dist/builder.js +2 -1
  4. package/dist/builder.js.map +1 -1
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +21 -6
  8. package/dist/cli.js.map +1 -1
  9. package/dist/codegen/freshness.d.ts +5 -5
  10. package/dist/codegen/freshness.d.ts.map +1 -1
  11. package/dist/codegen/freshness.js +26 -23
  12. package/dist/codegen/freshness.js.map +1 -1
  13. package/dist/codegen/report.d.ts +2 -1
  14. package/dist/codegen/report.d.ts.map +1 -1
  15. package/dist/codegen/report.js +4 -2
  16. package/dist/codegen/report.js.map +1 -1
  17. package/dist/codegen/run-codegen.d.ts +8 -8
  18. package/dist/codegen/run-codegen.d.ts.map +1 -1
  19. package/dist/codegen/run-codegen.js +72 -43
  20. package/dist/codegen/run-codegen.js.map +1 -1
  21. package/dist/codegen/store-resolver.d.ts +8 -5
  22. package/dist/codegen/store-resolver.d.ts.map +1 -1
  23. package/dist/codegen/store-resolver.js +26 -6
  24. package/dist/codegen/store-resolver.js.map +1 -1
  25. package/dist/commands/build.d.ts +4 -3
  26. package/dist/commands/build.d.ts.map +1 -1
  27. package/dist/commands/build.js +2 -1
  28. package/dist/commands/build.js.map +1 -1
  29. package/dist/commands/codegen.d.ts +2 -1
  30. package/dist/commands/codegen.d.ts.map +1 -1
  31. package/dist/commands/codegen.js +6 -4
  32. package/dist/commands/codegen.js.map +1 -1
  33. package/dist/commands/dev.d.ts +2 -1
  34. package/dist/commands/dev.d.ts.map +1 -1
  35. package/dist/commands/dev.js +2 -1
  36. package/dist/commands/dev.js.map +1 -1
  37. package/dist/commands/docs.d.ts +2 -1
  38. package/dist/commands/docs.d.ts.map +1 -1
  39. package/dist/commands/docs.js +22 -7
  40. package/dist/commands/docs.js.map +1 -1
  41. package/dist/dev/icon-dir.d.ts +2 -1
  42. package/dist/dev/icon-dir.d.ts.map +1 -1
  43. package/dist/dev/icon-dir.js +6 -3
  44. package/dist/dev/icon-dir.js.map +1 -1
  45. package/dist/dev/refresh-tracker.d.ts +5 -4
  46. package/dist/dev/refresh-tracker.d.ts.map +1 -1
  47. package/dist/dev/refresh-tracker.js +6 -5
  48. package/dist/dev/refresh-tracker.js.map +1 -1
  49. package/dist/dev/runner-deps.d.ts +2 -1
  50. package/dist/dev/runner-deps.d.ts.map +1 -1
  51. package/dist/dev/runner-deps.js +20 -4
  52. package/dist/dev/runner-deps.js.map +1 -1
  53. package/dist/dev/runner-main.d.ts +2 -1
  54. package/dist/dev/runner-main.d.ts.map +1 -1
  55. package/dist/dev/runner-main.js +3 -2
  56. package/dist/dev/runner-main.js.map +1 -1
  57. package/dist/dev/runner.d.ts +6 -5
  58. package/dist/dev/runner.d.ts.map +1 -1
  59. package/dist/dev/runner.js +69 -38
  60. package/dist/dev/runner.js.map +1 -1
  61. package/dist/dev/schema-dir.d.ts +2 -1
  62. package/dist/dev/schema-dir.d.ts.map +1 -1
  63. package/dist/dev/schema-dir.js +4 -2
  64. package/dist/dev/schema-dir.js.map +1 -1
  65. package/dist/dev/supervisor.d.ts +7 -6
  66. package/dist/dev/supervisor.d.ts.map +1 -1
  67. package/dist/dev/supervisor.js +107 -67
  68. package/dist/dev/supervisor.js.map +1 -1
  69. package/dist/dev/vite-dev-server.d.ts +3 -3
  70. package/dist/dev/vite-dev-server.d.ts.map +1 -1
  71. package/dist/dev/vite-dev-server.js +2 -1
  72. package/dist/dev/vite-dev-server.js.map +1 -1
  73. package/dist/internal/banner.d.ts +2 -1
  74. package/dist/internal/banner.d.ts.map +1 -1
  75. package/dist/internal/banner.js +2 -1
  76. package/dist/internal/banner.js.map +1 -1
  77. package/dist/internal/data-dir.d.ts +3 -2
  78. package/dist/internal/data-dir.d.ts.map +1 -1
  79. package/dist/internal/data-dir.js +29 -14
  80. package/dist/internal/data-dir.js.map +1 -1
  81. package/dist/internal/entry-arg.d.ts +5 -4
  82. package/dist/internal/entry-arg.d.ts.map +1 -1
  83. package/dist/internal/entry-arg.js +10 -7
  84. package/dist/internal/entry-arg.js.map +1 -1
  85. package/dist/internal/errors.d.ts +3 -2
  86. package/dist/internal/errors.d.ts.map +1 -1
  87. package/dist/internal/errors.js +7 -5
  88. package/dist/internal/errors.js.map +1 -1
  89. package/dist/internal/list-files.d.ts +3 -2
  90. package/dist/internal/list-files.d.ts.map +1 -1
  91. package/dist/internal/list-files.js +5 -3
  92. package/dist/internal/list-files.js.map +1 -1
  93. package/dist/internal/resolve-cli-tool.d.ts +2 -1
  94. package/dist/internal/resolve-cli-tool.d.ts.map +1 -1
  95. package/dist/internal/resolve-cli-tool.js +17 -10
  96. package/dist/internal/resolve-cli-tool.js.map +1 -1
  97. package/dist/internal/staging-dir.d.ts +3 -2
  98. package/dist/internal/staging-dir.d.ts.map +1 -1
  99. package/dist/internal/staging-dir.js +3 -2
  100. package/dist/internal/staging-dir.js.map +1 -1
  101. package/dist/internal/xdg-data-dirs.d.ts +2 -1
  102. package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
  103. package/dist/internal/xdg-data-dirs.js +4 -2
  104. package/dist/internal/xdg-data-dirs.js.map +1 -1
  105. package/dist/mcp/client.d.ts +9 -4
  106. package/dist/mcp/client.d.ts.map +1 -1
  107. package/dist/mcp/client.js +106 -81
  108. package/dist/mcp/client.js.map +1 -1
  109. package/dist/mcp/handlers.d.ts +3 -2
  110. package/dist/mcp/handlers.d.ts.map +1 -1
  111. package/dist/mcp/handlers.js +89 -61
  112. package/dist/mcp/handlers.js.map +1 -1
  113. package/dist/mcp/index.d.ts +2 -2
  114. package/dist/mcp/index.d.ts.map +1 -1
  115. package/dist/mcp/index.js +30 -13
  116. package/dist/mcp/index.js.map +1 -1
  117. package/dist/mcp/serialize-widget.d.ts +3 -3
  118. package/dist/mcp/serialize-widget.d.ts.map +1 -1
  119. package/dist/mcp/serialize-widget.js +9 -6
  120. package/dist/mcp/serialize-widget.js.map +1 -1
  121. package/dist/mcp/testing-loader.d.ts +3 -4
  122. package/dist/mcp/testing-loader.d.ts.map +1 -1
  123. package/dist/mcp/testing-loader.js +43 -21
  124. package/dist/mcp/testing-loader.js.map +1 -1
  125. package/dist/mcp/widget-registry.d.ts +3 -2
  126. package/dist/mcp/widget-registry.d.ts.map +1 -1
  127. package/dist/mcp/widget-registry.js +4 -3
  128. package/dist/mcp/widget-registry.js.map +1 -1
  129. package/dist/refresh-globals.d.ts +2 -0
  130. package/dist/refresh-globals.d.ts.map +1 -0
  131. package/dist/refresh-globals.js +8 -0
  132. package/dist/refresh-globals.js.map +1 -0
  133. package/dist/refresh-runtime.d.ts +5 -11
  134. package/dist/refresh-runtime.d.ts.map +1 -1
  135. package/dist/refresh-runtime.js +16 -14
  136. package/dist/refresh-runtime.js.map +1 -1
  137. package/dist/settings/compile.d.ts +2 -1
  138. package/dist/settings/compile.d.ts.map +1 -1
  139. package/dist/settings/compile.js +6 -4
  140. package/dist/settings/compile.js.map +1 -1
  141. package/dist/settings/parser.d.ts +6 -5
  142. package/dist/settings/parser.d.ts.map +1 -1
  143. package/dist/settings/parser.js +26 -22
  144. package/dist/settings/parser.js.map +1 -1
  145. package/dist/settings/render.d.ts +3 -2
  146. package/dist/settings/render.d.ts.map +1 -1
  147. package/dist/settings/render.js +38 -64
  148. package/dist/settings/render.js.map +1 -1
  149. package/dist/settings/schema.d.ts +8 -8
  150. package/dist/settings/schema.d.ts.map +1 -1
  151. package/dist/settings/schema.js +61 -43
  152. package/dist/settings/schema.js.map +1 -1
  153. package/dist/vite-plugins/asset-extensions.d.ts +4 -3
  154. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  155. package/dist/vite-plugins/asset-extensions.js +4 -3
  156. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  157. package/dist/vite-plugins/built-url.d.ts +2 -1
  158. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  159. package/dist/vite-plugins/built-url.js +19 -13
  160. package/dist/vite-plugins/built-url.js.map +1 -1
  161. package/dist/vite-plugins/css.d.ts +2 -1
  162. package/dist/vite-plugins/css.d.ts.map +1 -1
  163. package/dist/vite-plugins/css.js +23 -15
  164. package/dist/vite-plugins/css.js.map +1 -1
  165. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +5 -4
  166. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -1
  167. package/dist/vite-plugins/fast-refresh/refresh-filter.js +11 -7
  168. package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -1
  169. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +4 -3
  170. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -1
  171. package/dist/vite-plugins/fast-refresh/swc-refresh.js +45 -41
  172. package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -1
  173. package/dist/vite-plugins/icons.d.ts +2 -1
  174. package/dist/vite-plugins/icons.d.ts.map +1 -1
  175. package/dist/vite-plugins/icons.js +24 -20
  176. package/dist/vite-plugins/icons.js.map +1 -1
  177. package/dist/vite-plugins/index.d.ts +2 -1
  178. package/dist/vite-plugins/index.d.ts.map +1 -1
  179. package/dist/vite-plugins/index.js +3 -2
  180. package/dist/vite-plugins/index.js.map +1 -1
  181. package/dist/vite-plugins/native.d.ts +2 -1
  182. package/dist/vite-plugins/native.d.ts.map +1 -1
  183. package/dist/vite-plugins/native.js +6 -4
  184. package/dist/vite-plugins/native.js.map +1 -1
  185. package/dist/vite-plugins/react-compiler.d.ts +3 -2
  186. package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
  187. package/dist/vite-plugins/react-compiler.js +26 -20
  188. package/dist/vite-plugins/react-compiler.js.map +1 -1
  189. package/dist/vite-plugins/react-dom-prebundle.d.ts +2 -1
  190. package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -1
  191. package/dist/vite-plugins/react-dom-prebundle.js +2 -1
  192. package/dist/vite-plugins/react-dom-prebundle.js.map +1 -1
  193. package/dist/vite-plugins/resource-init-module.d.ts +2 -1
  194. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
  195. package/dist/vite-plugins/resource-init-module.js +39 -38
  196. package/dist/vite-plugins/resource-init-module.js.map +1 -1
  197. package/dist/vite-plugins/resource-shared.d.ts +7 -5
  198. package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
  199. package/dist/vite-plugins/resource-shared.js +17 -10
  200. package/dist/vite-plugins/resource-shared.js.map +1 -1
  201. package/dist/vite-plugins/resources.d.ts +3 -2
  202. package/dist/vite-plugins/resources.d.ts.map +1 -1
  203. package/dist/vite-plugins/resources.js +94 -54
  204. package/dist/vite-plugins/resources.js.map +1 -1
  205. package/dist/vite-plugins/settings-worker-env.d.ts +2 -1
  206. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  207. package/dist/vite-plugins/settings-worker-env.js +4 -2
  208. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  209. package/dist/vite-plugins/settings.d.ts +2 -1
  210. package/dist/vite-plugins/settings.d.ts.map +1 -1
  211. package/dist/vite-plugins/settings.js +56 -32
  212. package/dist/vite-plugins/settings.js.map +1 -1
  213. package/dist/vite-plugins/strip-query.d.ts +3 -0
  214. package/dist/vite-plugins/strip-query.d.ts.map +1 -0
  215. package/dist/vite-plugins/strip-query.js +6 -0
  216. package/dist/vite-plugins/strip-query.js.map +1 -0
  217. package/dist/vite-plugins/undeclared-library.d.ts +2 -1
  218. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  219. package/dist/vite-plugins/undeclared-library.js +8 -5
  220. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  221. package/dist/vite-plugins/virtual-module.d.ts +2 -2
  222. package/dist/vite-plugins/virtual-module.d.ts.map +1 -1
  223. package/dist/vite-plugins/virtual-module.js +5 -3
  224. package/dist/vite-plugins/virtual-module.js.map +1 -1
  225. package/dist/vitest-plugin.d.ts.map +1 -1
  226. package/dist/vitest-plugin.js.map +1 -1
  227. package/env.d.ts +21 -61
  228. package/package.json +12 -14
  229. package/src/babel-modules.d.ts +0 -2
  230. package/src/builder.ts +7 -6
  231. package/src/cli.ts +26 -6
  232. package/src/codegen/freshness.ts +38 -25
  233. package/src/codegen/report.ts +10 -2
  234. package/src/codegen/run-codegen.ts +135 -60
  235. package/src/codegen/store-resolver.ts +56 -15
  236. package/src/commands/build.ts +3 -2
  237. package/src/commands/codegen.ts +9 -4
  238. package/src/commands/dev.ts +3 -4
  239. package/src/commands/docs.ts +36 -7
  240. package/src/dev/icon-dir.ts +13 -3
  241. package/src/dev/refresh-tracker.ts +12 -8
  242. package/src/dev/runner-deps.ts +30 -7
  243. package/src/dev/runner-main.ts +4 -3
  244. package/src/dev/runner.ts +100 -41
  245. package/src/dev/schema-dir.ts +7 -2
  246. package/src/dev/supervisor.ts +169 -71
  247. package/src/dev/vite-dev-server.ts +4 -2
  248. package/src/internal/banner.ts +5 -1
  249. package/src/internal/data-dir.ts +47 -16
  250. package/src/internal/entry-arg.ts +16 -8
  251. package/src/internal/errors.ts +15 -7
  252. package/src/internal/list-files.ts +10 -4
  253. package/src/internal/resolve-cli-tool.ts +28 -12
  254. package/src/internal/staging-dir.ts +5 -2
  255. package/src/internal/xdg-data-dirs.ts +8 -2
  256. package/src/mcp/client.ts +143 -85
  257. package/src/mcp/handlers.ts +126 -69
  258. package/src/mcp/index.ts +38 -14
  259. package/src/mcp/serialize-widget.ts +15 -8
  260. package/src/mcp/testing-loader.ts +64 -23
  261. package/src/mcp/widget-registry.ts +11 -5
  262. package/src/refresh-globals.ts +10 -0
  263. package/src/refresh-runtime.ts +20 -16
  264. package/src/settings/compile.ts +7 -4
  265. package/src/settings/parser.ts +69 -30
  266. package/src/settings/render.ts +66 -66
  267. package/src/settings/schema.ts +100 -46
  268. package/src/vite-plugins/asset-extensions.ts +4 -3
  269. package/src/vite-plugins/built-url.ts +30 -15
  270. package/src/vite-plugins/css.ts +35 -20
  271. package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -10
  272. package/src/vite-plugins/fast-refresh/swc-refresh.ts +60 -47
  273. package/src/vite-plugins/icons.ts +40 -24
  274. package/src/vite-plugins/index.ts +6 -3
  275. package/src/vite-plugins/native.ts +14 -5
  276. package/src/vite-plugins/react-compiler.ts +44 -30
  277. package/src/vite-plugins/react-dom-prebundle.ts +4 -1
  278. package/src/vite-plugins/resource-init-module.ts +41 -38
  279. package/src/vite-plugins/resource-shared.ts +27 -14
  280. package/src/vite-plugins/resources.ts +153 -59
  281. package/src/vite-plugins/settings-worker-env.ts +7 -2
  282. package/src/vite-plugins/settings.ts +107 -42
  283. package/src/vite-plugins/strip-query.ts +7 -0
  284. package/src/vite-plugins/undeclared-library.ts +21 -12
  285. package/src/vite-plugins/virtual-module.ts +9 -2
  286. package/src/vitest-plugin.ts +1 -2
@@ -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 = 30000;
10
- export class McpClient {
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
- async connect() {
25
- return new Promise((resolve, reject) => {
26
- this.pendingConnectReject = reject;
27
- this.attemptConnect(() => {
28
- this.pendingConnectReject = null;
29
- resolve();
30
- }, (error) => {
31
- this.pendingConnectReject = null;
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
- disconnect() {
37
- this.isStopping = true;
38
- if (this.pendingConnectReject) {
39
- this.pendingConnectReject(new Error("Client disconnected before connection registered"));
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
- if (this.reconnectTimer) {
43
- clearTimeout(this.reconnectTimer);
44
- this.reconnectTimer = null;
60
+ else {
61
+ error("Socket error:", socketError.message);
45
62
  }
46
- this.connection?.rejectPending(new Error("Client disconnected"));
47
- if (this.socket) {
48
- this.connection?.write({ id: crypto.randomUUID(), method: "app.unregister" });
49
- this.socket.destroy();
50
- this.socket = null;
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(onSuccess, onError) {
56
- let settled = false;
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.register()
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
- if (this.hasConnected) {
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
- const code = socketError.code;
88
- const isDisconnectError = code === "ENOENT" || code === "ECONNREFUSED" || code === "EPIPE" || code === "ECONNRESET";
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).catch((cause) => {
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 handleRequest(request) {
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 (error) {
142
- if (error instanceof ProtocolError) {
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
@@ -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"]}
@@ -1,8 +1,9 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
2
  import type { WidgetRegistry } from "./widget-registry.js";
3
- export type HandlerContext = {
3
+ type HandlerContext = {
4
4
  app: Gtk.Application;
5
5
  registry: WidgetRegistry;
6
6
  };
7
- export declare const dispatch: (method: string, params: unknown, ctx: HandlerContext) => Promise<unknown>;
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":"AAEA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AAYpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,MAAM,cAAc,GAAG;IACzB,GAAG,EAAE,GAAG,CAAC,WAAW,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;CAC5B,CAAC;AAmIF,eAAO,MAAM,QAAQ,WAAkB,MAAM,UAAU,OAAO,OAAO,cAAc,KAAG,OAAO,CAAC,OAAO,CAKpG,CAAC"}
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"}
@@ -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 validated = (schema, handler) => {
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 handleQuery = async ({ app, registry }, params) => {
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 = typeof params.value === "string"
36
- ? Gtk.AccessibleRole[params.value]
37
- : params.value;
38
- widgets = await testing.findAllByRole(app, roleValue, params.options);
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
- case "name":
45
- widgets = await testing.findAllByName(app, String(params.value), params.options);
109
+ }
110
+ case "name": {
111
+ widgets = await matchesOrEmpty(() => testing.findAllByName(app, String(params.value), params.options));
46
112
  break;
47
- case "labelText":
48
- widgets = await testing.findAllByLabelText(app, String(params.value), params.options);
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.idFor(widget), testing)) };
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
- const handleScreenshot = async ({ registry }, params) => {
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
- export const dispatch = async (method, params, ctx) => {
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
@@ -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":"AAAA,OAAO,KAAK,GAAG,MAAM,cAAc,CAAC;AACpC,OAAO,EACH,mBAAmB,EACnB,mBAAmB,EAInB,0BAA0B,EAC1B,mBAAmB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAsB,MAAM,qBAAqB,CAAC;AAU5E,MAAM,QAAQ,GAAoD;IAC9D,gBAAgB,EAAE,SAAS,CAAC,0BAA0B,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CACvF,OAAO,CAAC,OAAO,CAAC;QACZ,OAAO,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC3C,EAAE,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;YAClC,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE;SAC3B,CAAC,CAAC;KACN,CAAC,CAAC;IACP,gBAAgB,EAAE,SAAS,CAAC,0BAA0B,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE;QAC1G,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAE7F,OAAO;YACH,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE;gBAClC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;gBACvC,SAAS,EAAE,KAAK;gBAChB,GAAG,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;aACxE,CAAC;SACL,CAAC;IACN,CAAC,CAAC;IACF,cAAc,EAAE,SAAS,CAAC,0BAA0B,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC;IAClF,iBAAiB,EAAE,SAAS,CAAC,0BAA0B,CAAC,iBAAiB,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE;QACvG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE1E,OAAO,eAAe,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IACxF,CAAC,CAAC;IACF,cAAc,EAAE,SAAS,CAAC,0BAA0B,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE;QACjG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1E,MAAM,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAEtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC;IACF,aAAa,EAAE,SAAS,CAAC,0BAA0B,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE;QAC/F,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE1E,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;QAED,MAAM,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAElD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC;IACF,kBAAkB,EAAE,SAAS,CAAC,0BAA0B,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE;QACzG,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1E,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3E,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC;QAE9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC;IACF,mBAAmB,EAAE,SAAS,CAAC,0BAA0B,CAAC,mBAAmB,CAAC,EAAE,gBAAgB,CAAC;CACpG,CAAC;AAEF,SAAS,SAAS,CACd,MAA4B,EAC5B,OAAkE;IAElE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;QACnB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAE9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAClB,MAAM,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,YAAY,GAAG,KAAK,EAAE,QAAwB,EAAE,QAA4B,EAAyB,EAAE,CAAC,CAAC;IAC3G,OAAO,EAAE,MAAM,iBAAiB,EAAE;IAClC,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC5C,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,QAAwB,EAAE,QAA4B,EAAc,EAAE;IACzF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,GAAY,EAAW,EAAE;IAC/C,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,MAAM,IAAI,GAAG,IAAI,OAAO,IAAI,GAAG,CAAC;IAE9F,OAAO,UAAU,CAAC,CAAC,CAAE,GAA0B,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;AAChE,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,KAAsB,EAAkC,EAAE;IAC3E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,EAAqC,CAAC,CAAC;IAE5F,OAAO,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/D,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,KAAK,EAAE,IAAiC,EAAyB,EAAE;IACtF,IAAI,CAAC;QACD,OAAO,MAAM,IAAI,EAAE,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC,CAAC;AAEF,KAAK,UAAU,WAAW,CACtB,EAAE,GAAG,EAAE,QAAQ,EAAkB,EACjC,MAA2C;IAE3C,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1C,IAAI,OAAO,GAAiB,EAAE,CAAC;IAE/B,QAAQ,MAAM,CAAC,EAAE,EAAE,CAAC;QAChB,KAAK,MAAM,CAAC,CAAC,CAAC;YACV,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,mBAAmB,CACrB,4BAA4B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC;oBACzF,yEAAyE,CAC5E,CAAC;YACN,CAAC;YAED,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5F,MAAM;QACV,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACV,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACvG,MAAM;QACV,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACV,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACvG,MAAM;QACV,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACf,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5G,MAAM;QACV,CAAC;IACL,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACvH,CAAC;AAED,MAAM,uBAAuB,GAAG,CAAC,QAAwB,EAAc,EAAE;IACrE,MAAM,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;IAExC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC,CAAC;AAEF,KAAK,UAAU,gBAAgB,CAC3B,EAAE,QAAQ,EAAkB,EAC5B,MAAgD;IAEhD,MAAM,OAAO,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAC9G,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAEhD,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,aAAa,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;QAE/D,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IACpF,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,uBAAuB,GAAG,CAAC,MAAc,EAAmC,EAAE,CAChF,MAAM,CAAC,MAAM,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC;AAEtD,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAE,MAAe,EAAE,GAAmB,EAAoB,EAAE;IAC9F,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,OAAO,EAAE,QAAQ,EAAuB,CAAC","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"]}
@@ -1,4 +1,4 @@
1
1
  import { McpClient } from "./client.js";
2
- export declare const startMcpClient: (applicationId: string) => Promise<McpClient>;
3
- export declare const stopMcpClient: () => void;
2
+ declare const startMcpClient: (applicationId: string) => Promise<McpClient>, stopMcpClient: () => void;
3
+ export { startMcpClient, stopMcpClient };
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAIxC,eAAO,MAAM,cAAc,kBAAyB,MAAM,KAAG,OAAO,CAAC,SAAS,CAU7E,CAAC;AAEF,eAAO,MAAM,aAAa,QAAO,IAKhC,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"}