@remcp/remcp 0.2.216 → 0.2.218
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.218",
|
|
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-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",
|
|
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-update-lock.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/npm-global.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { PACKAGE_NAME } from '../version.mjs';
|
|
|
6
6
|
import { home, npm } from './env.mjs';
|
|
7
7
|
import { buildVettedMacTemperatureAddon } from './mac-native-temperature.mjs';
|
|
8
8
|
import { output, run } from './shell.mjs';
|
|
9
|
+
import { withWindowsUpdateLock } from './windows-update-lock.mjs';
|
|
9
10
|
|
|
10
11
|
export function servicePlatform() {
|
|
11
12
|
return process.env.NODE_ENV === 'test' && process.env.REMCP_TEST_PLATFORM ? process.env.REMCP_TEST_PLATFORM : process.platform;
|
|
@@ -53,8 +54,10 @@ function npmGlobalInstallArgs(specs, { preferOnline = false } = {}) {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export function npmGlobalInstall(...specs) {
|
|
56
|
-
assertSafeReMcpNpmPrefix(globalPrefix()); // Reject an unsafe prefix before npm writes anything.
|
|
57
|
-
run(npm.command, [...npm.args, ...npmGlobalInstallArgs(specs)]);
|
|
57
|
+
const prefix = assertSafeReMcpNpmPrefix(globalPrefix()); // Reject an unsafe prefix before npm writes anything.
|
|
58
|
+
const install = () => run(npm.command, [...npm.args, ...npmGlobalInstallArgs(specs)]);
|
|
59
|
+
if (servicePlatform() === 'win32') withWindowsUpdateLock(prefix, install);
|
|
60
|
+
else install();
|
|
58
61
|
if (servicePlatform() === 'darwin' && specs.some(spec => /^@remcp\/runtime@[0-9]/.test(spec))) {
|
|
59
62
|
try {
|
|
60
63
|
buildVettedMacTemperatureAddon({
|
|
@@ -241,7 +244,7 @@ export function npmGlobalUpdate(packageNames, ...specs) {
|
|
|
241
244
|
return;
|
|
242
245
|
}
|
|
243
246
|
if (platform === 'win32') {
|
|
244
|
-
transactionalWindowsGlobalInstall({ resolved:npm, prefix, packageNames, specs, preferOnline:true });
|
|
247
|
+
withWindowsUpdateLock(prefix, () => transactionalWindowsGlobalInstall({ resolved:npm, prefix, packageNames, specs, preferOnline:true }));
|
|
245
248
|
return;
|
|
246
249
|
}
|
|
247
250
|
run(npm.command, [...npm.args, ...npmGlobalInstallArgs(specs, { preferOnline:true })]);
|
|
@@ -256,7 +259,7 @@ export function npmGlobalUpdateForNode(nodePath, packageNames, ...specs) {
|
|
|
256
259
|
return;
|
|
257
260
|
}
|
|
258
261
|
if (platform === 'win32') {
|
|
259
|
-
transactionalWindowsGlobalInstall({ resolved, prefix, packageNames, specs, preferOnline:true });
|
|
262
|
+
withWindowsUpdateLock(prefix, () => transactionalWindowsGlobalInstall({ resolved, prefix, packageNames, specs, preferOnline:true }));
|
|
260
263
|
return;
|
|
261
264
|
}
|
|
262
265
|
run(resolved.command, [...resolved.args, ...npmGlobalInstallArgs(specs, { preferOnline:true })]);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Serializes writes to one Windows npm global prefix across manual and agent updates.
|
|
2
|
+
// The lock exists outside the npm-managed package/shim trees and is held through rollback.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
import process from 'node:process';
|
|
7
|
+
|
|
8
|
+
const LOCK_NAME = '.remcp-update-lock';
|
|
9
|
+
const OWNER_NAME = 'owner.json';
|
|
10
|
+
|
|
11
|
+
function lockOwnerFile(directory) {
|
|
12
|
+
return path.join(directory, OWNER_NAME);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function ownerIsLive(directory) {
|
|
16
|
+
try {
|
|
17
|
+
const owner = JSON.parse(fs.readFileSync(lockOwnerFile(directory), 'utf8'));
|
|
18
|
+
if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0) return true;
|
|
19
|
+
process.kill(owner.pid, 0);
|
|
20
|
+
return true;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
// On Windows and Unix, only ESRCH is reliable evidence the owner has exited.
|
|
23
|
+
// Permission errors and damaged/incomplete owner metadata must fail closed.
|
|
24
|
+
return error?.code !== 'ESRCH';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function removeOwnedLock(directory, token) {
|
|
29
|
+
let owner;
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.lstatSync(directory).isDirectory()) return;
|
|
32
|
+
owner = JSON.parse(fs.readFileSync(lockOwnerFile(directory), 'utf8'));
|
|
33
|
+
} catch { return; }
|
|
34
|
+
if (owner.token !== token || owner.pid !== process.pid) return;
|
|
35
|
+
try {
|
|
36
|
+
fs.unlinkSync(lockOwnerFile(directory));
|
|
37
|
+
fs.rmdirSync(directory);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// Never force-remove unexpected files created by another process.
|
|
40
|
+
if (error?.code !== 'ENOENT') console.error('ReMCP update lock cleanup failed:', error?.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function withWindowsUpdateLock(prefix, operation) {
|
|
45
|
+
if (!path.isAbsolute(prefix)) throw new Error('Windows update lock requires an absolute npm prefix.');
|
|
46
|
+
if (typeof operation !== 'function') throw new TypeError('Windows update operation is required.');
|
|
47
|
+
fs.mkdirSync(prefix, { recursive:true });
|
|
48
|
+
const directory = path.join(prefix, LOCK_NAME);
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(directory, { mode:0o700 });
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
53
|
+
const stale = !ownerIsLive(directory);
|
|
54
|
+
throw new Error(stale
|
|
55
|
+
? 'ReMCP previous Windows update exited without releasing its lock; repair the stale update lock before retrying.'
|
|
56
|
+
: 'ReMCP update already in progress for this Windows npm prefix; retry after it finishes.');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const token = crypto.randomUUID();
|
|
60
|
+
try {
|
|
61
|
+
fs.writeFileSync(lockOwnerFile(directory), JSON.stringify({
|
|
62
|
+
pid:process.pid, token, startedAt:Date.now(),
|
|
63
|
+
}), { flag:'wx', mode:0o600 });
|
|
64
|
+
} catch (error) {
|
|
65
|
+
try { fs.rmdirSync(directory); } catch {}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const onExit = () => removeOwnedLock(directory, token);
|
|
70
|
+
process.once('exit', onExit);
|
|
71
|
+
try {
|
|
72
|
+
return operation();
|
|
73
|
+
} finally {
|
|
74
|
+
process.removeListener('exit', onExit);
|
|
75
|
+
removeOwnedLock(directory, token);
|
|
76
|
+
}
|
|
77
|
+
}
|