@remcp/remcp 0.2.189 → 0.2.190
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.190",
|
|
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-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/npm-global.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/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-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/npm-global.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/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": {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Bounded, device-originated HTTPS probe. Never accept a destination from relay callers. */
|
|
2
|
+
export const NETWORK_PROBE_MIN_AGENT = '0.2.190';
|
|
3
|
+
export function agentNetworkProbeSupported(version) {
|
|
4
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:\+[0-9A-Za-z.-]+)?$/.exec(version || '');
|
|
5
|
+
if (!match) return false;
|
|
6
|
+
const parts=match.slice(1).map(Number), min=[0,2,190];
|
|
7
|
+
for(let i=0;i<3;i++) {
|
|
8
|
+
if(parts[i]>min[i])return true;
|
|
9
|
+
if(parts[i]<min[i])return false;
|
|
10
|
+
}
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
export const DEVICE_PROBE_DOWNLOAD_BYTES = 12 * 1024 * 1024;
|
|
14
|
+
export const DEVICE_PROBE_UPLOAD_BYTES = 4 * 1024 * 1024;
|
|
15
|
+
const median = values => [...values].sort((a,b)=>a-b)[Math.floor(values.length/2)];
|
|
16
|
+
const mbps = (bytes,ms) => Math.round(bytes * 8 / ms / 1000 * 10) / 10;
|
|
17
|
+
export async function runAgentNetworkProbe({ serverUrl, deviceToken, signal, request = fetch, clock = () => performance.now() }) {
|
|
18
|
+
if (!/^https:\/\//.test(serverUrl) && !/^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(serverUrl))
|
|
19
|
+
throw new Error('Device network diagnostics require a trusted HTTPS ReMCP endpoint');
|
|
20
|
+
const endpoint = serverUrl + '/api/agent-network-probe';
|
|
21
|
+
const auth = {Authorization:'Bearer ' + deviceToken,'Cache-Control':'no-store'};
|
|
22
|
+
const timeout = AbortSignal.timeout(35_000);
|
|
23
|
+
const combined = signal ? AbortSignal.any([signal,timeout]) : timeout;
|
|
24
|
+
const sample = async (path,options={}) => {
|
|
25
|
+
const started = clock();
|
|
26
|
+
const response = await request(endpoint + path,{...options,headers:{...auth,...options.headers},signal:combined,redirect:'error'});
|
|
27
|
+
if (!response.ok) throw new Error('Device network probe HTTP ' + response.status);
|
|
28
|
+
return {response,elapsed:()=>clock()-started};
|
|
29
|
+
};
|
|
30
|
+
const rtts=[];
|
|
31
|
+
for (let i=0;i<3;i++) {
|
|
32
|
+
const ping=await sample('/ping');
|
|
33
|
+
await ping.response.body?.cancel();
|
|
34
|
+
rtts.push(ping.elapsed());
|
|
35
|
+
}
|
|
36
|
+
const down=await sample('/download');
|
|
37
|
+
let received=0;
|
|
38
|
+
if (!down.response.body) throw new Error('Device download stream unavailable');
|
|
39
|
+
const reader=down.response.body.getReader();
|
|
40
|
+
try {
|
|
41
|
+
while (true) {
|
|
42
|
+
const {value,done}=await reader.read();
|
|
43
|
+
if (done) break;
|
|
44
|
+
received+=value.byteLength;
|
|
45
|
+
if (received>DEVICE_PROBE_DOWNLOAD_BYTES) throw new Error('Invalid device download size');
|
|
46
|
+
}
|
|
47
|
+
} finally { reader.releaseLock(); }
|
|
48
|
+
if (received!==DEVICE_PROBE_DOWNLOAD_BYTES) throw new Error('Incomplete device download');
|
|
49
|
+
const downloadMbps=mbps(received,Math.max(1,down.elapsed()));
|
|
50
|
+
const uploadBody=Buffer.alloc(DEVICE_PROBE_UPLOAD_BYTES,0x5a);
|
|
51
|
+
const up=await sample('/upload',{method:'POST',body:uploadBody,headers:{'Content-Type':'application/octet-stream'}});
|
|
52
|
+
const payload=await up.response.json();
|
|
53
|
+
if (payload?.receivedBytes!==DEVICE_PROBE_UPLOAD_BYTES) throw new Error('Incomplete device upload');
|
|
54
|
+
const uploadMbps=mbps(DEVICE_PROBE_UPLOAD_BYTES,Math.max(1,up.elapsed()));
|
|
55
|
+
return {scope:'device_to_remcp',downloadMbps,uploadMbps,
|
|
56
|
+
latencyMs:Math.round(median(rtts)*10)/10,
|
|
57
|
+
jitterMs:Math.round((Math.abs(rtts[1]-rtts[0])+Math.abs(rtts[2]-rtts[1]))/2*10)/10,
|
|
58
|
+
downloadBytes:received,uploadBytes:DEVICE_PROBE_UPLOAD_BYTES,
|
|
59
|
+
publicIp:typeof payload?.publicIp==='string'?payload.publicIp:null,measuredAt:Date.now()};
|
|
60
|
+
}
|
package/src/agent-responder.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import { VERSION } from './version.mjs';
|
|
3
|
+
import { runAgentNetworkProbe } from './agent-network-probe-client.mjs';
|
|
3
4
|
import { sendRelayMessage } from './agent-utilities.mjs';
|
|
4
5
|
|
|
5
|
-
export function createAgentResponder({ inFlight, onPendingChange, getRuntimeState }) {
|
|
6
|
+
export function createAgentResponder({ inFlight, onPendingChange, getRuntimeState, serverUrl, deviceToken }) {
|
|
6
7
|
async function respond(ws, message) {
|
|
7
8
|
onPendingChange(1);
|
|
8
9
|
// The relay forwards a cancel when the MCP client goes away. Without it a cancelled tool call
|
|
@@ -14,6 +15,8 @@ export function createAgentResponder({ inFlight, onPendingChange, getRuntimeStat
|
|
|
14
15
|
let result;
|
|
15
16
|
if (message.method === 'ping') {
|
|
16
17
|
result = { ok: true, hostname: os.hostname(), platform: process.platform, arch: process.arch, uptimeSeconds: Math.floor(os.uptime()), agentVersion: VERSION, runtimeVersion, runtimeRestarts };
|
|
18
|
+
} else if (message.method === 'network_probe') {
|
|
19
|
+
result = await runAgentNetworkProbe({serverUrl,deviceToken,signal:controller.signal});
|
|
17
20
|
} else if (!runtimeDown && mcp) {
|
|
18
21
|
const options = { timeout: callTimeoutMs, signal: controller.signal };
|
|
19
22
|
if (message.method === 'tools/list') result = await mcp.listTools(undefined, options);
|
package/src/agent.mjs
CHANGED
|
@@ -246,6 +246,7 @@ export async function runAgent(options) {
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
const respond = createAgentResponder({
|
|
249
|
+
serverUrl, deviceToken,
|
|
249
250
|
inFlight,
|
|
250
251
|
onPendingChange: delta => { pendingRequests = Math.max(0, pendingRequests + delta); },
|
|
251
252
|
getRuntimeState: () => ({ mcp, runtimeDown, runtimeVersion, runtimeRestarts, callTimeoutMs }),
|