@remcp/remcp 0.2.116 → 0.2.117

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
@@ -9,10 +9,12 @@ remcp --version
9
9
  remcp status
10
10
  ```
11
11
 
12
- Pairing commands are generated in the workspace at <https://remcp.site/app/connect>. The
13
- generated command runs `remcp connect --server … --code … --install`, which stores a per-device
14
- credential under `~/.config/remcp/`, installs the runtime from npm, and registers a user service
15
- (systemd on Linux, LaunchAgent on macOS, Scheduled Task on Windows).
12
+ Pairing commands are generated in the workspace at <https://remcp.site/app/connect>. On Windows,
13
+ the browser shows a PowerShell bootstrap that needs no preinstalled system Node.js/npm/npx/winget;
14
+ it keeps the ReMCP runtime under `%LOCALAPPDATA%\ReMCP`. Generated one-time commands ultimately run
15
+ `remcp connect --server … --code … --install`, which stores a per-device credential, installs the
16
+ runtime, and registers a user service (systemd on Linux, LaunchAgent on macOS, Scheduled Task on
17
+ Windows).
16
18
 
17
19
  ## Commands
18
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/remcp",
3
- "version": "0.2.116",
3
+ "version": "0.2.117",
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",
package/src/cli/env.mjs CHANGED
@@ -16,6 +16,7 @@ export const macServiceLabel = 'com.remcp.agent';
16
16
  export const macServiceFile = path.join(home, 'Library', 'LaunchAgents', `${macServiceLabel}.plist`);
17
17
  export const macLogFile = path.join(home, 'Library', 'Logs', 'remcp-agent.log');
18
18
  export const windowsTaskName = 'ReMCP Agent';
19
+ export const windowsServiceLauncherFile = path.join(configDir, 'remcp-agent.cmd');
19
20
  // How npm is invoked is resolved from the running node when possible: a background service has a
20
21
  // minimal PATH, which is why auto-update used to find no npm on macOS. See src/npm.mjs.
21
22
  export const npm = resolveNpm();
@@ -9,7 +9,7 @@ import { resolveNpm } from '../npm.mjs';
9
9
  import { PACKAGE_NAME, VERSION } from '../version.mjs';
10
10
 
11
11
  import { saveConfig } from './config.mjs';
12
- import { configDir, home, linuxServiceFile, linuxServiceLauncherFile, macLogFile, macServiceFile, macServiceLabel, npm, windowsTaskName } from './env.mjs';
12
+ import { configDir, home, linuxServiceFile, linuxServiceLauncherFile, macLogFile, macServiceFile, macServiceLabel, npm, windowsServiceLauncherFile, windowsTaskName } from './env.mjs';
13
13
  import { output, run } from './shell.mjs';
14
14
 
15
15
  export function servicePlatform() {
@@ -350,8 +350,32 @@ export function installMacService(cliPath = globalCliPath(), { restart = true, n
350
350
  return 'bootstrapped';
351
351
  }
352
352
 
353
- export function installWindowsService(cliPath = globalCliPath()) {
354
- const command = `"${cliPath}" start --service`;
353
+ function windowsBatchValue(value) {
354
+ return String(value).replaceAll('%', '%%');
355
+ }
356
+
357
+ export function writeWindowsServiceLauncher(cliPath = globalCliPath(), nodePath = process.execPath) {
358
+ const prefix = path.dirname(cliPath);
359
+ const nodeDir = path.dirname(nodePath);
360
+ const launcher = [
361
+ '@echo off',
362
+ 'setlocal DisableDelayedExpansion',
363
+ `set "NPM_CONFIG_PREFIX=${windowsBatchValue(prefix)}"`,
364
+ `set "PATH=${windowsBatchValue(nodeDir)};%PATH%"`,
365
+ `call "${windowsBatchValue(cliPath)}" start --service`,
366
+ 'exit /b %ERRORLEVEL%',
367
+ '',
368
+ ].join('\r\n');
369
+ fs.mkdirSync(configDir, { recursive:true, mode:0o700 });
370
+ const temporary = `${windowsServiceLauncherFile}.${process.pid}.${Date.now()}.tmp`;
371
+ fs.writeFileSync(temporary, launcher, { mode:0o700 });
372
+ fs.renameSync(temporary, windowsServiceLauncherFile);
373
+ return windowsServiceLauncherFile;
374
+ }
375
+
376
+ export function installWindowsService(cliPath = globalCliPath(), { nodePath = process.execPath } = {}) {
377
+ const launcherFile = writeWindowsServiceLauncher(cliPath, nodePath);
378
+ const command = `"${launcherFile}"`;
355
379
  run('schtasks.exe', ['/Create', '/TN', windowsTaskName, '/TR', command, '/SC', 'ONLOGON', '/RL', 'HIGHEST', '/F']);
356
380
  run('schtasks.exe', ['/Run', '/TN', windowsTaskName]);
357
381
  }
@@ -450,13 +474,24 @@ function packageVersionBesideCli(cliPath, packageName) {
450
474
  return versionFromPackageJson(path.join(nodeModules, ...String(packageName).split('/'), 'package.json'));
451
475
  }
452
476
 
477
+ function packageVersionForCli(cliPath, packageName) {
478
+ const beside = packageVersionBesideCli(cliPath, packageName);
479
+ if (beside || servicePlatform() !== 'win32' || !cliPath || !packageName) return beside;
480
+ // npm's Windows global executable is a .cmd shim in the prefix root, not a symlink into
481
+ // node_modules. realpath() therefore cannot reveal the package directory like it does on Unix.
482
+ // Resolve the package from the same prefix layout npm uses on Windows.
483
+ if (path.basename(cliPath).toLowerCase() !== 'remcp.cmd') return null;
484
+ const prefix = path.dirname(cliPath);
485
+ return versionFromPackageJson(path.join(prefix, 'node_modules', ...String(packageName).split('/'), 'package.json'));
486
+ }
487
+
453
488
  export function installationVersionsAtCliPath(cliPath, runtimePackageName) {
454
489
  if (!cliPath) return { cliVersion:null, runtimeVersion:null, cliPath:null };
455
490
  let resolvedCliPath = cliPath;
456
491
  try { if (fs.existsSync(cliPath)) resolvedCliPath = fs.realpathSync(cliPath); } catch {}
457
492
  return {
458
- cliVersion:packageVersionBesideCli(resolvedCliPath, PACKAGE_NAME),
459
- runtimeVersion:packageVersionBesideCli(resolvedCliPath, runtimePackageName),
493
+ cliVersion:packageVersionForCli(resolvedCliPath, PACKAGE_NAME),
494
+ runtimeVersion:packageVersionForCli(resolvedCliPath, runtimePackageName),
460
495
  cliPath:resolvedCliPath,
461
496
  };
462
497
  }
@@ -468,8 +503,8 @@ export function serviceInstallationInfo(config) {
468
503
  let resolvedCliPath = cliPath;
469
504
  try { if (cliPath && fs.existsSync(cliPath)) resolvedCliPath = fs.realpathSync(cliPath); } catch {}
470
505
  return {
471
- cliVersion:packageVersionBesideCli(resolvedCliPath, PACKAGE_NAME),
472
- runtimeVersion:packageVersionBesideCli(resolvedCliPath, config?.runtime?.packageName),
506
+ cliVersion:packageVersionForCli(resolvedCliPath, PACKAGE_NAME),
507
+ runtimeVersion:packageVersionForCli(resolvedCliPath, config?.runtime?.packageName),
473
508
  cliPath:resolvedCliPath || cliPath,
474
509
  nodePath,
475
510
  };
@@ -480,7 +515,7 @@ export function currentInstallationInfo(config) {
480
515
  try { if (cliPath && fs.existsSync(cliPath)) cliPath = fs.realpathSync(cliPath); } catch {}
481
516
  return {
482
517
  cliVersion:VERSION,
483
- runtimeVersion:config?.runtime?.packageName ? packageVersionBesideCli(cliPath, config.runtime.packageName) : null,
518
+ runtimeVersion:config?.runtime?.packageName ? packageVersionForCli(cliPath, config.runtime.packageName) : null,
484
519
  cliPath:cliPath || null,
485
520
  nodePath:process.execPath,
486
521
  };
@@ -599,7 +634,7 @@ export function installPersistentAgent(config) {
599
634
  else if (platform === 'darwin') {
600
635
  installMacService(cliPath, { nodePath });
601
636
  ensureMacCliCommand(config, { cliPath:cliScript, nodePath });
602
- } else installWindowsService(cliPath);
637
+ } else installWindowsService(cliPath, { nodePath });
603
638
  configurePostInstallAccess();
604
639
  saveConfig({ ...config, serviceInstalled: true, serviceCliPath: cliScript, serviceNodePath: nodePath });
605
640
  console.log('ReMCP is installed as a background service. Future updates: remcp update');
@@ -663,7 +698,7 @@ export function ensureServiceIfRecorded(config) {
663
698
  const state = installMacService(cliPath, { restart: false, nodePath });
664
699
  ensureMacCliCommand(config, { cliPath, nodePath });
665
700
  if (state === 'reload-scheduled') restartScheduled = macServiceLabel;
666
- } else if (platform === 'win32') installWindowsService(cliPath);
701
+ } else if (platform === 'win32') installWindowsService(cliPath, { nodePath });
667
702
  // Upgrade the legacy inferred state only after the supervisor repair succeeded. A failed repair
668
703
  // must not turn a stale artifact into a permanent "managed service" declaration.
669
704
  const cliScript = resolvedCliScript(cliPath);
@@ -722,5 +757,6 @@ export function uninstallPersistentService() {
722
757
  } else if (platform === 'win32') {
723
758
  spawnSync('schtasks.exe', ['/End', '/TN', windowsTaskName], { stdio: 'ignore' });
724
759
  spawnSync('schtasks.exe', ['/Delete', '/TN', windowsTaskName, '/F'], { stdio: 'ignore' });
760
+ try { fs.unlinkSync(windowsServiceLauncherFile); } catch {}
725
761
  }
726
762
  }