flower-trellis 0.6.1-beta.5 → 0.6.2
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/enhancements/MANIFEST.json +1 -1
- package/package.json +3 -3
- package/src/lib/telemetry.js +2 -2
- package/src/lib/trellis-runner.js +82 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flower-trellis",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "一键安装/升级 Trellis 并自动融合 skill-garden 强化包(默认 Claude + agents)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"commit-and-tag-version": "^12.7.3"
|
|
71
71
|
},
|
|
72
72
|
"flowerReleaseNotes": {
|
|
73
|
-
"version": "0.6.
|
|
73
|
+
"version": "0.6.2",
|
|
74
74
|
"source": "CHANGELOG.md",
|
|
75
|
-
"body": "### 🐛 修复 Bug Fixes\n\n* **
|
|
75
|
+
"body": "### 🐛 修复 Bug Fixes\n\n* **flower:** 修正遥测上报地址并忽略上游 npm 升级提示 ([3030472](https://github.com/SilentFlower/flower-trellis/commit/303047282032270267cf3b26ffc20c13f9892324))",
|
|
76
76
|
"truncated": false
|
|
77
77
|
},
|
|
78
78
|
"optionalDependencies": {
|
package/src/lib/telemetry.js
CHANGED
|
@@ -8,8 +8,8 @@ import { readGitDeveloper } from "./developer.js";
|
|
|
8
8
|
import { readManifest, readUpdateCheck } from "./manifest.js";
|
|
9
9
|
import { flowerVersion, trellisVersion } from "./versions.js";
|
|
10
10
|
|
|
11
|
-
/** Flower
|
|
12
|
-
export const FLOWER_TELEMETRY_ENDPOINT = "https://ai-api.hub.flower-cli.com/";
|
|
11
|
+
/** Flower 遥测接收地址。主机名与 `/api/flower-trellis` 路由前缀都由服务端契约固定,缺一不可。 */
|
|
12
|
+
export const FLOWER_TELEMETRY_ENDPOINT = "https://ai-api.hub.flower-cli.com/api/flower-trellis/telemetry";
|
|
13
13
|
|
|
14
14
|
const TELEMETRY_SCHEMA_VERSION = 1;
|
|
15
15
|
/** 遥测请求保留 10 秒公网预算,避免慢速 TLS 建连或服务端响应被过早中止。 */
|
|
@@ -60,6 +60,45 @@ function classifyHeaderLine(line, bannerSet) {
|
|
|
60
60
|
return "content";
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 上游 `trellis update` 的 npm 落后提示行。用宽松匹配容忍上游微调文案,
|
|
65
|
+
* 捕获组取出它自报的 CLI 版本,复用到 Flower 的替代说明里。
|
|
66
|
+
*/
|
|
67
|
+
const UPSTREAM_UPGRADE_NOTICE = /Your CLI \(([^)]+)\) is behind npm\b/;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 上游紧跟其后的引导动作行。只匹配独立成行的 `Run: trellis upgrade`,
|
|
71
|
+
* 避免误伤降级分支里 `1. Update your CLI: trellis upgrade` 这类真实错误指引。
|
|
72
|
+
*/
|
|
73
|
+
const UPSTREAM_UPGRADE_ACTION = /^Run:\s*trellis upgrade$/;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 正文开始后继续按行过滤的行数上限。
|
|
77
|
+
*
|
|
78
|
+
* 上游版本提示固定打印在 `trellis update` 输出最前面,给一个有限窗口即可覆盖;
|
|
79
|
+
* 设上限是为了不让不完整行长期滞留在缓冲区里,影响后续交互输出的实时性。
|
|
80
|
+
*/
|
|
81
|
+
const UPSTREAM_NOTICE_LINE_BUDGET = 40;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 改写上游 trellis 的 npm 升级提示。
|
|
85
|
+
*
|
|
86
|
+
* Flower 在 package.json 里固定 `@mindfoldhq/trellis` 版本,并由 postinstall 同步全局 CLI,
|
|
87
|
+
* 所以上游「CLI 落后于 npm,请运行 trellis upgrade」是错误引导:照做会让全局 CLI 脱离
|
|
88
|
+
* Flower 的版本控制,下次安装又被同步回去。这里替换成 Flower 自己的说明而不是静默丢弃,
|
|
89
|
+
* 让用户仍然知道版本检查发生过、以及为什么不需要照做。
|
|
90
|
+
*
|
|
91
|
+
* @param {string} line 未去色的原始输出行
|
|
92
|
+
* @returns {string|null|undefined} 替换文案;null 表示丢弃该行;undefined 表示不匹配
|
|
93
|
+
*/
|
|
94
|
+
export function rewriteUpstreamUpgradeNotice(line) {
|
|
95
|
+
const t = stripAnsi(line).trim();
|
|
96
|
+
const notice = UPSTREAM_UPGRADE_NOTICE.exec(t);
|
|
97
|
+
if (notice) return ` · Trellis 版本由 Flower 固定(${notice[1]}),已忽略上游 npm 升级提示`;
|
|
98
|
+
if (UPSTREAM_UPGRADE_ACTION.test(t)) return null;
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
63
102
|
/**
|
|
64
103
|
* 用当前 node 执行 trellis bin(普通 spawn),透传子命令与参数。
|
|
65
104
|
*
|
|
@@ -83,6 +122,11 @@ export function runTrellis(args, cwd, opts = {}) {
|
|
|
83
122
|
const banner = trellisBannerLines();
|
|
84
123
|
const rl = createInterface({ input: child.stdout });
|
|
85
124
|
rl.on("line", (line) => {
|
|
125
|
+
const notice = rewriteUpstreamUpgradeNotice(line);
|
|
126
|
+
if (notice !== undefined) {
|
|
127
|
+
if (notice !== null) process.stdout.write(notice + "\n");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
86
130
|
const kind = classifyHeaderLine(line, banner);
|
|
87
131
|
// 普通(非 pty)场景一直按行过滤即可:trellis 此时是 -y 非交互,无 inquirer
|
|
88
132
|
if (kind === "skip") return;
|
|
@@ -99,10 +143,15 @@ export function runTrellis(args, cwd, opts = {}) {
|
|
|
99
143
|
|
|
100
144
|
/**
|
|
101
145
|
* 在伪终端(node-pty)里运行 trellis,**保留其全部交互**(模板 / monorepo / 冲突菜单),
|
|
102
|
-
* 同时过滤掉它开头重复打印的启动 banner / 副标题 / Developer / Mode
|
|
146
|
+
* 同时过滤掉它开头重复打印的启动 banner / 副标题 / Developer / Mode,
|
|
147
|
+
* 以及它自带的、与 Flower 版本固定策略冲突的 npm 升级提示。
|
|
148
|
+
*
|
|
149
|
+
* 过滤分三个阶段:
|
|
150
|
+
* - "header":逐行识别 banner 类行并丢弃(proxy 保留),遇到正文行转入 "notice";
|
|
151
|
+
* - "notice":只改写上游 npm 升级提示,其余原样输出,超出行数预算后转入 "raw";
|
|
152
|
+
* - "raw":完全透传。
|
|
103
153
|
*
|
|
104
|
-
*
|
|
105
|
-
* 一旦遇到正文行或 inquirer 渲染(隐藏光标序列 ESC[?25l)立即停止过滤、转为完全透传,
|
|
154
|
+
* 任一阶段遇到 inquirer 渲染(隐藏光标序列 ESC[?25l)都立即转入 "raw",
|
|
106
155
|
* 以免破坏交互菜单的光标控制。
|
|
107
156
|
*
|
|
108
157
|
* @param {string[]} args
|
|
@@ -133,17 +182,18 @@ export function runTrellisPty(args, cwd, opts = {}) {
|
|
|
133
182
|
}
|
|
134
183
|
|
|
135
184
|
const bannerSet = trellisBannerLines();
|
|
136
|
-
let
|
|
185
|
+
let phase = opts.stripBanner ? "header" : "raw";
|
|
186
|
+
let noticeBudget = UPSTREAM_NOTICE_LINE_BUDGET;
|
|
137
187
|
let buf = "";
|
|
138
188
|
|
|
139
189
|
const dataSubscription = child.onData((data) => {
|
|
140
|
-
if (
|
|
190
|
+
if (phase === "raw") {
|
|
141
191
|
stdout.write(data);
|
|
142
192
|
return;
|
|
143
193
|
}
|
|
144
194
|
// inquirer 开始渲染(隐藏光标)→ 立即停止过滤,整体透传,保住交互菜单
|
|
145
195
|
if (data.includes("\x1b[?25l")) {
|
|
146
|
-
|
|
196
|
+
phase = "raw";
|
|
147
197
|
stdout.write(buf + data);
|
|
148
198
|
buf = "";
|
|
149
199
|
return;
|
|
@@ -152,16 +202,32 @@ export function runTrellisPty(args, cwd, opts = {}) {
|
|
|
152
202
|
const parts = buf.split(/\r?\n/);
|
|
153
203
|
buf = parts.pop(); // 末尾不完整行留待下次
|
|
154
204
|
for (const line of parts) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
205
|
+
if (phase === "raw") {
|
|
206
|
+
stdout.write(line + "\r\n");
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (phase === "notice" && noticeBudget-- <= 0) {
|
|
210
|
+
phase = "raw";
|
|
158
211
|
stdout.write(line + "\r\n");
|
|
159
212
|
continue;
|
|
160
213
|
}
|
|
161
|
-
|
|
214
|
+
const notice = rewriteUpstreamUpgradeNotice(line);
|
|
215
|
+
if (notice !== undefined) {
|
|
216
|
+
if (notice !== null) stdout.write(notice + "\r\n");
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (phase === "header") {
|
|
220
|
+
const kind = classifyHeaderLine(line, bannerSet);
|
|
221
|
+
if (kind === "skip") continue;
|
|
222
|
+
if (kind === "keep") {
|
|
223
|
+
stdout.write(line + "\r\n");
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
phase = "notice"; // 正文开始:banner 过滤结束,但仍在有限窗口内改写上游版本提示
|
|
227
|
+
}
|
|
162
228
|
stdout.write(line + "\r\n");
|
|
163
229
|
}
|
|
164
|
-
if (
|
|
230
|
+
if (phase === "raw" && buf) {
|
|
165
231
|
stdout.write(buf);
|
|
166
232
|
buf = "";
|
|
167
233
|
}
|
|
@@ -185,6 +251,11 @@ export function runTrellisPty(args, cwd, opts = {}) {
|
|
|
185
251
|
stdout.on("resize", onResize);
|
|
186
252
|
|
|
187
253
|
child.onExit(({ exitCode, signal }) => {
|
|
254
|
+
// 过滤阶段可能扣着一行不完整输出(末尾无换行),退出前补发,避免最后一行被吞掉。
|
|
255
|
+
if (buf) {
|
|
256
|
+
stdout.write(buf);
|
|
257
|
+
buf = "";
|
|
258
|
+
}
|
|
188
259
|
// 先停止接收子进程输出,再恢复宿主终端;否则迟到的 9001h 会重新污染父终端。
|
|
189
260
|
dataSubscription.dispose();
|
|
190
261
|
stdin.off("data", onStdin);
|