@remcp/remcp 0.2.191 → 0.2.193

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/remcp",
3
- "version": "0.2.191",
3
+ "version": "0.2.193",
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",
@@ -245,6 +245,9 @@ export function restartPersistentServiceIfInstalled(config, { deferLinux = false
245
245
  if (platform === 'win32') {
246
246
  const result = spawnSync('schtasks.exe', ['/Query', '/TN', windowsTaskName], { stdio: 'ignore' });
247
247
  if (result.status === 0) {
248
+ // /Run does not restart a task that is already running; stop the previous
249
+ // process first so a rotated credential is loaded by the replacement.
250
+ spawnSync('schtasks.exe', ['/End', '/TN', windowsTaskName], { stdio: 'ignore' });
248
251
  run('schtasks.exe', ['/Run', '/TN', windowsTaskName]);
249
252
  return windowsTaskName;
250
253
  }
package/src/cli.mjs CHANGED
@@ -11,7 +11,7 @@ import { ensureMachineId, loadConfig, readJsonFile, saveConfig, setTelemetry, te
11
11
  import { assertRuntimeTrust, assertSecureServerUrl, pairWithDeviceCode } from './cli/connect.mjs';
12
12
  import { diagnoseLocalRuntime, installedVersion, runtimeAllowedRoots } from './cli/doctor.mjs';
13
13
  import { configFile, npm, officialOrigin, runtimeConfigFile } from './cli/env.mjs';
14
- import { currentInstallationInfo, ensureMacCliCommand, installPersistentAgent, persistentServiceState, rememberCurrentInstallation, restartPersistentServiceIfInstalled, serviceInstallationInfo, uninstallPersistentService } from './cli/service.mjs';
14
+ import { currentInstallationInfo, ensureMacCliCommand, installPersistentAgent, persistentServiceState, servicePlatform, rememberCurrentInstallation, restartPersistentServiceIfInstalled, serviceInstallationInfo, uninstallPersistentService } from './cli/service.mjs';
15
15
  import { run } from './cli/shell.mjs';
16
16
  import { updateCommand } from './cli/update.mjs';
17
17
  function fetchConfiguredServer(url, options) {
@@ -49,6 +49,11 @@ export async function main(argv = process.argv.slice(2)) {
49
49
  // Like the desktop-app flow this mirrors: with no flags the command talks to the official
50
50
  // server, prints a code, opens the browser to approve it, and pairs. `--code` keeps working for
51
51
  // the workspace-generated command and for CI, and `--server` for self-hosted deployments.
52
+ const previous = loadConfig(false);
53
+ const alreadyPaired = Boolean(previous?.deviceId && previous?.deviceToken);
54
+ if (alreadyPaired && flags['re-pair'] !== true) {
55
+ throw new Error('This computer is already paired. Use `remcp doctor` or `remcp install` to repair it without changing credentials. To intentionally replace the pairing (possibly moving it to another account), add --re-pair.');
56
+ }
52
57
  const server = assertSecureServerUrl(flags.server || officialOrigin, { allowInsecure: flags['allow-insecure-transport'] === true });
53
58
  const code = String(flags.code || '').replace(/\s+/g, '').toUpperCase();
54
59
  assertRuntimeTrust(server, flags);
@@ -60,7 +65,7 @@ export async function main(argv = process.argv.slice(2)) {
60
65
  method: 'POST',
61
66
  redirect: 'error',
62
67
  headers: { 'content-type': 'application/json' },
63
- body: JSON.stringify({ code, machineId: ensureMachineId(), name: String(flags.name || os.hostname()), hostname: os.hostname(), platform: process.platform, arch: process.arch }),
68
+ body: JSON.stringify({ code, machineId: ensureMachineId(), name: String(flags.name || os.hostname()), hostname: os.hostname(), platform: process.platform, arch: process.arch, rePair: flags['re-pair'] === true }),
64
69
 
65
70
  });
66
71
  if (!response.ok) throw new Error(`Pairing failed (${response.status}): ${await response.text()}`);
@@ -75,6 +80,7 @@ export async function main(argv = process.argv.slice(2)) {
75
80
  console.log('This computer was paired to another ReMCP account. That device was revoked and this machine now belongs to the account you approved.');
76
81
  }
77
82
  const config = {
83
+ ...(previous || {}),
78
84
  serverUrl: server,
79
85
  deviceId: paired.deviceId || paired.device_id,
80
86
  deviceToken: paired.deviceToken || paired.device_token,
@@ -90,8 +96,25 @@ export async function main(argv = process.argv.slice(2)) {
90
96
  saveConfig(config);
91
97
  const accountEmail = String(paired.account?.email || '');
92
98
  console.log(`Paired ${os.hostname()} with ${server}${accountEmail ? ` as ${accountEmail}` : ''}`);
99
+ // Credential rotation must reload the managed agent; a normal install of an already
100
+ // running systemd service does not restart it, and Windows /Run may keep the old task.
101
+ const managedBefore = alreadyPaired &&
102
+ (previous.serviceInstalled === true ||
103
+ (previous.serviceInstalled === undefined && persistentServiceState(previous).active));
93
104
  if (flags.install) {
94
105
  installPersistentAgent(config);
106
+ if (alreadyPaired && servicePlatform() !== 'darwin') {
107
+ restartPersistentServiceIfInstalled(config, { deferLinux: true });
108
+ }
109
+ return;
110
+ }
111
+ if (managedBefore) {
112
+ const restarted = restartPersistentServiceIfInstalled(config, { deferLinux: true });
113
+ if (!restarted) installPersistentAgent(config);
114
+ return;
115
+ }
116
+ if (alreadyPaired) {
117
+ console.log('Pairing credentials replaced. Restart any manually managed ReMCP agent to use the new token.');
95
118
  return;
96
119
  }
97
120
  // Only the command that asked for its own code keeps running: someone who ran `remcp connect` on