batch-run-local 1.1.2 → 1.2.1
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/dist/cli/index.js +169 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/web/index.html +11 -2
- package/dist/web/main.js +414 -64
- package/dist/web/style.css +122 -1
- package/package.json +9 -1
package/dist/cli/index.js
CHANGED
|
@@ -13,6 +13,12 @@ import Conf from "conf";
|
|
|
13
13
|
import { randomUUID } from "crypto";
|
|
14
14
|
import { access } from "fs/promises";
|
|
15
15
|
import { resolve } from "path";
|
|
16
|
+
|
|
17
|
+
// src/shared/types.ts
|
|
18
|
+
var UNGROUPED_TAG = "";
|
|
19
|
+
var UNGROUPED_TAG_LABEL = "\u672A\u5206\u7EC4";
|
|
20
|
+
|
|
21
|
+
// src/config/store.ts
|
|
16
22
|
var defaultSettings = {
|
|
17
23
|
shell: "git-bash",
|
|
18
24
|
port: 1234
|
|
@@ -24,13 +30,85 @@ var store = new Conf({
|
|
|
24
30
|
settings: defaultSettings
|
|
25
31
|
}
|
|
26
32
|
});
|
|
33
|
+
function normalizeTag(tag) {
|
|
34
|
+
const trimmed = tag?.trim();
|
|
35
|
+
if (!trimmed || trimmed === UNGROUPED_TAG_LABEL) {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
return trimmed;
|
|
39
|
+
}
|
|
40
|
+
function getProjectTagKey(project) {
|
|
41
|
+
return project.tag ?? UNGROUPED_TAG;
|
|
42
|
+
}
|
|
43
|
+
function collectTagKeysFromProjects(projects) {
|
|
44
|
+
const keys = [];
|
|
45
|
+
const seen = /* @__PURE__ */ new Set();
|
|
46
|
+
for (const project of projects) {
|
|
47
|
+
const key = getProjectTagKey(project);
|
|
48
|
+
if (!seen.has(key)) {
|
|
49
|
+
seen.add(key);
|
|
50
|
+
keys.push(key);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return keys;
|
|
54
|
+
}
|
|
55
|
+
function groupProjectsByTag(projects) {
|
|
56
|
+
const groups = /* @__PURE__ */ new Map();
|
|
57
|
+
for (const project of projects) {
|
|
58
|
+
const key = getProjectTagKey(project);
|
|
59
|
+
const group = groups.get(key);
|
|
60
|
+
if (group) {
|
|
61
|
+
group.push(project);
|
|
62
|
+
} else {
|
|
63
|
+
groups.set(key, [project]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return groups;
|
|
67
|
+
}
|
|
68
|
+
function flattenProjectsByTagOrder(projects, tagOrder) {
|
|
69
|
+
const grouped = groupProjectsByTag(projects);
|
|
70
|
+
const nextProjects = [];
|
|
71
|
+
for (const tagKey of tagOrder) {
|
|
72
|
+
const group = grouped.get(tagKey);
|
|
73
|
+
if (group) {
|
|
74
|
+
nextProjects.push(...group);
|
|
75
|
+
grouped.delete(tagKey);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const group of grouped.values()) {
|
|
79
|
+
nextProjects.push(...group);
|
|
80
|
+
}
|
|
81
|
+
return nextProjects;
|
|
82
|
+
}
|
|
83
|
+
function syncTagOrder(projects, preferredOrder) {
|
|
84
|
+
const projectTagKeys = collectTagKeysFromProjects(projects);
|
|
85
|
+
const projectTagKeySet = new Set(projectTagKeys);
|
|
86
|
+
const sourceOrder = preferredOrder ?? store.get("tagOrder") ?? [];
|
|
87
|
+
const nextOrder = [];
|
|
88
|
+
const seen = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const tagKey of sourceOrder) {
|
|
90
|
+
if (!seen.has(tagKey) && projectTagKeySet.has(tagKey)) {
|
|
91
|
+
seen.add(tagKey);
|
|
92
|
+
nextOrder.push(tagKey);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const tagKey of projectTagKeys) {
|
|
96
|
+
if (!seen.has(tagKey)) {
|
|
97
|
+
seen.add(tagKey);
|
|
98
|
+
nextOrder.push(tagKey);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
store.set("tagOrder", nextOrder);
|
|
102
|
+
return nextOrder;
|
|
103
|
+
}
|
|
27
104
|
function getConfig() {
|
|
28
105
|
return {
|
|
29
106
|
projects: store.get("projects") ?? [],
|
|
30
107
|
settings: {
|
|
31
108
|
...defaultSettings,
|
|
32
109
|
...store.get("settings") ?? {}
|
|
33
|
-
}
|
|
110
|
+
},
|
|
111
|
+
tagOrder: store.get("tagOrder")
|
|
34
112
|
};
|
|
35
113
|
}
|
|
36
114
|
function getProjects() {
|
|
@@ -39,6 +117,12 @@ function getProjects() {
|
|
|
39
117
|
function getProject(projectId) {
|
|
40
118
|
return getProjects().find((project) => project.id === projectId);
|
|
41
119
|
}
|
|
120
|
+
function getTagOrder() {
|
|
121
|
+
return syncTagOrder(getProjects());
|
|
122
|
+
}
|
|
123
|
+
function getTags() {
|
|
124
|
+
return getTagOrder().filter((tagKey) => tagKey !== UNGROUPED_TAG);
|
|
125
|
+
}
|
|
42
126
|
async function addProject(input) {
|
|
43
127
|
const root = resolve(input.root);
|
|
44
128
|
await access(root);
|
|
@@ -47,6 +131,7 @@ async function addProject(input) {
|
|
|
47
131
|
id: randomUUID(),
|
|
48
132
|
name: input.name.trim(),
|
|
49
133
|
root,
|
|
134
|
+
tag: normalizeTag(input.tag),
|
|
50
135
|
createdAt: now,
|
|
51
136
|
updatedAt: now
|
|
52
137
|
};
|
|
@@ -54,7 +139,9 @@ async function addProject(input) {
|
|
|
54
139
|
if (projects.some((item) => item.root.toLowerCase() === root.toLowerCase())) {
|
|
55
140
|
throw new Error("\u8BE5\u9879\u76EE\u8DEF\u5F84\u5DF2\u7ECF\u5B58\u5728");
|
|
56
141
|
}
|
|
57
|
-
|
|
142
|
+
const nextProjects = [...projects, project];
|
|
143
|
+
store.set("projects", nextProjects);
|
|
144
|
+
syncTagOrder(nextProjects);
|
|
58
145
|
return project;
|
|
59
146
|
}
|
|
60
147
|
async function updateProject(projectId, input) {
|
|
@@ -72,17 +159,20 @@ async function updateProject(projectId, input) {
|
|
|
72
159
|
...projects[projectIndex],
|
|
73
160
|
name: input.name.trim(),
|
|
74
161
|
root,
|
|
162
|
+
tag: normalizeTag(input.tag),
|
|
75
163
|
updatedAt: Date.now()
|
|
76
164
|
};
|
|
77
165
|
const nextProjects = [...projects];
|
|
78
166
|
nextProjects[projectIndex] = nextProject;
|
|
79
167
|
store.set("projects", nextProjects);
|
|
168
|
+
syncTagOrder(nextProjects);
|
|
80
169
|
return nextProject;
|
|
81
170
|
}
|
|
82
171
|
function removeProject(projectId) {
|
|
83
172
|
const projects = getProjects();
|
|
84
173
|
const nextProjects = projects.filter((project) => project.id !== projectId);
|
|
85
174
|
store.set("projects", nextProjects);
|
|
175
|
+
syncTagOrder(nextProjects);
|
|
86
176
|
return nextProjects.length !== projects.length;
|
|
87
177
|
}
|
|
88
178
|
function reorderProjects(projectIds) {
|
|
@@ -100,6 +190,27 @@ function reorderProjects(projectIds) {
|
|
|
100
190
|
return project;
|
|
101
191
|
});
|
|
102
192
|
store.set("projects", nextProjects);
|
|
193
|
+
syncTagOrder(nextProjects);
|
|
194
|
+
return nextProjects;
|
|
195
|
+
}
|
|
196
|
+
function reorderTagGroups(tagKeys) {
|
|
197
|
+
const projects = getProjects();
|
|
198
|
+
const projectTagKeys = collectTagKeysFromProjects(projects);
|
|
199
|
+
const uniqueTagKeys = new Set(tagKeys);
|
|
200
|
+
if (uniqueTagKeys.size !== tagKeys.length) {
|
|
201
|
+
throw new Error("\u6807\u7B7E\u6392\u5E8F\u5305\u542B\u91CD\u590D\u9879");
|
|
202
|
+
}
|
|
203
|
+
if (uniqueTagKeys.size !== projectTagKeys.length) {
|
|
204
|
+
throw new Error("\u6807\u7B7E\u6392\u5E8F\u6570\u636E\u4E0D\u5B8C\u6574");
|
|
205
|
+
}
|
|
206
|
+
for (const tagKey of projectTagKeys) {
|
|
207
|
+
if (!uniqueTagKeys.has(tagKey)) {
|
|
208
|
+
throw new Error("\u6807\u7B7E\u6392\u5E8F\u5305\u542B\u4E0D\u5B58\u5728\u7684\u6807\u7B7E");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const nextProjects = flattenProjectsByTagOrder(projects, tagKeys);
|
|
212
|
+
store.set("tagOrder", tagKeys);
|
|
213
|
+
store.set("projects", nextProjects);
|
|
103
214
|
return nextProjects;
|
|
104
215
|
}
|
|
105
216
|
function getSettings() {
|
|
@@ -119,6 +230,7 @@ function getConfigPath() {
|
|
|
119
230
|
|
|
120
231
|
// src/cli/commands/add.ts
|
|
121
232
|
async function addCommand() {
|
|
233
|
+
const existingTags = getTags();
|
|
122
234
|
const answers = await prompts([
|
|
123
235
|
{
|
|
124
236
|
type: "text",
|
|
@@ -131,6 +243,12 @@ async function addCommand() {
|
|
|
131
243
|
name: "root",
|
|
132
244
|
message: "\u9879\u76EE\u6839\u8DEF\u5F84",
|
|
133
245
|
validate: (value) => value.trim().length > 0 || "\u8BF7\u8F93\u5165\u9879\u76EE\u6839\u8DEF\u5F84"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
type: existingTags.length > 0 ? "autocomplete" : "text",
|
|
249
|
+
name: "tag",
|
|
250
|
+
message: "\u6807\u7B7E\uFF08\u4E0D\u586B\u65F6\u9ED8\u8BA4\u672A\u5206\u7EC4\uFF09",
|
|
251
|
+
choices: existingTags.map((tag) => ({ title: tag, value: tag }))
|
|
134
252
|
}
|
|
135
253
|
]);
|
|
136
254
|
if (!answers.name || !answers.root) {
|
|
@@ -138,9 +256,13 @@ async function addCommand() {
|
|
|
138
256
|
}
|
|
139
257
|
const project = await addProject({
|
|
140
258
|
name: answers.name,
|
|
141
|
-
root: answers.root
|
|
259
|
+
root: answers.root,
|
|
260
|
+
tag: answers.tag
|
|
142
261
|
});
|
|
143
262
|
console.log(`\u5DF2\u6DFB\u52A0\u9879\u76EE: ${project.name}`);
|
|
263
|
+
if (project.tag) {
|
|
264
|
+
console.log(` tag: ${project.tag}`);
|
|
265
|
+
}
|
|
144
266
|
console.log(project.root);
|
|
145
267
|
}
|
|
146
268
|
|
|
@@ -175,6 +297,9 @@ async function configCommand() {
|
|
|
175
297
|
}
|
|
176
298
|
|
|
177
299
|
// src/cli/commands/list.ts
|
|
300
|
+
function getTagLabel(tagKey) {
|
|
301
|
+
return tagKey === UNGROUPED_TAG ? UNGROUPED_TAG_LABEL : tagKey;
|
|
302
|
+
}
|
|
178
303
|
function listCommand() {
|
|
179
304
|
const projects = getProjects();
|
|
180
305
|
if (projects.length === 0) {
|
|
@@ -182,13 +307,21 @@ function listCommand() {
|
|
|
182
307
|
console.log(`\u914D\u7F6E\u6587\u4EF6: ${getConfigPath()}`);
|
|
183
308
|
return;
|
|
184
309
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
310
|
+
const tagOrder = getTagOrder();
|
|
311
|
+
for (const tagKey of tagOrder) {
|
|
312
|
+
const groupedProjects = projects.filter((project) => (project.tag ?? UNGROUPED_TAG) === tagKey);
|
|
313
|
+
if (groupedProjects.length === 0) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
console.log(`[${getTagLabel(tagKey)}]`);
|
|
317
|
+
for (const project of groupedProjects) {
|
|
318
|
+
console.log(` ${project.name}`);
|
|
319
|
+
console.log(` id: ${project.id}`);
|
|
320
|
+
console.log(` root: ${project.root}`);
|
|
321
|
+
}
|
|
322
|
+
console.log("");
|
|
189
323
|
}
|
|
190
|
-
console.log(`
|
|
191
|
-
\u914D\u7F6E\u6587\u4EF6: ${getConfigPath()}`);
|
|
324
|
+
console.log(`\u914D\u7F6E\u6587\u4EF6: ${getConfigPath()}`);
|
|
192
325
|
}
|
|
193
326
|
|
|
194
327
|
// src/cli/commands/remove.ts
|
|
@@ -507,7 +640,12 @@ function processEnvWithoutGitOverrides2() {
|
|
|
507
640
|
// src/server/routes.ts
|
|
508
641
|
async function registerApiRoutes(app, settings) {
|
|
509
642
|
app.get("/api/projects", async () => ({
|
|
510
|
-
projects: getProjects()
|
|
643
|
+
projects: getProjects(),
|
|
644
|
+
tagOrder: getTagOrder()
|
|
645
|
+
}));
|
|
646
|
+
app.get("/api/tags", async () => ({
|
|
647
|
+
tags: getTags(),
|
|
648
|
+
tagOrder: getTagOrder()
|
|
511
649
|
}));
|
|
512
650
|
app.post("/api/projects", async (request, reply) => {
|
|
513
651
|
const name = request.body.name?.trim();
|
|
@@ -515,7 +653,7 @@ async function registerApiRoutes(app, settings) {
|
|
|
515
653
|
if (!name || !root) {
|
|
516
654
|
return reply.code(400).send({ message: "\u9879\u76EE\u540D\u79F0\u548C\u6839\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A" });
|
|
517
655
|
}
|
|
518
|
-
const project = await addProject({ name, root });
|
|
656
|
+
const project = await addProject({ name, root, tag: request.body.tag });
|
|
519
657
|
return { project };
|
|
520
658
|
});
|
|
521
659
|
app.put("/api/projects/order", async (request, reply) => {
|
|
@@ -524,12 +662,30 @@ async function registerApiRoutes(app, settings) {
|
|
|
524
662
|
return reply.code(400).send({ message: "\u9879\u76EE\u6392\u5E8F\u6570\u636E\u65E0\u6548" });
|
|
525
663
|
}
|
|
526
664
|
try {
|
|
527
|
-
return {
|
|
665
|
+
return {
|
|
666
|
+
projects: reorderProjects(projectIds),
|
|
667
|
+
tagOrder: getTagOrder()
|
|
668
|
+
};
|
|
528
669
|
} catch (error) {
|
|
529
670
|
const message = error instanceof Error ? error.message : "\u4FDD\u5B58\u9879\u76EE\u6392\u5E8F\u5931\u8D25";
|
|
530
671
|
return reply.code(400).send({ message });
|
|
531
672
|
}
|
|
532
673
|
});
|
|
674
|
+
app.put("/api/tag-order", async (request, reply) => {
|
|
675
|
+
const tagKeys = request.body.tagKeys;
|
|
676
|
+
if (!Array.isArray(tagKeys) || !tagKeys.every((tagKey) => typeof tagKey === "string")) {
|
|
677
|
+
return reply.code(400).send({ message: "\u6807\u7B7E\u6392\u5E8F\u6570\u636E\u65E0\u6548" });
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
return {
|
|
681
|
+
projects: reorderTagGroups(tagKeys),
|
|
682
|
+
tagOrder: getTagOrder()
|
|
683
|
+
};
|
|
684
|
+
} catch (error) {
|
|
685
|
+
const message = error instanceof Error ? error.message : "\u4FDD\u5B58\u6807\u7B7E\u6392\u5E8F\u5931\u8D25";
|
|
686
|
+
return reply.code(400).send({ message });
|
|
687
|
+
}
|
|
688
|
+
});
|
|
533
689
|
app.put("/api/projects/:id", async (request, reply) => {
|
|
534
690
|
const name = request.body.name?.trim();
|
|
535
691
|
const root = request.body.root?.trim();
|
|
@@ -537,7 +693,7 @@ async function registerApiRoutes(app, settings) {
|
|
|
537
693
|
return reply.code(400).send({ message: "\u9879\u76EE\u540D\u79F0\u548C\u6839\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A" });
|
|
538
694
|
}
|
|
539
695
|
try {
|
|
540
|
-
const project = await updateProject(request.params.id, { name, root });
|
|
696
|
+
const project = await updateProject(request.params.id, { name, root, tag: request.body.tag });
|
|
541
697
|
if (!project) {
|
|
542
698
|
return reply.code(404).send({ message: "\u9879\u76EE\u4E0D\u5B58\u5728" });
|
|
543
699
|
}
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/add.ts","../../src/config/store.ts","../../src/cli/commands/config.ts","../../src/cli/commands/list.ts","../../src/cli/commands/remove.ts","../../src/cli/commands/run.ts","../../src/project/package-json.ts","../../src/runner/git-bash.ts","../../src/runner/path.ts","../../src/server/index.ts","../../src/server/routes.ts","../../src/project/git.ts","../../src/cli/commands/ui.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { cac } from 'cac'\r\nimport { readFileSync } from 'node:fs'\r\nimport prompts from 'prompts'\r\nimport { addCommand } from './commands/add.js'\r\nimport { configCommand } from './commands/config.js'\r\nimport { listCommand } from './commands/list.js'\r\nimport { removeCommand } from './commands/remove.js'\r\nimport { runCommand } from './commands/run.js'\r\nimport { uiCommand } from './commands/ui.js'\r\n\r\nconst packageJson = JSON.parse(\r\n readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),\r\n) as { version?: string }\r\nconst packageVersion = packageJson.version ?? '0.0.0'\r\n\r\nconst cli = cac('br')\r\n\r\ncli.command('add', '新增项目配置').action(wrap(addCommand))\r\n\r\ncli.command('list', '查看项目配置').action(wrap(async () => listCommand()))\r\n\r\ncli.command('run', '选择并执行项目脚本').action(wrap(runCommand))\r\n\r\ncli.command('remove', '删除项目配置').action(wrap(removeCommand))\r\n\r\ncli.command('ui', '打开 Web 管理页面').action(wrap(uiCommand))\r\n\r\ncli.command('config', '配置 Git Bash 路径和 Web 端口').action(wrap(configCommand))\r\n\r\ncli.help()\r\ncli.version(packageVersion)\r\n\r\nasync function mainMenu(): Promise<void> {\r\n const answer = await prompts({\r\n type: 'select',\r\n name: 'action',\r\n message: '请选择操作',\r\n choices: [\r\n { title: '打开可视化操作页面', value: 'ui' },\r\n { title: '启动项目', value: 'run' },\r\n { title: '查看项目', value: 'list' },\r\n { title: '新增项目', value: 'add' },\r\n { title: '删除项目', value: 'remove' },\r\n { title: '设置 Git Bash 路径', value: 'config' },\r\n ],\r\n })\r\n\r\n switch (answer.action) {\r\n case 'run':\r\n await runCommand()\r\n break\r\n case 'add':\r\n await addCommand()\r\n break\r\n case 'list':\r\n listCommand()\r\n break\r\n case 'remove':\r\n await removeCommand()\r\n break\r\n case 'ui':\r\n await uiCommand()\r\n break\r\n case 'config':\r\n await configCommand()\r\n break\r\n }\r\n}\r\n\r\nfunction wrap(fn: () => Promise<void>) {\r\n return async () => {\r\n try {\r\n await fn()\r\n } catch (error) {\r\n handleError(error)\r\n }\r\n }\r\n}\r\n\r\nfunction handleError(error: unknown): void {\r\n const message = error instanceof Error ? error.message : String(error)\r\n console.error(`执行失败: ${message}`)\r\n process.exitCode = 1\r\n}\r\n\r\nif (process.argv.length <= 2) {\r\n await wrap(mainMenu)()\r\n} else {\r\n cli.parse()\r\n}\r\n","import prompts from \"prompts\";\r\nimport { addProject } from \"../../config/store.js\";\r\n\r\nexport async function addCommand(): Promise<void> {\r\n const answers = await prompts([\r\n {\r\n type: \"text\",\r\n name: \"name\",\r\n message: \"项目名称\",\r\n validate: (value) => value.trim().length > 0 || \"请输入项目名称\",\r\n },\r\n {\r\n type: \"text\",\r\n name: \"root\",\r\n message: \"项目根路径\",\r\n validate: (value) => value.trim().length > 0 || \"请输入项目根路径\",\r\n },\r\n ]);\r\n\r\n if (!answers.name || !answers.root) {\r\n return;\r\n }\r\n\r\n const project = await addProject({\r\n name: answers.name,\r\n root: answers.root,\r\n });\r\n\r\n console.log(`已添加项目: ${project.name}`);\r\n console.log(project.root);\r\n}\r\n","import Conf from \"conf\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport { access } from \"node:fs/promises\";\r\nimport { resolve } from \"node:path\";\r\nimport type { AppConfig, AppSettings, ProjectConfig } from \"../shared/types.js\";\r\n\r\nconst defaultSettings: AppSettings = {\r\n shell: \"git-bash\",\r\n port: 1234,\r\n};\r\n\r\nconst store = new Conf<AppConfig>({\r\n projectName: \"batch-run-local\",\r\n defaults: {\r\n projects: [],\r\n settings: defaultSettings,\r\n },\r\n});\r\n\r\nexport function getConfig(): AppConfig {\r\n return {\r\n projects: store.get(\"projects\") ?? [],\r\n settings: {\r\n ...defaultSettings,\r\n ...(store.get(\"settings\") ?? {}),\r\n },\r\n };\r\n}\r\n\r\nexport function getProjects(): ProjectConfig[] {\r\n return getConfig().projects;\r\n}\r\n\r\nexport function getProject(projectId: string): ProjectConfig | undefined {\r\n return getProjects().find((project) => project.id === projectId);\r\n}\r\n\r\nexport async function addProject(input: { name: string; root: string }): Promise<ProjectConfig> {\r\n const root = resolve(input.root);\r\n await access(root);\r\n\r\n const now = Date.now();\r\n const project: ProjectConfig = {\r\n id: randomUUID(),\r\n name: input.name.trim(),\r\n root,\r\n createdAt: now,\r\n updatedAt: now,\r\n };\r\n\r\n const projects = getProjects();\r\n if (projects.some((item) => item.root.toLowerCase() === root.toLowerCase())) {\r\n throw new Error(\"该项目路径已经存在\");\r\n }\r\n\r\n store.set(\"projects\", [...projects, project]);\r\n return project;\r\n}\r\n\r\nexport async function updateProject(\r\n projectId: string,\r\n input: { name: string; root: string },\r\n): Promise<ProjectConfig | undefined> {\r\n const root = resolve(input.root);\r\n await access(root);\r\n\r\n const projects = getProjects();\r\n const projectIndex = projects.findIndex((project) => project.id === projectId);\r\n if (projectIndex < 0) {\r\n return undefined;\r\n }\r\n\r\n if (projects.some((project) => project.id !== projectId && project.root.toLowerCase() === root.toLowerCase())) {\r\n throw new Error(\"该项目路径已经存在\");\r\n }\r\n\r\n const nextProject: ProjectConfig = {\r\n ...projects[projectIndex],\r\n name: input.name.trim(),\r\n root,\r\n updatedAt: Date.now(),\r\n };\r\n const nextProjects = [...projects];\r\n nextProjects[projectIndex] = nextProject;\r\n store.set(\"projects\", nextProjects);\r\n return nextProject;\r\n}\r\n\r\nexport function removeProject(projectId: string): boolean {\r\n const projects = getProjects();\r\n const nextProjects = projects.filter((project) => project.id !== projectId);\r\n store.set(\"projects\", nextProjects);\r\n return nextProjects.length !== projects.length;\r\n}\r\n\r\nexport function reorderProjects(projectIds: string[]): ProjectConfig[] {\r\n const projects = getProjects();\r\n const projectMap = new Map(projects.map((project) => [project.id, project]));\r\n const uniqueProjectIds = new Set(projectIds);\r\n\r\n if (uniqueProjectIds.size !== projects.length) {\r\n throw new Error(\"项目排序数据不完整\");\r\n }\r\n\r\n const nextProjects = projectIds.map((projectId) => {\r\n const project = projectMap.get(projectId);\r\n if (!project) {\r\n throw new Error(\"项目排序包含不存在的项目\");\r\n }\r\n return project;\r\n });\r\n\r\n store.set(\"projects\", nextProjects);\r\n return nextProjects;\r\n}\r\n\r\nexport function getSettings(): AppSettings {\r\n return getConfig().settings;\r\n}\r\n\r\nexport function updateSettings(settings: Partial<AppSettings>): AppSettings {\r\n const nextSettings = {\r\n ...getSettings(),\r\n ...settings,\r\n };\r\n store.set(\"settings\", nextSettings);\r\n return nextSettings;\r\n}\r\n\r\nexport function getConfigPath(): string {\r\n return store.path;\r\n}\r\n","import prompts from \"prompts\";\r\nimport { getConfigPath, getSettings, updateSettings } from \"../../config/store.js\";\r\n\r\nexport async function configCommand(): Promise<void> {\r\n const settings = getSettings();\r\n\r\n console.log(`当前配置文件: ${getConfigPath()}`);\r\n console.log(`当前 Git Bash 路径: ${settings.gitBashPath ?? \"自动检测\"}`);\r\n console.log(`当前 Web 端口: ${settings.port}`);\r\n\r\n const answers = await prompts([\r\n {\r\n type: \"text\",\r\n name: \"gitBashPath\",\r\n message: \"Git Bash 路径,留空表示自动检测\",\r\n initial: settings.gitBashPath ?? \"\",\r\n },\r\n {\r\n type: \"number\",\r\n name: \"port\",\r\n message: \"Web 端口\",\r\n initial: settings.port,\r\n min: 1,\r\n max: 65535,\r\n },\r\n ]);\r\n\r\n updateSettings({\r\n gitBashPath: answers.gitBashPath?.trim() || undefined,\r\n port: answers.port || settings.port,\r\n });\r\n\r\n console.log(\"配置已更新。\");\r\n}\r\n","import { getConfigPath, getProjects } from \"../../config/store.js\";\r\n\r\nexport function listCommand(): void {\r\n const projects = getProjects();\r\n\r\n if (projects.length === 0) {\r\n console.log(\"暂无项目配置,可运行 br add 新增。\");\r\n console.log(`配置文件: ${getConfigPath()}`);\r\n return;\r\n }\r\n\r\n for (const project of projects) {\r\n console.log(`${project.name}`);\r\n console.log(` id: ${project.id}`);\r\n console.log(` root: ${project.root}`);\r\n }\r\n\r\n console.log(`\\n配置文件: ${getConfigPath()}`);\r\n}\r\n","import prompts from \"prompts\";\r\nimport { getProjects, removeProject } from \"../../config/store.js\";\r\n\r\nexport async function removeCommand(): Promise<void> {\r\n const projects = getProjects();\r\n\r\n if (projects.length === 0) {\r\n console.log(\"暂无项目可删除。\");\r\n return;\r\n }\r\n\r\n const answer = await prompts({\r\n type: \"select\",\r\n name: \"projectId\",\r\n message: \"选择要删除的项目\",\r\n choices: projects.map((project) => ({\r\n title: project.name,\r\n description: project.root,\r\n value: project.id,\r\n })),\r\n });\r\n\r\n if (!answer.projectId) {\r\n return;\r\n }\r\n\r\n const removed = removeProject(answer.projectId);\r\n console.log(removed ? \"已删除项目。\" : \"未找到项目。\");\r\n}\r\n","import prompts from \"prompts\";\r\nimport { getProjects, getSettings } from \"../../config/store.js\";\r\nimport { readPackageScripts } from \"../../project/package-json.js\";\r\nimport { runScriptInGitBash } from \"../../runner/git-bash.js\";\r\n\r\nexport async function runCommand(): Promise<void> {\r\n const projects = getProjects();\r\n\r\n if (projects.length === 0) {\r\n console.log(\"暂无项目配置,可运行 br add 新增。\");\r\n return;\r\n }\r\n\r\n const projectAnswer = await prompts({\r\n type: \"select\",\r\n name: \"projectId\",\r\n message: \"选择项目\",\r\n choices: projects.map((project) => ({\r\n title: project.name,\r\n description: project.root,\r\n value: project.id,\r\n })),\r\n });\r\n\r\n const project = projects.find((item) => item.id === projectAnswer.projectId);\r\n if (!project) {\r\n return;\r\n }\r\n\r\n const scripts = await readPackageScripts(project.root);\r\n const scriptEntries = Object.entries(scripts);\r\n\r\n if (scriptEntries.length === 0) {\r\n console.log(\"该项目 package.json 中没有 scripts。\");\r\n return;\r\n }\r\n\r\n const scriptAnswer = await prompts({\r\n type: \"select\",\r\n name: \"script\",\r\n message: \"选择要执行的脚本\",\r\n choices: scriptEntries.map(([name, command]) => ({\r\n title: name,\r\n description: command,\r\n value: name,\r\n })),\r\n });\r\n\r\n if (!scriptAnswer.script) {\r\n return;\r\n }\r\n\r\n const result = runScriptInGitBash(project, scriptAnswer.script, getSettings());\r\n console.log(`已在 Git Bash 窗口执行: npm run ${scriptAnswer.script}`);\r\n if (result.pid) {\r\n console.log(`进程 PID: ${result.pid}`);\r\n }\r\n}\r\n","import { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { PackageScripts } from \"../shared/types.js\";\r\n\r\ntype PackageJson = {\r\n scripts?: PackageScripts;\r\n};\r\n\r\nexport async function readPackageJson(projectRoot: string): Promise<PackageJson> {\r\n const packageJsonPath = join(projectRoot, \"package.json\");\r\n const content = await readFile(packageJsonPath, \"utf8\");\r\n\r\n try {\r\n return JSON.parse(content) as PackageJson;\r\n } catch {\r\n throw new Error(`package.json 解析失败: ${packageJsonPath}`);\r\n }\r\n}\r\n\r\nexport async function readPackageScripts(projectRoot: string): Promise<PackageScripts> {\r\n const packageJson = await readPackageJson(projectRoot);\r\n return packageJson.scripts ?? {};\r\n}\r\n","import { spawn } from \"node:child_process\";\nimport { mkdtempSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AppSettings, ProjectConfig } from \"../shared/types.js\";\nimport { findBashExecutablePath, findGitBashPath, findMinttyPath, shellSingleQuote, toGitBashPath } from \"./path.js\";\n\n/** Strip Git repo overrides so child processes (e.g. Vite calling `git`) use cwd discovery, not a stale inherited GIT_DIR. */\nconst GIT_ENV_KEYS = [\n \"GIT_DIR\",\n \"GIT_WORK_TREE\",\n \"GIT_INDEX_FILE\",\n \"GIT_PREFIX\",\n \"GIT_OBJECT_DIRECTORY\",\n \"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n] as const;\n\nfunction processEnvWithoutGitOverrides(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n for (const key of GIT_ENV_KEYS) {\n delete env[key];\n }\n return env;\n}\n\nexport type RunResult = {\n pid?: number;\n command: string;\n};\n\nexport function runScriptInGitBash(project: ProjectConfig, scriptName: string, settings: AppSettings): RunResult {\n const gitBashPath = findGitBashPath(settings.gitBashPath);\n\n if (!gitBashPath) {\n throw new Error(\"未找到 Git Bash,请先通过 br config 设置 git-bash.exe 路径\");\n }\n\n const title = createTerminalTitle(project.name, scriptName);\n const gitBashRoot = toGitBashPath(project.root);\n const scriptPath = writeRunScript(project, scriptName, title, gitBashRoot);\n const gitBashScriptPath = toGitBashPath(scriptPath);\n const bashPath = findBashExecutablePath(gitBashPath);\n const command = `${bashPath} --login ${scriptPath}`;\n\n const minttyPath = findMinttyPath(gitBashPath);\n const child = minttyPath\n ? spawn(minttyPath, [\"--title\", title, \"--dir\", gitBashRoot, toGitBashPath(bashPath), \"--login\", gitBashScriptPath], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: false,\n env: processEnvWithoutGitOverrides(),\n })\n : spawn(\"cmd.exe\", [\"/d\", \"/c\", \"start\", title, \"/D\", project.root, bashPath, \"--login\", gitBashScriptPath], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: false,\n env: processEnvWithoutGitOverrides(),\n });\n\n child.unref();\n\n return {\n pid: child.pid,\n command,\n };\n}\n\nfunction createTerminalTitle(projectName: string, scriptName: string): string {\n const title = `batch-run - ${projectName} - npm run ${scriptName}`;\n return title.replace(/[\\u0000-\\u001f\\u007f]/g, \" \").slice(0, 120);\n}\n\nfunction writeRunScript(project: ProjectConfig, scriptName: string, title: string, gitBashRoot: string): string {\n const tempDir = mkdtempSync(join(tmpdir(), \"batch-run-\"));\n const scriptPath = join(tempDir, \"run.sh\");\n const scriptContent = [\n \"#!/usr/bin/env bash\",\n \"set +e\",\n `cd ${shellSingleQuote(gitBashRoot)}`,\n 'rm -f \"$0\" >/dev/null 2>&1 || true',\n `unset ${GIT_ENV_KEYS.join(\" \")}`,\n `__batch_run_title=${shellSingleQuote(title)}`,\n \"set_batch_run_title() { printf '\\\\033]0;%s\\\\007' \\\"$__batch_run_title\\\"; }\",\n \"set_batch_run_title\",\n \"(while true; do set_batch_run_title; sleep 2; done) &\",\n \"__batch_run_title_pid=$!\",\n \"cleanup_batch_run_title() {\",\n ' kill \"$__batch_run_title_pid\" >/dev/null 2>&1 || true',\n ' wait \"$__batch_run_title_pid\" 2>/dev/null || true',\n \" set_batch_run_title\",\n \"}\",\n \"trap cleanup_batch_run_title EXIT\",\n `echo Running ${shellSingleQuote(project.name)}: npm run ${shellSingleQuote(scriptName)}`,\n `npm run ${shellSingleQuote(scriptName)}`,\n \"__batch_run_exit_code=$?\",\n \"set_batch_run_title\",\n \"echo\",\n 'echo \"Process exited with code $__batch_run_exit_code. Press Ctrl+D or close this window.\"',\n \"bash --login\",\n 'exit \"$__batch_run_exit_code\"',\n \"\",\n ].join(\"\\n\");\n\n writeFileSync(scriptPath, scriptContent, \"utf8\");\n return scriptPath;\n}\n","import { existsSync } from \"node:fs\";\r\nimport { platform } from \"node:os\";\r\nimport { basename, dirname, join } from \"node:path\";\r\n\r\nconst defaultWindowsGitBashPaths = [\r\n \"C:\\\\Program Files\\\\Git\\\\git-bash.exe\",\r\n \"C:\\\\Program Files (x86)\\\\Git\\\\git-bash.exe\",\r\n];\r\n\r\nexport function findGitBashPath(configuredPath?: string): string | undefined {\r\n if (configuredPath && existsSync(configuredPath) && isGitBashExecutable(configuredPath)) {\r\n return configuredPath;\r\n }\r\n\r\n if (platform() !== \"win32\") {\r\n return undefined;\r\n }\r\n\r\n return defaultWindowsGitBashPaths.find((item) => existsSync(item));\r\n}\r\n\r\nexport function toGitBashPath(inputPath: string): string {\r\n const normalized = inputPath.replaceAll(\"\\\\\", \"/\");\r\n const driveMatch = /^([a-zA-Z]):\\/(.*)$/.exec(normalized);\r\n\r\n if (!driveMatch) {\r\n return normalized;\r\n }\r\n\r\n const [, drive, rest] = driveMatch;\r\n return `/${drive.toLowerCase()}/${rest}`;\r\n}\r\n\r\nexport function shellSingleQuote(value: string): string {\r\n return `'${value.replaceAll(\"'\", \"'\\\\''\")}'`;\r\n}\r\n\r\nexport function findMinttyPath(gitBashPath: string): string | undefined {\r\n const gitBashDir = dirname(gitBashPath);\r\n const candidates = [\r\n join(gitBashDir, \"usr\", \"bin\", \"mintty.exe\"),\r\n join(gitBashDir, \"mintty.exe\"),\r\n join(dirname(gitBashDir), \"usr\", \"bin\", \"mintty.exe\"),\r\n ];\r\n\r\n return candidates.find((minttyPath) => existsSync(minttyPath));\r\n}\r\n\r\nexport function findBashExecutablePath(gitBashPath: string): string {\r\n const executableName = basename(gitBashPath).toLowerCase();\r\n if (executableName === \"bash.exe\" || executableName === \"sh.exe\") {\r\n return gitBashPath;\r\n }\r\n\r\n const gitBashDir = dirname(gitBashPath);\r\n const candidates = [\r\n join(gitBashDir, \"bin\", \"bash.exe\"),\r\n join(gitBashDir, \"usr\", \"bin\", \"bash.exe\"),\r\n join(gitBashDir, \"bin\", \"sh.exe\"),\r\n join(gitBashDir, \"usr\", \"bin\", \"sh.exe\"),\r\n ];\r\n\r\n return candidates.find((bashPath) => existsSync(bashPath)) ?? gitBashPath;\r\n}\r\n\r\nfunction isGitBashExecutable(inputPath: string): boolean {\r\n const executableName = basename(inputPath).toLowerCase();\r\n return executableName === \"git-bash.exe\" || executableName === \"bash.exe\" || executableName === \"sh.exe\";\r\n}\r\n","import fastify from \"fastify\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { dirname, join } from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport open from \"open\";\r\nimport { getSettings } from \"../config/store.js\";\r\nimport { registerApiRoutes } from \"./routes.js\";\r\n\r\nfunction getWebRoot(): string {\r\n return join(dirname(dirname(fileURLToPath(import.meta.url))), \"web\");\r\n}\r\n\r\nexport async function startServer(options: { openBrowser?: boolean } = {}): Promise<void> {\r\n const settings = getSettings();\r\n const app = fastify({\r\n logger: false,\r\n });\r\n const webRoot = getWebRoot();\r\n\r\n await registerApiRoutes(app, settings);\r\n\r\n app.get(\"/\", async (_request, reply) => {\r\n return reply.type(\"text/html\").send(await readFile(join(webRoot, \"index.html\"), \"utf8\"));\r\n });\r\n\r\n app.get(\"/main.js\", async (_request, reply) => {\r\n return reply.type(\"application/javascript\").send(await readFile(join(webRoot, \"main.js\"), \"utf8\"));\r\n });\r\n\r\n app.get(\"/style.css\", async (_request, reply) => {\r\n return reply.type(\"text/css\").send(await readFile(join(webRoot, \"style.css\"), \"utf8\"));\r\n });\r\n\r\n app.get(\"/favicon.ico\", async (_request, reply) => {\r\n return reply.type(\"image/x-icon\").send(await readFile(join(webRoot, \"favicon.ico\")));\r\n });\r\n\r\n const url = await app.listen({\r\n host: \"127.0.0.1\",\r\n port: settings.port,\r\n });\r\n\r\n console.log(`Batch Run Web 已启动: ${url}`);\r\n\r\n if (options.openBrowser ?? true) {\r\n await open(url);\r\n }\r\n}\r\n","import type { FastifyInstance } from \"fastify\";\r\nimport open from \"open\";\r\nimport { addProject, getProject, getProjects, removeProject, reorderProjects, updateProject } from \"../config/store.js\";\r\nimport { readGitBranches, switchGitBranch } from \"../project/git.js\";\r\nimport { readPackageScripts } from \"../project/package-json.js\";\r\nimport { runScriptInGitBash } from \"../runner/git-bash.js\";\r\nimport type { AppSettings } from \"../shared/types.js\";\r\n\r\ntype AddProjectBody = {\r\n name?: string;\r\n root?: string;\r\n};\r\n\r\ntype UpdateProjectBody = AddProjectBody;\r\n\r\ntype RunScriptBody = {\r\n script?: string;\r\n};\r\n\r\ntype SwitchBranchBody = {\r\n branch?: string;\r\n};\r\n\r\ntype ReorderProjectsBody = {\r\n projectIds?: string[];\r\n};\r\n\r\nexport async function registerApiRoutes(app: FastifyInstance, settings: AppSettings): Promise<void> {\r\n app.get(\"/api/projects\", async () => ({\r\n projects: getProjects(),\r\n }));\r\n\r\n app.post<{ Body: AddProjectBody }>(\"/api/projects\", async (request, reply) => {\r\n const name = request.body.name?.trim();\r\n const root = request.body.root?.trim();\r\n\r\n if (!name || !root) {\r\n return reply.code(400).send({ message: \"项目名称和根路径不能为空\" });\r\n }\r\n\r\n const project = await addProject({ name, root });\r\n return { project };\r\n });\r\n\r\n app.put<{ Body: ReorderProjectsBody }>(\"/api/projects/order\", async (request, reply) => {\r\n const projectIds = request.body.projectIds;\r\n\r\n if (!Array.isArray(projectIds) || !projectIds.every((projectId) => typeof projectId === \"string\")) {\r\n return reply.code(400).send({ message: \"项目排序数据无效\" });\r\n }\r\n\r\n try {\r\n return { projects: reorderProjects(projectIds) };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"保存项目排序失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.put<{ Params: { id: string }; Body: UpdateProjectBody }>(\"/api/projects/:id\", async (request, reply) => {\r\n const name = request.body.name?.trim();\r\n const root = request.body.root?.trim();\r\n\r\n if (!name || !root) {\r\n return reply.code(400).send({ message: \"项目名称和根路径不能为空\" });\r\n }\r\n\r\n try {\r\n const project = await updateProject(request.params.id, { name, root });\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n return { project };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"更新项目失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.delete<{ Params: { id: string } }>(\"/api/projects/:id\", async (request, reply) => {\r\n const removed = removeProject(request.params.id);\r\n\r\n if (!removed) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n return { ok: true };\r\n });\r\n\r\n app.get<{ Params: { id: string } }>(\"/api/projects/:id/scripts\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n const scripts = await readPackageScripts(project.root);\r\n return { scripts };\r\n });\r\n\r\n app.get<{ Params: { id: string } }>(\"/api/projects/:id/branches\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n try {\r\n return await readGitBranches(project.root);\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"读取 Git 分支失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.post<{ Params: { id: string }; Body: SwitchBranchBody }>(\"/api/projects/:id/branches/switch\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n const branch = request.body.branch?.trim();\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n if (!branch) {\r\n return reply.code(400).send({ message: \"分支名称不能为空\" });\r\n }\r\n\r\n try {\r\n const branchState = await switchGitBranch(project.root, branch);\r\n const scripts = await readPackageScripts(project.root);\r\n return {\r\n ...branchState,\r\n scripts,\r\n };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"切换 Git 分支失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.post<{ Params: { id: string } }>(\"/api/projects/:id/open\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n await open(project.root);\r\n return { ok: true };\r\n });\r\n\r\n app.post<{ Params: { id: string }; Body: RunScriptBody }>(\"/api/projects/:id/run\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n const script = request.body.script?.trim();\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n if (!script) {\r\n return reply.code(400).send({ message: \"脚本名称不能为空\" });\r\n }\r\n\r\n const scripts = await readPackageScripts(project.root);\r\n if (!scripts[script]) {\r\n return reply.code(404).send({ message: \"脚本不存在\" });\r\n }\r\n\r\n const result = runScriptInGitBash(project, script, settings);\r\n return {\r\n ok: true,\r\n pid: result.pid,\r\n };\r\n });\r\n}\r\n","import { execFile } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { GitBranch, GitBranchState } from \"../shared/types.js\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport async function readGitBranches(projectRoot: string): Promise<GitBranchState> {\r\n const [current, localBranches] = await Promise.all([\r\n runGit(projectRoot, [\"branch\", \"--show-current\"]),\r\n runGit(projectRoot, [\"branch\", \"--format=%(refname:short)\"]),\r\n ]);\r\n\r\n const currentBranch = current.trim() || undefined;\r\n const localNames = parseBranchLines(localBranches);\r\n const branches: GitBranch[] = localNames.map((name) => ({\r\n name,\r\n current: name === currentBranch,\r\n remote: false,\r\n }));\r\n\r\n return {\r\n current: currentBranch,\r\n branches,\r\n };\r\n}\r\n\r\nexport async function switchGitBranch(projectRoot: string, branchName: string): Promise<GitBranchState> {\r\n const name = branchName.trim();\r\n if (!name) {\r\n throw new Error(\"分支名称不能为空\");\r\n }\r\n\r\n const branchState = await readGitBranches(projectRoot);\r\n const branch = branchState.branches.find((item) => item.name === name);\r\n if (!branch) {\r\n throw new Error(\"本地分支不存在\");\r\n }\r\n\r\n if (branch.current) {\r\n return branchState;\r\n }\r\n\r\n await runGit(projectRoot, [\"switch\", name]);\r\n\r\n return readGitBranches(projectRoot);\r\n}\r\n\r\nasync function runGit(projectRoot: string, args: string[]): Promise<string> {\r\n try {\r\n const { stdout } = await execFileAsync(\"git\", args, {\r\n cwd: projectRoot,\r\n env: processEnvWithoutGitOverrides(),\r\n windowsHide: true,\r\n });\r\n return stdout;\r\n } catch (error) {\r\n const stderr = typeof error === \"object\" && error && \"stderr\" in error ? String(error.stderr) : \"\";\r\n const message = stderr.trim() || (error instanceof Error ? error.message : \"Git 命令执行失败\");\r\n throw new Error(message);\r\n }\r\n}\r\n\r\nfunction parseBranchLines(output: string): string[] {\r\n return output\r\n .split(/\\r?\\n/)\r\n .map((line) => line.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nfunction processEnvWithoutGitOverrides(): NodeJS.ProcessEnv {\r\n const env = { ...process.env };\r\n delete env.GIT_DIR;\r\n delete env.GIT_WORK_TREE;\r\n delete env.GIT_INDEX_FILE;\r\n delete env.GIT_PREFIX;\r\n delete env.GIT_OBJECT_DIRECTORY;\r\n delete env.GIT_ALTERNATE_OBJECT_DIRECTORIES;\r\n return env;\r\n}\r\n","import { startServer } from \"../../server/index.js\";\r\n\r\nexport async function uiCommand(): Promise<void> {\r\n await startServer({ openBrowser: true });\r\n}\r\n"],"mappings":"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","names":["prompts","prompts","prompts","prompts","prompts","prompts","packageJson","join","join","join","prompts","project","readFile","dirname","join","open","processEnvWithoutGitOverrides","join","dirname","readFile","open","prompts"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/add.ts","../../src/config/store.ts","../../src/shared/types.ts","../../src/cli/commands/config.ts","../../src/cli/commands/list.ts","../../src/cli/commands/remove.ts","../../src/cli/commands/run.ts","../../src/project/package-json.ts","../../src/runner/git-bash.ts","../../src/runner/path.ts","../../src/server/index.ts","../../src/server/routes.ts","../../src/project/git.ts","../../src/cli/commands/ui.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { cac } from 'cac'\r\nimport { readFileSync } from 'node:fs'\r\nimport prompts from 'prompts'\r\nimport { addCommand } from './commands/add.js'\r\nimport { configCommand } from './commands/config.js'\r\nimport { listCommand } from './commands/list.js'\r\nimport { removeCommand } from './commands/remove.js'\r\nimport { runCommand } from './commands/run.js'\r\nimport { uiCommand } from './commands/ui.js'\r\n\r\nconst packageJson = JSON.parse(\r\n readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),\r\n) as { version?: string }\r\nconst packageVersion = packageJson.version ?? '0.0.0'\r\n\r\nconst cli = cac('br')\r\n\r\ncli.command('add', '新增项目配置').action(wrap(addCommand))\r\n\r\ncli.command('list', '查看项目配置').action(wrap(async () => listCommand()))\r\n\r\ncli.command('run', '选择并执行项目脚本').action(wrap(runCommand))\r\n\r\ncli.command('remove', '删除项目配置').action(wrap(removeCommand))\r\n\r\ncli.command('ui', '打开 Web 管理页面').action(wrap(uiCommand))\r\n\r\ncli.command('config', '配置 Git Bash 路径和 Web 端口').action(wrap(configCommand))\r\n\r\ncli.help()\r\ncli.version(packageVersion)\r\n\r\nasync function mainMenu(): Promise<void> {\r\n const answer = await prompts({\r\n type: 'select',\r\n name: 'action',\r\n message: '请选择操作',\r\n choices: [\r\n { title: '打开可视化操作页面', value: 'ui' },\r\n { title: '启动项目', value: 'run' },\r\n { title: '查看项目', value: 'list' },\r\n { title: '新增项目', value: 'add' },\r\n { title: '删除项目', value: 'remove' },\r\n { title: '设置 Git Bash 路径', value: 'config' },\r\n ],\r\n })\r\n\r\n switch (answer.action) {\r\n case 'run':\r\n await runCommand()\r\n break\r\n case 'add':\r\n await addCommand()\r\n break\r\n case 'list':\r\n listCommand()\r\n break\r\n case 'remove':\r\n await removeCommand()\r\n break\r\n case 'ui':\r\n await uiCommand()\r\n break\r\n case 'config':\r\n await configCommand()\r\n break\r\n }\r\n}\r\n\r\nfunction wrap(fn: () => Promise<void>) {\r\n return async () => {\r\n try {\r\n await fn()\r\n } catch (error) {\r\n handleError(error)\r\n }\r\n }\r\n}\r\n\r\nfunction handleError(error: unknown): void {\r\n const message = error instanceof Error ? error.message : String(error)\r\n console.error(`执行失败: ${message}`)\r\n process.exitCode = 1\r\n}\r\n\r\nif (process.argv.length <= 2) {\r\n await wrap(mainMenu)()\r\n} else {\r\n cli.parse()\r\n}\r\n","import prompts from \"prompts\";\r\nimport { addProject, getTags } from \"../../config/store.js\";\r\n\r\nexport async function addCommand(): Promise<void> {\r\n const existingTags = getTags();\r\n const answers = await prompts([\r\n {\r\n type: \"text\",\r\n name: \"name\",\r\n message: \"项目名称\",\r\n validate: (value) => value.trim().length > 0 || \"请输入项目名称\",\r\n },\r\n {\r\n type: \"text\",\r\n name: \"root\",\r\n message: \"项目根路径\",\r\n validate: (value) => value.trim().length > 0 || \"请输入项目根路径\",\r\n },\r\n {\r\n type: existingTags.length > 0 ? \"autocomplete\" : \"text\",\r\n name: \"tag\",\r\n message: \"标签(不填时默认未分组)\",\r\n choices: existingTags.map((tag) => ({ title: tag, value: tag })),\r\n },\r\n ]);\r\n\r\n if (!answers.name || !answers.root) {\r\n return;\r\n }\r\n\r\n const project = await addProject({\r\n name: answers.name,\r\n root: answers.root,\r\n tag: answers.tag,\r\n });\r\n\r\n console.log(`已添加项目: ${project.name}`);\r\n if (project.tag) {\r\n console.log(` tag: ${project.tag}`);\r\n }\r\n console.log(project.root);\r\n}\r\n","import Conf from \"conf\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport { access } from \"node:fs/promises\";\r\nimport { resolve } from \"node:path\";\r\nimport type { AppConfig, AppSettings, ProjectConfig } from \"../shared/types.js\";\r\nimport { UNGROUPED_TAG, UNGROUPED_TAG_LABEL } from \"../shared/types.js\";\r\n\r\nconst defaultSettings: AppSettings = {\r\n shell: \"git-bash\",\r\n port: 1234,\r\n};\r\n\r\nconst store = new Conf<AppConfig>({\r\n projectName: \"batch-run-local\",\r\n defaults: {\r\n projects: [],\r\n settings: defaultSettings,\r\n },\r\n});\r\n\r\nexport function normalizeTag(tag?: string): string | undefined {\r\n const trimmed = tag?.trim();\r\n if (!trimmed || trimmed === UNGROUPED_TAG_LABEL) {\r\n return undefined;\r\n }\r\n return trimmed;\r\n}\r\n\r\nexport function getProjectTagKey(project: ProjectConfig): string {\r\n return project.tag ?? UNGROUPED_TAG;\r\n}\r\n\r\nfunction collectTagKeysFromProjects(projects: ProjectConfig[]): string[] {\r\n const keys: string[] = [];\r\n const seen = new Set<string>();\r\n\r\n for (const project of projects) {\r\n const key = getProjectTagKey(project);\r\n if (!seen.has(key)) {\r\n seen.add(key);\r\n keys.push(key);\r\n }\r\n }\r\n\r\n return keys;\r\n}\r\n\r\nfunction groupProjectsByTag(projects: ProjectConfig[]): Map<string, ProjectConfig[]> {\r\n const groups = new Map<string, ProjectConfig[]>();\r\n\r\n for (const project of projects) {\r\n const key = getProjectTagKey(project);\r\n const group = groups.get(key);\r\n if (group) {\r\n group.push(project);\r\n } else {\r\n groups.set(key, [project]);\r\n }\r\n }\r\n\r\n return groups;\r\n}\r\n\r\nfunction flattenProjectsByTagOrder(projects: ProjectConfig[], tagOrder: string[]): ProjectConfig[] {\r\n const grouped = groupProjectsByTag(projects);\r\n const nextProjects: ProjectConfig[] = [];\r\n\r\n for (const tagKey of tagOrder) {\r\n const group = grouped.get(tagKey);\r\n if (group) {\r\n nextProjects.push(...group);\r\n grouped.delete(tagKey);\r\n }\r\n }\r\n\r\n for (const group of grouped.values()) {\r\n nextProjects.push(...group);\r\n }\r\n\r\n return nextProjects;\r\n}\r\n\r\nfunction syncTagOrder(projects: ProjectConfig[], preferredOrder?: string[]): string[] {\r\n const projectTagKeys = collectTagKeysFromProjects(projects);\r\n const projectTagKeySet = new Set(projectTagKeys);\r\n const sourceOrder = preferredOrder ?? store.get(\"tagOrder\") ?? [];\r\n const nextOrder: string[] = [];\r\n const seen = new Set<string>();\r\n\r\n for (const tagKey of sourceOrder) {\r\n if (!seen.has(tagKey) && projectTagKeySet.has(tagKey)) {\r\n seen.add(tagKey);\r\n nextOrder.push(tagKey);\r\n }\r\n }\r\n\r\n for (const tagKey of projectTagKeys) {\r\n if (!seen.has(tagKey)) {\r\n seen.add(tagKey);\r\n nextOrder.push(tagKey);\r\n }\r\n }\r\n\r\n store.set(\"tagOrder\", nextOrder);\r\n return nextOrder;\r\n}\r\n\r\nexport function getConfig(): AppConfig {\r\n return {\r\n projects: store.get(\"projects\") ?? [],\r\n settings: {\r\n ...defaultSettings,\r\n ...(store.get(\"settings\") ?? {}),\r\n },\r\n tagOrder: store.get(\"tagOrder\"),\r\n };\r\n}\r\n\r\nexport function getProjects(): ProjectConfig[] {\r\n return getConfig().projects;\r\n}\r\n\r\nexport function getProject(projectId: string): ProjectConfig | undefined {\r\n return getProjects().find((project) => project.id === projectId);\r\n}\r\n\r\nexport function getTagOrder(): string[] {\r\n return syncTagOrder(getProjects());\r\n}\r\n\r\nexport function getTags(): string[] {\r\n return getTagOrder().filter((tagKey) => tagKey !== UNGROUPED_TAG);\r\n}\r\n\r\nexport async function addProject(input: { name: string; root: string; tag?: string }): Promise<ProjectConfig> {\r\n const root = resolve(input.root);\r\n await access(root);\r\n\r\n const now = Date.now();\r\n const project: ProjectConfig = {\r\n id: randomUUID(),\r\n name: input.name.trim(),\r\n root,\r\n tag: normalizeTag(input.tag),\r\n createdAt: now,\r\n updatedAt: now,\r\n };\r\n\r\n const projects = getProjects();\r\n if (projects.some((item) => item.root.toLowerCase() === root.toLowerCase())) {\r\n throw new Error(\"该项目路径已经存在\");\r\n }\r\n\r\n const nextProjects = [...projects, project];\r\n store.set(\"projects\", nextProjects);\r\n syncTagOrder(nextProjects);\r\n return project;\r\n}\r\n\r\nexport async function updateProject(\r\n projectId: string,\r\n input: { name: string; root: string; tag?: string },\r\n): Promise<ProjectConfig | undefined> {\r\n const root = resolve(input.root);\r\n await access(root);\r\n\r\n const projects = getProjects();\r\n const projectIndex = projects.findIndex((project) => project.id === projectId);\r\n if (projectIndex < 0) {\r\n return undefined;\r\n }\r\n\r\n if (projects.some((project) => project.id !== projectId && project.root.toLowerCase() === root.toLowerCase())) {\r\n throw new Error(\"该项目路径已经存在\");\r\n }\r\n\r\n const nextProject: ProjectConfig = {\r\n ...projects[projectIndex],\r\n name: input.name.trim(),\r\n root,\r\n tag: normalizeTag(input.tag),\r\n updatedAt: Date.now(),\r\n };\r\n const nextProjects = [...projects];\r\n nextProjects[projectIndex] = nextProject;\r\n store.set(\"projects\", nextProjects);\r\n syncTagOrder(nextProjects);\r\n return nextProject;\r\n}\r\n\r\nexport function removeProject(projectId: string): boolean {\r\n const projects = getProjects();\r\n const nextProjects = projects.filter((project) => project.id !== projectId);\r\n store.set(\"projects\", nextProjects);\r\n syncTagOrder(nextProjects);\r\n return nextProjects.length !== projects.length;\r\n}\r\n\r\nexport function reorderProjects(projectIds: string[]): ProjectConfig[] {\r\n const projects = getProjects();\r\n const projectMap = new Map(projects.map((project) => [project.id, project]));\r\n const uniqueProjectIds = new Set(projectIds);\r\n\r\n if (uniqueProjectIds.size !== projects.length) {\r\n throw new Error(\"项目排序数据不完整\");\r\n }\r\n\r\n const nextProjects = projectIds.map((projectId) => {\r\n const project = projectMap.get(projectId);\r\n if (!project) {\r\n throw new Error(\"项目排序包含不存在的项目\");\r\n }\r\n return project;\r\n });\r\n\r\n store.set(\"projects\", nextProjects);\r\n syncTagOrder(nextProjects);\r\n return nextProjects;\r\n}\r\n\r\nexport function reorderTagGroups(tagKeys: string[]): ProjectConfig[] {\r\n const projects = getProjects();\r\n const projectTagKeys = collectTagKeysFromProjects(projects);\r\n const uniqueTagKeys = new Set(tagKeys);\r\n\r\n if (uniqueTagKeys.size !== tagKeys.length) {\r\n throw new Error(\"标签排序包含重复项\");\r\n }\r\n\r\n if (uniqueTagKeys.size !== projectTagKeys.length) {\r\n throw new Error(\"标签排序数据不完整\");\r\n }\r\n\r\n for (const tagKey of projectTagKeys) {\r\n if (!uniqueTagKeys.has(tagKey)) {\r\n throw new Error(\"标签排序包含不存在的标签\");\r\n }\r\n }\r\n\r\n const nextProjects = flattenProjectsByTagOrder(projects, tagKeys);\r\n store.set(\"tagOrder\", tagKeys);\r\n store.set(\"projects\", nextProjects);\r\n return nextProjects;\r\n}\r\n\r\nexport function getSettings(): AppSettings {\r\n return getConfig().settings;\r\n}\r\n\r\nexport function updateSettings(settings: Partial<AppSettings>): AppSettings {\r\n const nextSettings = {\r\n ...getSettings(),\r\n ...settings,\r\n };\r\n store.set(\"settings\", nextSettings);\r\n return nextSettings;\r\n}\r\n\r\nexport function getConfigPath(): string {\r\n return store.path;\r\n}\r\n","export const UNGROUPED_TAG = \"\";\r\nexport const UNGROUPED_TAG_LABEL = \"未分组\";\r\n\r\nexport type ProjectConfig = {\r\n id: string;\r\n name: string;\r\n root: string;\r\n tag?: string;\r\n createdAt: number;\r\n updatedAt: number;\r\n};\r\n\r\nexport type AppSettings = {\r\n shell: \"git-bash\";\r\n gitBashPath?: string;\r\n port: number;\r\n};\r\n\r\nexport type AppConfig = {\r\n projects: ProjectConfig[];\r\n settings: AppSettings;\r\n tagOrder?: string[];\r\n};\r\n\r\nexport type PackageScripts = Record<string, string>;\r\n\r\nexport type GitBranch = {\r\n name: string;\r\n current: boolean;\r\n remote: boolean;\r\n};\r\n\r\nexport type GitBranchState = {\r\n current?: string;\r\n branches: GitBranch[];\r\n};\r\n","import prompts from \"prompts\";\r\nimport { getConfigPath, getSettings, updateSettings } from \"../../config/store.js\";\r\n\r\nexport async function configCommand(): Promise<void> {\r\n const settings = getSettings();\r\n\r\n console.log(`当前配置文件: ${getConfigPath()}`);\r\n console.log(`当前 Git Bash 路径: ${settings.gitBashPath ?? \"自动检测\"}`);\r\n console.log(`当前 Web 端口: ${settings.port}`);\r\n\r\n const answers = await prompts([\r\n {\r\n type: \"text\",\r\n name: \"gitBashPath\",\r\n message: \"Git Bash 路径,留空表示自动检测\",\r\n initial: settings.gitBashPath ?? \"\",\r\n },\r\n {\r\n type: \"number\",\r\n name: \"port\",\r\n message: \"Web 端口\",\r\n initial: settings.port,\r\n min: 1,\r\n max: 65535,\r\n },\r\n ]);\r\n\r\n updateSettings({\r\n gitBashPath: answers.gitBashPath?.trim() || undefined,\r\n port: answers.port || settings.port,\r\n });\r\n\r\n console.log(\"配置已更新。\");\r\n}\r\n","import { getConfigPath, getProjects, getTagOrder } from \"../../config/store.js\";\r\nimport { UNGROUPED_TAG, UNGROUPED_TAG_LABEL } from \"../../shared/types.js\";\r\n\r\nfunction getTagLabel(tagKey: string): string {\r\n return tagKey === UNGROUPED_TAG ? UNGROUPED_TAG_LABEL : tagKey;\r\n}\r\n\r\nexport function listCommand(): void {\r\n const projects = getProjects();\r\n\r\n if (projects.length === 0) {\r\n console.log(\"暂无项目配置,可运行 br add 新增。\");\r\n console.log(`配置文件: ${getConfigPath()}`);\r\n return;\r\n }\r\n\r\n const tagOrder = getTagOrder();\r\n\r\n for (const tagKey of tagOrder) {\r\n const groupedProjects = projects.filter((project) => (project.tag ?? UNGROUPED_TAG) === tagKey);\r\n if (groupedProjects.length === 0) {\r\n continue;\r\n }\r\n\r\n console.log(`[${getTagLabel(tagKey)}]`);\r\n for (const project of groupedProjects) {\r\n console.log(` ${project.name}`);\r\n console.log(` id: ${project.id}`);\r\n console.log(` root: ${project.root}`);\r\n }\r\n console.log(\"\");\r\n }\r\n\r\n console.log(`配置文件: ${getConfigPath()}`);\r\n}\r\n","import prompts from \"prompts\";\r\nimport { getProjects, removeProject } from \"../../config/store.js\";\r\n\r\nexport async function removeCommand(): Promise<void> {\r\n const projects = getProjects();\r\n\r\n if (projects.length === 0) {\r\n console.log(\"暂无项目可删除。\");\r\n return;\r\n }\r\n\r\n const answer = await prompts({\r\n type: \"select\",\r\n name: \"projectId\",\r\n message: \"选择要删除的项目\",\r\n choices: projects.map((project) => ({\r\n title: project.name,\r\n description: project.root,\r\n value: project.id,\r\n })),\r\n });\r\n\r\n if (!answer.projectId) {\r\n return;\r\n }\r\n\r\n const removed = removeProject(answer.projectId);\r\n console.log(removed ? \"已删除项目。\" : \"未找到项目。\");\r\n}\r\n","import prompts from \"prompts\";\r\nimport { getProjects, getSettings } from \"../../config/store.js\";\r\nimport { readPackageScripts } from \"../../project/package-json.js\";\r\nimport { runScriptInGitBash } from \"../../runner/git-bash.js\";\r\n\r\nexport async function runCommand(): Promise<void> {\r\n const projects = getProjects();\r\n\r\n if (projects.length === 0) {\r\n console.log(\"暂无项目配置,可运行 br add 新增。\");\r\n return;\r\n }\r\n\r\n const projectAnswer = await prompts({\r\n type: \"select\",\r\n name: \"projectId\",\r\n message: \"选择项目\",\r\n choices: projects.map((project) => ({\r\n title: project.name,\r\n description: project.root,\r\n value: project.id,\r\n })),\r\n });\r\n\r\n const project = projects.find((item) => item.id === projectAnswer.projectId);\r\n if (!project) {\r\n return;\r\n }\r\n\r\n const scripts = await readPackageScripts(project.root);\r\n const scriptEntries = Object.entries(scripts);\r\n\r\n if (scriptEntries.length === 0) {\r\n console.log(\"该项目 package.json 中没有 scripts。\");\r\n return;\r\n }\r\n\r\n const scriptAnswer = await prompts({\r\n type: \"select\",\r\n name: \"script\",\r\n message: \"选择要执行的脚本\",\r\n choices: scriptEntries.map(([name, command]) => ({\r\n title: name,\r\n description: command,\r\n value: name,\r\n })),\r\n });\r\n\r\n if (!scriptAnswer.script) {\r\n return;\r\n }\r\n\r\n const result = runScriptInGitBash(project, scriptAnswer.script, getSettings());\r\n console.log(`已在 Git Bash 窗口执行: npm run ${scriptAnswer.script}`);\r\n if (result.pid) {\r\n console.log(`进程 PID: ${result.pid}`);\r\n }\r\n}\r\n","import { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { PackageScripts } from \"../shared/types.js\";\r\n\r\ntype PackageJson = {\r\n scripts?: PackageScripts;\r\n};\r\n\r\nexport async function readPackageJson(projectRoot: string): Promise<PackageJson> {\r\n const packageJsonPath = join(projectRoot, \"package.json\");\r\n const content = await readFile(packageJsonPath, \"utf8\");\r\n\r\n try {\r\n return JSON.parse(content) as PackageJson;\r\n } catch {\r\n throw new Error(`package.json 解析失败: ${packageJsonPath}`);\r\n }\r\n}\r\n\r\nexport async function readPackageScripts(projectRoot: string): Promise<PackageScripts> {\r\n const packageJson = await readPackageJson(projectRoot);\r\n return packageJson.scripts ?? {};\r\n}\r\n","import { spawn } from \"node:child_process\";\nimport { mkdtempSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AppSettings, ProjectConfig } from \"../shared/types.js\";\nimport { findBashExecutablePath, findGitBashPath, findMinttyPath, shellSingleQuote, toGitBashPath } from \"./path.js\";\n\n/** Strip Git repo overrides so child processes (e.g. Vite calling `git`) use cwd discovery, not a stale inherited GIT_DIR. */\nconst GIT_ENV_KEYS = [\n \"GIT_DIR\",\n \"GIT_WORK_TREE\",\n \"GIT_INDEX_FILE\",\n \"GIT_PREFIX\",\n \"GIT_OBJECT_DIRECTORY\",\n \"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n] as const;\n\nfunction processEnvWithoutGitOverrides(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n for (const key of GIT_ENV_KEYS) {\n delete env[key];\n }\n return env;\n}\n\nexport type RunResult = {\n pid?: number;\n command: string;\n};\n\nexport function runScriptInGitBash(project: ProjectConfig, scriptName: string, settings: AppSettings): RunResult {\n const gitBashPath = findGitBashPath(settings.gitBashPath);\n\n if (!gitBashPath) {\n throw new Error(\"未找到 Git Bash,请先通过 br config 设置 git-bash.exe 路径\");\n }\n\n const title = createTerminalTitle(project.name, scriptName);\n const gitBashRoot = toGitBashPath(project.root);\n const scriptPath = writeRunScript(project, scriptName, title, gitBashRoot);\n const gitBashScriptPath = toGitBashPath(scriptPath);\n const bashPath = findBashExecutablePath(gitBashPath);\n const command = `${bashPath} --login ${scriptPath}`;\n\n const minttyPath = findMinttyPath(gitBashPath);\n const child = minttyPath\n ? spawn(minttyPath, [\"--title\", title, \"--dir\", gitBashRoot, toGitBashPath(bashPath), \"--login\", gitBashScriptPath], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: false,\n env: processEnvWithoutGitOverrides(),\n })\n : spawn(\"cmd.exe\", [\"/d\", \"/c\", \"start\", title, \"/D\", project.root, bashPath, \"--login\", gitBashScriptPath], {\n detached: true,\n stdio: \"ignore\",\n windowsHide: false,\n env: processEnvWithoutGitOverrides(),\n });\n\n child.unref();\n\n return {\n pid: child.pid,\n command,\n };\n}\n\nfunction createTerminalTitle(projectName: string, scriptName: string): string {\n const title = `batch-run - ${projectName} - npm run ${scriptName}`;\n return title.replace(/[\\u0000-\\u001f\\u007f]/g, \" \").slice(0, 120);\n}\n\nfunction writeRunScript(project: ProjectConfig, scriptName: string, title: string, gitBashRoot: string): string {\n const tempDir = mkdtempSync(join(tmpdir(), \"batch-run-\"));\n const scriptPath = join(tempDir, \"run.sh\");\n const scriptContent = [\n \"#!/usr/bin/env bash\",\n \"set +e\",\n `cd ${shellSingleQuote(gitBashRoot)}`,\n 'rm -f \"$0\" >/dev/null 2>&1 || true',\n `unset ${GIT_ENV_KEYS.join(\" \")}`,\n `__batch_run_title=${shellSingleQuote(title)}`,\n \"set_batch_run_title() { printf '\\\\033]0;%s\\\\007' \\\"$__batch_run_title\\\"; }\",\n \"set_batch_run_title\",\n \"(while true; do set_batch_run_title; sleep 2; done) &\",\n \"__batch_run_title_pid=$!\",\n \"cleanup_batch_run_title() {\",\n ' kill \"$__batch_run_title_pid\" >/dev/null 2>&1 || true',\n ' wait \"$__batch_run_title_pid\" 2>/dev/null || true',\n \" set_batch_run_title\",\n \"}\",\n \"trap cleanup_batch_run_title EXIT\",\n `echo Running ${shellSingleQuote(project.name)}: npm run ${shellSingleQuote(scriptName)}`,\n `npm run ${shellSingleQuote(scriptName)}`,\n \"__batch_run_exit_code=$?\",\n \"set_batch_run_title\",\n \"echo\",\n 'echo \"Process exited with code $__batch_run_exit_code. Press Ctrl+D or close this window.\"',\n \"bash --login\",\n 'exit \"$__batch_run_exit_code\"',\n \"\",\n ].join(\"\\n\");\n\n writeFileSync(scriptPath, scriptContent, \"utf8\");\n return scriptPath;\n}\n","import { existsSync } from \"node:fs\";\r\nimport { platform } from \"node:os\";\r\nimport { basename, dirname, join } from \"node:path\";\r\n\r\nconst defaultWindowsGitBashPaths = [\r\n \"C:\\\\Program Files\\\\Git\\\\git-bash.exe\",\r\n \"C:\\\\Program Files (x86)\\\\Git\\\\git-bash.exe\",\r\n];\r\n\r\nexport function findGitBashPath(configuredPath?: string): string | undefined {\r\n if (configuredPath && existsSync(configuredPath) && isGitBashExecutable(configuredPath)) {\r\n return configuredPath;\r\n }\r\n\r\n if (platform() !== \"win32\") {\r\n return undefined;\r\n }\r\n\r\n return defaultWindowsGitBashPaths.find((item) => existsSync(item));\r\n}\r\n\r\nexport function toGitBashPath(inputPath: string): string {\r\n const normalized = inputPath.replaceAll(\"\\\\\", \"/\");\r\n const driveMatch = /^([a-zA-Z]):\\/(.*)$/.exec(normalized);\r\n\r\n if (!driveMatch) {\r\n return normalized;\r\n }\r\n\r\n const [, drive, rest] = driveMatch;\r\n return `/${drive.toLowerCase()}/${rest}`;\r\n}\r\n\r\nexport function shellSingleQuote(value: string): string {\r\n return `'${value.replaceAll(\"'\", \"'\\\\''\")}'`;\r\n}\r\n\r\nexport function findMinttyPath(gitBashPath: string): string | undefined {\r\n const gitBashDir = dirname(gitBashPath);\r\n const candidates = [\r\n join(gitBashDir, \"usr\", \"bin\", \"mintty.exe\"),\r\n join(gitBashDir, \"mintty.exe\"),\r\n join(dirname(gitBashDir), \"usr\", \"bin\", \"mintty.exe\"),\r\n ];\r\n\r\n return candidates.find((minttyPath) => existsSync(minttyPath));\r\n}\r\n\r\nexport function findBashExecutablePath(gitBashPath: string): string {\r\n const executableName = basename(gitBashPath).toLowerCase();\r\n if (executableName === \"bash.exe\" || executableName === \"sh.exe\") {\r\n return gitBashPath;\r\n }\r\n\r\n const gitBashDir = dirname(gitBashPath);\r\n const candidates = [\r\n join(gitBashDir, \"bin\", \"bash.exe\"),\r\n join(gitBashDir, \"usr\", \"bin\", \"bash.exe\"),\r\n join(gitBashDir, \"bin\", \"sh.exe\"),\r\n join(gitBashDir, \"usr\", \"bin\", \"sh.exe\"),\r\n ];\r\n\r\n return candidates.find((bashPath) => existsSync(bashPath)) ?? gitBashPath;\r\n}\r\n\r\nfunction isGitBashExecutable(inputPath: string): boolean {\r\n const executableName = basename(inputPath).toLowerCase();\r\n return executableName === \"git-bash.exe\" || executableName === \"bash.exe\" || executableName === \"sh.exe\";\r\n}\r\n","import fastify from \"fastify\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { dirname, join } from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport open from \"open\";\r\nimport { getSettings } from \"../config/store.js\";\r\nimport { registerApiRoutes } from \"./routes.js\";\r\n\r\nfunction getWebRoot(): string {\r\n return join(dirname(dirname(fileURLToPath(import.meta.url))), \"web\");\r\n}\r\n\r\nexport async function startServer(options: { openBrowser?: boolean } = {}): Promise<void> {\r\n const settings = getSettings();\r\n const app = fastify({\r\n logger: false,\r\n });\r\n const webRoot = getWebRoot();\r\n\r\n await registerApiRoutes(app, settings);\r\n\r\n app.get(\"/\", async (_request, reply) => {\r\n return reply.type(\"text/html\").send(await readFile(join(webRoot, \"index.html\"), \"utf8\"));\r\n });\r\n\r\n app.get(\"/main.js\", async (_request, reply) => {\r\n return reply.type(\"application/javascript\").send(await readFile(join(webRoot, \"main.js\"), \"utf8\"));\r\n });\r\n\r\n app.get(\"/style.css\", async (_request, reply) => {\r\n return reply.type(\"text/css\").send(await readFile(join(webRoot, \"style.css\"), \"utf8\"));\r\n });\r\n\r\n app.get(\"/favicon.ico\", async (_request, reply) => {\r\n return reply.type(\"image/x-icon\").send(await readFile(join(webRoot, \"favicon.ico\")));\r\n });\r\n\r\n const url = await app.listen({\r\n host: \"127.0.0.1\",\r\n port: settings.port,\r\n });\r\n\r\n console.log(`Batch Run Web 已启动: ${url}`);\r\n\r\n if (options.openBrowser ?? true) {\r\n await open(url);\r\n }\r\n}\r\n","import type { FastifyInstance } from \"fastify\";\r\nimport open from \"open\";\r\nimport { addProject, getProject, getProjects, getTagOrder, getTags, removeProject, reorderProjects, reorderTagGroups, updateProject } from \"../config/store.js\";\r\nimport { readGitBranches, switchGitBranch } from \"../project/git.js\";\r\nimport { readPackageScripts } from \"../project/package-json.js\";\r\nimport { runScriptInGitBash } from \"../runner/git-bash.js\";\r\nimport type { AppSettings } from \"../shared/types.js\";\r\n\r\ntype AddProjectBody = {\r\n name?: string;\r\n root?: string;\r\n tag?: string;\r\n};\r\n\r\ntype UpdateProjectBody = AddProjectBody;\r\n\r\ntype RunScriptBody = {\r\n script?: string;\r\n};\r\n\r\ntype SwitchBranchBody = {\r\n branch?: string;\r\n};\r\n\r\ntype ReorderProjectsBody = {\r\n projectIds?: string[];\r\n};\r\n\r\ntype ReorderTagGroupsBody = {\r\n tagKeys?: string[];\r\n};\r\n\r\nexport async function registerApiRoutes(app: FastifyInstance, settings: AppSettings): Promise<void> {\r\n app.get(\"/api/projects\", async () => ({\r\n projects: getProjects(),\r\n tagOrder: getTagOrder(),\r\n }));\r\n\r\n app.get(\"/api/tags\", async () => ({\r\n tags: getTags(),\r\n tagOrder: getTagOrder(),\r\n }));\r\n\r\n app.post<{ Body: AddProjectBody }>(\"/api/projects\", async (request, reply) => {\r\n const name = request.body.name?.trim();\r\n const root = request.body.root?.trim();\r\n\r\n if (!name || !root) {\r\n return reply.code(400).send({ message: \"项目名称和根路径不能为空\" });\r\n }\r\n\r\n const project = await addProject({ name, root, tag: request.body.tag });\r\n return { project };\r\n });\r\n\r\n app.put<{ Body: ReorderProjectsBody }>(\"/api/projects/order\", async (request, reply) => {\r\n const projectIds = request.body.projectIds;\r\n\r\n if (!Array.isArray(projectIds) || !projectIds.every((projectId) => typeof projectId === \"string\")) {\r\n return reply.code(400).send({ message: \"项目排序数据无效\" });\r\n }\r\n\r\n try {\r\n return {\r\n projects: reorderProjects(projectIds),\r\n tagOrder: getTagOrder(),\r\n };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"保存项目排序失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.put<{ Body: ReorderTagGroupsBody }>(\"/api/tag-order\", async (request, reply) => {\r\n const tagKeys = request.body.tagKeys;\r\n\r\n if (!Array.isArray(tagKeys) || !tagKeys.every((tagKey) => typeof tagKey === \"string\")) {\r\n return reply.code(400).send({ message: \"标签排序数据无效\" });\r\n }\r\n\r\n try {\r\n return {\r\n projects: reorderTagGroups(tagKeys),\r\n tagOrder: getTagOrder(),\r\n };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"保存标签排序失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.put<{ Params: { id: string }; Body: UpdateProjectBody }>(\"/api/projects/:id\", async (request, reply) => {\r\n const name = request.body.name?.trim();\r\n const root = request.body.root?.trim();\r\n\r\n if (!name || !root) {\r\n return reply.code(400).send({ message: \"项目名称和根路径不能为空\" });\r\n }\r\n\r\n try {\r\n const project = await updateProject(request.params.id, { name, root, tag: request.body.tag });\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n return { project };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"更新项目失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.delete<{ Params: { id: string } }>(\"/api/projects/:id\", async (request, reply) => {\r\n const removed = removeProject(request.params.id);\r\n\r\n if (!removed) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n return { ok: true };\r\n });\r\n\r\n app.get<{ Params: { id: string } }>(\"/api/projects/:id/scripts\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n const scripts = await readPackageScripts(project.root);\r\n return { scripts };\r\n });\r\n\r\n app.get<{ Params: { id: string } }>(\"/api/projects/:id/branches\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n try {\r\n return await readGitBranches(project.root);\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"读取 Git 分支失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.post<{ Params: { id: string }; Body: SwitchBranchBody }>(\"/api/projects/:id/branches/switch\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n const branch = request.body.branch?.trim();\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n if (!branch) {\r\n return reply.code(400).send({ message: \"分支名称不能为空\" });\r\n }\r\n\r\n try {\r\n const branchState = await switchGitBranch(project.root, branch);\r\n const scripts = await readPackageScripts(project.root);\r\n return {\r\n ...branchState,\r\n scripts,\r\n };\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : \"切换 Git 分支失败\";\r\n return reply.code(400).send({ message });\r\n }\r\n });\r\n\r\n app.post<{ Params: { id: string } }>(\"/api/projects/:id/open\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n await open(project.root);\r\n return { ok: true };\r\n });\r\n\r\n app.post<{ Params: { id: string }; Body: RunScriptBody }>(\"/api/projects/:id/run\", async (request, reply) => {\r\n const project = getProject(request.params.id);\r\n const script = request.body.script?.trim();\r\n\r\n if (!project) {\r\n return reply.code(404).send({ message: \"项目不存在\" });\r\n }\r\n\r\n if (!script) {\r\n return reply.code(400).send({ message: \"脚本名称不能为空\" });\r\n }\r\n\r\n const scripts = await readPackageScripts(project.root);\r\n if (!scripts[script]) {\r\n return reply.code(404).send({ message: \"脚本不存在\" });\r\n }\r\n\r\n const result = runScriptInGitBash(project, script, settings);\r\n return {\r\n ok: true,\r\n pid: result.pid,\r\n };\r\n });\r\n}\r\n","import { execFile } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { GitBranch, GitBranchState } from \"../shared/types.js\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\n\r\nexport async function readGitBranches(projectRoot: string): Promise<GitBranchState> {\r\n const [current, localBranches] = await Promise.all([\r\n runGit(projectRoot, [\"branch\", \"--show-current\"]),\r\n runGit(projectRoot, [\"branch\", \"--format=%(refname:short)\"]),\r\n ]);\r\n\r\n const currentBranch = current.trim() || undefined;\r\n const localNames = parseBranchLines(localBranches);\r\n const branches: GitBranch[] = localNames.map((name) => ({\r\n name,\r\n current: name === currentBranch,\r\n remote: false,\r\n }));\r\n\r\n return {\r\n current: currentBranch,\r\n branches,\r\n };\r\n}\r\n\r\nexport async function switchGitBranch(projectRoot: string, branchName: string): Promise<GitBranchState> {\r\n const name = branchName.trim();\r\n if (!name) {\r\n throw new Error(\"分支名称不能为空\");\r\n }\r\n\r\n const branchState = await readGitBranches(projectRoot);\r\n const branch = branchState.branches.find((item) => item.name === name);\r\n if (!branch) {\r\n throw new Error(\"本地分支不存在\");\r\n }\r\n\r\n if (branch.current) {\r\n return branchState;\r\n }\r\n\r\n await runGit(projectRoot, [\"switch\", name]);\r\n\r\n return readGitBranches(projectRoot);\r\n}\r\n\r\nasync function runGit(projectRoot: string, args: string[]): Promise<string> {\r\n try {\r\n const { stdout } = await execFileAsync(\"git\", args, {\r\n cwd: projectRoot,\r\n env: processEnvWithoutGitOverrides(),\r\n windowsHide: true,\r\n });\r\n return stdout;\r\n } catch (error) {\r\n const stderr = typeof error === \"object\" && error && \"stderr\" in error ? String(error.stderr) : \"\";\r\n const message = stderr.trim() || (error instanceof Error ? error.message : \"Git 命令执行失败\");\r\n throw new Error(message);\r\n }\r\n}\r\n\r\nfunction parseBranchLines(output: string): string[] {\r\n return output\r\n .split(/\\r?\\n/)\r\n .map((line) => line.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nfunction processEnvWithoutGitOverrides(): NodeJS.ProcessEnv {\r\n const env = { ...process.env };\r\n delete env.GIT_DIR;\r\n delete env.GIT_WORK_TREE;\r\n delete env.GIT_INDEX_FILE;\r\n delete env.GIT_PREFIX;\r\n delete env.GIT_OBJECT_DIRECTORY;\r\n delete env.GIT_ALTERNATE_OBJECT_DIRECTORIES;\r\n return env;\r\n}\r\n","import { startServer } from \"../../server/index.js\";\r\n\r\nexport async function uiCommand(): Promise<void> {\r\n await startServer({ openBrowser: true });\r\n}\r\n"],"mappings":"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","names":["prompts","prompts","prompts","prompts","prompts","prompts","packageJson","join","join","join","prompts","project","readFile","dirname","join","open","processEnvWithoutGitOverrides","join","dirname","readFile","open","prompts"]}
|
package/dist/web/index.html
CHANGED
|
@@ -56,13 +56,22 @@
|
|
|
56
56
|
|
|
57
57
|
<form id="projectForm" class="form">
|
|
58
58
|
<label>
|
|
59
|
-
|
|
59
|
+
<span>
|
|
60
|
+
项目名称<span class="required">*</span>
|
|
61
|
+
</span>
|
|
60
62
|
<input id="projectName" name="name" placeholder="例如:后台服务" required />
|
|
61
63
|
</label>
|
|
62
64
|
<label>
|
|
63
|
-
|
|
65
|
+
<span>
|
|
66
|
+
项目根路径<span class="required">*</span>
|
|
67
|
+
</span>
|
|
64
68
|
<input id="projectRoot" name="root" placeholder="C:\wang\project\server" required />
|
|
65
69
|
</label>
|
|
70
|
+
<label>
|
|
71
|
+
标签
|
|
72
|
+
<input id="projectTag" name="tag" list="tagOptions" placeholder="选择已有标签,或输入新标签(不填默认未分组)" />
|
|
73
|
+
<datalist id="tagOptions"></datalist>
|
|
74
|
+
</label>
|
|
66
75
|
<button id="projectSubmit" type="submit" class="secondary">新增项目</button>
|
|
67
76
|
</form>
|
|
68
77
|
</section>
|