remotelink 0.3.2 → 0.3.4

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 CHANGED
@@ -9,8 +9,8 @@ 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.3.2";
13
- var DEFAULT_ORIGIN = "https://remotearc.app";
12
+ var VERSION = "0.3.4";
13
+ var DEFAULT_ORIGIN = "https://mcp.remotearc.app";
14
14
  var CONFIG_DIR = path.join(os.homedir(), ".remotearc");
15
15
  var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
16
16
  var LEGACY_CONFIG_PATH = path.join(os.homedir(), ".remote-link", "config.json");
@@ -249,6 +249,17 @@ async function connectAgent(config) {
249
249
  logLine("success", "Device presence published.");
250
250
  process.stdout.write(" " + dim(config.origin) + "\n");
251
251
  process.stdout.write(" " + dim("Ctrl+C to disconnect") + "\n\n");
252
+ const sendHeartbeat = async () => {
253
+ try {
254
+ await fetch(new URL("/api/device/heartbeat", config.origin), {
255
+ method: "POST",
256
+ headers: { Authorization: "Bearer " + config.deviceToken }
257
+ });
258
+ } catch {
259
+ }
260
+ };
261
+ void sendHeartbeat();
262
+ const heartbeatTimer = setInterval(() => void sendHeartbeat(), 6e4);
252
263
  await new Promise((resolve) => {
253
264
  ws.on("message", async (raw) => {
254
265
  let message;
@@ -289,6 +300,7 @@ async function connectAgent(config) {
289
300
  ws.once("close", resolve);
290
301
  ws.once("error", resolve);
291
302
  });
303
+ clearInterval(heartbeatTimer);
292
304
  } catch (error) {
293
305
  logLine(
294
306
  "error",
@@ -341,6 +353,11 @@ async function main() {
341
353
  }
342
354
  const origin = process.env.REMOTEARC_ORIGIN || process.env.REMOTE_LINK_ORIGIN || process.argv.find((value) => value.startsWith("--origin="))?.slice(9) || DEFAULT_ORIGIN;
343
355
  let config = await readConfig();
356
+ if (config && ["https://remote.samyao.me", "https://remotearc.app"].includes(config.origin)) {
357
+ config.origin = DEFAULT_ORIGIN;
358
+ await writeConfig(config);
359
+ logLine("info", "Migrated relay origin to " + DEFAULT_ORIGIN);
360
+ }
344
361
  if (!config) {
345
362
  config = await pair(origin, selectedMode());
346
363
  } else if (argFlag("--safe") || argFlag("--developer")) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
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":[]}
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.4\";\nconst DEFAULT_ORIGIN = \"https://mcp.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 const sendHeartbeat = async () => {\n try {\n await fetch(new URL(\"/api/device/heartbeat\", config.origin), {\n method: \"POST\",\n headers: { Authorization: \"Bearer \" + config.deviceToken },\n });\n } catch {\n // WebSocket reconnect logic remains the source of truth for connectivity.\n }\n };\n void sendHeartbeat();\n const heartbeatTimer = setInterval(() => void sendHeartbeat(), 60_000);\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 clearInterval(heartbeatTimer);\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 && [\"https://remote.samyao.me\", \"https://remotearc.app\"].includes(config.origin)) {\n config.origin = DEFAULT_ORIGIN;\n await writeConfig(config);\n logLine(\"info\", \"Migrated relay origin to \" + DEFAULT_ORIGIN);\n }\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,6 +1,6 @@
1
1
  {
2
2
  "name": "remotelink",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Connect a computer to Remote Arc with one command.",
5
5
  "type": "module",
6
6
  "bin": {