remotelink 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import os from "os";
5
+ import path from "path";
6
+ import fs from "fs/promises";
7
+ import { spawn } from "child_process";
8
+ import process from "process";
9
+ import { Client } from "@modelcontextprotocol/client";
10
+ import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
11
+ import WebSocket from "ws";
12
+ var VERSION = "0.2.0";
13
+ var DEFAULT_ORIGIN = "https://remote.samyao.me";
14
+ var CONFIG_DIR = path.join(os.homedir(), ".remote-link");
15
+ var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
16
+ var SAFE_TOOLS = /* @__PURE__ */ new Set([
17
+ "list_directory",
18
+ "read_file",
19
+ "get_file_info",
20
+ "list_processes"
21
+ ]);
22
+ var DEVELOPER_TOOLS = /* @__PURE__ */ new Set([
23
+ ...SAFE_TOOLS,
24
+ "start_process",
25
+ "write_file",
26
+ "edit_block"
27
+ ]);
28
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
29
+ function argFlag(name) {
30
+ return process.argv.includes(name);
31
+ }
32
+ function selectedMode() {
33
+ if (argFlag("--safe")) return "safe";
34
+ return "developer";
35
+ }
36
+ async function readConfig() {
37
+ try {
38
+ return JSON.parse(await fs.readFile(CONFIG_PATH, "utf8"));
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ async function writeConfig(config) {
44
+ await fs.mkdir(CONFIG_DIR, { recursive: true });
45
+ await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", {
46
+ mode: 384
47
+ });
48
+ }
49
+ async function resetConfig() {
50
+ await fs.rm(CONFIG_PATH, { force: true });
51
+ process.stdout.write("Remote Link device credentials removed.\n");
52
+ }
53
+ function openBrowser(url) {
54
+ const command = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
55
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
56
+ const child = spawn(command, args, {
57
+ detached: true,
58
+ stdio: "ignore"
59
+ });
60
+ child.unref();
61
+ }
62
+ async function pair(origin, mode) {
63
+ const deviceName = os.hostname();
64
+ const response = await fetch(origin + "/api/device/start", {
65
+ method: "POST",
66
+ headers: { "content-type": "application/json" },
67
+ body: JSON.stringify({
68
+ device_name: deviceName,
69
+ platform: process.platform,
70
+ arch: process.arch,
71
+ hostname: os.hostname()
72
+ })
73
+ });
74
+ if (!response.ok) {
75
+ throw new Error("Could not start device pairing: " + response.status);
76
+ }
77
+ const pairing = await response.json();
78
+ process.stdout.write("\nRemote Link\n\n");
79
+ process.stdout.write("Pair this computer in your browser.\n\n");
80
+ process.stdout.write(" " + pairing.user_code + "\n\n");
81
+ process.stdout.write(pairing.verification_uri_complete + "\n\n");
82
+ process.stdout.write("Opening browser...\n");
83
+ openBrowser(pairing.verification_uri_complete);
84
+ const startedAt = Date.now();
85
+ while (Date.now() - startedAt < pairing.expires_in * 1e3) {
86
+ await sleep(Math.max(pairing.interval, 2) * 1e3);
87
+ const tokenResponse = await fetch(origin + "/api/device/token", {
88
+ method: "POST",
89
+ headers: { "content-type": "application/json" },
90
+ body: JSON.stringify({
91
+ device_code: pairing.device_code,
92
+ device_secret: pairing.device_secret
93
+ })
94
+ });
95
+ if (tokenResponse.status === 428) {
96
+ process.stdout.write(".");
97
+ continue;
98
+ }
99
+ if (!tokenResponse.ok) {
100
+ const body = await tokenResponse.text();
101
+ throw new Error("Pairing failed: " + body);
102
+ }
103
+ const result = await tokenResponse.json();
104
+ process.stdout.write("\n\n\u2713 Device authorized\n");
105
+ const config = {
106
+ deviceId: result.device_id,
107
+ deviceToken: result.device_token,
108
+ deviceName,
109
+ origin,
110
+ mode
111
+ };
112
+ await writeConfig(config);
113
+ return config;
114
+ }
115
+ throw new Error("Pairing expired. Run Remote Link again to retry.");
116
+ }
117
+ var ExecutionCore = class {
118
+ constructor(mode) {
119
+ this.mode = mode;
120
+ }
121
+ mode;
122
+ client = null;
123
+ async connect() {
124
+ if (this.client) return this.client;
125
+ const client = new Client({
126
+ name: "remote-link-cli-core",
127
+ version: VERSION
128
+ });
129
+ const command = process.platform === "win32" ? "npx.cmd" : "npx";
130
+ const transport = new StdioClientTransport({
131
+ command,
132
+ args: ["-y", "@wonderwhy-er/desktop-commander@latest"],
133
+ stderr: "inherit"
134
+ });
135
+ await client.connect(transport);
136
+ this.client = client;
137
+ return client;
138
+ }
139
+ async tools() {
140
+ const list = await (await this.connect()).listTools();
141
+ const allowed = this.mode === "developer" ? DEVELOPER_TOOLS : SAFE_TOOLS;
142
+ return list.tools.filter((tool) => allowed.has(tool.name));
143
+ }
144
+ async call(name, args) {
145
+ const allowed = this.mode === "developer" ? DEVELOPER_TOOLS : SAFE_TOOLS;
146
+ if (!allowed.has(name)) {
147
+ throw new Error("Tool blocked by local permission mode: " + name);
148
+ }
149
+ return (await this.connect()).callTool({
150
+ name,
151
+ arguments: args
152
+ });
153
+ }
154
+ async close() {
155
+ if (!this.client) return;
156
+ await this.client.close();
157
+ this.client = null;
158
+ }
159
+ };
160
+ async function connectAgent(config) {
161
+ const core = new ExecutionCore(config.mode);
162
+ const tools = await core.tools();
163
+ const wsUrl = new URL("/agent", config.origin.replace(/^http/, "ws"));
164
+ let stopped = false;
165
+ let backoff = 1e3;
166
+ const shutdown = async () => {
167
+ stopped = true;
168
+ await core.close().catch(() => void 0);
169
+ process.exit(0);
170
+ };
171
+ process.on("SIGINT", shutdown);
172
+ process.on("SIGTERM", shutdown);
173
+ while (!stopped) {
174
+ const ws = new WebSocket(wsUrl, {
175
+ headers: {
176
+ Authorization: "Bearer " + config.deviceToken
177
+ }
178
+ });
179
+ try {
180
+ await new Promise((resolve, reject) => {
181
+ ws.once("open", resolve);
182
+ ws.once("error", reject);
183
+ });
184
+ backoff = 1e3;
185
+ ws.send(
186
+ JSON.stringify({
187
+ type: "hello",
188
+ device: {
189
+ id: config.deviceId,
190
+ name: config.deviceName,
191
+ platform: process.platform,
192
+ arch: process.arch,
193
+ hostname: os.hostname(),
194
+ agentVersion: VERSION
195
+ },
196
+ tools: tools.map((tool) => tool.name)
197
+ })
198
+ );
199
+ process.stdout.write(
200
+ "\u2713 Connected as " + config.deviceName + " (" + config.mode + " mode)\n"
201
+ );
202
+ process.stdout.write(" " + config.origin + "\n");
203
+ process.stdout.write("Press Ctrl+C to disconnect.\n");
204
+ await new Promise((resolve) => {
205
+ ws.on("message", async (raw) => {
206
+ let message;
207
+ try {
208
+ message = JSON.parse(raw.toString());
209
+ } catch {
210
+ return;
211
+ }
212
+ if (message.type !== "call" || !message.id || !message.tool) {
213
+ return;
214
+ }
215
+ try {
216
+ const result = await core.call(
217
+ message.tool,
218
+ message.arguments || {}
219
+ );
220
+ ws.send(
221
+ JSON.stringify({
222
+ type: "result",
223
+ id: message.id,
224
+ result
225
+ })
226
+ );
227
+ } catch (error) {
228
+ ws.send(
229
+ JSON.stringify({
230
+ type: "result",
231
+ id: message.id,
232
+ error: error instanceof Error ? error.message : String(error)
233
+ })
234
+ );
235
+ }
236
+ });
237
+ ws.once("close", resolve);
238
+ ws.once("error", resolve);
239
+ });
240
+ } catch (error) {
241
+ process.stderr.write(
242
+ "Remote Link connection failed: " + (error instanceof Error ? error.message : String(error)) + "\n"
243
+ );
244
+ } finally {
245
+ ws.removeAllListeners();
246
+ ws.close();
247
+ }
248
+ if (!stopped) {
249
+ process.stdout.write(
250
+ "Disconnected. Reconnecting in " + Math.round(backoff / 1e3) + "s...\n"
251
+ );
252
+ await sleep(backoff);
253
+ backoff = Math.min(backoff * 2, 3e4);
254
+ }
255
+ }
256
+ }
257
+ async function main() {
258
+ if (argFlag("--help") || argFlag("-h")) {
259
+ process.stdout.write(
260
+ [
261
+ "Remote Link",
262
+ "",
263
+ "Usage:",
264
+ " npx --yes --package=github:yaohuangguan/remote-link remote-link",
265
+ "",
266
+ "After the npm release:",
267
+ " npx remotelink@latest",
268
+ "",
269
+ "Options:",
270
+ " --safe Read-only local capability mode",
271
+ " --developer Read/write/shell mode (default)",
272
+ " --reset Remove this computer's saved pairing",
273
+ " --version Print CLI version",
274
+ " --help Show this help",
275
+ ""
276
+ ].join("\n")
277
+ );
278
+ return;
279
+ }
280
+ if (argFlag("--version") || argFlag("-v")) {
281
+ process.stdout.write(VERSION + "\n");
282
+ return;
283
+ }
284
+ if (argFlag("--reset")) {
285
+ await resetConfig();
286
+ return;
287
+ }
288
+ const origin = process.env.REMOTE_LINK_ORIGIN || process.argv.find((value) => value.startsWith("--origin="))?.slice(9) || DEFAULT_ORIGIN;
289
+ let config = await readConfig();
290
+ if (!config) {
291
+ config = await pair(origin, selectedMode());
292
+ } else if (argFlag("--safe") || argFlag("--developer")) {
293
+ config.mode = selectedMode();
294
+ await writeConfig(config);
295
+ }
296
+ await connectAgent(config);
297
+ }
298
+ main().catch((error) => {
299
+ process.stderr.write(
300
+ "\nRemote Link error: " + (error instanceof Error ? error.message : String(error)) + "\n"
301
+ );
302
+ process.exit(1);
303
+ });
304
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport os from \"node:os\";\r\nimport path from \"node:path\";\r\nimport fs from \"node:fs/promises\";\r\nimport { spawn } from \"node:child_process\";\r\nimport process from \"node:process\";\r\nimport { Client } from \"@modelcontextprotocol/client\";\r\nimport { StdioClientTransport } from \"@modelcontextprotocol/client/stdio\";\r\nimport WebSocket from \"ws\";\r\n\r\nconst VERSION = \"0.2.0\";\r\nconst DEFAULT_ORIGIN = \"https://remote.samyao.me\";\r\nconst CONFIG_DIR = path.join(os.homedir(), \".remote-link\");\r\nconst CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\r\n\r\ntype Mode = \"safe\" | \"developer\";\r\n\r\ntype SavedConfig = {\r\n deviceId: string;\r\n deviceToken: string;\r\n deviceName: string;\r\n origin: string;\r\n mode: Mode;\r\n};\r\n\r\ntype PairingStart = {\r\n device_code: string;\r\n device_secret: string;\r\n user_code: string;\r\n verification_uri: string;\r\n verification_uri_complete: string;\r\n expires_in: number;\r\n interval: number;\r\n};\r\n\r\ntype PairingToken = {\r\n device_id: string;\r\n device_token: string;\r\n relay_url: string;\r\n};\r\n\r\nconst SAFE_TOOLS = new Set([\r\n \"list_directory\",\r\n \"read_file\",\r\n \"get_file_info\",\r\n \"list_processes\",\r\n]);\r\n\r\nconst DEVELOPER_TOOLS = new Set([\r\n ...SAFE_TOOLS,\r\n \"start_process\",\r\n \"write_file\",\r\n \"edit_block\",\r\n]);\r\n\r\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\r\n\r\nfunction argFlag(name: string) {\r\n return process.argv.includes(name);\r\n}\r\n\r\nfunction selectedMode(): Mode {\r\n if (argFlag(\"--safe\")) return \"safe\";\r\n return \"developer\";\r\n}\r\n\r\nasync function readConfig(): Promise<SavedConfig | null> {\r\n try {\r\n return JSON.parse(await fs.readFile(CONFIG_PATH, \"utf8\")) as SavedConfig;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nasync function writeConfig(config: SavedConfig) {\r\n await fs.mkdir(CONFIG_DIR, { recursive: true });\r\n await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2) + \"\\n\", {\r\n mode: 0o600,\r\n });\r\n}\r\n\r\nasync function resetConfig() {\r\n await fs.rm(CONFIG_PATH, { force: true });\r\n process.stdout.write(\"Remote Link device credentials removed.\\n\");\r\n}\r\n\r\nfunction openBrowser(url: string) {\r\n const command =\r\n process.platform === \"win32\"\r\n ? \"cmd\"\r\n : process.platform === \"darwin\"\r\n ? \"open\"\r\n : \"xdg-open\";\r\n const args =\r\n process.platform === \"win32\"\r\n ? [\"/c\", \"start\", \"\", url]\r\n : [url];\r\n\r\n const child = spawn(command, args, {\r\n detached: true,\r\n stdio: \"ignore\",\r\n });\r\n child.unref();\r\n}\r\n\r\nasync function pair(origin: string, mode: Mode): Promise<SavedConfig> {\r\n const deviceName = os.hostname();\r\n const response = await fetch(origin + \"/api/device/start\", {\r\n method: \"POST\",\r\n headers: { \"content-type\": \"application/json\" },\r\n body: JSON.stringify({\r\n device_name: deviceName,\r\n platform: process.platform,\r\n arch: process.arch,\r\n hostname: os.hostname(),\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(\"Could not start device pairing: \" + response.status);\r\n }\r\n\r\n const pairing = (await response.json()) as PairingStart;\r\n\r\n process.stdout.write(\"\\nRemote Link\\n\\n\");\r\n process.stdout.write(\"Pair this computer in your browser.\\n\\n\");\r\n process.stdout.write(\" \" + pairing.user_code + \"\\n\\n\");\r\n process.stdout.write(pairing.verification_uri_complete + \"\\n\\n\");\r\n process.stdout.write(\"Opening browser...\\n\");\r\n openBrowser(pairing.verification_uri_complete);\r\n\r\n const startedAt = Date.now();\r\n while (Date.now() - startedAt < pairing.expires_in * 1000) {\r\n await sleep(Math.max(pairing.interval, 2) * 1000);\r\n\r\n const tokenResponse = await fetch(origin + \"/api/device/token\", {\r\n method: \"POST\",\r\n headers: { \"content-type\": \"application/json\" },\r\n body: JSON.stringify({\r\n device_code: pairing.device_code,\r\n device_secret: pairing.device_secret,\r\n }),\r\n });\r\n\r\n if (tokenResponse.status === 428) {\r\n process.stdout.write(\".\");\r\n continue;\r\n }\r\n\r\n if (!tokenResponse.ok) {\r\n const body = await tokenResponse.text();\r\n throw new Error(\"Pairing failed: \" + body);\r\n }\r\n\r\n const result = (await tokenResponse.json()) as PairingToken;\r\n process.stdout.write(\"\\n\\n✓ Device authorized\\n\");\r\n\r\n const config: SavedConfig = {\r\n deviceId: result.device_id,\r\n deviceToken: result.device_token,\r\n deviceName,\r\n origin,\r\n mode,\r\n };\r\n await writeConfig(config);\r\n return config;\r\n }\r\n\r\n throw new Error(\"Pairing expired. Run Remote Link again to retry.\");\r\n}\r\n\r\nclass ExecutionCore {\r\n private client: Client | null = null;\r\n\r\n constructor(private readonly mode: Mode) {}\r\n\r\n async connect() {\r\n if (this.client) return this.client;\r\n\r\n const client = new Client({\r\n name: \"remote-link-cli-core\",\r\n version: VERSION,\r\n });\r\n\r\n const command = process.platform === \"win32\" ? \"npx.cmd\" : \"npx\";\r\n const transport = new StdioClientTransport({\r\n command,\r\n args: [\"-y\", \"@wonderwhy-er/desktop-commander@latest\"],\r\n stderr: \"inherit\",\r\n });\r\n\r\n await client.connect(transport);\r\n this.client = client;\r\n return client;\r\n }\r\n\r\n async tools() {\r\n const list = await (await this.connect()).listTools();\r\n const allowed = this.mode === \"developer\" ? DEVELOPER_TOOLS : SAFE_TOOLS;\r\n return list.tools.filter((tool) => allowed.has(tool.name));\r\n }\r\n\r\n async call(name: string, args: Record<string, unknown>) {\r\n const allowed = this.mode === \"developer\" ? DEVELOPER_TOOLS : SAFE_TOOLS;\r\n if (!allowed.has(name)) {\r\n throw new Error(\"Tool blocked by local permission mode: \" + name);\r\n }\r\n\r\n return (await this.connect()).callTool({\r\n name,\r\n arguments: args,\r\n });\r\n }\r\n\r\n async close() {\r\n if (!this.client) return;\r\n await this.client.close();\r\n this.client = null;\r\n }\r\n}\r\n\r\nasync function connectAgent(config: SavedConfig) {\r\n const core = new ExecutionCore(config.mode);\r\n const tools = await core.tools();\r\n const wsUrl = new URL(\"/agent\", config.origin.replace(/^http/, \"ws\"));\r\n\r\n let stopped = false;\r\n let backoff = 1000;\r\n\r\n const shutdown = async () => {\r\n stopped = true;\r\n await core.close().catch(() => undefined);\r\n process.exit(0);\r\n };\r\n process.on(\"SIGINT\", shutdown);\r\n process.on(\"SIGTERM\", shutdown);\r\n\r\n while (!stopped) {\r\n const ws = new WebSocket(wsUrl, {\r\n headers: {\r\n Authorization: \"Bearer \" + config.deviceToken,\r\n },\r\n });\r\n\r\n try {\r\n await new Promise<void>((resolve, reject) => {\r\n ws.once(\"open\", resolve);\r\n ws.once(\"error\", reject);\r\n });\r\n\r\n backoff = 1000;\r\n ws.send(\r\n JSON.stringify({\r\n type: \"hello\",\r\n device: {\r\n id: config.deviceId,\r\n name: config.deviceName,\r\n platform: process.platform,\r\n arch: process.arch,\r\n hostname: os.hostname(),\r\n agentVersion: VERSION,\r\n },\r\n tools: tools.map((tool) => tool.name),\r\n }),\r\n );\r\n\r\n process.stdout.write(\r\n \"✓ Connected as \" +\r\n config.deviceName +\r\n \" (\" +\r\n config.mode +\r\n \" mode)\\n\",\r\n );\r\n process.stdout.write(\" \" + config.origin + \"\\n\");\r\n process.stdout.write(\"Press Ctrl+C to disconnect.\\n\");\r\n\r\n await new Promise<void>((resolve) => {\r\n ws.on(\"message\", async (raw) => {\r\n let message: {\r\n type?: string;\r\n id?: string;\r\n tool?: string;\r\n arguments?: Record<string, unknown>;\r\n };\r\n\r\n try {\r\n message = JSON.parse(raw.toString());\r\n } catch {\r\n return;\r\n }\r\n\r\n if (\r\n message.type !== \"call\" ||\r\n !message.id ||\r\n !message.tool\r\n ) {\r\n return;\r\n }\r\n\r\n try {\r\n const result = await core.call(\r\n message.tool,\r\n message.arguments || {},\r\n );\r\n ws.send(\r\n JSON.stringify({\r\n type: \"result\",\r\n id: message.id,\r\n result,\r\n }),\r\n );\r\n } catch (error) {\r\n ws.send(\r\n JSON.stringify({\r\n type: \"result\",\r\n id: message.id,\r\n error: error instanceof Error ? error.message : String(error),\r\n }),\r\n );\r\n }\r\n });\r\n\r\n ws.once(\"close\", resolve);\r\n ws.once(\"error\", resolve);\r\n });\r\n } catch (error) {\r\n process.stderr.write(\r\n \"Remote Link connection failed: \" +\r\n (error instanceof Error ? error.message : String(error)) +\r\n \"\\n\",\r\n );\r\n } finally {\r\n ws.removeAllListeners();\r\n ws.close();\r\n }\r\n\r\n if (!stopped) {\r\n process.stdout.write(\r\n \"Disconnected. Reconnecting in \" + Math.round(backoff / 1000) + \"s...\\n\",\r\n );\r\n await sleep(backoff);\r\n backoff = Math.min(backoff * 2, 30_000);\r\n }\r\n }\r\n}\r\n\r\nasync function main() {\r\n if (argFlag(\"--help\") || argFlag(\"-h\")) {\r\n process.stdout.write(\r\n [\r\n \"Remote Link\",\r\n \"\",\r\n \"Usage:\",\r\n \" npx --yes --package=github:yaohuangguan/remote-link remote-link\",\r\n \"\",\r\n \"After the npm release:\",\r\n \" npx remotelink@latest\",\r\n \"\",\r\n \"Options:\",\r\n \" --safe Read-only local capability mode\",\r\n \" --developer Read/write/shell mode (default)\",\r\n \" --reset Remove this computer's saved pairing\",\r\n \" --version Print CLI version\",\r\n \" --help Show this help\",\r\n \"\",\r\n ].join(\"\\n\"),\r\n );\r\n return;\r\n }\r\n\r\n if (argFlag(\"--version\") || argFlag(\"-v\")) {\r\n process.stdout.write(VERSION + \"\\n\");\r\n return;\r\n }\r\n\r\n if (argFlag(\"--reset\")) {\r\n await resetConfig();\r\n return;\r\n }\r\n\r\n const origin =\r\n process.env.REMOTE_LINK_ORIGIN ||\r\n process.argv.find((value) => value.startsWith(\"--origin=\"))?.slice(9) ||\r\n DEFAULT_ORIGIN;\r\n\r\n let config = await readConfig();\r\n if (!config) {\r\n config = await pair(origin, selectedMode());\r\n } else if (argFlag(\"--safe\") || argFlag(\"--developer\")) {\r\n config.mode = selectedMode();\r\n await writeConfig(config);\r\n }\r\n\r\n await connectAgent(config);\r\n}\r\n\r\nmain().catch((error) => {\r\n process.stderr.write(\r\n \"\\nRemote Link error: \" +\r\n (error instanceof Error ? error.message : String(error)) +\r\n \"\\n\",\r\n );\r\n process.exit(1);\r\n});\r\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "remotelink",
3
+ "version": "0.2.0",
4
+ "description": "Connect a computer to Remote Link with one command.",
5
+ "type": "module",
6
+ "bin": {
7
+ "remotelink": "dist/index.js",
8
+ "remote-link": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format esm --platform node --target node20 --sourcemap",
18
+ "typecheck": "tsc --noEmit",
19
+ "prepublishOnly": "pnpm build"
20
+ },
21
+ "dependencies": {
22
+ "@modelcontextprotocol/client": "^2.1.0",
23
+ "ws": "^8.18.3"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^24.13.6",
27
+ "@types/ws": "^8.18.1",
28
+ "tsup": "^8.5.0",
29
+ "typescript": "^6.0.3"
30
+ }
31
+ }