factory.js
5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
* 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 };