cerevox
Version:
TypeScript SDK for browser automation and secure command execution in highly available and scalable micro computer environments
135 lines • 6.88 kB
JavaScript
;
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