@remcp/remcp 0.2.208 → 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 +1 -1
- package/package.json +2 -2
- package/src/cli/config.mjs +6 -8
- package/src/cli/installation-audit.mjs +79 -0
- package/src/cli.mjs +36 -9
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
|
|
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.
|
|
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": {
|
package/src/cli/config.mjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// the server uses to recognise this computer across pairings.
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import process from 'node:process';
|
|
6
5
|
import { randomUUID } from 'node:crypto';
|
|
7
6
|
import { normalizeRuntime } from '../runtime.mjs';
|
|
8
7
|
|
|
@@ -79,13 +78,12 @@ export function writeJsonFile(file, value) {
|
|
|
79
78
|
fs.chmodSync(file, 0o600);
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
export function autoUpdateEnabled(config
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return platform !== 'linux';
|
|
81
|
+
export function autoUpdateEnabled(config) {
|
|
82
|
+
// This preference controls the two first-party ReMCP npm packages only. It never
|
|
83
|
+
// upgrades the Node.js interpreter or npm itself. All supported OSes keep the
|
|
84
|
+
// historical default of automatic ReMCP releases unless the owner opts out.
|
|
85
|
+
// Updates use the verified managed service prefix, not a shared global prefix.
|
|
86
|
+
return config?.autoUpdate !== false;
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
export function flagEnabled(value) {
|
|
@@ -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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import os from 'node:os';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import process from 'node:process';
|
|
@@ -12,6 +13,7 @@ import { assertRuntimeTrust, assertSecureServerUrl, pairWithDeviceCode } from '.
|
|
|
12
13
|
import { diagnoseLocalRuntime, installedVersion, runtimeAllowedRoots } from './cli/doctor.mjs';
|
|
13
14
|
import { configFile, npm, officialOrigin, runtimeConfigFile } from './cli/env.mjs';
|
|
14
15
|
import { currentInstallationInfo, ensureMacCliCommand, installPersistentAgent, persistentServiceState, servicePlatform, rememberCurrentInstallation, restartPersistentServiceIfInstalled, serviceInstallationInfo, uninstallPersistentService } from './cli/service.mjs';
|
|
16
|
+
import { auditKnownInstallations } from './cli/installation-audit.mjs';
|
|
15
17
|
import { scheduleMacTemperatureRepair } from './cli/mac-native-temperature.mjs';
|
|
16
18
|
import { repairMacTemperatureAfterStart } from './cli/npm-global.mjs';
|
|
17
19
|
import { run } from './cli/shell.mjs';
|
|
@@ -36,7 +38,20 @@ function parse(argv) {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function printHelp() {
|
|
39
|
-
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.`);
|
|
41
|
+
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 auto-update [status|on|off]\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.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The running agent snapshots update policy at startup. A saved toggle alone does not
|
|
45
|
+
// change its six-hour polling loop, so reload a managed service after a policy change.
|
|
46
|
+
// Defer Linux handoff to avoid terminating a remote command before it returns.
|
|
47
|
+
export function applyAutoUpdatePreference(cfg, enabled, {
|
|
48
|
+
persist = saveConfig,
|
|
49
|
+
restart = restartPersistentServiceIfInstalled,
|
|
50
|
+
} = {}) {
|
|
51
|
+
const changed = autoUpdateEnabled(cfg) !== enabled;
|
|
52
|
+
const updated = { ...cfg, autoUpdate: enabled };
|
|
53
|
+
persist(updated);
|
|
54
|
+
return changed ? restart(updated, { deferLinux:true }) : null;
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
export async function main(argv = process.argv.slice(2)) {
|
|
@@ -201,8 +216,15 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
201
216
|
console.log(JSON.stringify({ autoUpdate: autoUpdateEnabled(cfg), checkIntervalHours: 6 }, null, 2));
|
|
202
217
|
return;
|
|
203
218
|
}
|
|
204
|
-
if (
|
|
205
|
-
|
|
219
|
+
if (['on', 'enable', 'off', 'disable'].includes(action)) {
|
|
220
|
+
const enabled = action === 'on' || action === 'enable';
|
|
221
|
+
const changed = autoUpdateEnabled(cfg) !== enabled;
|
|
222
|
+
const restarted = applyAutoUpdatePreference(cfg, enabled);
|
|
223
|
+
console.log(enabled ? 'Auto-update enabled.' : 'Auto-update disabled. Run `remcp update` yourself when you want a new version.');
|
|
224
|
+
if (restarted) console.log(`ReMCP agent ${restarted} to apply the update policy.`);
|
|
225
|
+
else if (changed) console.log('Restart any manually managed ReMCP agent to apply the update policy.');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
206
228
|
throw new Error('Usage: remcp auto-update [status|on|off]');
|
|
207
229
|
}
|
|
208
230
|
|
|
@@ -284,12 +306,13 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
284
306
|
const targetClient = String(advertised?.cliVersion || '').trim() || null;
|
|
285
307
|
const targetRuntime = String(advertised?.runtime || '').trim() || null;
|
|
286
308
|
const configuredRuntime = String(cfg.runtime.packageSpec || '');
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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;
|
|
293
316
|
const knownCliVersions = [VERSION, serviceInstall.cliVersion].filter(Boolean);
|
|
294
317
|
const knownRuntimeVersions = [installedRuntime, serviceInstall.runtimeVersion].filter(Boolean);
|
|
295
318
|
const versionConsistent = cfg.runtime.packageName === '@remcp/runtime'
|
|
@@ -343,6 +366,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
343
366
|
targetClient,
|
|
344
367
|
targetRuntime,
|
|
345
368
|
multipleInstallations,
|
|
369
|
+
knownPackageCopies:installationAudit.packageCopyCount,
|
|
370
|
+
additionalKnownPackageCopies:installationAudit.otherCopyCount,
|
|
371
|
+
installationCountScope:'verified_physical_packages_not_running_agents',
|
|
346
372
|
versionConsistent,
|
|
347
373
|
state:updateState,
|
|
348
374
|
upToDate,
|
|
@@ -357,6 +383,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
357
383
|
serviceInstalled:service.installed,
|
|
358
384
|
runtimeEntry:cfg.runtime.entry,
|
|
359
385
|
npmManaged:true,
|
|
386
|
+
installations:installationAudit,
|
|
360
387
|
};
|
|
361
388
|
}
|
|
362
389
|
console.log(JSON.stringify(report, null, 2));
|