@remcp/remcp 0.2.33 → 0.2.34
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 +2 -2
- package/src/cli/connect.mjs +12 -3
- package/src/cli/update.mjs +74 -0
- package/src/cli.mjs +8 -63
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remcp/remcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.34",
|
|
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.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/service.mjs && node --check src/cli/shell.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.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/service.mjs && node --check src/cli/shell.mjs && node --check src/cli/update.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/connect.mjs
CHANGED
|
@@ -8,13 +8,22 @@ import { ensureMachineId } from './config.mjs';
|
|
|
8
8
|
import { officialOrigin } from './env.mjs';
|
|
9
9
|
import { sleep } from './shell.mjs';
|
|
10
10
|
|
|
11
|
+
// Keep the server-provided approval URL as data all the way to the OS. In particular, do not route it
|
|
12
|
+
// through `cmd /c start` on Windows: cmd reparses metacharacters such as `&`, so a custom pairing
|
|
13
|
+
// server could otherwise turn a verification URL into a local shell command.
|
|
14
|
+
export function browserLaunchCommand(url, platform = process.platform) {
|
|
15
|
+
const target = String(url);
|
|
16
|
+
if (platform === 'darwin') return { command: 'open', args: [target] };
|
|
17
|
+
if (platform === 'win32') return { command: 'explorer.exe', args: [target] };
|
|
18
|
+
return { command: 'xdg-open', args: [target] };
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
// Opens the approval page in the person's browser. A machine that nobody is looking at only gets the
|
|
12
22
|
// printed URL, so every failure here is silent and non-fatal.
|
|
13
23
|
export function openInBrowser(url) {
|
|
14
24
|
try {
|
|
15
|
-
const command
|
|
16
|
-
const
|
|
17
|
-
const child = spawn(command, args, { stdio: 'ignore', detached: true });
|
|
25
|
+
const { command, args } = browserLaunchCommand(url);
|
|
26
|
+
const child = spawn(command, args, { stdio: 'ignore', detached: true, shell: false });
|
|
18
27
|
child.on('error', () => {});
|
|
19
28
|
child.unref();
|
|
20
29
|
} catch {}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// `remcp update`: install what the server advertises, then let whoever supervises the agent restart
|
|
2
|
+
// it. Only a spec the configured package can satisfy is installed, so a pairing response from a
|
|
3
|
+
// custom server cannot point this machine at a different package or run a git/URL spec.
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
|
|
7
|
+
import { supervisorRestart } from '../agent.mjs';
|
|
8
|
+
import { isRuntimeSpecFor, normalizeRuntime } from '../runtime.mjs';
|
|
9
|
+
import { PACKAGE_NAME, VERSION } from '../version.mjs';
|
|
10
|
+
|
|
11
|
+
import { loadConfig, saveConfig } from './config.mjs';
|
|
12
|
+
import { installedVersion } from './doctor.mjs';
|
|
13
|
+
import { linuxServiceFile, macServiceFile, officialOrigin } from './env.mjs';
|
|
14
|
+
import { ensureServiceIfRecorded, npmGlobalInstall, restartPersistentServiceIfInstalled } from './service.mjs';
|
|
15
|
+
|
|
16
|
+
export async function updateCommand(flags) {
|
|
17
|
+
const cfg = loadConfig();
|
|
18
|
+
// The server advertises which runtime version it expects; an agent that is updating
|
|
19
|
+
// itself passes it through so client and runtime move together.
|
|
20
|
+
const requested = typeof flags.runtime === 'string' ? flags.runtime.trim() : '';
|
|
21
|
+
// Whatever the server advertises is installed globally, so it is validated exactly like the
|
|
22
|
+
// metadata from a pairing response: same package as the configured runtime, a spec that
|
|
23
|
+
// parses, and an explicit --trust-runtime before a custom server may change it.
|
|
24
|
+
let runtimeSpec = cfg.runtime.packageSpec;
|
|
25
|
+
if (requested) {
|
|
26
|
+
// Only `<configured package>@<semver>` is installable: an alias, a git/URL/file spec, a tag or
|
|
27
|
+
// a range would run code the user never agreed to.
|
|
28
|
+
if (!isRuntimeSpecFor(cfg.runtime.packageName, requested)) {
|
|
29
|
+
throw new Error(`--runtime must be ${cfg.runtime.packageName}@<version>`);
|
|
30
|
+
}
|
|
31
|
+
const trusted = cfg.trustRuntime === true
|
|
32
|
+
|| Boolean(flags['trust-runtime'])
|
|
33
|
+
|| process.env.REMCP_TRUST_RUNTIME === '1'
|
|
34
|
+
// A configuration written before the field existed paired with the official server, which is
|
|
35
|
+
// trusted by definition; refusing it would silently stop every existing device updating.
|
|
36
|
+
|| new URL(cfg.serverUrl).origin === officialOrigin;
|
|
37
|
+
if (!trusted) {
|
|
38
|
+
throw new Error(`This machine was paired without trusting ${cfg.serverUrl} to choose a runtime version. Re-run with --trust-runtime if you trust that server.`);
|
|
39
|
+
}
|
|
40
|
+
runtimeSpec = normalizeRuntime({ kind: 'npm', packageName: cfg.runtime.packageName, packageSpec: requested, entry: cfg.runtime.entry }).packageSpec;
|
|
41
|
+
}
|
|
42
|
+
if (flags.check) {
|
|
43
|
+
console.log(JSON.stringify({
|
|
44
|
+
current: VERSION,
|
|
45
|
+
installedRuntime: installedVersion(cfg.runtime.packageName),
|
|
46
|
+
runtimeSpec: runtimeSpec,
|
|
47
|
+
available: `${PACKAGE_NAME}@latest`,
|
|
48
|
+
managedService: fs.existsSync(linuxServiceFile) || fs.existsSync(macServiceFile),
|
|
49
|
+
supervisor: supervisorRestart() ?? 'none',
|
|
50
|
+
}, null, 2));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const before = { cli: VERSION, runtime: installedVersion(cfg.runtime.packageName) };
|
|
54
|
+
console.log(`Updating ReMCP to the latest published version (${runtimeSpec})…`);
|
|
55
|
+
npmGlobalInstall(`${PACKAGE_NAME}@latest`, runtimeSpec);
|
|
56
|
+
// The npm prefix can change across Node-manager upgrades. Repair an existing persistent-service
|
|
57
|
+
// launcher only after the install, when globalCliPath() points at the CLI we just installed.
|
|
58
|
+
ensureServiceIfRecorded(cfg);
|
|
59
|
+
// Only a validated spec is persisted, so a failed update cannot leave the install unable to start.
|
|
60
|
+
if (requested && runtimeSpec !== cfg.runtime.packageSpec) saveConfig({ ...cfg, runtime: { ...cfg.runtime, packageSpec: runtimeSpec } });
|
|
61
|
+
const after = { cli: installedVersion(PACKAGE_NAME), runtime: installedVersion(cfg.runtime.packageName) };
|
|
62
|
+
const restarted = restartPersistentServiceIfInstalled();
|
|
63
|
+
if (restarted) {
|
|
64
|
+
console.log(`ReMCP updated and ${restarted} restarted (client ${before.cli} → ${after.cli ?? '?'}, runtime ${before.runtime ?? '?'} → ${after.runtime ?? '?'}).`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// This process is the updater, not the agent: exiting here would restart nothing. The agent sees
|
|
68
|
+
// the exit status, verifies the installed version, and restarts itself.
|
|
69
|
+
console.log(`ReMCP updated (client ${before.cli} → ${after.cli ?? '?'}, runtime ${before.runtime ?? '?'} → ${after.runtime ?? '?'}). The running agent restarts itself to apply it.`);
|
|
70
|
+
if (after.cli === before.cli && after.runtime === before.runtime) {
|
|
71
|
+
console.log('Nothing changed: the installed versions already match the requested ones.');
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
package/src/cli.mjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import os from 'node:os';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import process from 'node:process';
|
|
5
|
-
import { runAgent
|
|
6
|
-
import {
|
|
4
|
+
import { runAgent } from './agent.mjs';
|
|
5
|
+
import { normalizeRuntime } from './runtime.mjs';
|
|
7
6
|
import { PACKAGE_NAME, VERSION } from './version.mjs';
|
|
8
7
|
import { probeFilesystemAccess } from './fs-access.mjs';
|
|
9
8
|
|
|
10
9
|
import { ensureMachineId, loadConfig, readJsonFile, saveConfig, setTelemetry, telemetryState, writeJsonFile } from './cli/config.mjs';
|
|
11
10
|
import { assertRuntimeTrust, pairWithDeviceCode } from './cli/connect.mjs';
|
|
12
|
-
import { diagnoseLocalRuntime,
|
|
13
|
-
import { configFile,
|
|
14
|
-
import {
|
|
11
|
+
import { diagnoseLocalRuntime, runtimeAllowedRoots } from './cli/doctor.mjs';
|
|
12
|
+
import { configFile, npm, officialOrigin, runtimeConfigFile } from './cli/env.mjs';
|
|
13
|
+
import { installPersistentAgent, restartPersistentServiceIfInstalled, uninstallPersistentService } from './cli/service.mjs';
|
|
15
14
|
import { run } from './cli/shell.mjs';
|
|
15
|
+
import { updateCommand } from './cli/update.mjs';
|
|
16
16
|
function parse(argv) {
|
|
17
17
|
const [command = 'help', ...rest] = argv;
|
|
18
18
|
const flags = {};
|
|
@@ -208,62 +208,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
if (command === 'update') {
|
|
211
|
-
|
|
212
|
-
// The server advertises which runtime version it expects; an agent that is updating
|
|
213
|
-
// itself passes it through so client and runtime move together.
|
|
214
|
-
const requested = typeof flags.runtime === 'string' ? flags.runtime.trim() : '';
|
|
215
|
-
// Whatever the server advertises is installed globally, so it is validated exactly like the
|
|
216
|
-
// metadata from a pairing response: same package as the configured runtime, a spec that
|
|
217
|
-
// parses, and an explicit --trust-runtime before a custom server may change it.
|
|
218
|
-
let runtimeSpec = cfg.runtime.packageSpec;
|
|
219
|
-
if (requested) {
|
|
220
|
-
// Only `<configured package>@<semver>` is installable: an alias, a git/URL/file spec, a tag or
|
|
221
|
-
// a range would run code the user never agreed to.
|
|
222
|
-
if (!isRuntimeSpecFor(cfg.runtime.packageName, requested)) {
|
|
223
|
-
throw new Error(`--runtime must be ${cfg.runtime.packageName}@<version>`);
|
|
224
|
-
}
|
|
225
|
-
const trusted = cfg.trustRuntime === true
|
|
226
|
-
|| Boolean(flags['trust-runtime'])
|
|
227
|
-
|| process.env.REMCP_TRUST_RUNTIME === '1'
|
|
228
|
-
// A configuration written before the field existed paired with the official server, which is
|
|
229
|
-
// trusted by definition; refusing it would silently stop every existing device updating.
|
|
230
|
-
|| new URL(cfg.serverUrl).origin === officialOrigin;
|
|
231
|
-
if (!trusted) {
|
|
232
|
-
throw new Error(`This machine was paired without trusting ${cfg.serverUrl} to choose a runtime version. Re-run with --trust-runtime if you trust that server.`);
|
|
233
|
-
}
|
|
234
|
-
runtimeSpec = normalizeRuntime({ kind: 'npm', packageName: cfg.runtime.packageName, packageSpec: requested, entry: cfg.runtime.entry }).packageSpec;
|
|
235
|
-
}
|
|
236
|
-
if (flags.check) {
|
|
237
|
-
console.log(JSON.stringify({
|
|
238
|
-
current: VERSION,
|
|
239
|
-
installedRuntime: installedVersion(cfg.runtime.packageName),
|
|
240
|
-
runtimeSpec: runtimeSpec,
|
|
241
|
-
available: `${PACKAGE_NAME}@latest`,
|
|
242
|
-
managedService: fs.existsSync(linuxServiceFile) || fs.existsSync(macServiceFile),
|
|
243
|
-
supervisor: supervisorRestart() ?? 'none',
|
|
244
|
-
}, null, 2));
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
const before = { cli: VERSION, runtime: installedVersion(cfg.runtime.packageName) };
|
|
248
|
-
console.log(`Updating ReMCP to the latest published version (${runtimeSpec})…`);
|
|
249
|
-
npmGlobalInstall(`${PACKAGE_NAME}@latest`, runtimeSpec);
|
|
250
|
-
// The npm prefix can change across Node-manager upgrades. Repair an existing persistent-service
|
|
251
|
-
// launcher only after the install, when globalCliPath() points at the CLI we just installed.
|
|
252
|
-
ensureServiceIfRecorded(cfg);
|
|
253
|
-
// Only a validated spec is persisted, so a failed update cannot leave the install unable to start.
|
|
254
|
-
if (requested && runtimeSpec !== cfg.runtime.packageSpec) saveConfig({ ...cfg, runtime: { ...cfg.runtime, packageSpec: runtimeSpec } });
|
|
255
|
-
const after = { cli: installedVersion(PACKAGE_NAME), runtime: installedVersion(cfg.runtime.packageName) };
|
|
256
|
-
const restarted = restartPersistentServiceIfInstalled();
|
|
257
|
-
if (restarted) {
|
|
258
|
-
console.log(`ReMCP updated and ${restarted} restarted (client ${before.cli} → ${after.cli ?? '?'}, runtime ${before.runtime ?? '?'} → ${after.runtime ?? '?'}).`);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
// This process is the updater, not the agent: exiting here would restart nothing. The agent sees
|
|
262
|
-
// the exit status, verifies the installed version, and restarts itself.
|
|
263
|
-
console.log(`ReMCP updated (client ${before.cli} → ${after.cli ?? '?'}, runtime ${before.runtime ?? '?'} → ${after.runtime ?? '?'}). The running agent restarts itself to apply it.`);
|
|
264
|
-
if (after.cli === before.cli && after.runtime === before.runtime) {
|
|
265
|
-
console.log('Nothing changed: the installed versions already match the requested ones.');
|
|
266
|
-
}
|
|
211
|
+
await updateCommand(flags);
|
|
267
212
|
return;
|
|
268
213
|
}
|
|
269
214
|
|
|
@@ -278,4 +223,4 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
278
223
|
}
|
|
279
224
|
|
|
280
225
|
printHelp();
|
|
281
|
-
}
|
|
226
|
+
}
|