UNPKG

@openai/agents-core

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.

181 lines • 7.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnixLocalFiles = exports.preparedFileIO = void 0; exports.withUnixLocalFileAccess = withUnixLocalFileAccess; const node_child_process_1 = require("node:child_process"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const errors_1 = require("../../../errors.js"); const unixLocalFileWorker_1 = require("./unixLocalFileWorker.js"); // Transfer trusted preparation across setup without selecting a backend twice. exports.preparedFileIO = Symbol('preparedFileIO'); const fileAccessTails = new WeakMap(); /** Keep native file changes and snapshot capture separate from state inspection. */ async function withUnixLocalFileAccess(state, operation) { const previous = fileAccessTails.get(state) ?? Promise.resolve(); const current = previous.catch(() => { }).then(operation); fileAccessTails.set(state, current); try { return await current; } finally { if (fileAccessTails.get(state) === current) fileAccessTails.delete(state); } } // Resolve from trusted host configuration, never the manifest's command environment. function pythonExecutable(mode) { if (mode === 'off') { throw new errors_1.UserError('UnixLocal file I/O protection cannot be disabled. Use a trusted Python 3 installation with descriptor-relative filesystem support.'); } if (process.platform === 'win32') { throw new errors_1.UserError('Required file I/O protection is supported only on Unix hosts.'); } const configured = process.env.OPENAI_AGENTS_PYTHON; const candidates = configured ? (0, node_path_1.isAbsolute)(configured) ? [configured] : [] : ['/usr/bin', '/usr/local/bin', '/opt/homebrew/bin'].map((directory) => (0, node_path_1.join)(directory, 'python3')); for (const candidate of candidates) { try { const executable = (0, node_fs_1.realpathSync)(candidate); (0, node_fs_1.accessSync)(executable, node_fs_1.constants.X_OK); const probe = (0, node_child_process_1.spawnSync)(executable, [ '-I', '-S', '-c', unixLocalFileWorker_1.UNIX_LOCAL_FILE_WORKER, JSON.stringify({ operation: 'probe' }), ], { cwd: '/', env: { PATH: '/usr/bin:/bin' }, timeout: 5000, maxBuffer: 4096, encoding: 'utf8', }); if (!probe.error && probe.status === 0 && probe.stdout === 'ready') return executable; } catch { // Try the next trusted installation path. } } throw new errors_1.UserError('Required file I/O protection needs a trusted Python 3 installation with descriptor-relative filesystem support. Set the host OPENAI_AGENTS_PYTHON to an absolute executable path.'); } /** Owns trusted file workers independently of sandbox shell processes. */ class UnixLocalFiles { executable; constructor(mode = 'required') { this.executable = pythonExecutable(mode); } active = new Set(); closed = false; async close() { this.closed = true; await this.stop(); } async stop() { const active = [...this.active]; for (const worker of active) worker.cancel(); await Promise.all(active.map((worker) => worker.done)); } async run(request, options = {}) { if (this.closed) throw new errors_1.UserError('UnixLocal file operations are closed.'); const child = (0, node_child_process_1.spawn)(this.executable, [ '-I', '-S', '-c', unixLocalFileWorker_1.UNIX_LOCAL_FILE_WORKER, JSON.stringify({ ...request, identity: options.identity }), ], { cwd: '/', env: { PATH: '/usr/bin:/bin' }, stdio: ['pipe', 'pipe', 'pipe'], }); let primaryError; let cancelled = false; const stdout = []; const stderr = []; let stderrBytes = 0; let resolveDone; const done = new Promise((resolve) => { resolveDone = resolve; }); const worker = { done, cancel: () => { if (cancelled) return; cancelled = true; primaryError ??= new errors_1.UserError('UnixLocal file operation cancelled.'); child.kill('SIGTERM'); }, }; this.active.add(worker); try { return await new Promise((resolve, reject) => { child.stdout.on('data', (chunk) => { stdout.push(chunk); }); child.stderr.on('data', (chunk) => { const remaining = 8192 - stderrBytes; if (remaining > 0) { stderr.push(chunk.subarray(0, remaining)); stderrBytes += Math.min(chunk.length, remaining); } }); child.stdin.on('error', () => { // The worker's exit/error owns failures such as a denied open or a closed pipe. }); child.on('error', (error) => { primaryError ??= error; }); if (options.update) { child.stdout.on('end', () => { if (primaryError) return; const current = Buffer.concat(stdout); if (!current.subarray(0, 6).equals(Buffer.from('READY\n'))) return; try { const next = options.update(current.subarray(6).toString('utf8')); child.stdin.end(`W${next}`); } catch (error) { primaryError = error; child.stdin.end(); } }); } else { child.stdin.end(options.input ?? ''); } child.on('close', (code, signal) => { if (!primaryError && (code !== 0 || signal)) { let detail = {}; try { detail = JSON.parse(Buffer.concat(stderr).toString('utf8')); } catch { /* Preserve a bounded diagnostic below. */ } primaryError = Object.assign(new Error(`${detail.code ?? 'EIO'}: ${detail.message ?? 'UnixLocal file worker failed. Python 3 with its standard library is required.'}`), { code: detail.code ?? 'EIO', path: request.path.path }); } if (primaryError) reject(primaryError); else resolve(Buffer.concat(stdout)); }); }); } finally { this.active.delete(worker); resolveDone(); } } } exports.UnixLocalFiles = UnixLocalFiles; //# sourceMappingURL=unixLocalFiles.js.map