@remcp/remcp 0.2.193 → 0.2.195

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/README.md CHANGED
@@ -16,6 +16,16 @@ it keeps the ReMCP runtime under `%LOCALAPPDATA%\ReMCP`. Generated one-time comm
16
16
  runtime, and registers a user service (systemd on Linux, LaunchAgent on macOS, Scheduled Task on
17
17
  Windows).
18
18
 
19
+ ## Scan to pair with a phone
20
+
21
+ Run `remcp connect` in an interactive terminal to display a short-lived pairing code,
22
+ a link, and a compact QR that opens the same ReMCP approval page on your phone.
23
+ Sign in on your phone if needed, check the computer name, then explicitly approve.
24
+ The QR contains only the one-time code in the existing device URL: it does **not**
25
+ authorize the device on its own. The code expires and cannot be reused after approval.
26
+ If your terminal cannot display a QR, use the printed link or code. Add `--no-qr`
27
+ to suppress the QR; a custom non-HTTPS or cross-origin URL never gets one.
28
+
19
29
  ## Commands
20
30
 
21
31
  ```text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/remcp",
3
- "version": "0.2.193",
3
+ "version": "0.2.195",
4
4
  "description": "ReMCP device client: pair a computer with ReMCP and run the outbound-only agent that hosts the local MCP runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,11 +18,12 @@
18
18
  "README.md"
19
19
  ],
20
20
  "scripts": {
21
- "check": "node --check bin/remcp.mjs && node --check src/agent-network-probe-client.mjs && node --check src/agent-relay-connection.mjs && node --check src/agent-responder.mjs && node --check src/agent-runtime-capabilities.mjs && node --check src/agent-update-monitor.mjs && node --check src/agent-update.mjs && node --check src/agent-utilities.mjs && node --check src/agent.mjs && node --check src/cli.mjs && node --check src/cli/config.mjs && node --check src/cli/connect.mjs && node --check src/cli/doctor.mjs && node --check src/cli/env.mjs && node --check src/cli/npm-global.mjs && node --check src/cli/service-inventory.mjs && node --check src/cli/service-macos.mjs && node --check src/cli/service-paths.mjs && node --check src/cli/service.mjs && node --check src/cli/shell.mjs && node --check src/cli/update.mjs && node --check src/cli/windows-supervisor.mjs && node --check src/fs-access.mjs && node --check src/npm.mjs && node --check src/runtime.mjs && node --check src/version.mjs",
21
+ "check": "node --check bin/remcp.mjs && node --check src/agent-network-probe-client.mjs && node --check src/agent-relay-connection.mjs && node --check src/agent-responder.mjs && node --check src/agent-runtime-capabilities.mjs && node --check src/agent-update-monitor.mjs && node --check src/agent-update.mjs && node --check src/agent-utilities.mjs && node --check src/agent.mjs && node --check src/cli.mjs && node --check src/cli/config.mjs && node --check src/cli/connect.mjs && node --check src/cli/doctor.mjs && node --check src/cli/env.mjs && node --check src/cli/npm-global.mjs && node --check src/cli/pairing-qr.mjs && node --check src/cli/service-inventory.mjs && node --check src/cli/service-macos.mjs && node --check src/cli/service-paths.mjs && node --check src/cli/service.mjs && node --check src/cli/shell.mjs && node --check src/cli/update.mjs && node --check src/cli/windows-supervisor.mjs && node --check src/fs-access.mjs && node --check src/npm.mjs && node --check src/runtime.mjs && node --check src/version.mjs",
22
22
  "test": "node --test test/*.test.mjs"
23
23
  },
24
24
  "dependencies": {
25
25
  "@modelcontextprotocol/client": "^2.3.1",
26
+ "uqr": "0.1.3",
26
27
  "ws": "^8.21.3"
27
28
  },
28
29
  "repository": {
@@ -7,6 +7,7 @@ import { spawn } from 'node:child_process';
7
7
  import { ensureMachineId } from './config.mjs';
8
8
  import { officialOrigin } from './env.mjs';
9
9
  import { sleep } from './shell.mjs';
10
+ import { printPairingQr } from './pairing-qr.mjs';
10
11
 
11
12
  // Keep the server-provided approval URL as data all the way to the OS. In particular, do not route it
12
13
  // through `cmd /c start` on Windows: cmd reparses metacharacters such as `&`, so a custom pairing
@@ -78,6 +79,7 @@ export async function pairWithDeviceCode(server, flags) {
78
79
  const approvalUrl = grant.verification_uri_complete || grant.verification_uri;
79
80
  console.log(`Approve this computer in your browser: ${approvalUrl}`);
80
81
  console.log(`Pairing code: ${grant.user_code} (expires in ${Math.max(1, Math.round(Number(grant.expires_in || 600) / 60))} minutes)`);
82
+ if (flags?.['no-qr'] !== true) printPairingQr({ server, approvalUrl, userCode: grant.user_code });
81
83
  openInBrowser(approvalUrl);
82
84
  console.log('Waiting for approval… (Ctrl+C to cancel)');
83
85
  const deadline = Date.now() + (Number(grant.expires_in) || 600) * 1000;
@@ -0,0 +1,43 @@
1
+ import { renderUnicodeCompact } from 'uqr';
2
+
3
+ const USER_CODE_PATTERN = /^[A-Z2-9]{4}-[A-Z2-9]{4}$/;
4
+
5
+ // A QR is a shortcut to the existing device review screen, not a login token.
6
+ // Only encode the same-origin HTTPS /device URL containing this exact short-lived code.
7
+ export function formatPairingQr({ server, approvalUrl, userCode, output = process.stdout, env = process.env }) {
8
+ if (!output?.isTTY || env.TERM === 'dumb' || env.NO_COLOR) return null;
9
+ if (!USER_CODE_PATTERN.test(String(userCode))) return null;
10
+
11
+ let origin;
12
+ let url;
13
+ try {
14
+ origin = new URL(server);
15
+ url = new URL(approvalUrl);
16
+ } catch { return null; }
17
+ if (origin.protocol !== 'https:' || url.protocol !== 'https:' || url.origin !== origin.origin ||
18
+ url.username || url.password || url.hash || url.pathname !== '/device' ||
19
+ url.searchParams.size !== 1 || url.searchParams.get('user_code') !== userCode) return null;
20
+
21
+ // Each pair of QR rows fits into one terminal line. Use explicit light/dark ANSI
22
+ // colors so the quiet zone remains scannable regardless of terminal theme.
23
+ let qr;
24
+ try { qr = renderUnicodeCompact(url.href, { ecc: 'M', border: 3 }); }
25
+ catch { return null; }
26
+ const columns = Number.isFinite(output.columns) && output.columns > 0 ? output.columns : 80;
27
+ const rows = qr.split('\n');
28
+ if (rows.some(row => [...row].length + 2 > columns)) return null;
29
+ // The library paints a dark lower half outside odd-sized matrices.
30
+ // Restore the white bottom quiet zone so phone cameras can detect it.
31
+ const last = rows.length - 1;
32
+ rows[last] = rows[last].replaceAll('▀', '█');
33
+ return rows.map(row => `\x1b[97;40m${row}\x1b[0m`).join('\n');
34
+ }
35
+
36
+ export function printPairingQr({ server, approvalUrl, userCode, output = process.stdout, env = process.env }) {
37
+ const qr = formatPairingQr({ server, approvalUrl, userCode, output, env });
38
+ if (!qr) return false;
39
+ output.write('Scan with your phone to review this computer:\n');
40
+ output.write(qr + '\n');
41
+ output.write('Check the computer name before approving.\n');
42
+ return true;
43
+ }
package/src/cli.mjs CHANGED
@@ -34,7 +34,7 @@ function parse(argv) {
34
34
  }
35
35
 
36
36
  function printHelp() {
37
- console.log(`ReMCP ${VERSION}\n\nCommands:\n remcp start\n remcp status\n remcp doctor\n remcp update\n remcp install\n remcp uninstall\n remcp uninstall --purge\n remcp telemetry [status|on|off]\n remcp godmode [status|on|off]\n remcp --version\n\nPairing commands are generated in the ReMCP workspace.\n\nUsage metrics are opt-out (tool names, timings, outcomes only, sent to your own ReMCP\naccount through the paired agent). Disable them at any time with: remcp telemetry off\n\nUnrestricted mode (remcp godmode on) lifts the access roots and the command guardrails for this\ncomputer only. The MCP configuration tool cannot enable it; protect remote terminal access.`);
37
+ console.log(`ReMCP ${VERSION}\n\nCommands:\n remcp connect [--no-qr]\n remcp start\n remcp status\n remcp doctor\n remcp update\n remcp install\n remcp uninstall\n remcp uninstall --purge\n remcp telemetry [status|on|off]\n remcp godmode [status|on|off]\n remcp --version\n\nPairing commands are generated in the ReMCP workspace.\n\nUsage metrics are opt-out (tool names, timings, outcomes only, sent to your own ReMCP\naccount through the paired agent). Disable them at any time with: remcp telemetry off\n\nUnrestricted mode (remcp godmode on) lifts the access roots and the command guardrails for this\ncomputer only. The MCP configuration tool cannot enable it; protect remote terminal access.`);
38
38
  }
39
39
 
40
40
  export async function main(argv = process.argv.slice(2)) {