@remcp/remcp 0.2.227 → 0.2.229

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.227",
3
+ "version": "0.2.229",
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,7 +18,7 @@
18
18
  "README.md"
19
19
  ],
20
20
  "scripts": {
21
- "check": "node --check bin/remcp.mjs && node --check src/agent-deploy-reconnect.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/installation-audit.mjs && node --check src/cli/mac-native-temperature.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/supervised-agent-lifecycle.mjs && node --check src/cli/update.mjs && node --check src/cli/windows-owned-job-source.mjs && node --check src/cli/windows-owned-job.mjs && node --check src/cli/windows-supervisor.mjs && node --check src/cli/windows-update-lock.mjs && node --check src/cli/windows-user-supervisor.mjs && node --check src/fetch-update-manifest.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-deploy-reconnect.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/installation-audit.mjs && node --check src/cli/mac-native-temperature.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/supervised-agent-lifecycle.mjs && node --check src/cli/update.mjs && node --check src/cli/windows-cli-shim.mjs && node --check src/cli/windows-owned-job-source.mjs && node --check src/cli/windows-owned-job.mjs && node --check src/cli/windows-supervisor.mjs && node --check src/cli/windows-update-lock.mjs && node --check src/cli/windows-user-supervisor.mjs && node --check src/fetch-update-manifest.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": {
@@ -43,7 +43,9 @@ export function createAgentRelayConnection({
43
43
  reconnectTimer = null;
44
44
  connect();
45
45
  }, safeDelay);
46
- reconnectTimer.unref?.();
46
+ // A failed handshake may leave no other referenced handles. Keep the reconnect
47
+ // timer referenced, or Node exits 0 and a supervisor can restart it forever.
48
+ // clearReconnect() releases the timer on intentional shutdown.
47
49
  }
48
50
 
49
51
  function scheduleBackoffReconnect() {
@@ -7,6 +7,7 @@ import { home, npm } from './env.mjs';
7
7
  import { buildVettedMacTemperatureAddon } from './mac-native-temperature.mjs';
8
8
  import { output, run } from './shell.mjs';
9
9
  import { withWindowsUpdateLock } from './windows-update-lock.mjs';
10
+ import { assertWindowsNpmPowerShellShimOwned, preferWindowsCmdShim } from './windows-cli-shim.mjs';
10
11
 
11
12
  export function servicePlatform() {
12
13
  return process.env.NODE_ENV === 'test' && process.env.REMCP_TEST_PLATFORM ? process.env.REMCP_TEST_PLATFORM : process.platform;
@@ -55,7 +56,14 @@ function npmGlobalInstallArgs(specs, { preferOnline = false } = {}) {
55
56
 
56
57
  export function npmGlobalInstall(...specs) {
57
58
  const prefix = assertSafeReMcpNpmPrefix(globalPrefix()); // Reject an unsafe prefix before npm writes anything.
58
- const install = () => run(npm.command, [...npm.args, ...npmGlobalInstallArgs(specs)]);
59
+ const install = () => {
60
+ const updatingClient = servicePlatform() === 'win32' && isManagedWindowsRuntimePrefix(prefix)
61
+ && specs.some(spec => /^@remcp\/remcp@/.test(spec));
62
+ // A custom remcp.ps1 must be refused BEFORE npm overwrites its contents.
63
+ if (updatingClient) assertWindowsNpmPowerShellShimOwned(prefix);
64
+ run(npm.command, [...npm.args, ...npmGlobalInstallArgs(specs)]);
65
+ if (updatingClient) preferWindowsCmdShim(prefix);
66
+ };
59
67
  if (servicePlatform() === 'win32') withWindowsUpdateLock(prefix, install);
60
68
  else install();
61
69
  if (servicePlatform() === 'darwin' && specs.some(spec => /^@remcp\/runtime@[0-9]/.test(spec))) {
@@ -184,6 +192,9 @@ function transactionalWindowsGlobalInstall({ resolved, prefix, packageNames, spe
184
192
  return;
185
193
  }
186
194
 
195
+ // The transaction deletes its previous shim backup after success. Reject a
196
+ // user-created remcp.ps1 before touching npm or moving any existing files.
197
+ if (packageNames.includes(PACKAGE_NAME)) assertWindowsNpmPowerShellShimOwned(prefix);
187
198
  const backupRoot = path.join(prefix, `.remcp-update-backup-${process.pid}-${Date.now()}`);
188
199
  const moved = [];
189
200
  const moveAside = (source, label) => {
@@ -221,6 +232,7 @@ function transactionalWindowsGlobalInstall({ resolved, prefix, packageNames, spe
221
232
  for (const shim of ['remcp', 'remcp.cmd', 'remcp.ps1']) moveAside(path.join(prefix, shim), `bin-${shim}`);
222
233
  }
223
234
  run(resolved.command, [...resolved.args, ...npmGlobalInstallArgs(specs, { preferOnline })]);
235
+ if (uniqueNames.includes(PACKAGE_NAME)) preferWindowsCmdShim(prefix);
224
236
  } catch (error) {
225
237
  restore(error);
226
238
  throw error;
@@ -0,0 +1,39 @@
1
+ // ReMCP's private Windows npm prefix is called from both cmd.exe and PowerShell.
2
+ // npm generates both remcp.cmd and remcp.ps1, but PowerShell prefers the .ps1
3
+ // even when ExecutionPolicy=Restricted. That makes a successful installation
4
+ // appear broken. Keep the batch shim, and remove only the recognizable npm shim.
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+
8
+ export function assertWindowsNpmPowerShellShimOwned(prefix) {
9
+ const powershell = path.join(prefix, 'remcp.ps1');
10
+ let metadata;
11
+ try { metadata = fs.lstatSync(powershell); }
12
+ catch (error) { if (error?.code === 'ENOENT') return false; throw error; }
13
+ if (!metadata.isFile() || metadata.isSymbolicLink()) {
14
+ throw new Error('Refusing to replace an unknown ReMCP PowerShell launcher');
15
+ }
16
+ const contents = fs.readFileSync(powershell, 'utf8');
17
+ if (contents.length > 16000 || !contents.startsWith('#!/usr/bin/env pwsh')
18
+ || !contents.includes('$basedir/node_modules/@remcp/remcp/bin/remcp.mjs')) {
19
+ throw new Error('Refusing to remove a non-npm ReMCP PowerShell launcher');
20
+ }
21
+ return true;
22
+ }
23
+
24
+ export function preferWindowsCmdShim(prefix) {
25
+ if (!assertWindowsNpmPowerShellShimOwned(prefix)) return false;
26
+ const batch = path.join(prefix, 'remcp.cmd');
27
+ const powershell = path.join(prefix, 'remcp.ps1');
28
+ let batchStat;
29
+ try { batchStat = fs.lstatSync(batch); }
30
+ catch (error) {
31
+ if (error?.code !== 'ENOENT') throw error;
32
+ throw new Error('Cannot remove the PowerShell launcher without a ReMCP .cmd launcher');
33
+ }
34
+ if (!batchStat.isFile() || batchStat.isSymbolicLink()) {
35
+ throw new Error('Cannot remove the PowerShell launcher without a trusted ReMCP .cmd launcher');
36
+ }
37
+ fs.unlinkSync(powershell);
38
+ return true;
39
+ }