@remcp/remcp 0.2.209 → 0.2.210

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
@@ -90,7 +90,7 @@ permission, and `remcp doctor` reports the state of each one.
90
90
 
91
91
  ## Updates
92
92
 
93
- The agent asks the server which versions it should run on every successful connection and every six hours, installs the exact trusted client/runtime release pair in the background, and restarts the managed service so it takes effect. The updater resolves itself and the sibling runtime from the package that is actually running instead of depending on an interactive shell PATH. When the same computer has been used from more than one Node manager (for example NVM plus Hermes/FNM/Homebrew), ReMCP keeps the canonical service installation stable and converges the known local installations on the same release pair. `remcp status` reports mixed installations instead of hiding the skew. A failed install is retried no more often than every thirty minutes and update-check failures are logged, so a broken release cannot become a silent install loop. `remcp update` does the same immediately; `remcp auto-update off` disables automatic checks.
93
+ The agent asks the server which versions it should run on every successful connection and every six hours, installs the exact trusted client/runtime release pair in the background, and restarts the managed service so it takes effect. The updater resolves itself and the sibling runtime from the package that is actually running instead of depending on an interactive shell PATH. When a computer has several Node.js environments (for example NVM, FNM or a system installation), ReMCP updates only the canonical managed service packages. It does not upgrade Node.js, rewrite unrelated npm packages, or silently remove old ReMCP copies. `remcp status` counts package roots verified from the current CLI/service and bounded installation history; aliases and vanished paths are not extra installations. `remcp doctor` adds the exact read-only package roots, corresponding client/runtime versions, aliases and historical/cache classifications. These are **physical package copies, not a count of running agents**. Old copies are never automatically removed; verifying process, startup and script dependencies is required before an explicit owner-approved cleanup. A failed install is retried no more often than every thirty minutes and update-check failures are logged, so a broken release cannot become a silent install loop. `remcp update` does the same immediately; `remcp auto-update off` disables automatic checks.
94
94
 
95
95
  ## Computer-use routing
96
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/remcp",
3
- "version": "0.2.209",
3
+ "version": "0.2.210",
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/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/update.mjs && node --check src/cli/windows-supervisor.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/update.mjs && node --check src/cli/windows-supervisor.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": {
@@ -0,0 +1,79 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const CLIENT_PACKAGE = '@remcp/remcp';
5
+ const MAX_HISTORY = 32;
6
+
7
+ // Inventory only paths previously recorded by this owner and the two current CLI
8
+ // entrypoints. Never crawl npm/node directories or infer running processes.
9
+ function installedPackageAt(cliPath) {
10
+ if (typeof cliPath !== 'string' || !cliPath.trim() || !fs.existsSync(cliPath)) return null;
11
+ let resolved;
12
+ try { resolved = fs.realpathSync(cliPath); } catch { return null; }
13
+ const normalized = resolved.replaceAll('\\', '/');
14
+ const marker = '/node_modules/@remcp/remcp/';
15
+ const markerAt = normalized.lastIndexOf(marker);
16
+ let root = markerAt < 0 ? null : resolved.slice(0, markerAt + marker.length - 1);
17
+ // npm on Windows uses a .cmd shim in the global prefix instead of a link.
18
+ if (!root && path.basename(resolved).toLowerCase() === 'remcp.cmd') {
19
+ root = path.join(path.dirname(resolved), 'node_modules', '@remcp', 'remcp');
20
+ }
21
+ if (!root) return null;
22
+ try {
23
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
24
+ if (manifest.name !== CLIENT_PACKAGE || typeof manifest.version !== 'string') return null;
25
+ let runtimeVersion = null;
26
+ try {
27
+ const runtime = JSON.parse(fs.readFileSync(path.join(root, '..', 'runtime', 'package.json'), 'utf8'));
28
+ if (runtime.name === '@remcp/runtime' && typeof runtime.version === 'string') runtimeVersion = runtime.version;
29
+ } catch {}
30
+ return { root:fs.realpathSync(root), resolved, version:manifest.version, runtimeVersion };
31
+ } catch { return null; }
32
+ }
33
+
34
+ export function auditKnownInstallations(config, { currentCliPath, serviceCliPath } = {}) {
35
+ const history = Array.isArray(config?.installations) ? config.installations.slice(-MAX_HISTORY) : [];
36
+ const candidates = [
37
+ { path:currentCliPath, source:'invoked_cli' },
38
+ { path:serviceCliPath, source:'managed_service' },
39
+ ...history.map(item => ({ path:item?.cliPath, source:'saved_history' })),
40
+ ];
41
+ const packages = new Map();
42
+ const aliases = [];
43
+ let ignoredUnverified = 0;
44
+ for (const candidate of candidates) {
45
+ if (typeof candidate.path !== 'string' || !candidate.path.trim() || !fs.existsSync(candidate.path)) continue;
46
+ const installed = installedPackageAt(candidate.path);
47
+ if (!installed) { ignoredUnverified++; continue; }
48
+ let copy = packages.get(installed.root);
49
+ if (!copy) {
50
+ copy = { packageRoot:installed.root, version:installed.version, runtimeVersion:installed.runtimeVersion, sources:new Set(), cliPaths:new Set() };
51
+ packages.set(installed.root, copy);
52
+ }
53
+ copy.sources.add(candidate.source);
54
+ copy.cliPaths.add(candidate.path);
55
+ if (candidate.path !== installed.resolved && !aliases.some(a => a.path === candidate.path)) {
56
+ aliases.push({ path:candidate.path, resolvesTo:installed.resolved });
57
+ }
58
+ }
59
+ const copies = [...packages.values()].map(item => ({
60
+ packageRoot:item.packageRoot,
61
+ version:item.version,
62
+ runtimeVersion:item.runtimeVersion,
63
+ sources:[...item.sources],
64
+ kind:item.packageRoot.replaceAll('\\', '/').includes('/_npx/') ? 'npx_cache' : 'npm_package',
65
+ isInvoked:item.sources.has('invoked_cli'),
66
+ isManagedService:item.sources.has('managed_service'),
67
+ cliPaths:[...item.cliPaths],
68
+ }));
69
+ return {
70
+ scope:'recorded_paths_only',
71
+ processConcurrency:'not_measured',
72
+ packageCopyCount:copies.length,
73
+ otherCopyCount:copies.filter(item => !item.isInvoked && !item.isManagedService).length,
74
+ ignoredUnverifiedPathCount:ignoredUnverified,
75
+ aliases,
76
+ copies,
77
+ removal:'disabled; explicit owner review and process/dependency audit required',
78
+ };
79
+ }
package/src/cli.mjs CHANGED
@@ -13,6 +13,7 @@ import { assertRuntimeTrust, assertSecureServerUrl, pairWithDeviceCode } from '.
13
13
  import { diagnoseLocalRuntime, installedVersion, runtimeAllowedRoots } from './cli/doctor.mjs';
14
14
  import { configFile, npm, officialOrigin, runtimeConfigFile } from './cli/env.mjs';
15
15
  import { currentInstallationInfo, ensureMacCliCommand, installPersistentAgent, persistentServiceState, servicePlatform, rememberCurrentInstallation, restartPersistentServiceIfInstalled, serviceInstallationInfo, uninstallPersistentService } from './cli/service.mjs';
16
+ import { auditKnownInstallations } from './cli/installation-audit.mjs';
16
17
  import { scheduleMacTemperatureRepair } from './cli/mac-native-temperature.mjs';
17
18
  import { repairMacTemperatureAfterStart } from './cli/npm-global.mjs';
18
19
  import { run } from './cli/shell.mjs';
@@ -305,16 +306,13 @@ export async function main(argv = process.argv.slice(2)) {
305
306
  const targetClient = String(advertised?.cliVersion || '').trim() || null;
306
307
  const targetRuntime = String(advertised?.runtime || '').trim() || null;
307
308
  const configuredRuntime = String(cfg.runtime.packageSpec || '');
308
- // The history also records CLI paths from old Node.js environments. An entry for
309
- // a no-longer-existing path is not an additional installed package or agent.
310
- const installationPaths = new Set([
311
- currentInstall.cliPath,
312
- serviceInstall.cliPath,
313
- ...(Array.isArray(cfg.installations) ? cfg.installations.map(item => item?.cliPath) : []),
314
- ].filter(file => typeof file === 'string' && fs.existsSync(file)).map(file => {
315
- try { return fs.realpathSync(file); } catch { return file; }
316
- }));
317
- const multipleInstallations = installationPaths.size > 1;
309
+ // Count verified physical ReMCP package roots, not merely surviving paths or
310
+ // shell aliases. This is NOT a concurrent-agent/process count.
311
+ const installationAudit = auditKnownInstallations(cfg, {
312
+ currentCliPath:currentInstall.cliPath,
313
+ serviceCliPath:service.installed ? serviceInstall.cliPath : null,
314
+ });
315
+ const multipleInstallations = installationAudit.packageCopyCount > 1;
318
316
  const knownCliVersions = [VERSION, serviceInstall.cliVersion].filter(Boolean);
319
317
  const knownRuntimeVersions = [installedRuntime, serviceInstall.runtimeVersion].filter(Boolean);
320
318
  const versionConsistent = cfg.runtime.packageName === '@remcp/runtime'
@@ -368,6 +366,9 @@ export async function main(argv = process.argv.slice(2)) {
368
366
  targetClient,
369
367
  targetRuntime,
370
368
  multipleInstallations,
369
+ knownPackageCopies:installationAudit.packageCopyCount,
370
+ additionalKnownPackageCopies:installationAudit.otherCopyCount,
371
+ installationCountScope:'verified_physical_packages_not_running_agents',
371
372
  versionConsistent,
372
373
  state:updateState,
373
374
  upToDate,
@@ -382,6 +383,7 @@ export async function main(argv = process.argv.slice(2)) {
382
383
  serviceInstalled:service.installed,
383
384
  runtimeEntry:cfg.runtime.entry,
384
385
  npmManaged:true,
386
+ installations:installationAudit,
385
387
  };
386
388
  }
387
389
  console.log(JSON.stringify(report, null, 2));