UNPKG

cerevox

Version:

TypeScript SDK for browser automation and secure command execution in highly available and scalable micro computer environments

135 lines 6.88 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CodeRunner = void 0; const base_1 = require("./base"); const uuid_1 = require("uuid"); const detect_code_1 = require("../utils/detect-code"); const session_1 = require("./session"); const constants_1 = require("../utils/constants"); const node_path_1 = __importDefault(require("node:path")); let CodeRunner = class CodeRunner extends base_1.BaseClass { /** * 创建 CodeRunner 实例 * @param sandbox - E2B 沙箱实例 * @param envs - 环境变量配置 * @param logLevel - 日志级别 */ constructor(session) { super(session.getLogger().level); this.terminal = session.terminal.create(); this.session = session; } async injectCode(code, type) { const wsUrl = await this.session.browser.getCDPEndpoint(); if (type === 'py') { // 替换 python 调用 code = code.replace(/\.launch\s*\(/gm, `.connect_over_cdp('${wsUrl}',`); code = code.replace(/\.launch_persistent_context\s*\(/gm, `.connect_over_cdp('${wsUrl}',`); // 移除 launch_persistent_context 调用中的 user_data_dir 参数(位置参数) code = code.replace(/\.connect_over_cdp\('([^']+)',\s*'[^']*',/gm, `.connect_over_cdp('$1',`); code = code.replace(/\.connect_over_cdp\('([^']+)',\s*"[^"]*",/gm, `.connect_over_cdp('$1',`); // 移除单独的位置参数(没有其他参数的情况) code = code.replace(/\.connect_over_cdp\('([^']+)',\s*'[^']*'\s*\)/gm, `.connect_over_cdp('$1')`); code = code.replace(/\.connect_over_cdp\('([^']+)',\s*"[^"]*"\s*\)/gm, `.connect_over_cdp('$1')`); // 只保留 slow_mo 和 timeout 参数,其他所有具名参数都删除 const paramsToReserve = ['slow_mo', 'timeout']; // 移除所有不在保留列表中的具名参数 // 使用通用正则表达式匹配所有具名参数,然后只保留指定的参数 code = code.replace(/\.connect_over_cdp\('([^']+)'([^)]*?)\)/gm, (match, url, params) => { if (!params.trim()) { return `.connect_over_cdp('${url}')`; } // 提取所有参数 const reservedParams = []; paramsToReserve.forEach(param => { // 匹配简单值参数 const simpleMatch = params.match(new RegExp(`\\b${param}\\s*=\\s*[^,)\\n{\\[]+`, 'g')); if (simpleMatch) { reservedParams.push(...simpleMatch); } // 匹配字典参数 const dictMatch = params.match(new RegExp(`\\b${param}\\s*=\\s*\\{[^}]*\\}`, 'g')); if (dictMatch) { reservedParams.push(...dictMatch); } // 匹配列表参数 const listMatch = params.match(new RegExp(`\\b${param}\\s*=\\s*\\[[^\\]]*\\]`, 'g')); if (listMatch) { reservedParams.push(...listMatch); } }); // 构建结果字符串 let result; if (reservedParams.length === 0) { result = `.connect_over_cdp('${url}')`; } else { result = `.connect_over_cdp('${url}', ${reservedParams.join(', ')})`; } // 只对当前匹配的函数调用进行清理,避免影响其他代码 result = result.replace(/,\s*\)/g, ')'); result = result.replace(/\(\s*,/g, '('); result = result.replace(/,\s*,/g, ','); // 处理只有路径参数的情况,移除多余的逗号 result = result.replace(/\.connect_over_cdp\('([^']+)',\s*\)/g, `.connect_over_cdp('$1')`); return result; }); } else { // 替换 js 调用 code = code.replace(/\.launch\s*\(/gm, `.connectOverCDP('${wsUrl}',`); code = code.replace(/\.launchPersistentContext\s*\([^,]*?,/gm, `.connectOverCDP('${wsUrl}',`); } return code; } async run(code, options = {}) { const type = (0, detect_code_1.detectCodeType)(code); if (options.inject && type !== 'sh') { code = await this.injectCode(code, type); } const scriptId = (0, uuid_1.v4)(); const workDir = `/home/user/scripts/${scriptId}`; await this.session.files.mkdir(workDir); const scriptFile = `/home/user/scripts/script-${scriptId}.${type === 'esm' ? 'mjs' : type}`; await this.session.files.write(scriptFile, code); const terminal = this.terminal; const timeout = options.timeout || 300; const syncDir = options.syncDir || './cerevox-output'; let cmd; if (type === 'sh') { cmd = `cd ${workDir} && chmod +x ${scriptFile} && ${scriptFile}`; } else { cmd = `cd ${workDir} && ${type === 'py' ? 'python3' : 'node'} ${scriptFile}`; } const ret = await terminal.run(cmd, { timeoutMs: timeout * 1000 }); ret.stdout.pipe(process.stdout); ret.stderr.pipe(process.stderr); return ret.json().then(async (result) => { // fs.rmSync('./cerevox-output', { recursive: true }); await this.session.files.syncDownloadsDirectory(syncDir, workDir); return { ...result, syncDir: node_path_1.default.resolve(syncDir), }; }); } }; exports.CodeRunner = CodeRunner; exports.CodeRunner = CodeRunner = __decorate([ (0, base_1.Logger)({ VERSION: constants_1.VERSION }), __metadata("design:paramtypes", [session_1.Session]) ], CodeRunner); //# sourceMappingURL=code-runner.js.map