type Request = { action: 'list' } | { action: 'content'; path: string } | { action: 'search'; query: string } export function remoteModuleFilesScript(root: string, request: Request): string { const input = Buffer.from(JSON.stringify({ root, ...request })).toString('base64') return `python3 - '${input}' <<'PY' import base64, json, os, stat, sys args = json.loads(base64.b64decode(sys.argv[1])) root = os.path.realpath(args['root']) skip = {'.git', 'build', 'install', 'log', 'node_modules', 'docker'} extensions = {'.c', '.cc', '.cpp', '.cxx', '.h', '.hpp', '.hh', '.py', '.xml', '.urdf', '.xacro', '.sdf', '.world', '.config', '.yaml', '.yml', '.json', '.txt', '.md', '.cmake', '.launch', '.sh', '.bash', '.toml', '.ini', '.cfg', '.rviz'} placeholder = 'Binary or large file preview is not available.' def content(relative): if os.path.isabs(relative) or '..' in relative.split('/'): raise ValueError('Invalid module path') path = os.path.realpath(os.path.join(root, relative)) if os.path.commonpath([root, path]) != root: raise ValueError('Invalid module path') info = os.stat(path) if not stat.S_ISREG(info.st_mode): raise ValueError('Not a regular file') if os.path.splitext(path)[1].lower() not in extensions or info.st_size > 1048576: return placeholder with open(path, 'rb') as fp: data = fp.read(1048577) if len(data) > 1048576 or b'\\x00' in data: return placeholder return data.decode('utf-8', errors='replace') def entries(): def fail(error): raise error for current, dirs, files in os.walk(root, onerror=fail, followlinks=False): depth = len(os.path.relpath(current, root).split('/')) if current != root else 0 dirs[:] = sorted(d for d in dirs if d not in skip and not os.path.islink(os.path.join(current, d))) if depth >= 10: dirs[:] = [] continue for name in dirs: yield 'D', os.path.relpath(os.path.join(current, name), root) for name in sorted(files): path = os.path.join(current, name) if not os.path.islink(path) and os.path.isfile(path): yield 'F', os.path.relpath(path, root) if args['action'] == 'content': print(json.dumps({'content': content(args['path'])})) elif args['action'] == 'list': paths, folders = [], [] for kind, path in entries(): (folders if kind == 'D' else paths).append(path) print(json.dumps({'filePaths': paths, 'folders': folders})) else: query = args['query'].strip().lower() results = [] if len(query) >= 2: for kind, path in entries(): if kind != 'F': continue text = content(path) if text == placeholder: continue for number, line in enumerate(text.split('\\n'), 1): if query in line.lower(): results.append({'file': path, 'lineNumber': number, 'lineText': (line.strip() or line)[:2000]}) if len(results) >= 200: break if len(results) >= 200: break print(json.dumps(results)) PY` }