openteam 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/interfaces/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
listAllInstances,
|
|
17
17
|
} from '../foundation/state.js';
|
|
18
18
|
import { detectMultiplexer, getSessionState, hasSession, findSessionsByPrefix, attachSession, startSession, killSession, isInsideMux } from '../foundation/terminal.js';
|
|
19
|
-
import { buildWrapperCmd } from './daemon/panes.js';
|
|
19
|
+
import { buildWrapperCmd, buildWrapperOptions } from './daemon/panes.js';
|
|
20
20
|
|
|
21
21
|
// ── 输出辅助 ──
|
|
22
22
|
|
|
@@ -156,13 +156,10 @@ export async function cmdStart(teamName, options) {
|
|
|
156
156
|
const daemonCmd = `openteam daemon ${teamName} --port ${port} --dir "${projectDir}" --mux ${mux} --cli ${cliType}`;
|
|
157
157
|
|
|
158
158
|
// zellij: 构建 wrapper 命令,由 team layout 的 stacked panes 自动调用
|
|
159
|
-
const
|
|
160
|
-
const wrapperOptions = {
|
|
159
|
+
const wrapperOptions = buildWrapperOptions(teamConfig, {
|
|
161
160
|
serverUrl: `http://127.0.0.1:${port}`,
|
|
162
161
|
teamName, projectDir, cliType,
|
|
163
|
-
|
|
164
|
-
cliArgs,
|
|
165
|
-
};
|
|
162
|
+
});
|
|
166
163
|
const agentCmds = mux === 'zellij' ? teamConfig.agents.map(agent => ({
|
|
167
164
|
name: agent,
|
|
168
165
|
cmd: buildWrapperCmd(agent, wrapperOptions, { mux, sessionName }),
|
|
@@ -14,7 +14,7 @@ import { DEFAULTS, getSessionName } from '../../foundation/constants.js';
|
|
|
14
14
|
import { createLogger } from '../../foundation/logger.js';
|
|
15
15
|
import { cleanMuxEnv } from '../../foundation/terminal.js';
|
|
16
16
|
import { startServe, stopServe } from './serve.js';
|
|
17
|
-
import { createAllAgentPanes, checkAndRespawn } from './panes.js';
|
|
17
|
+
import { createAllAgentPanes, checkAndRespawn, buildWrapperOptions } from './panes.js';
|
|
18
18
|
import { createEmbeddedDashboard } from '../dashboard/index.js';
|
|
19
19
|
|
|
20
20
|
const log = createLogger('daemon');
|
|
@@ -70,7 +70,10 @@ export async function runDaemon(teamName, projectDir, options = {}) {
|
|
|
70
70
|
started: new Date().toISOString(),
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
// ── 2.
|
|
73
|
+
// ── 2. 读取 CLI 配置 ──
|
|
74
|
+
const keepDefaultSP = !!teamConfig.cli_config?.[cliType]?.keep_default_system_prompt;
|
|
75
|
+
|
|
76
|
+
// ── 3. 确保 agent/skill 软链接 ──
|
|
74
77
|
const { ensureLinks } = await import('./links.js');
|
|
75
78
|
const linkResult = ensureLinks({ teamName, projectDir, cliType, agents, skipAgentLinks: keepDefaultSP });
|
|
76
79
|
if (!linkResult.ok) {
|
|
@@ -86,19 +89,10 @@ export async function runDaemon(teamName, projectDir, options = {}) {
|
|
|
86
89
|
console.log(`已创建 ${linkResult.linked.length} 个链接`);
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
// ──
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const wrapperOptions = {
|
|
94
|
-
serverUrl: serve.url,
|
|
95
|
-
teamName,
|
|
96
|
-
projectDir,
|
|
97
|
-
cliType,
|
|
98
|
-
agents,
|
|
99
|
-
cliArgs,
|
|
100
|
-
keepDefaultSP,
|
|
101
|
-
};
|
|
92
|
+
// ── 4. wrapper 环境配置 ──
|
|
93
|
+
const wrapperOptions = buildWrapperOptions(teamConfig, {
|
|
94
|
+
serverUrl: serve.url, teamName, projectDir, cliType,
|
|
95
|
+
});
|
|
102
96
|
|
|
103
97
|
// ── 4. 创建 agent panes ──
|
|
104
98
|
// zellij: layout 已在 startSession 时创建好 stacked agents
|
|
@@ -152,3 +152,27 @@ export function buildWrapperCmd(agent, wrapperOptions, muxInfo) {
|
|
|
152
152
|
|
|
153
153
|
return `env ${envVars.join(' ')} ${wrapperBin}`;
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 从 teamConfig 构建 wrapperOptions — 唯一入口,消除重复构建
|
|
158
|
+
*
|
|
159
|
+
* @param {object} teamConfig - 团队配置
|
|
160
|
+
* @param {object} runtime - 运行时信息
|
|
161
|
+
* @param {string} runtime.serverUrl
|
|
162
|
+
* @param {string} runtime.teamName
|
|
163
|
+
* @param {string} runtime.projectDir
|
|
164
|
+
* @param {string} runtime.cliType
|
|
165
|
+
* @returns {object} wrapperOptions
|
|
166
|
+
*/
|
|
167
|
+
export function buildWrapperOptions(teamConfig, { serverUrl, teamName, projectDir, cliType }) {
|
|
168
|
+
const cliConf = teamConfig.cli_config?.[cliType] || {};
|
|
169
|
+
return {
|
|
170
|
+
serverUrl,
|
|
171
|
+
teamName,
|
|
172
|
+
projectDir,
|
|
173
|
+
cliType,
|
|
174
|
+
agents: teamConfig.agents,
|
|
175
|
+
cliArgs: cliConf.args || [],
|
|
176
|
+
keepDefaultSP: !!cliConf.keep_default_system_prompt,
|
|
177
|
+
};
|
|
178
|
+
}
|
package/src/wrapper/index.js
CHANGED
|
@@ -115,18 +115,24 @@ async function main() {
|
|
|
115
115
|
|
|
116
116
|
// ── 5. 启动消息轮询 ──
|
|
117
117
|
let polling = true;
|
|
118
|
+
let injecting = false; // 注入锁,防止轮询重入导致消息交叉
|
|
118
119
|
const pollTimer = setInterval(async () => {
|
|
119
|
-
if (!polling) return;
|
|
120
|
+
if (!polling || injecting) return;
|
|
120
121
|
try {
|
|
121
122
|
// 上报活动状态
|
|
122
123
|
const active = (Date.now() - lastPtyOutput) < ACTIVE_THRESHOLD;
|
|
123
124
|
heartbeat(serverUrl, agent, active).catch(() => {});
|
|
124
125
|
|
|
125
126
|
const messages = await pullMessages(serverUrl, agent);
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
if (messages.length > 0) {
|
|
128
|
+
injecting = true;
|
|
129
|
+
for (const msg of messages) {
|
|
130
|
+
await injectMessage(ptyProcess, msg.message);
|
|
131
|
+
}
|
|
132
|
+
injecting = false;
|
|
128
133
|
}
|
|
129
134
|
} catch (err) {
|
|
135
|
+
injecting = false;
|
|
130
136
|
log.warn('wrapper.poll.error', { error: err.message });
|
|
131
137
|
}
|
|
132
138
|
}, POLL_INTERVAL);
|
|
@@ -160,20 +166,30 @@ async function main() {
|
|
|
160
166
|
|
|
161
167
|
// ── 消息注入 ──
|
|
162
168
|
|
|
169
|
+
const INJECT_ENTER_DELAY = 200; // 写入文本后等待 TUI 渲染再发 Enter
|
|
170
|
+
const INJECT_BETWEEN_DELAY = 300; // 两条消息之间的间隔,确保前一条已提交
|
|
171
|
+
|
|
163
172
|
/**
|
|
164
173
|
* 通过 PTY master 注入消息到 CLI
|
|
165
|
-
*
|
|
174
|
+
* 返回 Promise,resolve 后才能注入下一条
|
|
166
175
|
*/
|
|
167
176
|
function injectMessage(ptyProcess, message) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
return new Promise((resolve) => {
|
|
178
|
+
try {
|
|
179
|
+
// 消息中的 \n 保持原样 — claude code 中 \n(0x0a) = 换行,\r(0x0d) = 提交
|
|
180
|
+
ptyProcess.write(message);
|
|
181
|
+
// 延迟发送 Enter 提交 — 确保 TUI 完成文本渲染
|
|
182
|
+
setTimeout(() => {
|
|
183
|
+
ptyProcess.write('\r');
|
|
184
|
+
log.info('wrapper.inject.ok', { preview: message.slice(0, 50) });
|
|
185
|
+
// 再等一段时间让 TUI 处理提交,然后才允许下一条
|
|
186
|
+
setTimeout(resolve, INJECT_BETWEEN_DELAY);
|
|
187
|
+
}, INJECT_ENTER_DELAY);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
log.warn('wrapper.inject.failed', { error: err.message, preview: message.slice(0, 50) });
|
|
190
|
+
resolve();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
177
193
|
}
|
|
178
194
|
|
|
179
195
|
// ── HTTP 辅助函数 ──
|