u-foo 2.5.2 → 2.5.4
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
|
@@ -662,6 +662,9 @@ class AgentLauncher {
|
|
|
662
662
|
// Enable Claude Code SDK session state events for precise idle/busy detection
|
|
663
663
|
...(this.agentType === "claude-code" ? { CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: "1" } : {}),
|
|
664
664
|
},
|
|
665
|
+
titlePrefix: process.env.UFOO_LAUNCH_MODE === "host"
|
|
666
|
+
? (this._originalNickname || process.env.UFOO_NICKNAME || "")
|
|
667
|
+
: "",
|
|
665
668
|
// 未来扩展:ioAdapter: new TerminalIOAdapter()
|
|
666
669
|
});
|
|
667
670
|
|
|
@@ -91,7 +91,9 @@ class AgentNotifier {
|
|
|
91
91
|
setTitle(nickname) {
|
|
92
92
|
if (!nickname) return;
|
|
93
93
|
if (!process.stdout || !process.stdout.isTTY) return;
|
|
94
|
-
process.
|
|
94
|
+
if (process.env.UFOO_LAUNCH_MODE !== "host") {
|
|
95
|
+
process.stdout.write(`\x1b]0;${nickname}\x07`);
|
|
96
|
+
}
|
|
95
97
|
if (isITerm2()) {
|
|
96
98
|
iterm2.setBadge(nickname);
|
|
97
99
|
iterm2.setCwd(this.projectRoot);
|
|
@@ -58,6 +58,14 @@ function stripAnsi(text) {
|
|
|
58
58
|
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
function rewriteTitleOscPrefix(text, prefix) {
|
|
62
|
+
if (!prefix || !text) return text;
|
|
63
|
+
return String(text).replace(/\x1b\]([012]);([^\x07\x1b]*)(\x07|\x1b\\)/g, (match, code, title, terminator) => {
|
|
64
|
+
if (!title || title.startsWith(`${prefix}:`)) return match;
|
|
65
|
+
return `\x1b]${code};${prefix}:${title}${terminator}`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
function parseInputMessage(message) {
|
|
62
70
|
if (!message) return { raw: false, text: "" };
|
|
63
71
|
if (parseStreamEnvelope(message)) return null;
|
|
@@ -419,8 +427,29 @@ async function runPtyRunner({ projectRoot, agentType = "codex", extraArgs = [] }
|
|
|
419
427
|
}
|
|
420
428
|
}
|
|
421
429
|
|
|
422
|
-
|
|
430
|
+
const titlePrefix = process.env.UFOO_LAUNCH_MODE === "host"
|
|
431
|
+
? String(process.env.UFOO_NICKNAME || "").trim()
|
|
432
|
+
: "";
|
|
433
|
+
let titleOscBuffer = "";
|
|
434
|
+
|
|
435
|
+
function prependTitlePrefix(data) {
|
|
436
|
+
if (!titlePrefix) return Buffer.from(data || "").toString("utf8");
|
|
423
437
|
const text = Buffer.from(data || "").toString("utf8");
|
|
438
|
+
const combined = titleOscBuffer + text;
|
|
439
|
+
titleOscBuffer = "";
|
|
440
|
+
const trailingOscStart = combined.lastIndexOf("\x1b]");
|
|
441
|
+
if (trailingOscStart >= 0) {
|
|
442
|
+
const trailing = combined.slice(trailingOscStart);
|
|
443
|
+
if (!/(?:\x07|\x1b\\)/.test(trailing)) {
|
|
444
|
+
titleOscBuffer = trailing;
|
|
445
|
+
return rewriteTitleOscPrefix(combined.slice(0, trailingOscStart), titlePrefix);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return rewriteTitleOscPrefix(combined, titlePrefix);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function broadcastOutput(data) {
|
|
452
|
+
const text = prependTitlePrefix(data);
|
|
424
453
|
if (!text) return;
|
|
425
454
|
if (process.stdout && process.stdout.isTTY && typeof process.stdout.write === "function") {
|
|
426
455
|
try {
|
|
@@ -1058,6 +1087,7 @@ module.exports = {
|
|
|
1058
1087
|
appendStartupBootstrapArg,
|
|
1059
1088
|
buildPtyInputFromEvent,
|
|
1060
1089
|
parseInputMessage,
|
|
1090
|
+
rewriteTitleOscPrefix,
|
|
1061
1091
|
resolvePtyBootstrapArgs,
|
|
1062
1092
|
resolveCommand,
|
|
1063
1093
|
runPtyRunner,
|
|
@@ -41,6 +41,8 @@ class PtyWrapper {
|
|
|
41
41
|
|
|
42
42
|
// 可插拔的IO适配器(未来扩展)
|
|
43
43
|
this.ioAdapter = options.ioAdapter || null;
|
|
44
|
+
this.titlePrefix = String(options.titlePrefix || "").trim();
|
|
45
|
+
this._titleOscBuffer = "";
|
|
44
46
|
|
|
45
47
|
// 事件处理器引用(用于cleanup)
|
|
46
48
|
this._stdinHandler = null;
|
|
@@ -111,8 +113,10 @@ class PtyWrapper {
|
|
|
111
113
|
_attachDirectStreams(stdin, stdout) {
|
|
112
114
|
// PTY输出 -> stdout
|
|
113
115
|
this._ptyDataHandler = (data) => {
|
|
116
|
+
const output = this._prependTitlePrefix(data);
|
|
117
|
+
|
|
114
118
|
// 1. 输出到terminal
|
|
115
|
-
stdout.write(
|
|
119
|
+
stdout.write(output);
|
|
116
120
|
|
|
117
121
|
// 2. 可选:日志记录(JSONL格式)
|
|
118
122
|
if (this.logger) {
|
|
@@ -185,6 +189,33 @@ class PtyWrapper {
|
|
|
185
189
|
this.pty.onExit(this._ptyExitHandler);
|
|
186
190
|
}
|
|
187
191
|
|
|
192
|
+
_prependTitlePrefix(data) {
|
|
193
|
+
if (!this.titlePrefix) return data;
|
|
194
|
+
const text = typeof data === "string" ? data : Buffer.from(data).toString("utf8");
|
|
195
|
+
const combined = this._titleOscBuffer + text;
|
|
196
|
+
this._titleOscBuffer = "";
|
|
197
|
+
|
|
198
|
+
const trailingOscStart = combined.lastIndexOf("\x1b]");
|
|
199
|
+
if (trailingOscStart >= 0) {
|
|
200
|
+
const trailing = combined.slice(trailingOscStart);
|
|
201
|
+
if (!/(?:\x07|\x1b\\)/.test(trailing)) {
|
|
202
|
+
this._titleOscBuffer = trailing;
|
|
203
|
+
return this._rewriteTitleOsc(combined.slice(0, trailingOscStart));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return this._rewriteTitleOsc(combined);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
_rewriteTitleOsc(text) {
|
|
211
|
+
if (!this.titlePrefix || !text) return text;
|
|
212
|
+
const prefix = this.titlePrefix;
|
|
213
|
+
return text.replace(/\x1b\]([012]);([^\x07\x1b]*)(\x07|\x1b\\)/g, (match, code, title, terminator) => {
|
|
214
|
+
if (!title || title.startsWith(`${prefix}:`)) return match;
|
|
215
|
+
return `\x1b]${code};${prefix}:${title}${terminator}`;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
188
219
|
/**
|
|
189
220
|
* 写入数据到PTY(用于外部inject)
|
|
190
221
|
*
|
|
@@ -586,8 +586,6 @@ async function spawnManagedHostAgent(
|
|
|
586
586
|
const hostExtraEnv = hostBootstrap.extraEnv;
|
|
587
587
|
const argText = args.length > 0 ? ` ${args.map(shellEscape).join(" ")}` : "";
|
|
588
588
|
|
|
589
|
-
const titleCmd = buildTitleCmd(nickname);
|
|
590
|
-
|
|
591
589
|
// Pass env vars to Horizon via the env parameter (Horizon will set them for the child process)
|
|
592
590
|
const env = {
|
|
593
591
|
UFOO_LAUNCH_MODE: "host",
|
|
@@ -616,9 +614,7 @@ async function spawnManagedHostAgent(
|
|
|
616
614
|
}
|
|
617
615
|
env.UFOO_FORCE_PTY = "1";
|
|
618
616
|
const directCmd = `${binary}${argText}`;
|
|
619
|
-
const runCmd =
|
|
620
|
-
? `cd ${shellEscape(projectRoot)} && ${titleCmd} && ${directCmd}`
|
|
621
|
-
: `cd ${shellEscape(projectRoot)} && ${directCmd}`;
|
|
617
|
+
const runCmd = `cd ${shellEscape(projectRoot)} && ${directCmd}`;
|
|
622
618
|
createOptions.command = runCmd;
|
|
623
619
|
createOptions.env = env;
|
|
624
620
|
|