@remcp/remcp 0.2.211 → 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.211",
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": {
@@ -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;
@@ -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('"', '\\"')}"`;
@@ -336,7 +337,7 @@ export function deferredLinuxAgentRestartArgs(pid) {
336
337
  'systemctl', '--user', 'restart', 'remcp-agent.service'];
337
338
  }
338
339
 
339
- export function restartPersistentServiceIfInstalled(config, { deferLinux = false } = {}) {
340
+ export function restartPersistentServiceIfInstalled(config, { deferLinux = false, deferWindows = false } = {}) {
340
341
  if (!persistentServiceExpected(config)) return null;
341
342
  const platform = servicePlatform();
342
343
  if (platform === 'linux' && fs.existsSync(linuxServiceFile)) {
@@ -371,6 +372,7 @@ export function restartPersistentServiceIfInstalled(config, { deferLinux = false
371
372
  }
372
373
  const result = spawnSync('schtasks.exe', ['/Query', '/TN', windowsTaskName], { stdio: 'ignore' });
373
374
  if (result.status === 0) {
375
+ if (deferWindows) return scheduleWindowsTaskRestart(canonicalServiceNodePath(config));
374
376
  // /Run does not restart a task that is already running; stop the previous
375
377
  // process first so a rotated credential is loaded by the replacement.
376
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 to allow the
275
- // remote command to finish before its agent schedules a Linux restart.
276
- // Local CLI and normal update operations retain synchronous semantics.
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 {