@remcp/runtime 0.2.230 → 0.2.232
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
package/src/error-classes.mjs
CHANGED
|
@@ -39,6 +39,20 @@ export function classifyRuntimeToolError(error, toolName = '') {
|
|
|
39
39
|
return 'browser_navigation_failed';
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
// Expected, local session expiry and nonmatching edit requests should not be
|
|
43
|
+
// indistinguishable from runtime crashes. Use only fixed signatures scoped to
|
|
44
|
+
// the affected tool; never emit process IDs, file content or error messages.
|
|
45
|
+
if (error instanceof ToolError) {
|
|
46
|
+
if (['read_process_output','wait_for_process_output','force_terminate','interact_with_process']
|
|
47
|
+
.includes(toolName) && /^no remcp session with pid [0-9]+$/.test(message)) return 'not_found';
|
|
48
|
+
if (toolName === 'edit_block') {
|
|
49
|
+
if (/^old_string was not found in the file(?:, even after whitespace normalization)?$/.test(message)) return 'not_found';
|
|
50
|
+
if (/^expected [0-9]+ occurrence\(s\) of old_string but found [0-9]+\. add more surrounding context\.$/.test(message) ||
|
|
51
|
+
/^old_string matched [0-9]+ block\(s\) after whitespace normalization, expected [0-9]+\. add more surrounding context\.$/.test(message)) {
|
|
52
|
+
return 'invalid_input';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
42
56
|
if (/permission denied|access is denied|not authorized|not authorised|accessibility permission/.test(message)) return 'permission';
|
|
43
57
|
if (/\b(?:file|path|source) not found\b|\bis not a directory\b/.test(message)) return 'not_found';
|
|
44
58
|
if (/timed out|timeout after|time limit exceeded/.test(message)) return 'timeout';
|
|
@@ -60,17 +60,17 @@ export async function createArchiveTool(args, extra = {}) {
|
|
|
60
60
|
if (format === 'zip') {
|
|
61
61
|
if (process.platform === 'win32') {
|
|
62
62
|
if (!tools.tar) fail('Windows archive backend is unavailable: tar.exe is required');
|
|
63
|
-
const result = spawnSync(tools.tar, ['-a', '-c', '-f', staging, '--', ...archiveNames], { cwd: snapshotRoot, encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024 });
|
|
63
|
+
const result = spawnSync(tools.tar, ['-a', '-c', '-f', staging, '--', ...archiveNames], { cwd: snapshotRoot, encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
64
64
|
if (result.status !== 0) fail(`tar.exe ZIP creation failed: ${(result.stderr || result.stdout || '').trim() || `exit ${result.status}`}`);
|
|
65
65
|
} else {
|
|
66
66
|
if (!tools.zip) fail('zip is not installed on this device; use format "tar.gz"');
|
|
67
|
-
const result = spawnSync(tools.zip, ['-r', '-q', '-y', staging, '--', ...archiveNames], { cwd: snapshotRoot, encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024 });
|
|
67
|
+
const result = spawnSync(tools.zip, ['-r', '-q', '-y', staging, '--', ...archiveNames], { cwd: snapshotRoot, encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
68
68
|
if (result.status !== 0) fail(`zip failed: ${(result.stderr || result.stdout || '').trim() || `exit ${result.status}`}`);
|
|
69
69
|
}
|
|
70
70
|
} else if (format === 'tar' || format === 'tar.gz' || format === 'tgz') {
|
|
71
71
|
if (!tools.tar) fail('tar is not installed on this device');
|
|
72
72
|
const flags = format === 'tar' ? '-cf' : '-czf';
|
|
73
|
-
const result = spawnSync(tools.tar, [flags, staging, '--', ...archiveNames], { cwd: snapshotRoot, encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024 });
|
|
73
|
+
const result = spawnSync(tools.tar, [flags, staging, '--', ...archiveNames], { cwd: snapshotRoot, encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
74
74
|
if (result.status !== 0) fail(`tar failed: ${(result.stderr || result.stdout || '').trim() || `exit ${result.status}`}`);
|
|
75
75
|
} else {
|
|
76
76
|
fail('format must be tar, tar.gz, or zip');
|
|
@@ -124,34 +124,34 @@ export async function extractArchiveTool(args) {
|
|
|
124
124
|
if (existingDestination?.isSymbolicLink() || (existingDestination && !existingDestination.isDirectory())) fail('Archive destination is not a safe directory');
|
|
125
125
|
if (format === 'zip' && process.platform !== 'win32') {
|
|
126
126
|
if (!tools.unzip) fail('unzip is not installed on this device');
|
|
127
|
-
const listing = spawnSync(tools.unzip, ['-Z', '-1', archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024 });
|
|
127
|
+
const listing = spawnSync(tools.unzip, ['-Z', '-1', archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
128
128
|
if (listing.error || listing.status !== 0) fail(`Could not inspect ZIP archive: ${(listing.stderr || listing.error?.message || `exit ${listing.status}`).trim()}`);
|
|
129
129
|
const names = String(listing.stdout || '').split(/\r?\n/).filter(Boolean);
|
|
130
130
|
if (names.length > 100_000 || names.some(name => !safeArchiveEntry(name))) fail('ZIP archive contains an unsafe path or too many entries');
|
|
131
|
-
const details = spawnSync(tools.unzip, ['-Z', '-v', archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024 });
|
|
131
|
+
const details = spawnSync(tools.unzip, ['-Z', '-v', archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
132
132
|
if (details.error || details.status !== 0) fail(`Could not inspect ZIP sizes: ${(details.stderr || details.error?.message || `exit ${details.status}`).trim()}`);
|
|
133
133
|
const detailText = String(details.stdout || '');
|
|
134
134
|
if (/symbolic link|Unix file attributes \([^)]*\b12\d{4}/i.test(detailText)) fail('ZIP archive contains a symbolic link');
|
|
135
135
|
const expandedBytes = archiveExpandedSize(detailText.split(/\r?\n/), /uncompressed size:\s*(\d+)\s*bytes/i, 'ZIP archive', names.length);
|
|
136
136
|
assertArchiveExpandedSize(expandedBytes, 'ZIP archive');
|
|
137
|
-
const result = spawnSync(tools.unzip, ['-o', '-q', archiveSnapshot, '-d', staging], { encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024 });
|
|
137
|
+
const result = spawnSync(tools.unzip, ['-o', '-q', archiveSnapshot, '-d', staging], { encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
138
138
|
if (result.status !== 0) fail(`unzip failed: ${(result.stderr || result.stdout || '').trim() || `exit ${result.status}`}`);
|
|
139
139
|
} else {
|
|
140
140
|
if (!tools.tar) fail(process.platform === 'win32' ? 'Windows archive backend is unavailable: tar.exe is required' : 'tar is not installed on this device');
|
|
141
141
|
const listFlags = format === 'tar.gz' ? '-tzf' : format === 'tar.bz2' ? '-tjf' : format === 'tar.xz' ? '-tJf' : '-tf';
|
|
142
|
-
const listing = spawnSync(tools.tar, [listFlags, archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024 });
|
|
142
|
+
const listing = spawnSync(tools.tar, [listFlags, archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
143
143
|
if (listing.error || listing.status !== 0) fail(`Could not inspect archive: ${(listing.stderr || listing.error?.message || `exit ${listing.status}`).trim()}`);
|
|
144
144
|
const names = String(listing.stdout || '').split(/\r?\n/).filter(Boolean);
|
|
145
145
|
if (names.length > 100_000 || names.some(name => !safeArchiveEntry(name))) fail('Archive contains an unsafe path or too many entries');
|
|
146
146
|
const verboseFlags = format === 'tar.gz' ? '-tvzf' : format === 'tar.bz2' ? '-tvjf' : format === 'tar.xz' ? '-tvJf' : '-tvf';
|
|
147
|
-
const details = spawnSync(tools.tar, [verboseFlags, archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024 });
|
|
147
|
+
const details = spawnSync(tools.tar, [verboseFlags, archiveSnapshot], { encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
148
148
|
if (details.error || details.status !== 0) fail(`Could not inspect archive sizes: ${(details.stderr || details.error?.message || `exit ${details.status}`).trim()}`);
|
|
149
149
|
const detailLines = String(details.stdout || '').split(/\r?\n/);
|
|
150
150
|
if (detailLines.some(line => /^[bclph]/.test(line))) fail('Archive contains a link or special file; archive destination is unsafe');
|
|
151
151
|
const expandedBytes = archiveTarExpandedSize(detailLines, 'archive', names.length);
|
|
152
152
|
assertArchiveExpandedSize(expandedBytes, 'archive');
|
|
153
153
|
const flags = format === 'tar.gz' ? '-xzf' : format === 'tar.bz2' ? '-xjf' : format === 'tar.xz' ? '-xJf' : '-xf';
|
|
154
|
-
const result = spawnSync(tools.tar, [flags, archiveSnapshot, '-C', staging], { encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024 });
|
|
154
|
+
const result = spawnSync(tools.tar, [flags, archiveSnapshot, '-C', staging], { encoding: 'utf8', timeout: 120_000, maxBuffer: 16 * 1024 * 1024, windowsHide:true });
|
|
155
155
|
if (result.status !== 0) fail(`tar failed: ${(result.stderr || result.stdout || '').trim() || `exit ${result.status}`}`);
|
|
156
156
|
}
|
|
157
157
|
await assertExtractedTree(staging);
|
|
@@ -368,7 +368,7 @@ export async function moveArchiveFile(source, destination) {
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
export function archiveCommandProbe(name, versionArgs = ['--version']) {
|
|
371
|
-
const result = spawnSync(name, versionArgs, { encoding: 'utf8' });
|
|
371
|
+
const result = spawnSync(name, versionArgs, { encoding: 'utf8', windowsHide:true });
|
|
372
372
|
return !result.error && result.status === 0 ? name : null;
|
|
373
373
|
}
|
|
374
374
|
|
|
@@ -135,8 +135,8 @@ export async function takeScreenshotTool(args) {
|
|
|
135
135
|
}
|
|
136
136
|
if (!await pathExists(file)) {
|
|
137
137
|
for (const candidate of SCREENSHOT_COMMANDS) {
|
|
138
|
-
if (spawnSync('which', [candidate.command], { encoding: 'utf8' }).status !== 0) continue;
|
|
139
|
-
const result = spawnSync(candidate.command, candidate.args(file), { encoding: 'utf8', timeout: 30000 });
|
|
138
|
+
if (spawnSync('which', [candidate.command], { encoding: 'utf8', windowsHide:true }).status !== 0) continue;
|
|
139
|
+
const result = spawnSync(candidate.command, candidate.args(file), { encoding: 'utf8', timeout: 30000, windowsHide:true });
|
|
140
140
|
if (result.status === 0 && await pathExists(file)) break;
|
|
141
141
|
attempts.push(`${candidate.command}: ${(result.stderr || '').trim() || ('exit ' + result.status)}`);
|
|
142
142
|
}
|
package/src/tools/search.mjs
CHANGED
|
@@ -27,7 +27,7 @@ function ripgrep() {
|
|
|
27
27
|
if (process.env.REMCP_RUNTIME_FORCE_FALLBACK === '1') return null;
|
|
28
28
|
if (ripgrepPath !== undefined) return ripgrepPath;
|
|
29
29
|
try {
|
|
30
|
-
const result = spawnSync('rg', ['--version'], { encoding: 'utf8' });
|
|
30
|
+
const result = spawnSync('rg', ['--version'], { encoding: 'utf8', windowsHide:true });
|
|
31
31
|
ripgrepPath = !result.error && result.status === 0 ? 'rg' : null;
|
|
32
32
|
} catch { ripgrepPath = null; }
|
|
33
33
|
return ripgrepPath;
|
|
@@ -200,7 +200,7 @@ function runRipgrep(session, { target, pattern, searchType, filePattern, ignoreC
|
|
|
200
200
|
if (contextLines) args.push('-C', String(contextLines));
|
|
201
201
|
args.push('--', pattern, searchTarget);
|
|
202
202
|
}
|
|
203
|
-
const child = spawn(ripgrep(), args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
203
|
+
const child = spawn(ripgrep(), args, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide:true });
|
|
204
204
|
session.cancel = () => child.kill('SIGTERM');
|
|
205
205
|
let buffer = '';
|
|
206
206
|
let stderr = '';
|