remotelink 0.2.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +98 -0
- package/dist/index.js +88 -30
- package/dist/index.js.map +1 -1
- package/package.json +53 -31
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Remote Arc Proprietary Source License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Remote Arc. All rights reserved.
|
|
4
|
+
|
|
5
|
+
The remotelink package is distributed for use with Remote Arc. You may install
|
|
6
|
+
and execute the officially distributed package for its intended purpose.
|
|
7
|
+
|
|
8
|
+
You may not, except where required by law or expressly authorized in writing,
|
|
9
|
+
modify, redistribute, sublicense, sell, white-label, or create derivative
|
|
10
|
+
products or competing services from this package or substantial portions of it.
|
|
11
|
+
|
|
12
|
+
Third-party dependencies remain governed by their respective licenses.
|
|
13
|
+
|
|
14
|
+
Historical versions previously distributed under the MIT License remain
|
|
15
|
+
subject to the MIT terms that applied to those specific versions.
|
|
16
|
+
|
|
17
|
+
For additional permissions or commercial licensing:
|
|
18
|
+
https://remotearc.app/support
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# remotelink
|
|
2
|
+
|
|
3
|
+
Connect a Windows, macOS, or Linux computer to **Remote Arc** with one command.
|
|
4
|
+
|
|
5
|
+
Remote Arc lets authorized AI clients such as ChatGPT, Claude, and Codex reach
|
|
6
|
+
the computers you explicitly pair through a secure outbound connection.
|
|
7
|
+
|
|
8
|
+
## Quick start
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx remotelink
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
On first run, `remotelink`:
|
|
15
|
+
|
|
16
|
+
1. creates a short-lived pairing request;
|
|
17
|
+
2. opens the Remote Arc pairing page in your browser;
|
|
18
|
+
3. asks you to sign in and approve the device;
|
|
19
|
+
4. stores a device credential locally in `~/.remotearc/config.json`;
|
|
20
|
+
5. connects the computer to Remote Arc over an outbound WebSocket.
|
|
21
|
+
|
|
22
|
+
No public IP, VPN, router port forwarding, repository clone, or manual token
|
|
23
|
+
copy is required.
|
|
24
|
+
|
|
25
|
+
## Connect your AI client
|
|
26
|
+
|
|
27
|
+
Remote MCP endpoint:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
https://remotearc.app/mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Website and device dashboard:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
https://remotearc.app
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Commands
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
npx remotelink Connect using developer mode
|
|
43
|
+
npx remotelink --safe Read-only capability mode
|
|
44
|
+
npx remotelink --developer Read/write/shell capability mode
|
|
45
|
+
npx remotelink --reset Remove local pairing credentials
|
|
46
|
+
npx remotelink --version Show the CLI version
|
|
47
|
+
npx remotelink --help Show help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The package also exposes the aliases `remote-link` and `remote-arc`.
|
|
51
|
+
|
|
52
|
+
## Local capability model
|
|
53
|
+
|
|
54
|
+
Safe mode exposes read-oriented capabilities such as directory listing,
|
|
55
|
+
file reading, file metadata, and process listing.
|
|
56
|
+
|
|
57
|
+
Developer mode additionally allows process execution and selected file
|
|
58
|
+
modification capabilities.
|
|
59
|
+
|
|
60
|
+
The connected device advertises its actual tool set to Remote Arc. The relay
|
|
61
|
+
will not forward a tool the device did not advertise.
|
|
62
|
+
|
|
63
|
+
## Security
|
|
64
|
+
|
|
65
|
+
- Each computer receives its own revocable credential.
|
|
66
|
+
- Raw device credentials are not stored in the hosted database.
|
|
67
|
+
- Connections are initiated outbound from your computer.
|
|
68
|
+
- MCP access uses OAuth 2.1 + PKCE.
|
|
69
|
+
- Device access is scoped to the authenticated Remote Arc account.
|
|
70
|
+
- You can revoke paired devices from the Remote Arc dashboard.
|
|
71
|
+
|
|
72
|
+
Remote computer control can modify files and execute commands. Review the
|
|
73
|
+
permissions you grant and use developer mode only on computers you control.
|
|
74
|
+
|
|
75
|
+
## Requirements
|
|
76
|
+
|
|
77
|
+
- Node.js 20 or later
|
|
78
|
+
- Windows, macOS, or Linux
|
|
79
|
+
|
|
80
|
+
## Product vs package name
|
|
81
|
+
|
|
82
|
+
**Remote Arc** is the product name.
|
|
83
|
+
|
|
84
|
+
`remotelink` is the npm package and CLI command.
|
|
85
|
+
|
|
86
|
+
## Support
|
|
87
|
+
|
|
88
|
+
- Website: https://remotearc.app
|
|
89
|
+
- Support: https://remotearc.app/support
|
|
90
|
+
- GitHub: https://github.com/yaohuangguan/remote-arc
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
The Remote Arc implementation is source-available under the **Remote Arc
|
|
95
|
+
Proprietary Source License**. It is not MIT-licensed for new releases.
|
|
96
|
+
|
|
97
|
+
Historical revisions that were previously released under MIT remain governed
|
|
98
|
+
by the MIT terms that applied to those revisions.
|
package/dist/index.js
CHANGED
|
@@ -9,10 +9,11 @@ import process from "process";
|
|
|
9
9
|
import { Client } from "@modelcontextprotocol/client";
|
|
10
10
|
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
11
11
|
import WebSocket from "ws";
|
|
12
|
-
var VERSION = "0.2
|
|
13
|
-
var DEFAULT_ORIGIN = "https://
|
|
14
|
-
var CONFIG_DIR = path.join(os.homedir(), ".
|
|
12
|
+
var VERSION = "0.3.2";
|
|
13
|
+
var DEFAULT_ORIGIN = "https://remotearc.app";
|
|
14
|
+
var CONFIG_DIR = path.join(os.homedir(), ".remotearc");
|
|
15
15
|
var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
16
|
+
var LEGACY_CONFIG_PATH = path.join(os.homedir(), ".remote-link", "config.json");
|
|
16
17
|
var SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
17
18
|
"list_directory",
|
|
18
19
|
"read_file",
|
|
@@ -26,6 +27,32 @@ var DEVELOPER_TOOLS = /* @__PURE__ */ new Set([
|
|
|
26
27
|
"edit_block"
|
|
27
28
|
]);
|
|
28
29
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
30
|
+
var supportsColor = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
31
|
+
var paint = (code, value) => supportsColor ? `\x1B[${code}m${value}\x1B[0m` : value;
|
|
32
|
+
var dim = (value) => paint("2", value);
|
|
33
|
+
var cyan = (value) => paint("36", value);
|
|
34
|
+
var green = (value) => paint("32", value);
|
|
35
|
+
var yellow = (value) => paint("33", value);
|
|
36
|
+
var red = (value) => paint("31", value);
|
|
37
|
+
var bold = (value) => paint("1", value);
|
|
38
|
+
function nowTime() {
|
|
39
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString([], {
|
|
40
|
+
hour: "2-digit",
|
|
41
|
+
minute: "2-digit",
|
|
42
|
+
second: "2-digit",
|
|
43
|
+
hour12: false
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function logLine(level, message) {
|
|
47
|
+
const icon = level === "success" ? green("\u2713") : level === "warn" ? yellow("!") : level === "error" ? red("\xD7") : level === "event" ? cyan("\u2192") : cyan("\u2022");
|
|
48
|
+
process.stdout.write(`${dim(nowTime())} ${icon} ${message}
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
function banner() {
|
|
52
|
+
process.stdout.write("\n");
|
|
53
|
+
process.stdout.write(bold("Remote Arc") + " " + dim(`v${VERSION}`) + "\n");
|
|
54
|
+
process.stdout.write(dim("Secure remote MCP bridge") + "\n\n");
|
|
55
|
+
}
|
|
29
56
|
function argFlag(name) {
|
|
30
57
|
return process.argv.includes(name);
|
|
31
58
|
}
|
|
@@ -37,7 +64,16 @@ async function readConfig() {
|
|
|
37
64
|
try {
|
|
38
65
|
return JSON.parse(await fs.readFile(CONFIG_PATH, "utf8"));
|
|
39
66
|
} catch {
|
|
40
|
-
|
|
67
|
+
try {
|
|
68
|
+
const legacy = JSON.parse(
|
|
69
|
+
await fs.readFile(LEGACY_CONFIG_PATH, "utf8")
|
|
70
|
+
);
|
|
71
|
+
await writeConfig(legacy);
|
|
72
|
+
logLine("success", "Migrated existing Remote Link pairing to Remote Arc.");
|
|
73
|
+
return legacy;
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
41
77
|
}
|
|
42
78
|
}
|
|
43
79
|
async function writeConfig(config) {
|
|
@@ -47,8 +83,11 @@ async function writeConfig(config) {
|
|
|
47
83
|
});
|
|
48
84
|
}
|
|
49
85
|
async function resetConfig() {
|
|
50
|
-
await
|
|
51
|
-
|
|
86
|
+
await Promise.all([
|
|
87
|
+
fs.rm(CONFIG_PATH, { force: true }),
|
|
88
|
+
fs.rm(LEGACY_CONFIG_PATH, { force: true })
|
|
89
|
+
]);
|
|
90
|
+
logLine("success", "Removed saved device credentials.");
|
|
52
91
|
}
|
|
53
92
|
function openBrowser(url) {
|
|
54
93
|
const command = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
@@ -75,12 +114,13 @@ async function pair(origin, mode) {
|
|
|
75
114
|
throw new Error("Could not start device pairing: " + response.status);
|
|
76
115
|
}
|
|
77
116
|
const pairing = await response.json();
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
process.stdout.write(pairing.
|
|
82
|
-
process.stdout.write("
|
|
117
|
+
banner();
|
|
118
|
+
logLine("info", `Device: ${bold(deviceName)} \xB7 ${process.platform}/${process.arch} \xB7 ${mode} mode`);
|
|
119
|
+
logLine("event", "Pairing required \u2014 opening secure browser approval.");
|
|
120
|
+
process.stdout.write("\n Pairing code " + bold(cyan(pairing.user_code)) + "\n");
|
|
121
|
+
process.stdout.write(" " + dim(pairing.verification_uri_complete) + "\n\n");
|
|
83
122
|
openBrowser(pairing.verification_uri_complete);
|
|
123
|
+
logLine("info", "Waiting for authorization\u2026");
|
|
84
124
|
const startedAt = Date.now();
|
|
85
125
|
while (Date.now() - startedAt < pairing.expires_in * 1e3) {
|
|
86
126
|
await sleep(Math.max(pairing.interval, 2) * 1e3);
|
|
@@ -93,7 +133,7 @@ async function pair(origin, mode) {
|
|
|
93
133
|
})
|
|
94
134
|
});
|
|
95
135
|
if (tokenResponse.status === 428) {
|
|
96
|
-
process.stdout.write("
|
|
136
|
+
if (process.stdout.isTTY) process.stdout.write(dim("\xB7"));
|
|
97
137
|
continue;
|
|
98
138
|
}
|
|
99
139
|
if (!tokenResponse.ok) {
|
|
@@ -101,7 +141,8 @@ async function pair(origin, mode) {
|
|
|
101
141
|
throw new Error("Pairing failed: " + body);
|
|
102
142
|
}
|
|
103
143
|
const result = await tokenResponse.json();
|
|
104
|
-
process.stdout.write("\n
|
|
144
|
+
process.stdout.write("\n");
|
|
145
|
+
logLine("success", "Device authorized.");
|
|
105
146
|
const config = {
|
|
106
147
|
deviceId: result.device_id,
|
|
107
148
|
deviceToken: result.device_token,
|
|
@@ -112,7 +153,7 @@ async function pair(origin, mode) {
|
|
|
112
153
|
await writeConfig(config);
|
|
113
154
|
return config;
|
|
114
155
|
}
|
|
115
|
-
throw new Error("Pairing expired. Run Remote
|
|
156
|
+
throw new Error("Pairing expired. Run Remote Arc again to retry.");
|
|
116
157
|
}
|
|
117
158
|
var ExecutionCore = class {
|
|
118
159
|
constructor(mode) {
|
|
@@ -123,7 +164,7 @@ var ExecutionCore = class {
|
|
|
123
164
|
async connect() {
|
|
124
165
|
if (this.client) return this.client;
|
|
125
166
|
const client = new Client({
|
|
126
|
-
name: "
|
|
167
|
+
name: "remotearc-cli-core",
|
|
127
168
|
version: VERSION
|
|
128
169
|
});
|
|
129
170
|
const command = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
@@ -132,8 +173,10 @@ var ExecutionCore = class {
|
|
|
132
173
|
args: ["-y", "@wonderwhy-er/desktop-commander@latest"],
|
|
133
174
|
stderr: "inherit"
|
|
134
175
|
});
|
|
176
|
+
logLine("info", "Starting local execution core\u2026");
|
|
135
177
|
await client.connect(transport);
|
|
136
178
|
this.client = client;
|
|
179
|
+
logLine("success", "Local execution core ready.");
|
|
137
180
|
return client;
|
|
138
181
|
}
|
|
139
182
|
async tools() {
|
|
@@ -159,7 +202,12 @@ var ExecutionCore = class {
|
|
|
159
202
|
};
|
|
160
203
|
async function connectAgent(config) {
|
|
161
204
|
const core = new ExecutionCore(config.mode);
|
|
205
|
+
banner();
|
|
206
|
+
logLine("info", `Device: ${bold(config.deviceName)} \xB7 ${process.platform}/${process.arch}`);
|
|
207
|
+
logLine("info", `Permission profile: ${config.mode === "developer" ? yellow("developer") : green("safe")}`);
|
|
162
208
|
const tools = await core.tools();
|
|
209
|
+
logLine("success", `Local tools ready: ${tools.length} exposed`);
|
|
210
|
+
process.stdout.write(" " + dim(tools.map((tool) => tool.name).join(" \xB7 ")) + "\n");
|
|
163
211
|
const wsUrl = new URL("/agent", config.origin.replace(/^http/, "ws"));
|
|
164
212
|
let stopped = false;
|
|
165
213
|
let backoff = 1e3;
|
|
@@ -171,6 +219,7 @@ async function connectAgent(config) {
|
|
|
171
219
|
process.on("SIGINT", shutdown);
|
|
172
220
|
process.on("SIGTERM", shutdown);
|
|
173
221
|
while (!stopped) {
|
|
222
|
+
logLine("event", `Connecting relay ${dim(wsUrl.host)}\u2026`);
|
|
174
223
|
const ws = new WebSocket(wsUrl, {
|
|
175
224
|
headers: {
|
|
176
225
|
Authorization: "Bearer " + config.deviceToken
|
|
@@ -196,11 +245,10 @@ async function connectAgent(config) {
|
|
|
196
245
|
tools: tools.map((tool) => tool.name)
|
|
197
246
|
})
|
|
198
247
|
);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
);
|
|
202
|
-
process.stdout.write("
|
|
203
|
-
process.stdout.write("Press Ctrl+C to disconnect.\n");
|
|
248
|
+
logLine("success", `Relay connected \xB7 ${bold(config.deviceName)} \xB7 ${config.mode} mode`);
|
|
249
|
+
logLine("success", "Device presence published.");
|
|
250
|
+
process.stdout.write(" " + dim(config.origin) + "\n");
|
|
251
|
+
process.stdout.write(" " + dim("Ctrl+C to disconnect") + "\n\n");
|
|
204
252
|
await new Promise((resolve) => {
|
|
205
253
|
ws.on("message", async (raw) => {
|
|
206
254
|
let message;
|
|
@@ -212,11 +260,14 @@ async function connectAgent(config) {
|
|
|
212
260
|
if (message.type !== "call" || !message.id || !message.tool) {
|
|
213
261
|
return;
|
|
214
262
|
}
|
|
263
|
+
const callStarted = Date.now();
|
|
264
|
+
logLine("event", `tool.call ${bold(message.tool)} \xB7 ${dim(message.id.slice(0, 8))}`);
|
|
215
265
|
try {
|
|
216
266
|
const result = await core.call(
|
|
217
267
|
message.tool,
|
|
218
268
|
message.arguments || {}
|
|
219
269
|
);
|
|
270
|
+
logLine("success", `tool.done ${message.tool} \xB7 ${Date.now() - callStarted}ms`);
|
|
220
271
|
ws.send(
|
|
221
272
|
JSON.stringify({
|
|
222
273
|
type: "result",
|
|
@@ -225,6 +276,7 @@ async function connectAgent(config) {
|
|
|
225
276
|
})
|
|
226
277
|
);
|
|
227
278
|
} catch (error) {
|
|
279
|
+
logLine("error", `tool.fail ${message.tool} \xB7 ${error instanceof Error ? error.message : String(error)}`);
|
|
228
280
|
ws.send(
|
|
229
281
|
JSON.stringify({
|
|
230
282
|
type: "result",
|
|
@@ -238,16 +290,18 @@ async function connectAgent(config) {
|
|
|
238
290
|
ws.once("error", resolve);
|
|
239
291
|
});
|
|
240
292
|
} catch (error) {
|
|
241
|
-
|
|
242
|
-
"
|
|
293
|
+
logLine(
|
|
294
|
+
"error",
|
|
295
|
+
"Relay connection failed: " + (error instanceof Error ? error.message : String(error))
|
|
243
296
|
);
|
|
244
297
|
} finally {
|
|
245
298
|
ws.removeAllListeners();
|
|
246
299
|
ws.close();
|
|
247
300
|
}
|
|
248
301
|
if (!stopped) {
|
|
249
|
-
|
|
250
|
-
"
|
|
302
|
+
logLine(
|
|
303
|
+
"warn",
|
|
304
|
+
"Relay disconnected \xB7 retrying in " + Math.round(backoff / 1e3) + "s"
|
|
251
305
|
);
|
|
252
306
|
await sleep(backoff);
|
|
253
307
|
backoff = Math.min(backoff * 2, 3e4);
|
|
@@ -258,13 +312,13 @@ async function main() {
|
|
|
258
312
|
if (argFlag("--help") || argFlag("-h")) {
|
|
259
313
|
process.stdout.write(
|
|
260
314
|
[
|
|
261
|
-
"Remote
|
|
315
|
+
"Remote Arc",
|
|
262
316
|
"",
|
|
263
317
|
"Usage:",
|
|
264
|
-
" npx
|
|
318
|
+
" npx remotelink",
|
|
265
319
|
"",
|
|
266
320
|
"After the npm release:",
|
|
267
|
-
" npx remotelink
|
|
321
|
+
" npx remotelink",
|
|
268
322
|
"",
|
|
269
323
|
"Options:",
|
|
270
324
|
" --safe Read-only local capability mode",
|
|
@@ -285,7 +339,7 @@ async function main() {
|
|
|
285
339
|
await resetConfig();
|
|
286
340
|
return;
|
|
287
341
|
}
|
|
288
|
-
const origin = process.env.REMOTE_LINK_ORIGIN || process.argv.find((value) => value.startsWith("--origin="))?.slice(9) || DEFAULT_ORIGIN;
|
|
342
|
+
const origin = process.env.REMOTEARC_ORIGIN || process.env.REMOTE_LINK_ORIGIN || process.argv.find((value) => value.startsWith("--origin="))?.slice(9) || DEFAULT_ORIGIN;
|
|
289
343
|
let config = await readConfig();
|
|
290
344
|
if (!config) {
|
|
291
345
|
config = await pair(origin, selectedMode());
|
|
@@ -293,11 +347,15 @@ async function main() {
|
|
|
293
347
|
config.mode = selectedMode();
|
|
294
348
|
await writeConfig(config);
|
|
295
349
|
}
|
|
350
|
+
if (config) {
|
|
351
|
+
logLine("info", `Using paired device identity ${dim(config.deviceId.slice(0, 8))}\u2026`);
|
|
352
|
+
}
|
|
296
353
|
await connectAgent(config);
|
|
297
354
|
}
|
|
298
355
|
main().catch((error) => {
|
|
299
|
-
|
|
300
|
-
"
|
|
356
|
+
logLine(
|
|
357
|
+
"error",
|
|
358
|
+
"Fatal: " + (error instanceof Error ? error.message : String(error))
|
|
301
359
|
);
|
|
302
360
|
process.exit(1);
|
|
303
361
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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":";;;AACA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,aAAa;AACtB,OAAO,aAAa;AACpB,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC,OAAO,eAAe;AAEtB,IAAM,UAAU;AAChB,IAAM,iBAAiB;AACvB,IAAM,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,cAAc;AACzD,IAAM,cAAc,KAAK,KAAK,YAAY,aAAa;AA4BvD,IAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,kBAAkB,oBAAI,IAAI;AAAA,EAC9B,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAE9E,SAAS,QAAQ,MAAc;AAC7B,SAAO,QAAQ,KAAK,SAAS,IAAI;AACnC;AAEA,SAAS,eAAqB;AAC5B,MAAI,QAAQ,QAAQ,EAAG,QAAO;AAC9B,SAAO;AACT;AAEA,eAAe,aAA0C;AACvD,MAAI;AACF,WAAO,KAAK,MAAM,MAAM,GAAG,SAAS,aAAa,MAAM,CAAC;AAAA,EAC1D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,YAAY,QAAqB;AAC9C,QAAM,GAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,GAAG,UAAU,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM;AAAA,IACtE,MAAM;AAAA,EACR,CAAC;AACH;AAEA,eAAe,cAAc;AAC3B,QAAM,GAAG,GAAG,aAAa,EAAE,OAAO,KAAK,CAAC;AACxC,UAAQ,OAAO,MAAM,2CAA2C;AAClE;AAEA,SAAS,YAAY,KAAa;AAChC,QAAM,UACJ,QAAQ,aAAa,UACjB,QACA,QAAQ,aAAa,WACnB,SACA;AACR,QAAM,OACJ,QAAQ,aAAa,UACjB,CAAC,MAAM,SAAS,IAAI,GAAG,IACvB,CAAC,GAAG;AAEV,QAAM,QAAQ,MAAM,SAAS,MAAM;AAAA,IACjC,UAAU;AAAA,IACV,OAAO;AAAA,EACT,CAAC;AACD,QAAM,MAAM;AACd;AAEA,eAAe,KAAK,QAAgB,MAAkC;AACpE,QAAM,aAAa,GAAG,SAAS;AAC/B,QAAM,WAAW,MAAM,MAAM,SAAS,qBAAqB;AAAA,IACzD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU;AAAA,MACnB,aAAa;AAAA,MACb,UAAU,QAAQ;AAAA,MAClB,MAAM,QAAQ;AAAA,MACd,UAAU,GAAG,SAAS;AAAA,IACxB,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,qCAAqC,SAAS,MAAM;AAAA,EACtE;AAEA,QAAM,UAAW,MAAM,SAAS,KAAK;AAErC,UAAQ,OAAO,MAAM,mBAAmB;AACxC,UAAQ,OAAO,MAAM,yCAAyC;AAC9D,UAAQ,OAAO,MAAM,OAAO,QAAQ,YAAY,MAAM;AACtD,UAAQ,OAAO,MAAM,QAAQ,4BAA4B,MAAM;AAC/D,UAAQ,OAAO,MAAM,sBAAsB;AAC3C,cAAY,QAAQ,yBAAyB;AAE7C,QAAM,YAAY,KAAK,IAAI;AAC3B,SAAO,KAAK,IAAI,IAAI,YAAY,QAAQ,aAAa,KAAM;AACzD,UAAM,MAAM,KAAK,IAAI,QAAQ,UAAU,CAAC,IAAI,GAAI;AAEhD,UAAM,gBAAgB,MAAM,MAAM,SAAS,qBAAqB;AAAA,MAC9D,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU;AAAA,QACnB,aAAa,QAAQ;AAAA,QACrB,eAAe,QAAQ;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAED,QAAI,cAAc,WAAW,KAAK;AAChC,cAAQ,OAAO,MAAM,GAAG;AACxB;AAAA,IACF;AAEA,QAAI,CAAC,cAAc,IAAI;AACrB,YAAM,OAAO,MAAM,cAAc,KAAK;AACtC,YAAM,IAAI,MAAM,qBAAqB,IAAI;AAAA,IAC3C;AAEA,UAAM,SAAU,MAAM,cAAc,KAAK;AACzC,YAAQ,OAAO,MAAM,gCAA2B;AAEhD,UAAM,SAAsB;AAAA,MAC1B,UAAU,OAAO;AAAA,MACjB,aAAa,OAAO;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,UAAM,YAAY,MAAM;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,IAAI,MAAM,kDAAkD;AACpE;AAEA,IAAM,gBAAN,MAAoB;AAAA,EAGlB,YAA6B,MAAY;AAAZ;AAAA,EAAa;AAAA,EAAb;AAAA,EAFrB,SAAwB;AAAA,EAIhC,MAAM,UAAU;AACd,QAAI,KAAK,OAAQ,QAAO,KAAK;AAE7B,UAAM,SAAS,IAAI,OAAO;AAAA,MACxB,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAED,UAAM,UAAU,QAAQ,aAAa,UAAU,YAAY;AAC3D,UAAM,YAAY,IAAI,qBAAqB;AAAA,MACzC;AAAA,MACA,MAAM,CAAC,MAAM,wCAAwC;AAAA,MACrD,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,OAAO,QAAQ,SAAS;AAC9B,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ;AACZ,UAAM,OAAO,OAAO,MAAM,KAAK,QAAQ,GAAG,UAAU;AACpD,UAAM,UAAU,KAAK,SAAS,cAAc,kBAAkB;AAC9D,WAAO,KAAK,MAAM,OAAO,CAAC,SAAS,QAAQ,IAAI,KAAK,IAAI,CAAC;AAAA,EAC3D;AAAA,EAEA,MAAM,KAAK,MAAc,MAA+B;AACtD,UAAM,UAAU,KAAK,SAAS,cAAc,kBAAkB;AAC9D,QAAI,CAAC,QAAQ,IAAI,IAAI,GAAG;AACtB,YAAM,IAAI,MAAM,4CAA4C,IAAI;AAAA,IAClE;AAEA,YAAQ,MAAM,KAAK,QAAQ,GAAG,SAAS;AAAA,MACrC;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ;AACZ,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,KAAK,OAAO,MAAM;AACxB,SAAK,SAAS;AAAA,EAChB;AACF;AAEA,eAAe,aAAa,QAAqB;AAC/C,QAAM,OAAO,IAAI,cAAc,OAAO,IAAI;AAC1C,QAAM,QAAQ,MAAM,KAAK,MAAM;AAC/B,QAAM,QAAQ,IAAI,IAAI,UAAU,OAAO,OAAO,QAAQ,SAAS,IAAI,CAAC;AAEpE,MAAI,UAAU;AACd,MAAI,UAAU;AAEd,QAAM,WAAW,YAAY;AAC3B,cAAU;AACV,UAAM,KAAK,MAAM,EAAE,MAAM,MAAM,MAAS;AACxC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAE9B,SAAO,CAAC,SAAS;AACf,UAAM,KAAK,IAAI,UAAU,OAAO;AAAA,MAC9B,SAAS;AAAA,QACP,eAAe,YAAY,OAAO;AAAA,MACpC;AAAA,IACF,CAAC;AAED,QAAI;AACF,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAG,KAAK,QAAQ,OAAO;AACvB,WAAG,KAAK,SAAS,MAAM;AAAA,MACzB,CAAC;AAED,gBAAU;AACV,SAAG;AAAA,QACD,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN,QAAQ;AAAA,YACN,IAAI,OAAO;AAAA,YACX,MAAM,OAAO;AAAA,YACb,UAAU,QAAQ;AAAA,YAClB,MAAM,QAAQ;AAAA,YACd,UAAU,GAAG,SAAS;AAAA,YACtB,cAAc;AAAA,UAChB;AAAA,UACA,OAAO,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,QACtC,CAAC;AAAA,MACH;AAEA,cAAQ,OAAO;AAAA,QACb,yBACE,OAAO,aACP,OACA,OAAO,OACP;AAAA,MACJ;AACA,cAAQ,OAAO,MAAM,OAAO,OAAO,SAAS,IAAI;AAChD,cAAQ,OAAO,MAAM,+BAA+B;AAEpD,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,WAAG,GAAG,WAAW,OAAO,QAAQ;AAC9B,cAAI;AAOJ,cAAI;AACF,sBAAU,KAAK,MAAM,IAAI,SAAS,CAAC;AAAA,UACrC,QAAQ;AACN;AAAA,UACF;AAEA,cACE,QAAQ,SAAS,UACjB,CAAC,QAAQ,MACT,CAAC,QAAQ,MACT;AACA;AAAA,UACF;AAEA,cAAI;AACF,kBAAM,SAAS,MAAM,KAAK;AAAA,cACxB,QAAQ;AAAA,cACR,QAAQ,aAAa,CAAC;AAAA,YACxB;AACA,eAAG;AAAA,cACD,KAAK,UAAU;AAAA,gBACb,MAAM;AAAA,gBACN,IAAI,QAAQ;AAAA,gBACZ;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF,SAAS,OAAO;AACd,eAAG;AAAA,cACD,KAAK,UAAU;AAAA,gBACb,MAAM;AAAA,gBACN,IAAI,QAAQ;AAAA,gBACZ,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,cAC9D,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,CAAC;AAED,WAAG,KAAK,SAAS,OAAO;AACxB,WAAG,KAAK,SAAS,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,OAAO;AAAA,QACb,qCACG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACtD;AAAA,MACJ;AAAA,IACF,UAAE;AACA,SAAG,mBAAmB;AACtB,SAAG,MAAM;AAAA,IACX;AAEA,QAAI,CAAC,SAAS;AACZ,cAAQ,OAAO;AAAA,QACb,mCAAmC,KAAK,MAAM,UAAU,GAAI,IAAI;AAAA,MAClE;AACA,YAAM,MAAM,OAAO;AACnB,gBAAU,KAAK,IAAI,UAAU,GAAG,GAAM;AAAA,IACxC;AAAA,EACF;AACF;AAEA,eAAe,OAAO;AACpB,MAAI,QAAQ,QAAQ,KAAK,QAAQ,IAAI,GAAG;AACtC,YAAQ,OAAO;AAAA,MACb;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AACA;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,KAAK,QAAQ,IAAI,GAAG;AACzC,YAAQ,OAAO,MAAM,UAAU,IAAI;AACnC;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,YAAY;AAClB;AAAA,EACF;AAEA,QAAM,SACJ,QAAQ,IAAI,sBACZ,QAAQ,KAAK,KAAK,CAAC,UAAU,MAAM,WAAW,WAAW,CAAC,GAAG,MAAM,CAAC,KACpE;AAEF,MAAI,SAAS,MAAM,WAAW;AAC9B,MAAI,CAAC,QAAQ;AACX,aAAS,MAAM,KAAK,QAAQ,aAAa,CAAC;AAAA,EAC5C,WAAW,QAAQ,QAAQ,KAAK,QAAQ,aAAa,GAAG;AACtD,WAAO,OAAO,aAAa;AAC3B,UAAM,YAAY,MAAM;AAAA,EAC1B;AAEA,QAAM,aAAa,MAAM;AAC3B;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,OAAO;AAAA,IACb,2BACG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACtD;AAAA,EACJ;AACA,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport { spawn } from \"node:child_process\";\nimport process from \"node:process\";\nimport { Client } from \"@modelcontextprotocol/client\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/client/stdio\";\nimport WebSocket from \"ws\";\n\nconst VERSION = \"0.3.2\";\nconst DEFAULT_ORIGIN = \"https://remotearc.app\";\nconst CONFIG_DIR = path.join(os.homedir(), \".remotearc\");\nconst CONFIG_PATH = path.join(CONFIG_DIR, \"config.json\");\nconst LEGACY_CONFIG_PATH = path.join(os.homedir(), \".remote-link\", \"config.json\");\n\ntype Mode = \"safe\" | \"developer\";\n\ntype SavedConfig = {\n deviceId: string;\n deviceToken: string;\n deviceName: string;\n origin: string;\n mode: Mode;\n};\n\ntype PairingStart = {\n device_code: string;\n device_secret: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n};\n\ntype PairingToken = {\n device_id: string;\n device_token: string;\n relay_url: string;\n};\n\nconst SAFE_TOOLS = new Set([\n \"list_directory\",\n \"read_file\",\n \"get_file_info\",\n \"list_processes\",\n]);\n\nconst DEVELOPER_TOOLS = new Set([\n ...SAFE_TOOLS,\n \"start_process\",\n \"write_file\",\n \"edit_block\",\n]);\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst supportsColor = process.stdout.isTTY && !process.env.NO_COLOR;\nconst paint = (code: string, value: string) =>\n supportsColor ? `\\x1b[${code}m${value}\\x1b[0m` : value;\nconst dim = (value: string) => paint(\"2\", value);\nconst cyan = (value: string) => paint(\"36\", value);\nconst green = (value: string) => paint(\"32\", value);\nconst yellow = (value: string) => paint(\"33\", value);\nconst red = (value: string) => paint(\"31\", value);\nconst bold = (value: string) => paint(\"1\", value);\n\nfunction nowTime() {\n return new Date().toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n });\n}\n\nfunction logLine(\n level: \"info\" | \"success\" | \"warn\" | \"error\" | \"event\",\n message: string,\n) {\n const icon =\n level === \"success\" ? green(\"✓\") :\n level === \"warn\" ? yellow(\"!\") :\n level === \"error\" ? red(\"×\") :\n level === \"event\" ? cyan(\"→\") :\n cyan(\"•\");\n process.stdout.write(`${dim(nowTime())} ${icon} ${message}\\n`);\n}\n\nfunction banner() {\n process.stdout.write(\"\\n\");\n process.stdout.write(bold(\"Remote Arc\") + \" \" + dim(`v${VERSION}`) + \"\\n\");\n process.stdout.write(dim(\"Secure remote MCP bridge\") + \"\\n\\n\");\n}\n\nfunction argFlag(name: string) {\n return process.argv.includes(name);\n}\n\nfunction selectedMode(): Mode {\n if (argFlag(\"--safe\")) return \"safe\";\n return \"developer\";\n}\n\nasync function readConfig(): Promise<SavedConfig | null> {\n try {\n return JSON.parse(await fs.readFile(CONFIG_PATH, \"utf8\")) as SavedConfig;\n } catch {\n try {\n const legacy = JSON.parse(\n await fs.readFile(LEGACY_CONFIG_PATH, \"utf8\"),\n ) as SavedConfig;\n await writeConfig(legacy);\n logLine(\"success\", \"Migrated existing Remote Link pairing to Remote Arc.\");\n return legacy;\n } catch {\n return null;\n }\n }\n}\n\nasync function writeConfig(config: SavedConfig) {\n await fs.mkdir(CONFIG_DIR, { recursive: true });\n await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\nasync function resetConfig() {\n await Promise.all([\n fs.rm(CONFIG_PATH, { force: true }),\n fs.rm(LEGACY_CONFIG_PATH, { force: true }),\n ]);\n logLine(\"success\", \"Removed saved device credentials.\");\n}\n\nfunction openBrowser(url: string) {\n const command =\n process.platform === \"win32\"\n ? \"cmd\"\n : process.platform === \"darwin\"\n ? \"open\"\n : \"xdg-open\";\n const args =\n process.platform === \"win32\"\n ? [\"/c\", \"start\", \"\", url]\n : [url];\n\n const child = spawn(command, args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n}\n\nasync function pair(origin: string, mode: Mode): Promise<SavedConfig> {\n const deviceName = os.hostname();\n const response = await fetch(origin + \"/api/device/start\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n device_name: deviceName,\n platform: process.platform,\n arch: process.arch,\n hostname: os.hostname(),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\"Could not start device pairing: \" + response.status);\n }\n\n const pairing = (await response.json()) as PairingStart;\n\n banner();\n logLine(\"info\", `Device: ${bold(deviceName)} · ${process.platform}/${process.arch} · ${mode} mode`);\n logLine(\"event\", \"Pairing required — opening secure browser approval.\");\n process.stdout.write(\"\\n Pairing code \" + bold(cyan(pairing.user_code)) + \"\\n\");\n process.stdout.write(\" \" + dim(pairing.verification_uri_complete) + \"\\n\\n\");\n openBrowser(pairing.verification_uri_complete);\n logLine(\"info\", \"Waiting for authorization…\");\n\n const startedAt = Date.now();\n while (Date.now() - startedAt < pairing.expires_in * 1000) {\n await sleep(Math.max(pairing.interval, 2) * 1000);\n\n const tokenResponse = await fetch(origin + \"/api/device/token\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n device_code: pairing.device_code,\n device_secret: pairing.device_secret,\n }),\n });\n\n if (tokenResponse.status === 428) {\n if (process.stdout.isTTY) process.stdout.write(dim(\"·\"));\n continue;\n }\n\n if (!tokenResponse.ok) {\n const body = await tokenResponse.text();\n throw new Error(\"Pairing failed: \" + body);\n }\n\n const result = (await tokenResponse.json()) as PairingToken;\n process.stdout.write(\"\\n\");\n logLine(\"success\", \"Device authorized.\");\n\n const config: SavedConfig = {\n deviceId: result.device_id,\n deviceToken: result.device_token,\n deviceName,\n origin,\n mode,\n };\n await writeConfig(config);\n return config;\n }\n\n throw new Error(\"Pairing expired. Run Remote Arc again to retry.\");\n}\n\nclass ExecutionCore {\n private client: Client | null = null;\n\n constructor(private readonly mode: Mode) {}\n\n async connect() {\n if (this.client) return this.client;\n\n const client = new Client({\n name: \"remotearc-cli-core\",\n version: VERSION,\n });\n\n const command = process.platform === \"win32\" ? \"npx.cmd\" : \"npx\";\n const transport = new StdioClientTransport({\n command,\n args: [\"-y\", \"@wonderwhy-er/desktop-commander@latest\"],\n stderr: \"inherit\",\n });\n\n logLine(\"info\", \"Starting local execution core…\");\n await client.connect(transport);\n this.client = client;\n logLine(\"success\", \"Local execution core ready.\");\n return client;\n }\n\n async tools() {\n const list = await (await this.connect()).listTools();\n const allowed = this.mode === \"developer\" ? DEVELOPER_TOOLS : SAFE_TOOLS;\n return list.tools.filter((tool) => allowed.has(tool.name));\n }\n\n async call(name: string, args: Record<string, unknown>) {\n const allowed = this.mode === \"developer\" ? DEVELOPER_TOOLS : SAFE_TOOLS;\n if (!allowed.has(name)) {\n throw new Error(\"Tool blocked by local permission mode: \" + name);\n }\n\n return (await this.connect()).callTool({\n name,\n arguments: args,\n });\n }\n\n async close() {\n if (!this.client) return;\n await this.client.close();\n this.client = null;\n }\n}\n\nasync function connectAgent(config: SavedConfig) {\n const core = new ExecutionCore(config.mode);\n banner();\n logLine(\"info\", `Device: ${bold(config.deviceName)} · ${process.platform}/${process.arch}`);\n logLine(\"info\", `Permission profile: ${config.mode === \"developer\" ? yellow(\"developer\") : green(\"safe\")}`);\n\n const tools = await core.tools();\n logLine(\"success\", `Local tools ready: ${tools.length} exposed`);\n process.stdout.write(\" \" + dim(tools.map((tool) => tool.name).join(\" · \")) + \"\\n\");\n const wsUrl = new URL(\"/agent\", config.origin.replace(/^http/, \"ws\"));\n\n let stopped = false;\n let backoff = 1000;\n\n const shutdown = async () => {\n stopped = true;\n await core.close().catch(() => undefined);\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n while (!stopped) {\n logLine(\"event\", `Connecting relay ${dim(wsUrl.host)}…`);\n const ws = new WebSocket(wsUrl, {\n headers: {\n Authorization: \"Bearer \" + config.deviceToken,\n },\n });\n\n try {\n await new Promise<void>((resolve, reject) => {\n ws.once(\"open\", resolve);\n ws.once(\"error\", reject);\n });\n\n backoff = 1000;\n ws.send(\n JSON.stringify({\n type: \"hello\",\n device: {\n id: config.deviceId,\n name: config.deviceName,\n platform: process.platform,\n arch: process.arch,\n hostname: os.hostname(),\n agentVersion: VERSION,\n },\n tools: tools.map((tool) => tool.name),\n }),\n );\n\n logLine(\"success\", `Relay connected · ${bold(config.deviceName)} · ${config.mode} mode`);\n logLine(\"success\", \"Device presence published.\");\n process.stdout.write(\" \" + dim(config.origin) + \"\\n\");\n process.stdout.write(\" \" + dim(\"Ctrl+C to disconnect\") + \"\\n\\n\");\n\n await new Promise<void>((resolve) => {\n ws.on(\"message\", async (raw) => {\n let message: {\n type?: string;\n id?: string;\n tool?: string;\n arguments?: Record<string, unknown>;\n };\n\n try {\n message = JSON.parse(raw.toString());\n } catch {\n return;\n }\n\n if (\n message.type !== \"call\" ||\n !message.id ||\n !message.tool\n ) {\n return;\n }\n\n const callStarted = Date.now();\n logLine(\"event\", `tool.call ${bold(message.tool)} · ${dim(message.id.slice(0, 8))}`);\n try {\n const result = await core.call(\n message.tool,\n message.arguments || {},\n );\n logLine(\"success\", `tool.done ${message.tool} · ${Date.now() - callStarted}ms`);\n ws.send(\n JSON.stringify({\n type: \"result\",\n id: message.id,\n result,\n }),\n );\n } catch (error) {\n logLine(\"error\", `tool.fail ${message.tool} · ${error instanceof Error ? error.message : String(error)}`);\n ws.send(\n JSON.stringify({\n type: \"result\",\n id: message.id,\n error: error instanceof Error ? error.message : String(error),\n }),\n );\n }\n });\n\n ws.once(\"close\", resolve);\n ws.once(\"error\", resolve);\n });\n } catch (error) {\n logLine(\n \"error\",\n \"Relay connection failed: \" +\n (error instanceof Error ? error.message : String(error)),\n );\n } finally {\n ws.removeAllListeners();\n ws.close();\n }\n\n if (!stopped) {\n logLine(\n \"warn\",\n \"Relay disconnected · retrying in \" + Math.round(backoff / 1000) + \"s\",\n );\n await sleep(backoff);\n backoff = Math.min(backoff * 2, 30_000);\n }\n }\n}\n\nasync function main() {\n if (argFlag(\"--help\") || argFlag(\"-h\")) {\n process.stdout.write(\n [\n \"Remote Arc\",\n \"\",\n \"Usage:\",\n \" npx remotelink\",\n \"\",\n \"After the npm release:\",\n \" npx remotelink\",\n \"\",\n \"Options:\",\n \" --safe Read-only local capability mode\",\n \" --developer Read/write/shell mode (default)\",\n \" --reset Remove this computer's saved pairing\",\n \" --version Print CLI version\",\n \" --help Show this help\",\n \"\",\n ].join(\"\\n\"),\n );\n return;\n }\n\n if (argFlag(\"--version\") || argFlag(\"-v\")) {\n process.stdout.write(VERSION + \"\\n\");\n return;\n }\n\n if (argFlag(\"--reset\")) {\n await resetConfig();\n return;\n }\n\n const origin =\n process.env.REMOTEARC_ORIGIN || process.env.REMOTE_LINK_ORIGIN ||\n process.argv.find((value) => value.startsWith(\"--origin=\"))?.slice(9) ||\n DEFAULT_ORIGIN;\n\n let config = await readConfig();\n if (!config) {\n config = await pair(origin, selectedMode());\n } else if (argFlag(\"--safe\") || argFlag(\"--developer\")) {\n config.mode = selectedMode();\n await writeConfig(config);\n }\n\n if (config) {\n logLine(\"info\", `Using paired device identity ${dim(config.deviceId.slice(0, 8))}…`);\n }\n await connectAgent(config);\n}\n\nmain().catch((error) => {\n logLine(\n \"error\",\n \"Fatal: \" + (error instanceof Error ? error.message : String(error)),\n );\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,31 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "remotelink",
|
|
3
|
-
"version": "0.2
|
|
4
|
-
"description": "Connect a computer to Remote
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"remotelink": "dist/index.js",
|
|
8
|
-
"remote-link": "dist/index.js"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "remotelink",
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Connect a computer to Remote Arc with one command.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"remotelink": "dist/index.js",
|
|
8
|
+
"remote-link": "dist/index.js",
|
|
9
|
+
"remote-arc": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format esm --platform node --target node20 --sourcemap",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"prepublishOnly": "pnpm build"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/client": "^2.1.0",
|
|
26
|
+
"ws": "^8.18.3"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^24.13.6",
|
|
30
|
+
"@types/ws": "^8.18.1",
|
|
31
|
+
"tsup": "^8.5.0",
|
|
32
|
+
"typescript": "^6.0.3"
|
|
33
|
+
},
|
|
34
|
+
"license": "UNLICENSED",
|
|
35
|
+
"homepage": "https://remotearc.app",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/yaohuangguan/remote-arc.git",
|
|
39
|
+
"directory": "packages/cli"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/yaohuangguan/remote-arc/issues"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"remote",
|
|
46
|
+
"mcp",
|
|
47
|
+
"chatgpt",
|
|
48
|
+
"claude",
|
|
49
|
+
"codex",
|
|
50
|
+
"remote-computer",
|
|
51
|
+
"developer-tools"
|
|
52
|
+
]
|
|
53
|
+
}
|