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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u-foo",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "Multi-Agent Workspace Protocol. Just add u. claude → uclaude, codex → ucodex.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://ufoo.dev",
@@ -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.stdout.write(`\x1b]0;${nickname}\x07`);
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
- function broadcastOutput(data) {
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(data);
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 = titleCmd
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