@remcp/remcp 0.2.212 → 0.2.213
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.
|
|
3
|
+
"version": "0.2.213",
|
|
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/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-task-restart.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/connect.mjs
CHANGED
|
@@ -9,14 +9,13 @@ import { officialOrigin } from './env.mjs';
|
|
|
9
9
|
import { sleep } from './shell.mjs';
|
|
10
10
|
import { printPairingQr } from './pairing-qr.mjs';
|
|
11
11
|
|
|
12
|
-
// Keep the server-provided approval URL as
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// where a URL query containing '&' could otherwise be interpreted as a command.
|
|
12
|
+
// Keep the server-provided approval URL as data all the way to the OS. In particular, do not route it
|
|
13
|
+
// through `cmd /c start` on Windows: cmd reparses metacharacters such as `&`, so a custom pairing
|
|
14
|
+
// server could otherwise turn a verification URL into a local shell command.
|
|
16
15
|
export function browserLaunchCommand(url, platform = process.platform) {
|
|
17
16
|
const target = String(url);
|
|
18
17
|
if (platform === 'darwin') return { command: 'open', args: [target] };
|
|
19
|
-
if (platform === 'win32') return { command: '
|
|
18
|
+
if (platform === 'win32') return { command: 'explorer.exe', args: [target] };
|
|
20
19
|
return { command: 'xdg-open', args: [target] };
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -72,6 +72,19 @@ function inferServiceIdentity() {
|
|
|
72
72
|
|
|
73
73
|
export function canonicalServiceCliPath(config) {
|
|
74
74
|
const configured = String(config?.serviceCliPath || '').trim();
|
|
75
|
+
if (servicePlatform() === 'win32' && configured) {
|
|
76
|
+
// Saved serviceCliPath is the resolved npm package entry (remcp.mjs), not
|
|
77
|
+
// the Windows executable shim. CMD `call remcp.mjs` opens the Windows
|
|
78
|
+
// "Choose an app" dialog instead of starting Node, and dirname(entry) is
|
|
79
|
+
// an invalid NPM_CONFIG_PREFIX. Recover the sibling npm global shim.
|
|
80
|
+
const suffix = '/node_modules/@remcp/remcp/bin/remcp.mjs';
|
|
81
|
+
if (configured.replaceAll('\\', '/').toLowerCase().endsWith(suffix)) {
|
|
82
|
+
const prefix = configured.slice(0, -suffix.length);
|
|
83
|
+
const shim = path.join(prefix, 'remcp.cmd');
|
|
84
|
+
if (!fs.existsSync(shim)) throw new Error('Windows ReMCP npm shim is missing; refusing a .mjs Scheduled Task');
|
|
85
|
+
return shim;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
75
88
|
if (configured && fs.existsSync(configured)) return configured;
|
|
76
89
|
const inferred = inferServiceIdentity().cliPath;
|
|
77
90
|
if (inferred && fs.existsSync(inferred)) return inferred;
|
package/src/cli/service.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { configDir, home, linuxServiceFile, linuxServiceLauncherFile, macService
|
|
|
19
19
|
import { run } from './shell.mjs';
|
|
20
20
|
import { windowsServiceLauncherText, windowsTaskReliabilityCommand, windowsTaskAbsenceCommand, windowsBatchUnsafeProfile, windowsStartupScriptText, windowsStartupScriptInstalled } from './windows-supervisor.mjs';
|
|
21
21
|
import { ensureWindowsSupervisorKey, windowsSupervisorCommand } from './windows-user-supervisor.mjs';
|
|
22
|
+
import { scheduleWindowsTaskRestart } from './windows-task-restart.mjs';
|
|
22
23
|
|
|
23
24
|
export function quoteSystemd(value) {
|
|
24
25
|
return `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
@@ -147,17 +148,6 @@ export function installWindowsService(cliPath = globalCliPath(), { nodePath = pr
|
|
|
147
148
|
installWindowsUserStartup(cliPath, nodePath);
|
|
148
149
|
return;
|
|
149
150
|
}
|
|
150
|
-
// Task Scheduler runs an /IT .cmd inside a *visible* terminal window.
|
|
151
|
-
// For fresh installations prefer our existing hidden WSH + Node supervisor.
|
|
152
|
-
// Keep already-registered scheduled tasks intact until an explicit migration;
|
|
153
|
-
// never start two supervisors for the same machine.
|
|
154
|
-
const previousTask = spawnSync('schtasks.exe', ['/Query', '/TN', windowsTaskName],
|
|
155
|
-
{ stdio:'ignore', windowsHide:true, timeout:10000 });
|
|
156
|
-
if (previousTask.status !== 0) {
|
|
157
|
-
requireWindowsScheduledTaskAbsent();
|
|
158
|
-
installWindowsUserStartup(cliPath, nodePath);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
151
|
const launcherFile = writeWindowsServiceLauncher(cliPath, nodePath);
|
|
162
152
|
const command = `"${launcherFile}"`;
|
|
163
153
|
// Current-user interactive tasks need /IT. /RL LIMITED avoids requiring elevation.
|
|
@@ -347,7 +337,7 @@ export function deferredLinuxAgentRestartArgs(pid) {
|
|
|
347
337
|
'systemctl', '--user', 'restart', 'remcp-agent.service'];
|
|
348
338
|
}
|
|
349
339
|
|
|
350
|
-
export function restartPersistentServiceIfInstalled(config, { deferLinux = false } = {}) {
|
|
340
|
+
export function restartPersistentServiceIfInstalled(config, { deferLinux = false, deferWindows = false } = {}) {
|
|
351
341
|
if (!persistentServiceExpected(config)) return null;
|
|
352
342
|
const platform = servicePlatform();
|
|
353
343
|
if (platform === 'linux' && fs.existsSync(linuxServiceFile)) {
|
|
@@ -382,6 +372,7 @@ export function restartPersistentServiceIfInstalled(config, { deferLinux = false
|
|
|
382
372
|
}
|
|
383
373
|
const result = spawnSync('schtasks.exe', ['/Query', '/TN', windowsTaskName], { stdio: 'ignore' });
|
|
384
374
|
if (result.status === 0) {
|
|
375
|
+
if (deferWindows) return scheduleWindowsTaskRestart(canonicalServiceNodePath(config));
|
|
385
376
|
// /Run does not restart a task that is already running; stop the previous
|
|
386
377
|
// process first so a rotated credential is loaded by the replacement.
|
|
387
378
|
spawnSync('schtasks.exe', ['/End', '/TN', windowsTaskName], { stdio: 'ignore' });
|
|
@@ -24,6 +24,12 @@ export function windowsBatchUnsafeProfile(...paths) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function windowsServiceLauncherText(cliPath, nodePath, { localAppData = process.env.LOCALAPPDATA } = {}) {
|
|
27
|
+
// CMD's `call` executes batch shims, not the underlying .mjs source. A saved
|
|
28
|
+
// package entry is valid for `node.exe remcp.mjs`, but invalid as a Task
|
|
29
|
+
// Scheduler command and must never be written into a persistent launcher.
|
|
30
|
+
if (/\.mjs$/i.test(String(cliPath))) {
|
|
31
|
+
throw new Error('Windows Scheduled Task requires the ReMCP .cmd shim, not a .mjs module');
|
|
32
|
+
}
|
|
27
33
|
// Tests can simulate win32 from a POSIX runner with injected temporary paths.
|
|
28
34
|
// Native Windows paths always use win32 parsing; POSIX-shaped fixtures keep their roots.
|
|
29
35
|
const dirname = file => /^[A-Za-z]:[\\/]/.test(file) || file.includes('\\')
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// ReMCP's confirmed remote God Mode change must acknowledge its command BEFORE
|
|
2
|
+
// killing the agent's own Task Scheduler process tree. Launch a fixed-purpose
|
|
3
|
+
// helper outside the CLI process; never interpolate a user-provided task or shell.
|
|
4
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import process from 'node:process';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { windowsTaskName } from './env.mjs';
|
|
10
|
+
|
|
11
|
+
const helperPath = fileURLToPath(import.meta.url);
|
|
12
|
+
const options = { windowsHide:true, encoding:'utf8', timeout:15000, maxBuffer:32768 };
|
|
13
|
+
|
|
14
|
+
export function scheduleWindowsTaskRestart(nodePath = process.execPath) {
|
|
15
|
+
if (!path.isAbsolute(nodePath) || !fs.existsSync(nodePath)) {
|
|
16
|
+
throw new Error('Windows deferred restart requires the existing Node interpreter');
|
|
17
|
+
}
|
|
18
|
+
const child = spawn(nodePath, [helperPath, '--relaunch'], {
|
|
19
|
+
detached:true, windowsHide:true, stdio:'ignore',
|
|
20
|
+
});
|
|
21
|
+
if (!Number.isSafeInteger(child.pid) || child.pid <= 0) {
|
|
22
|
+
throw new Error('Could not schedule independent Windows ReMCP restart');
|
|
23
|
+
}
|
|
24
|
+
child.unref();
|
|
25
|
+
return 'ReMCP Agent (restart scheduled)';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function executeWindowsTaskRestart({
|
|
29
|
+
pause = ms => new Promise(resolve => setTimeout(resolve,ms)),
|
|
30
|
+
invoke = spawnSync,
|
|
31
|
+
} = {}) {
|
|
32
|
+
await pause(2500);
|
|
33
|
+
// Do not run a second agent if Windows refused to stop the owned task.
|
|
34
|
+
const stopped = invoke('schtasks.exe', ['/End','/TN',windowsTaskName], options);
|
|
35
|
+
if (stopped.error || stopped.status !== 0) {
|
|
36
|
+
throw new Error('Windows ReMCP task could not be stopped safely');
|
|
37
|
+
}
|
|
38
|
+
const started = invoke('schtasks.exe', ['/Run','/TN',windowsTaskName], options);
|
|
39
|
+
if (started.error || started.status !== 0) {
|
|
40
|
+
throw new Error('Windows ReMCP task could not be started after stop');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === helperPath) {
|
|
45
|
+
if (process.argv[2] !== '--relaunch' || process.argv.length !== 3 || process.platform !== 'win32') {
|
|
46
|
+
process.exitCode = 2;
|
|
47
|
+
} else {
|
|
48
|
+
executeWindowsTaskRestart().catch(error => {
|
|
49
|
+
console.error(error.message);
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
package/src/cli.mjs
CHANGED
|
@@ -271,11 +271,12 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
271
271
|
if (!['on', 'off', 'enable', 'disable'].includes(action)) throw new Error('Usage: remcp godmode [status|on|off]');
|
|
272
272
|
const enabled = action === 'on' || action === 'enable';
|
|
273
273
|
writeJsonFile(runtimeConfigFile, { ...readJsonFile(runtimeConfigFile), unrestricted: enabled });
|
|
274
|
-
// The authenticated Workspace supplies this explicit flag
|
|
275
|
-
//
|
|
276
|
-
//
|
|
274
|
+
// The authenticated Workspace supplies this explicit flag so the remote
|
|
275
|
+
// command can acknowledge a policy change before Linux/Windows kills its
|
|
276
|
+
// own agent session. Local CLI operations retain synchronous semantics.
|
|
277
277
|
const restarted = restartPersistentServiceIfInstalled(loadConfig(false),
|
|
278
|
-
{ deferLinux: flags['defer-restart'] === true
|
|
278
|
+
{ deferLinux: flags['defer-restart'] === true,
|
|
279
|
+
deferWindows: flags['defer-restart'] === true });
|
|
279
280
|
if (enabled) {
|
|
280
281
|
console.error('Unrestricted mode is ON for this computer: every path and every command is allowed, including sudo. Anything the model is asked to do -- and anything a prompt injection asks it to do -- can now change this machine. Turn it off with `remcp godmode off` when you are done.');
|
|
281
282
|
} else {
|