@remcp/runtime 0.2.215 → 0.2.217
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
|
@@ -16,19 +16,30 @@ import { findOcrText, ocrImage } from './ocr.mjs';
|
|
|
16
16
|
import { boundedComputerSnapshot, clipboard, cursorPosition, displayInventory, listWindows, objectRecord, resultValue, screenshotRegion, snapshotError, suggestedUiAction, toolResultMessage, uiSnapshot } from './desktop-core.mjs';
|
|
17
17
|
import { activeWindowCaptureBounds, browserTitleHint, computerScreenshotTask, hasExplicitComputerScreenshotTarget, ocrScreenshotResult } from './desktop-vision.mjs';
|
|
18
18
|
|
|
19
|
-
//
|
|
20
|
-
// timeout on low-core hosts,
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
// Four simultaneous Windows PowerShell starts can stall until their shared
|
|
20
|
+
// timeout on low-core hosts, while two finish normally. Keep a small pool on
|
|
21
|
+
// Windows so the snapshot stays under the relay timeout; other platforms stay
|
|
22
|
+
// fully parallel.
|
|
23
|
+
export async function settleComputerSnapshotProbes(tasks, {
|
|
24
|
+
concurrency = process.platform === 'win32' ? 2 : Math.max(tasks.length, 1),
|
|
25
|
+
} = {}) {
|
|
26
|
+
const limit = Math.max(1, Math.min(Number(concurrency) || 1, tasks.length || 1));
|
|
27
|
+
if (limit >= tasks.length) return Promise.allSettled(tasks.map((task) => task()));
|
|
28
|
+
const results = new Array(tasks.length);
|
|
29
|
+
let next = 0;
|
|
30
|
+
const worker = async () => {
|
|
31
|
+
for (;;) {
|
|
32
|
+
const index = next;
|
|
33
|
+
next += 1;
|
|
34
|
+
if (index >= tasks.length) return;
|
|
35
|
+
try {
|
|
36
|
+
results[index] = { status: 'fulfilled', value: await tasks[index]() };
|
|
37
|
+
} catch (reason) {
|
|
38
|
+
results[index] = { status: 'rejected', reason };
|
|
39
|
+
}
|
|
30
40
|
}
|
|
31
|
-
}
|
|
41
|
+
};
|
|
42
|
+
await Promise.all(Array.from({ length: limit }, () => worker()));
|
|
32
43
|
return results;
|
|
33
44
|
}
|
|
34
45
|
|
|
@@ -3,7 +3,7 @@ import os from 'node:os';
|
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
5
5
|
import { constants, createReadStream } from 'node:fs';
|
|
6
|
-
import { access, chmod, chown, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, rename, rm, stat, unlink } from 'node:fs/promises';
|
|
6
|
+
import { access, chmod, chown, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, unlink } from 'node:fs/promises';
|
|
7
7
|
import { pipeline } from 'node:stream/promises';
|
|
8
8
|
import { liveConfig, runtimeConfig } from '../config.mjs';
|
|
9
9
|
import { documentKind, readDocxText, readPdfText } from '../documents.mjs';
|
|
@@ -316,14 +316,43 @@ export async function openRegularFile(absolute, flags = READ_NOFOLLOW) {
|
|
|
316
316
|
return openRegularFileAtPath(absolute, flags, absolute);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
async function openLinuxDirectoryAnchor(absolute, options = {}) {
|
|
320
|
+
try {
|
|
321
|
+
return await openDirectoryPath(absolute, options);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
if (runtimeConfig.allowedRoots.length) throw error;
|
|
324
|
+
// Unrestricted mode may traverse trusted links such as macOS-style aliases
|
|
325
|
+
// or user-created symlinked directories. Resolve only the anchor; returned
|
|
326
|
+
// tool paths stay lexical so the caller still sees the path it supplied.
|
|
327
|
+
if (options.create) await mkdir(absolute, { recursive:true }).catch(() => {});
|
|
328
|
+
const resolved = await realpath(absolute).catch(() => null);
|
|
329
|
+
if (!resolved) throw error;
|
|
330
|
+
return openDirectoryPath(resolved, options);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function trustedNonLinuxDirectory(absolute, { create = false } = {}) {
|
|
335
|
+
if (create) await mkdir(absolute, { recursive: true });
|
|
336
|
+
const info = await lstat(absolute);
|
|
337
|
+
if (info.isSymbolicLink()) {
|
|
338
|
+
// With configured roots the path is already canonicalized; a remaining
|
|
339
|
+
// symlink component could be a confinement escape. Only unrestricted mode
|
|
340
|
+
// may follow trusted system links such as macOS /tmp -> /private/tmp.
|
|
341
|
+
if (runtimeConfig.allowedRoots.length) fail('Filesystem path component is not a directory');
|
|
342
|
+
const resolved = await realpath(absolute);
|
|
343
|
+
const target = await stat(resolved);
|
|
344
|
+
if (!target.isDirectory()) fail('Filesystem path component is not a directory');
|
|
345
|
+
return resolved;
|
|
346
|
+
}
|
|
347
|
+
if (!info.isDirectory()) fail('Filesystem path component is not a directory');
|
|
348
|
+
return absolute;
|
|
349
|
+
}
|
|
350
|
+
|
|
319
351
|
export async function openWritableParent(absolute, options = {}) {
|
|
320
352
|
if (process.platform === 'linux' && constants.O_NOFOLLOW && constants.O_DIRECTORY) {
|
|
321
|
-
return
|
|
353
|
+
return openLinuxDirectoryAnchor(absolute, { create:true, ...options });
|
|
322
354
|
}
|
|
323
|
-
await
|
|
324
|
-
const info = await lstat(absolute);
|
|
325
|
-
if (info.isSymbolicLink() || !info.isDirectory()) fail('Filesystem path component is not a directory');
|
|
326
|
-
return { anchor:absolute, close:async() => {} };
|
|
355
|
+
return { anchor: await trustedNonLinuxDirectory(absolute, { create:true }), close:async() => {} };
|
|
327
356
|
}
|
|
328
357
|
|
|
329
358
|
export async function writeResolvedFile(absolute, content, { append = false } = {}) {
|
|
@@ -352,11 +381,9 @@ export async function withWritableParent(absolute, callback) {
|
|
|
352
381
|
|
|
353
382
|
export async function openExistingParent(absolute) {
|
|
354
383
|
if (process.platform === 'linux' && constants.O_NOFOLLOW && constants.O_DIRECTORY) {
|
|
355
|
-
return
|
|
384
|
+
return openLinuxDirectoryAnchor(absolute);
|
|
356
385
|
}
|
|
357
|
-
|
|
358
|
-
if (info.isSymbolicLink() || !info.isDirectory()) fail('Filesystem path component is not a directory');
|
|
359
|
-
return { anchor:absolute, close:async() => {} };
|
|
386
|
+
return { anchor: await trustedNonLinuxDirectory(absolute), close:async() => {} };
|
|
360
387
|
}
|
|
361
388
|
|
|
362
389
|
export async function withExistingParent(absolute, callback) {
|
|
@@ -370,13 +397,11 @@ export async function withExistingParent(absolute, callback) {
|
|
|
370
397
|
|
|
371
398
|
export async function ensureDirectoryPath(absolute, options = {}) {
|
|
372
399
|
if (process.platform === 'linux' && constants.O_NOFOLLOW && constants.O_DIRECTORY) {
|
|
373
|
-
const directory = await
|
|
400
|
+
const directory = await openLinuxDirectoryAnchor(absolute, { create:true, ...options });
|
|
374
401
|
await directory.close();
|
|
375
402
|
return;
|
|
376
403
|
}
|
|
377
|
-
await
|
|
378
|
-
const info = await lstat(absolute);
|
|
379
|
-
if (info.isSymbolicLink() || !info.isDirectory()) fail('Filesystem path component is not a directory');
|
|
404
|
+
await trustedNonLinuxDirectory(absolute, { create:true });
|
|
380
405
|
}
|
|
381
406
|
|
|
382
407
|
export async function readRegularBuffer(absolute, maxBytes = Infinity) {
|
package/src/tools/system.mjs
CHANGED
|
@@ -111,7 +111,7 @@ export async function listProcessesTool(args) {
|
|
|
111
111
|
}
|
|
112
112
|
parsed.sort((a, b) => b.cpu - a.cpu || b.mem - a.mem);
|
|
113
113
|
}
|
|
114
|
-
rows.push('pid,ppid,cpu%,mem%,elapsed,command');
|
|
114
|
+
rows.push(process.platform === 'win32' ? 'name,pid,session,mem' : 'pid,ppid,cpu%,mem%,elapsed,command');
|
|
115
115
|
rows.push(...parsed.slice(0, limit).map(entry => entry.row));
|
|
116
116
|
if (parsed.length > limit) rows.push(`… ${parsed.length - limit} more processes hidden; call list_processes with a higher limit to see them`);
|
|
117
117
|
return text(rows.join('\n'));
|