@zhipu/zp-cli 0.0.1 → 0.0.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/README.md +4 -9
- package/dist/build-dashboard-CqZVHPH7.mjs +502 -0
- package/dist/build-jobs-m5jbiNTx.mjs +191 -0
- package/dist/cli.mjs +361 -78
- package/package.json +53 -3
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { t as runCommandCaptured } from "./cli.mjs";
|
|
2
|
+
import { reactive } from "vue";
|
|
3
|
+
//#region src/utils/terminal-output.ts
|
|
4
|
+
function clearCurrentLine(output) {
|
|
5
|
+
const lastNewline = output.lastIndexOf("\n");
|
|
6
|
+
return lastNewline === -1 ? "" : output.slice(0, lastNewline + 1);
|
|
7
|
+
}
|
|
8
|
+
const ESC = "\x1B";
|
|
9
|
+
function startsWithClearLine(rest) {
|
|
10
|
+
if (rest.startsWith(`${ESC}[2K`)) return 4;
|
|
11
|
+
if (rest.startsWith(`${ESC}[K`)) return 3;
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function startsWithCursorColumn(rest) {
|
|
15
|
+
if (!rest.startsWith(`${ESC}[`)) return null;
|
|
16
|
+
let index = 2;
|
|
17
|
+
while (index < rest.length && rest[index] >= "0" && rest[index] <= "9") index += 1;
|
|
18
|
+
if (rest[index] !== "G") return null;
|
|
19
|
+
return index + 1;
|
|
20
|
+
}
|
|
21
|
+
function startsWithCursorPosition(rest) {
|
|
22
|
+
if (!rest.startsWith(`${ESC}[`)) return null;
|
|
23
|
+
let index = 2;
|
|
24
|
+
while (index < rest.length && rest[index] >= "0" && rest[index] <= "9") index += 1;
|
|
25
|
+
if (rest[index] !== ";") return null;
|
|
26
|
+
index += 1;
|
|
27
|
+
while (index < rest.length && rest[index] >= "0" && rest[index] <= "9") index += 1;
|
|
28
|
+
if (rest[index] !== "H") return null;
|
|
29
|
+
return index + 1;
|
|
30
|
+
}
|
|
31
|
+
function consumeAnsiSequence(text, index) {
|
|
32
|
+
if (text[index] !== ESC) return null;
|
|
33
|
+
const rest = text.slice(index);
|
|
34
|
+
const clearLength = startsWithClearLine(rest);
|
|
35
|
+
if (clearLength !== null) return {
|
|
36
|
+
length: clearLength,
|
|
37
|
+
clearLine: true
|
|
38
|
+
};
|
|
39
|
+
const cursorLength = startsWithCursorColumn(rest);
|
|
40
|
+
if (cursorLength !== null) return {
|
|
41
|
+
length: cursorLength,
|
|
42
|
+
clearLine: true
|
|
43
|
+
};
|
|
44
|
+
const positionLength = startsWithCursorPosition(rest);
|
|
45
|
+
if (positionLength !== null) return {
|
|
46
|
+
length: positionLength,
|
|
47
|
+
clearLine: false
|
|
48
|
+
};
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function holdIncompleteEscape(text) {
|
|
52
|
+
const escIndex = text.lastIndexOf(ESC);
|
|
53
|
+
if (escIndex === -1) return {
|
|
54
|
+
processable: text,
|
|
55
|
+
pending: ""
|
|
56
|
+
};
|
|
57
|
+
const tail = text.slice(escIndex);
|
|
58
|
+
if (tail.length === 1) return {
|
|
59
|
+
processable: text.slice(0, escIndex),
|
|
60
|
+
pending: tail
|
|
61
|
+
};
|
|
62
|
+
if (tail.startsWith(`${ESC}[`)) {
|
|
63
|
+
if (!(startsWithClearLine(tail) !== null || startsWithCursorColumn(tail) !== null || startsWithCursorPosition(tail) !== null)) return {
|
|
64
|
+
processable: text.slice(0, escIndex),
|
|
65
|
+
pending: tail
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
processable: text,
|
|
70
|
+
pending: ""
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function appendProcessable(output, text) {
|
|
74
|
+
let index = 0;
|
|
75
|
+
while (index < text.length) {
|
|
76
|
+
const ansi = consumeAnsiSequence(text, index);
|
|
77
|
+
if (ansi) {
|
|
78
|
+
if (ansi.clearLine) output = clearCurrentLine(output);
|
|
79
|
+
index += ansi.length;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const char = text[index];
|
|
83
|
+
if (char === "\r") {
|
|
84
|
+
output = clearCurrentLine(output);
|
|
85
|
+
index += 1;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (char === "\b") {
|
|
89
|
+
output = output.slice(0, -1);
|
|
90
|
+
index += 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
output += char;
|
|
94
|
+
index += 1;
|
|
95
|
+
}
|
|
96
|
+
return output;
|
|
97
|
+
}
|
|
98
|
+
/** 可跨 chunk 拼接的终端输出缓冲,避免 ANSI 转义被截断。 */
|
|
99
|
+
function createTerminalOutputBuffer() {
|
|
100
|
+
let output = "";
|
|
101
|
+
let pending = "";
|
|
102
|
+
return {
|
|
103
|
+
append(chunk) {
|
|
104
|
+
const { processable, pending: nextPending } = holdIncompleteEscape(pending + chunk);
|
|
105
|
+
pending = nextPending;
|
|
106
|
+
output = appendProcessable(output, processable);
|
|
107
|
+
return output;
|
|
108
|
+
},
|
|
109
|
+
get value() {
|
|
110
|
+
return output;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/tui/pack/build-jobs.ts
|
|
116
|
+
function createBuildJobsContext() {
|
|
117
|
+
const kills = [];
|
|
118
|
+
let aborted = false;
|
|
119
|
+
return {
|
|
120
|
+
registerKill: (kill) => kills.push(kill),
|
|
121
|
+
isAborted: () => aborted,
|
|
122
|
+
abort: () => {
|
|
123
|
+
aborted = true;
|
|
124
|
+
for (const kill of kills) kill();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function createBuildJobs(specs) {
|
|
129
|
+
return specs.map((spec) => reactive({
|
|
130
|
+
...spec,
|
|
131
|
+
status: "waiting",
|
|
132
|
+
output: "",
|
|
133
|
+
exitCode: null
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
async function runOneBuildJob(job, ctx) {
|
|
137
|
+
if (ctx.isAborted()) {
|
|
138
|
+
job.status = "failed";
|
|
139
|
+
job.exitCode = 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
job.status = "running";
|
|
143
|
+
const outputBuffer = createTerminalOutputBuffer();
|
|
144
|
+
const appendOutput = (chunk) => {
|
|
145
|
+
job.output = outputBuffer.append(chunk);
|
|
146
|
+
};
|
|
147
|
+
const handle = runCommandCaptured("npm", ["run", "build"], {
|
|
148
|
+
cwd: job.cwd,
|
|
149
|
+
env: job.env,
|
|
150
|
+
windowsCmd: true,
|
|
151
|
+
fakeTty: true,
|
|
152
|
+
onStdout: appendOutput,
|
|
153
|
+
onStderr: appendOutput
|
|
154
|
+
});
|
|
155
|
+
ctx.registerKill(() => handle.kill());
|
|
156
|
+
const { exitCode } = await handle.promise;
|
|
157
|
+
if (ctx.isAborted()) {
|
|
158
|
+
job.exitCode = exitCode ?? 1;
|
|
159
|
+
job.status = "failed";
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
job.exitCode = exitCode;
|
|
163
|
+
job.status = exitCode === 0 ? "success" : "failed";
|
|
164
|
+
}
|
|
165
|
+
async function runWithConcurrency(items, concurrency, worker) {
|
|
166
|
+
if (items.length === 0) return;
|
|
167
|
+
const limit = Number.isFinite(concurrency) && concurrency > 0 ? Math.min(concurrency, items.length) : items.length;
|
|
168
|
+
let nextIndex = 0;
|
|
169
|
+
async function runWorker() {
|
|
170
|
+
while (true) {
|
|
171
|
+
const index = nextIndex;
|
|
172
|
+
nextIndex += 1;
|
|
173
|
+
if (index >= items.length) return;
|
|
174
|
+
await worker(items[index], index);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
await Promise.all(Array.from({ length: limit }, () => runWorker()));
|
|
178
|
+
}
|
|
179
|
+
async function runBuildJobs(jobs, ctx, options = {}) {
|
|
180
|
+
await runWithConcurrency(jobs, options.concurrency ?? 0, async (job) => {
|
|
181
|
+
await runOneBuildJob(job, ctx);
|
|
182
|
+
});
|
|
183
|
+
return jobs;
|
|
184
|
+
}
|
|
185
|
+
function summarizeBuildFailures(jobs) {
|
|
186
|
+
const failed = jobs.filter((job) => job.exitCode !== 0);
|
|
187
|
+
if (failed.length === 0) return "";
|
|
188
|
+
return `[pack] 构建失败:\n${failed.map((job) => `${job.label} (退出码 ${job.exitCode ?? "?"})`).map((line) => ` - ${line}`).join("\n")}`;
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
export { createBuildJobs, createBuildJobsContext, runBuildJobs, runWithConcurrency, summarizeBuildFailures };
|