@dotobokuri/fleet-console 1.17.1 → 1.19.0
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/AGENTS.md +10 -9
- package/dist/cli.d.ts +13 -14
- package/dist/cli.mjs +1181 -2428
- package/dist/client/assets/SymbolsNerdFontMono-Regular-CoUzE12l.woff2 +0 -0
- package/dist/client/assets/{_baseUniq-B7sqIsqm.js → _baseUniq-C4_iQlZS.js} +1 -1
- package/dist/client/assets/{arc-CvtYLN8U.js → arc-BBAWxYi5.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-rlI7pNrR.js → architectureDiagram-Q4EWVU46-_oQqhexV.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-nwx9BNRz.js → blockDiagram-DXYQGD6D-3xT9VFiD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-CRL_0Ism.js → c4Diagram-AHTNJAMY-S-IMcMW2.js} +1 -1
- package/dist/client/assets/channel-5tV1napa.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DEkncDR3.js → chunk-4BX2VUAB-tS9Ys0P9.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-Bnot0Ti1.js → chunk-4TB4RGXK-YZgAG7SW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-EgKmMTlI.js → chunk-55IACEB6-m-CtqHoO.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-BKiKnVQE.js → chunk-EDXVE4YY-yoymxaEV.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BRC398vL.js → chunk-FMBD7UC4-REUkrQAG.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-D82gQaXN.js → chunk-OYMX7WX6-DzMuL105.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-BYdI5fky.js → chunk-QZHKN3VN-DXFFll8-.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-BhBgOoc0.js → chunk-YZCP3GAM-CmY7W-gg.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-WDM904ap.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-WDM904ap.js +1 -0
- package/dist/client/assets/clone-BJg9p0cC.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-J8dfEdWD.js → cose-bilkent-S5V4N54A-BupE-a40.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-zGd_6_Kv.js → dagre-KV5264BT-Jn67LxMS.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-BQpQGgZ3.js → diagram-5BDNPKRD-BAeFEORH.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-BbgPVITm.js → diagram-G4DWMVQ6-CnZrDABY.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-lJsMf0Ic.js → diagram-MMDJMWI5-DefG-kXU.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-BpEKTzOh.js → diagram-TYMM5635-BPkXKB-Q.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-CHHVwJ_a.js → erDiagram-SMLLAGMA-Bi4jlild.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-Cun8a4NF.js → flowDiagram-DWJPFMVM-BoZyLO7h.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CGCwH_yf.js → ganttDiagram-T4ZO3ILL-LgzSNjrh.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-BkY4qdoV.js → gitGraphDiagram-UUTBAWPF-B4fH-hPT.js} +1 -1
- package/dist/client/assets/{graph-HbRzXQtt.js → graph-D3ZAYMLG.js} +1 -1
- package/dist/client/assets/index-DctP_CJP.css +1 -0
- package/dist/client/assets/index-vgEn_EkG.js +404 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-B21Xtz8v.js → infoDiagram-42DDH7IO-pENdunXd.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DiABngEH.js → ishikawaDiagram-UXIWVN3A-WakLHKte.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-BKZ-Uivu.js → journeyDiagram-VCZTEJTY-CrqXKtzk.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-CoJzTtsH.js → kanban-definition-6JOO6SKY-DdSC9eJ-.js} +1 -1
- package/dist/client/assets/{layout-D5tmQpxt.js → layout-BI1Oko_a.js} +1 -1
- package/dist/client/assets/{linear-B_riNOXk.js → linear-KUjZtfMe.js} +1 -1
- package/dist/client/assets/{mermaid.core-CyRCP3U3.js → mermaid.core-DNlIf4_3.js} +4 -4
- package/dist/client/assets/{min-Cn8LZZXB.js → min-DCAecveI.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BN_7mZUt.js → mindmap-definition-QFDTVHPH-BSCyQA-4.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-DPP2-sYA.js → pieDiagram-DEJITSTG-C_1I63z5.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CQ_oo-f9.js → quadrantDiagram-34T5L4WZ-CtqeYPcA.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-BkGq7nxD.js → requirementDiagram-MS252O5E-Cpakdg-s.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-BVcoktBR.js → sankeyDiagram-XADWPNL6-CK_eMH3e.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-B5rr_VYJ.js → sequenceDiagram-FGHM5R23-D2YygHqF.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-BV8RqzB9.js → stateDiagram-FHFEXIEX-DEDhnbhU.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-ji50PgJQ.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-CJ8TM-3t.js → timeline-definition-GMOUNBTQ-Brgny70W.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-BETSq-Pu.js → vennDiagram-DHZGUBPP-B1I5E8Hy.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-CyT2qapq.js → wardley-RL74JXVD-P0VCYfKI.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CWHaiUEG.js → wardleyDiagram-NUSXRM2D-DhYC5Sus.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-oVPlZ_Yn.js → xychartDiagram-5P7HB3ND-ByJX99rJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/diff/routes.mjs +260 -32
- package/dist/fleet-plugins/file-explorer/routes.mjs +1 -1
- package/dist/fleet-plugins/skills/routes.mjs +108 -201
- package/dist/fleet-plugins/terminal/routes.mjs +821 -1283
- package/package.json +1 -1
- package/dist/client/assets/channel-Ct9EqqyG.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CPMS8Qpo.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CPMS8Qpo.js +0 -1
- package/dist/client/assets/clone-C5XNYXHD.js +0 -1
- package/dist/client/assets/index-CfNSyAUJ.css +0 -1
- package/dist/client/assets/index-CzCMC0e-.js +0 -402
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Bx_fpQWQ.js +0 -1
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import path from 'path';
|
|
4
2
|
import { spawn } from 'child_process';
|
|
3
|
+
import 'fs';
|
|
4
|
+
import path3 from 'path';
|
|
5
|
+
import fs from 'fs/promises';
|
|
5
6
|
|
|
6
7
|
// sdk/plugin/node.ts
|
|
7
8
|
function definePlugin(definition) {
|
|
8
9
|
return definition;
|
|
9
10
|
}
|
|
10
|
-
function registerRouter(ctx,
|
|
11
|
-
ctx.registerRouter(normalizePath(
|
|
11
|
+
function registerRouter(ctx, path4, handler) {
|
|
12
|
+
ctx.registerRouter(normalizePath(path4), handler);
|
|
12
13
|
}
|
|
13
|
-
function normalizePath(
|
|
14
|
-
if (
|
|
15
|
-
return
|
|
14
|
+
function normalizePath(path4) {
|
|
15
|
+
if (path4.length === 0 || path4 === "/") return "/";
|
|
16
|
+
return path4;
|
|
16
17
|
}
|
|
18
|
+
|
|
19
|
+
// ../../packages/core-process/src/hidden.ts
|
|
20
|
+
function withHidden(options) {
|
|
21
|
+
return { ...options ?? {}, windowsHide: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ../fleet-plugins/diff/server/git-executor.ts
|
|
17
25
|
var GitExecutorError = class extends Error {
|
|
18
26
|
code;
|
|
19
27
|
exitCode;
|
|
@@ -34,7 +42,7 @@ function runGit(args, opts) {
|
|
|
34
42
|
return new Promise((resolve, reject) => {
|
|
35
43
|
let child;
|
|
36
44
|
try {
|
|
37
|
-
child = spawn("git", args, { cwd: opts.cwd, shell: false
|
|
45
|
+
child = spawn("git", args, withHidden({ cwd: opts.cwd, shell: false }));
|
|
38
46
|
} catch (error) {
|
|
39
47
|
const code = error.code === "ENOENT" ? "git_unavailable" : "spawn_failed";
|
|
40
48
|
reject(new GitExecutorError(code, String(error)));
|
|
@@ -89,8 +97,6 @@ function runGit(args, opts) {
|
|
|
89
97
|
});
|
|
90
98
|
});
|
|
91
99
|
}
|
|
92
|
-
|
|
93
|
-
// ../fleet-plugins/diff/server/diff.ts
|
|
94
100
|
function isPlainObject(value) {
|
|
95
101
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96
102
|
}
|
|
@@ -141,9 +147,9 @@ function isNoHeadError(error) {
|
|
|
141
147
|
}
|
|
142
148
|
async function resolveGitCwd(theaterPath, rawSubPath) {
|
|
143
149
|
if (rawSubPath === "") return { gitCwd: theaterPath };
|
|
144
|
-
const resolvedSub =
|
|
145
|
-
if (!resolvedSub.startsWith(theaterPath +
|
|
146
|
-
if (
|
|
150
|
+
const resolvedSub = path3.resolve(theaterPath, rawSubPath);
|
|
151
|
+
if (!resolvedSub.startsWith(theaterPath + path3.sep) && resolvedSub !== theaterPath) return null;
|
|
152
|
+
if (path3.normalize(rawSubPath).startsWith("..")) return null;
|
|
147
153
|
let realTheater;
|
|
148
154
|
let realSub;
|
|
149
155
|
try {
|
|
@@ -154,7 +160,7 @@ async function resolveGitCwd(theaterPath, rawSubPath) {
|
|
|
154
160
|
} catch {
|
|
155
161
|
return null;
|
|
156
162
|
}
|
|
157
|
-
const norm = realTheater.endsWith(
|
|
163
|
+
const norm = realTheater.endsWith(path3.sep) ? realTheater : realTheater + path3.sep;
|
|
158
164
|
if (realSub !== realTheater && !realSub.startsWith(norm)) return null;
|
|
159
165
|
return { gitCwd: resolvedSub };
|
|
160
166
|
}
|
|
@@ -263,12 +269,12 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
263
269
|
}
|
|
264
270
|
const { gitCwd } = cwdResult;
|
|
265
271
|
const rawFilePath = body.filePath;
|
|
266
|
-
const resolvedFilePath =
|
|
267
|
-
if (!resolvedFilePath.startsWith(gitCwd +
|
|
272
|
+
const resolvedFilePath = path3.resolve(gitCwd, rawFilePath);
|
|
273
|
+
if (!resolvedFilePath.startsWith(gitCwd + path3.sep) && resolvedFilePath !== gitCwd) {
|
|
268
274
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
269
275
|
return;
|
|
270
276
|
}
|
|
271
|
-
const relativePath =
|
|
277
|
+
const relativePath = path3.normalize(rawFilePath);
|
|
272
278
|
if (relativePath.startsWith("..")) {
|
|
273
279
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
274
280
|
return;
|
|
@@ -286,7 +292,7 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
286
292
|
ctx.host.http.writeJson(res, 404, { error: "file_not_found" });
|
|
287
293
|
return;
|
|
288
294
|
}
|
|
289
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
295
|
+
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
|
|
290
296
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
291
297
|
return;
|
|
292
298
|
}
|
|
@@ -302,7 +308,7 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
302
308
|
fs.realpath(gitCwd),
|
|
303
309
|
fs.realpath(resolvedFilePath)
|
|
304
310
|
]);
|
|
305
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
311
|
+
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
|
|
306
312
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
307
313
|
return;
|
|
308
314
|
}
|
|
@@ -328,10 +334,169 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
328
334
|
throw error;
|
|
329
335
|
}
|
|
330
336
|
}
|
|
337
|
+
|
|
338
|
+
// ../fleet-plugins/diff/server/commit.ts
|
|
339
|
+
var REF_RE = /^[0-9a-f]{7,40}$/i;
|
|
340
|
+
function isPlainObject2(value) {
|
|
341
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
342
|
+
}
|
|
343
|
+
async function handleDiffCommit(req, res, ctx) {
|
|
344
|
+
if (req.method !== "POST") {
|
|
345
|
+
ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!ctx.host.security.isTerminalAuthorized(req)) {
|
|
349
|
+
ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const body = await ctx.host.http.readJsonBody(req);
|
|
353
|
+
if (!isPlainObject2(body)) {
|
|
354
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const theaterId = body.theaterId;
|
|
358
|
+
if (typeof theaterId !== "string") {
|
|
359
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const ref = body.ref;
|
|
363
|
+
if (typeof ref !== "string" || !REF_RE.test(ref)) {
|
|
364
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_ref" });
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const theaterPath = ctx.host.paths.resolveTheaterPath(theaterId);
|
|
368
|
+
if (!theaterPath) {
|
|
369
|
+
ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const rawSubPath = typeof body.subPath === "string" ? body.subPath : "";
|
|
373
|
+
const cwdResult = await resolveGitCwd(theaterPath, rawSubPath);
|
|
374
|
+
if (!cwdResult) {
|
|
375
|
+
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const { gitCwd } = cwdResult;
|
|
379
|
+
try {
|
|
380
|
+
const result = await runGit(["show", ref, "--unified=3", "--format="], { cwd: gitCwd });
|
|
381
|
+
ctx.host.http.writeJson(res, 200, { content: result.stdout, ...result.truncated ? { truncated: true } : {} });
|
|
382
|
+
} catch (error) {
|
|
383
|
+
if (error instanceof GitExecutorError) {
|
|
384
|
+
if (error.code === "no_git_repo" || error.code === "git_unavailable") {
|
|
385
|
+
ctx.host.http.writeJson(res, 422, { error: error.code });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
ctx.host.http.writeJson(res, 500, { error: "git_failed" });
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ../fleet-plugins/diff/server/log.ts
|
|
396
|
+
function isPlainObject3(value) {
|
|
397
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
398
|
+
}
|
|
399
|
+
function parseLogOutput(stdout) {
|
|
400
|
+
if (!stdout.trim()) return [];
|
|
401
|
+
const commits = [];
|
|
402
|
+
for (const chunk of stdout.split("")) {
|
|
403
|
+
const trimmed = chunk.trimStart();
|
|
404
|
+
if (!trimmed) continue;
|
|
405
|
+
const lines = trimmed.split("\n");
|
|
406
|
+
const firstLine = lines[0] ?? "";
|
|
407
|
+
const fields = firstLine.split("\0");
|
|
408
|
+
const fullHash = fields[0] ?? "";
|
|
409
|
+
const shortHash = fields[1] ?? "";
|
|
410
|
+
const subject = fields[2] ?? "";
|
|
411
|
+
const authorName = fields[3] ?? "";
|
|
412
|
+
const relTime = fields[4] ?? "";
|
|
413
|
+
const refsRaw = fields[5] ?? "";
|
|
414
|
+
const parentsRaw = fields[6] ?? "";
|
|
415
|
+
if (!fullHash) continue;
|
|
416
|
+
const refs = refsRaw.split(",").map((r) => r.trim()).filter(Boolean);
|
|
417
|
+
const parents = parentsRaw.split(" ").map((p) => p.trim()).filter(Boolean);
|
|
418
|
+
let additions = 0;
|
|
419
|
+
let deletions = 0;
|
|
420
|
+
for (const line of lines.slice(1)) {
|
|
421
|
+
if (!line.trim()) continue;
|
|
422
|
+
const parts = line.split(" ");
|
|
423
|
+
if (parts.length < 2) continue;
|
|
424
|
+
const a = parseInt(parts[0] ?? "0", 10);
|
|
425
|
+
const d = parseInt(parts[1] ?? "0", 10);
|
|
426
|
+
if (!isNaN(a)) additions += a;
|
|
427
|
+
if (!isNaN(d)) deletions += d;
|
|
428
|
+
}
|
|
429
|
+
commits.push({ shortHash, fullHash, subject, authorName, relTime, refs, parents, additions, deletions });
|
|
430
|
+
}
|
|
431
|
+
return commits;
|
|
432
|
+
}
|
|
433
|
+
function isNoHeadError2(error) {
|
|
434
|
+
if (!(error instanceof GitExecutorError)) return false;
|
|
435
|
+
if (error.code !== "non_zero_exit") return false;
|
|
436
|
+
return error.stderr.includes("unknown revision") || error.stderr.includes("bad revision");
|
|
437
|
+
}
|
|
438
|
+
async function handleDiffLog(req, res, ctx) {
|
|
439
|
+
if (req.method !== "POST") {
|
|
440
|
+
ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (!ctx.host.security.isTerminalAuthorized(req)) {
|
|
444
|
+
ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const body = await ctx.host.http.readJsonBody(req);
|
|
448
|
+
if (!isPlainObject3(body)) {
|
|
449
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const theaterId = body.theaterId;
|
|
453
|
+
if (typeof theaterId !== "string") {
|
|
454
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const theaterPath = ctx.host.paths.resolveTheaterPath(theaterId);
|
|
458
|
+
if (!theaterPath) {
|
|
459
|
+
ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const rawSubPath = typeof body.subPath === "string" ? body.subPath : "";
|
|
463
|
+
const cwdResult = await resolveGitCwd(theaterPath, rawSubPath);
|
|
464
|
+
if (!cwdResult) {
|
|
465
|
+
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const { gitCwd } = cwdResult;
|
|
469
|
+
try {
|
|
470
|
+
const result = await runGit(
|
|
471
|
+
["log", "-n", "50", "--numstat", "--pretty=format:%x1e%H%x00%h%x00%s%x00%an%x00%ar%x00%D%x00%P"],
|
|
472
|
+
{ cwd: gitCwd }
|
|
473
|
+
);
|
|
474
|
+
const commits = parseLogOutput(result.stdout);
|
|
475
|
+
ctx.host.http.writeJson(res, 200, { commits, ...result.truncated ? { truncated: true } : {} });
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (error instanceof GitExecutorError) {
|
|
478
|
+
if (error.code === "no_git_repo") {
|
|
479
|
+
ctx.host.http.writeJson(res, 200, { commits: [] });
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (error.code === "git_unavailable") {
|
|
483
|
+
ctx.host.http.writeJson(res, 422, { error: error.code });
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (isNoHeadError2(error)) {
|
|
487
|
+
ctx.host.http.writeJson(res, 200, { commits: [] });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
ctx.host.http.writeJson(res, 500, { error: "git_failed" });
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
331
496
|
var REPOS_CAP = 200;
|
|
332
497
|
var HARD_CAP_DEPTH = 8;
|
|
333
498
|
var DEFAULT_DEPTH = 3;
|
|
334
|
-
function
|
|
499
|
+
function isPlainObject4(value) {
|
|
335
500
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
336
501
|
}
|
|
337
502
|
async function resolveRepoBranch(repoDir) {
|
|
@@ -347,15 +512,49 @@ async function resolveRepoBranch(repoDir) {
|
|
|
347
512
|
return "unknown";
|
|
348
513
|
}
|
|
349
514
|
}
|
|
515
|
+
async function detectWorktreeParent(dir, gitFileContent) {
|
|
516
|
+
const match = gitFileContent.trim().match(/^gitdir:\s*(.+)$/m);
|
|
517
|
+
if (!match) return null;
|
|
518
|
+
const rawPointer = match[1].trim();
|
|
519
|
+
const resolvedGitdir = path3.normalize(
|
|
520
|
+
path3.isAbsolute(rawPointer) ? rawPointer : path3.resolve(dir, rawPointer)
|
|
521
|
+
);
|
|
522
|
+
if (resolvedGitdir.includes(`${path3.sep}modules${path3.sep}`)) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
const worktreesSegment = `${path3.sep}.git${path3.sep}worktrees${path3.sep}`;
|
|
526
|
+
const wtIdx = resolvedGitdir.indexOf(worktreesSegment);
|
|
527
|
+
if (wtIdx === -1) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
const parentAbsRaw = resolvedGitdir.slice(0, wtIdx);
|
|
531
|
+
let parentAbs;
|
|
532
|
+
try {
|
|
533
|
+
parentAbs = await fs.realpath(parentAbsRaw);
|
|
534
|
+
} catch {
|
|
535
|
+
parentAbs = path3.normalize(parentAbsRaw);
|
|
536
|
+
}
|
|
537
|
+
return { isWorktree: true, parentAbs };
|
|
538
|
+
}
|
|
350
539
|
async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDepth, repos, cap = REPOS_CAP) {
|
|
351
540
|
if (repos.length >= cap) return true;
|
|
352
|
-
const gitPath =
|
|
541
|
+
const gitPath = path3.join(dir, ".git");
|
|
353
542
|
try {
|
|
354
|
-
await fs.stat(gitPath);
|
|
355
|
-
const relPath =
|
|
356
|
-
const name = relPath === "" ?
|
|
543
|
+
const gitStat = await fs.stat(gitPath);
|
|
544
|
+
const relPath = path3.relative(theaterPath, dir);
|
|
545
|
+
const name = relPath === "" ? path3.basename(theaterPath) : path3.basename(dir);
|
|
357
546
|
const branch = await resolveRepoBranch(dir);
|
|
358
|
-
|
|
547
|
+
if (gitStat.isFile()) {
|
|
548
|
+
const content = await fs.readFile(gitPath, "utf8");
|
|
549
|
+
const worktreeResult = await detectWorktreeParent(dir, content);
|
|
550
|
+
if (worktreeResult) {
|
|
551
|
+
repos.push({ relPath, name, branch, isWorktree: true, _wtParentAbs: worktreeResult.parentAbs });
|
|
552
|
+
} else {
|
|
553
|
+
repos.push({ relPath, name, branch });
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
repos.push({ relPath, name, branch });
|
|
557
|
+
}
|
|
359
558
|
if (repos.length >= cap) return true;
|
|
360
559
|
} catch {
|
|
361
560
|
}
|
|
@@ -373,7 +572,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
373
572
|
dirent = await dirHandle.read();
|
|
374
573
|
continue;
|
|
375
574
|
}
|
|
376
|
-
const childPath =
|
|
575
|
+
const childPath = path3.join(dir, childName);
|
|
377
576
|
let realChildPath;
|
|
378
577
|
try {
|
|
379
578
|
realChildPath = await fs.realpath(childPath);
|
|
@@ -381,7 +580,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
381
580
|
dirent = await dirHandle.read();
|
|
382
581
|
continue;
|
|
383
582
|
}
|
|
384
|
-
const normalizedReal = realTheaterPath.endsWith(
|
|
583
|
+
const normalizedReal = realTheaterPath.endsWith(path3.sep) ? realTheaterPath : realTheaterPath + path3.sep;
|
|
385
584
|
if (realChildPath !== realTheaterPath && !realChildPath.startsWith(normalizedReal)) {
|
|
386
585
|
dirent = await dirHandle.read();
|
|
387
586
|
continue;
|
|
@@ -405,6 +604,26 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
405
604
|
return false;
|
|
406
605
|
}
|
|
407
606
|
}
|
|
607
|
+
function resolveWorktreeParents(raw, realTheaterPath) {
|
|
608
|
+
const absToRelPath = /* @__PURE__ */ new Map();
|
|
609
|
+
for (const entry of raw) {
|
|
610
|
+
const absDir = path3.normalize(
|
|
611
|
+
entry.relPath === "" ? realTheaterPath : path3.join(realTheaterPath, entry.relPath)
|
|
612
|
+
);
|
|
613
|
+
absToRelPath.set(absDir, entry.relPath);
|
|
614
|
+
}
|
|
615
|
+
return raw.map((entry) => {
|
|
616
|
+
const { _wtParentAbs, ...rest } = entry;
|
|
617
|
+
if (_wtParentAbs && entry.isWorktree) {
|
|
618
|
+
const normalizedParent = path3.normalize(_wtParentAbs);
|
|
619
|
+
const parentRelPath = absToRelPath.get(normalizedParent);
|
|
620
|
+
if (parentRelPath !== void 0) {
|
|
621
|
+
return { ...rest, worktreeOf: parentRelPath };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return rest;
|
|
625
|
+
});
|
|
626
|
+
}
|
|
408
627
|
async function handleDiffRepos(req, res, ctx) {
|
|
409
628
|
if (req.method !== "POST") {
|
|
410
629
|
ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
|
|
@@ -415,7 +634,7 @@ async function handleDiffRepos(req, res, ctx) {
|
|
|
415
634
|
return;
|
|
416
635
|
}
|
|
417
636
|
const body = await ctx.host.http.readJsonBody(req);
|
|
418
|
-
if (!
|
|
637
|
+
if (!isPlainObject4(body)) {
|
|
419
638
|
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
420
639
|
return;
|
|
421
640
|
}
|
|
@@ -438,14 +657,15 @@ async function handleDiffRepos(req, res, ctx) {
|
|
|
438
657
|
ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
|
|
439
658
|
return;
|
|
440
659
|
}
|
|
441
|
-
const
|
|
442
|
-
const truncated = await scanRepos(theaterPath, realTheaterPath, theaterPath, 0, maxDepth,
|
|
443
|
-
|
|
660
|
+
const raw = [];
|
|
661
|
+
const truncated = await scanRepos(theaterPath, realTheaterPath, theaterPath, 0, maxDepth, raw);
|
|
662
|
+
const resolved = resolveWorktreeParents(raw, realTheaterPath);
|
|
663
|
+
resolved.sort((a, b) => {
|
|
444
664
|
if (a.relPath === "") return -1;
|
|
445
665
|
if (b.relPath === "") return 1;
|
|
446
666
|
return a.relPath.localeCompare(b.relPath);
|
|
447
667
|
});
|
|
448
|
-
const result = { repos, ...truncated ? { truncated: true } : {} };
|
|
668
|
+
const result = { repos: resolved, ...truncated ? { truncated: true } : {} };
|
|
449
669
|
ctx.host.http.writeJson(res, 200, result);
|
|
450
670
|
}
|
|
451
671
|
|
|
@@ -465,6 +685,14 @@ var routes_default = definePlugin({
|
|
|
465
685
|
await handleDiffFile(req, res, ctx);
|
|
466
686
|
return true;
|
|
467
687
|
});
|
|
688
|
+
registerRouter(ctx, "log", async ({ req, res }) => {
|
|
689
|
+
await handleDiffLog(req, res, ctx);
|
|
690
|
+
return true;
|
|
691
|
+
});
|
|
692
|
+
registerRouter(ctx, "commit", async ({ req, res }) => {
|
|
693
|
+
await handleDiffCommit(req, res, ctx);
|
|
694
|
+
return true;
|
|
695
|
+
});
|
|
468
696
|
}
|
|
469
697
|
});
|
|
470
698
|
|