/** * Connection 工厂 — 组合 Launcher(生命周期)+ Transport(通信) * * 四维正交架构: * Agent 定义 = 身份 × 消息协议 × 传输方式 × 生命周期 * * 本模块负责后两个维度的组合: * - 根据 launcher config 创建 Launcher 实例(管理 CLI 进程) * - 根据 transport type 创建 Transport 实例(管理通信通道) * - 将两者连接起来,返回 { transport, launcher } * * Protocol 层只拿到 transport,Launcher 的生命周期由外层管理。 */ const fs = require('fs'); const path = require('path'); /** * 解析 envFile + env 列表,返回 ["KEY=VALUE", ...] 数组 * 用于 Launcher 的环境变量注入 */ function resolveEnvVars(launcherConfig, workingDir) { const envMap = new Map(); if (launcherConfig.envFile) { const envFilePath = path.resolve(workingDir, launcherConfig.envFile); try { for (const line of fs.readFileSync(envFilePath, 'utf-8').split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eqIdx = trimmed.indexOf('='); if (eqIdx > 0) { const key = trimmed.substring(0, eqIdx).trim(); const val = trimmed.substring(eqIdx + 1).trim().replace(/^["']|["']$/g, ''); envMap.set(key, `${key}=${val}`); } } console.log(`[factory] 加载 envFile: ${envFilePath} (${envMap.size} 项)`); } catch (err) { console.warn(`[factory] envFile 读取失败: ${envFilePath} — ${err.message}`); } } for (const entry of (launcherConfig.env || [])) { if (entry.includes('=')) { const key = entry.substring(0, entry.indexOf('=')); envMap.set(key, entry); } else if (process.env[entry]) { envMap.set(entry, `${entry}=${process.env[entry]}`); } } return Array.from(envMap.values()); } /** * 创建连接(Launcher + Transport 组合) * * @param {object} agentDef - agent 配置(新 schema) * @param {string} agentDef.protocol - 消息协议名 * @param {string} agentDef.transport - 传输方式("pipe" | "websocket") * @param {string} agentDef.launcher - 引用的 launcher 名称 * @param {object} [agentDef.transportConfig] - 传输额外配置(如 { url }) * @param {object} launcherDef - launcher 配置 * @param {string} workingDir - 工作目录 * @param {object} [protocolOverrides] - 协议层补充参数(如 CLI args 覆盖) * @returns {Promise<{ transport: Transport, launcher: Launcher }>} */ async function createConnection(agentDef, launcherDef, workingDir, protocolOverrides = {}) { // ─── 1. 创建 Launcher ─── const launcher = createLauncher(launcherDef); // 准备 launcher 启动参数 const launchOptions = buildLaunchOptions(launcherDef, workingDir, protocolOverrides); const handle = await launcher.start(launchOptions); // ─── 2. 创建 Transport ─── const transportType = agentDef.transport || inferTransport(launcherDef); const transport = await createTransport(transportType, handle, agentDef.transportConfig); return { transport, launcher }; } /** * 根据 launcher 配置推断默认 transport 类型 */ function inferTransport(launcherDef) { if (launcherDef.type === 'process') return 'pipe'; return 'websocket'; // none 默认走 ws } /** * 创建 Launcher 实例 */ function createLauncher(launcherDef) { switch (launcherDef.type) { case 'process': { const { ProcessLauncher } = require('../launcher/ProcessLauncher'); return new ProcessLauncher(); } case 'none': { const { NoneLauncher } = require('../launcher/NoneLauncher'); return new NoneLauncher(); } default: throw new Error(`[factory] 未知 launcher 类型: ${launcherDef.type}`); } } /** * 构建 Launcher.start() 的参数 */ function buildLaunchOptions(launcherDef, workingDir, protocolOverrides) { switch (launcherDef.type) { case 'process': { // 强制删除 CLAUDECODE 以允许嵌套会话 const env = { ...protocolOverrides.env }; delete env.CLAUDECODE; console.log('[factory] buildLaunchOptions: CLAUDECODE deleted'); return { command: protocolOverrides.command || launcherDef.command, args: protocolOverrides.args || launcherDef.args || [], env: env, cwd: workingDir, }; } case 'none': return { url: launcherDef.url }; default: return {}; } } /** * 创建 Transport 并连接 */ async function createTransport(type, handle, transportConfig = {}) { switch (type) { case 'pipe': { const { PipeTransport } = require('./PipeTransport'); const transport = new PipeTransport(); await transport.connect(handle); return transport; } case 'websocket': { const { WebSocketTransport } = require('./WebSocketTransport'); const transport = new WebSocketTransport(); const url = handle.url || transportConfig.url; const timeout = transportConfig.timeout || 10000; await transport.connect(url, [], { timeout }); return transport; } default: throw new Error(`[factory] 未知 transport 类型: ${type}`); } } module.exports = { createConnection, createLauncher, createTransport, resolveEnvVars };