@dotobokuri/fleet-console 1.50.1 → 1.52.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 -5
- package/README.md +10 -8
- package/dist/cli.d.ts +3 -0
- package/dist/cli.mjs +8507 -8365
- package/dist/client/assets/{_baseUniq-CXYajthv.js → _baseUniq-CYpcPDgB.js} +1 -1
- package/dist/client/assets/{arc-CN2U0RJi.js → arc-BuehZLUz.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-KMYgwTU-.js → architectureDiagram-Q4EWVU46-Bqn18zpp.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-B2n_Ujhi.js → blockDiagram-DXYQGD6D-DmtLdC53.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BmExz-H8.js → c4Diagram-AHTNJAMY-BEBhZ5GG.js} +1 -1
- package/dist/client/assets/channel-BQlgz3sc.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-WxXjYCDb.js → chunk-4BX2VUAB-B8PizWjA.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-CoSmkS4d.js → chunk-4TB4RGXK-C1ofR8zt.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-BQ6DMyRz.js → chunk-55IACEB6-CbKQ3d6C.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-DEhxxADi.js → chunk-EDXVE4YY-DU-bru4J.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BUW_vTgY.js → chunk-FMBD7UC4-DD9GbN05.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-xqJeGGdu.js → chunk-OYMX7WX6-DZKa6Wtf.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-DPXuiqFZ.js → chunk-QZHKN3VN-fK2o17Jf.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-CJBloo9Q.js → chunk-YZCP3GAM-z-7p86Bc.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CQs4cWly.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CQs4cWly.js +1 -0
- package/dist/client/assets/clone-B5bOHdOg.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-C7eVojQK.js → cose-bilkent-S5V4N54A-x5L9kNFv.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-DYPawRSP.js → dagre-KV5264BT-944maqqT.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-9nbdU3Kr.js → diagram-5BDNPKRD-PV4GQ8C6.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-DtSzEakz.js → diagram-G4DWMVQ6-GcoFZYg0.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-2FX1CuCi.js → diagram-MMDJMWI5-8cIlnSEJ.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-D1aBD1Sv.js → diagram-TYMM5635-BlIMSlQ5.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-AK79bFYW.js → erDiagram-SMLLAGMA-KjlbhDy3.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-C1ApmkLT.js → flowDiagram-DWJPFMVM-DqYNn08z.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CQvFUGlj.js → ganttDiagram-T4ZO3ILL-BplrlRlU.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-C-Vh-bKl.js → gitGraphDiagram-UUTBAWPF-CISgtHAu.js} +1 -1
- package/dist/client/assets/{graph-DeEdzsTp.js → graph-DaTRgbvC.js} +1 -1
- package/dist/client/assets/index-DVQ5EO_a.css +1 -0
- package/dist/client/assets/index-alR6ElCr.js +479 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-B_0wD24T.js → infoDiagram-42DDH7IO-BbFeDUi-.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DgGbx2eb.js → ishikawaDiagram-UXIWVN3A-BQTo9ri4.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-BdQqxMsJ.js → journeyDiagram-VCZTEJTY-CiRkOhp8.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-DU8AwVYp.js → kanban-definition-6JOO6SKY-IIn_MSzy.js} +1 -1
- package/dist/client/assets/{layout-BF-OB3sH.js → layout-Cr58D4FS.js} +1 -1
- package/dist/client/assets/{linear-Cd22n3Ol.js → linear-DNaIPcgl.js} +1 -1
- package/dist/client/assets/{mermaid.core-CV1wZraa.js → mermaid.core-Dq_ukAsT.js} +4 -4
- package/dist/client/assets/{min-Avezts6s.js → min-DpLyTgPW.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BiYy2OKo.js → mindmap-definition-QFDTVHPH-C2DLYGq2.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-Bg9O56EU.js → pieDiagram-DEJITSTG-C3Cxmz-n.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-B9NeeqFq.js → quadrantDiagram-34T5L4WZ-CEdmiODY.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-DV1FugSk.js → requirementDiagram-MS252O5E-B-Vj0Wyw.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-BvIHcJf7.js → sankeyDiagram-XADWPNL6-CyvEd7TA.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-BCQ2xDy1.js → sequenceDiagram-FGHM5R23-B2MiPHOT.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-BpK4LvW_.js → stateDiagram-FHFEXIEX-Dsa-VSCx.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BKHWSGsO.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-BEwHf_wL.js → timeline-definition-GMOUNBTQ-B9p0xkPR.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-Cd0iA5Vu.js → vennDiagram-DHZGUBPP-DeU4CNd8.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-cubfgt6B.js → wardley-RL74JXVD-BQyG38I_.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-DTdlOJlu.js → wardleyDiagram-NUSXRM2D-DjrdDkEs.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-BguY4oGj.js → xychartDiagram-5P7HB3ND-CG0KUfn_.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/desktop-protocol.mjs +3 -3
- package/dist/fleet-plugins/file-explorer/routes.mjs +4 -4
- package/dist/fleet-plugins/ledger/routes.mjs +61 -16510
- package/dist/fleet-plugins/quota/routes.mjs +2864 -2483
- package/dist/fleet-plugins/repository/routes.mjs +107 -50
- package/dist/fleet-plugins/scuttlebutt/routes.mjs +4955 -6895
- package/dist/fleet-plugins/skills/routes.mjs +75 -16523
- package/dist/fleet-plugins/terminal/routes.mjs +24129 -34156
- package/dist/fleet.mjs +54671 -0
- package/package.json +6 -4
- package/dist/client/assets/channel-7u-T-USm.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CdK4kqo5.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CdK4kqo5.js +0 -1
- package/dist/client/assets/clone-BJX0Y00S.js +0 -1
- package/dist/client/assets/index-PYKMOCH4.css +0 -1
- package/dist/client/assets/index-rFuEoKaA.js +0 -482
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CEPxcteM.js +0 -1
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'child_process';
|
|
3
|
-
import 'fs';
|
|
4
|
-
import
|
|
5
|
-
import fs2 from 'fs/promises';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import fs2 from 'node:fs/promises';
|
|
4
|
+
import path3 from 'node:path';
|
|
6
5
|
|
|
7
6
|
// sdk/plugin/node.ts
|
|
8
7
|
function definePlugin(definition) {
|
|
9
8
|
return definition;
|
|
10
9
|
}
|
|
11
|
-
function registerRouter(ctx,
|
|
12
|
-
ctx.registerRouter(normalizePath(
|
|
10
|
+
function registerRouter(ctx, path9, handler) {
|
|
11
|
+
ctx.registerRouter(normalizePath(path9), handler);
|
|
13
12
|
}
|
|
14
|
-
function normalizePath(
|
|
15
|
-
if (
|
|
16
|
-
return
|
|
13
|
+
function normalizePath(path9) {
|
|
14
|
+
if (path9.length === 0 || path9 === "/") return "/";
|
|
15
|
+
return path9;
|
|
17
16
|
}
|
|
17
|
+
|
|
18
|
+
// ../../packages/core-process/src/bin-resolver.ts
|
|
18
19
|
function withHidden(options) {
|
|
19
20
|
return { ...options ?? {}, windowsHide: true };
|
|
20
21
|
}
|
|
@@ -35,6 +36,7 @@ var GitExecutorError = class extends Error {
|
|
|
35
36
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
36
37
|
var DEFAULT_MAX_BUFFER = 8 * 1024 * 1024;
|
|
37
38
|
var STDERR_MAX_BUFFER = 1024 * 1024;
|
|
39
|
+
var PROBE_TIMEOUT_MS = 5e3;
|
|
38
40
|
var GIT_HARDENING_PREFIX = ["-c", "core.fsmonitor=false", "--no-optional-locks"];
|
|
39
41
|
function sanitizeGitEnvironment(environment) {
|
|
40
42
|
const sanitized = {};
|
|
@@ -129,8 +131,13 @@ function runGit(args, opts) {
|
|
|
129
131
|
resolve({ stdout: stdout2, stderr, truncated, stderrTruncated });
|
|
130
132
|
return;
|
|
131
133
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
if (stderr.toLowerCase().includes("not a git repository")) {
|
|
135
|
+
reject(new GitExecutorError("no_git_repo", stderr, code ?? void 0));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
isInsideRepository(opts.cwd).then((inside) => {
|
|
139
|
+
reject(new GitExecutorError(inside ? "non_zero_exit" : "no_git_repo", stderr, code ?? void 0));
|
|
140
|
+
});
|
|
134
141
|
return;
|
|
135
142
|
}
|
|
136
143
|
const stdout = Buffer.concat(stdoutChunks).toString("utf8");
|
|
@@ -138,21 +145,55 @@ function runGit(args, opts) {
|
|
|
138
145
|
});
|
|
139
146
|
});
|
|
140
147
|
}
|
|
148
|
+
function isInsideRepository(cwd) {
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
let settled = false;
|
|
151
|
+
const done = (inside) => {
|
|
152
|
+
if (settled) return;
|
|
153
|
+
settled = true;
|
|
154
|
+
resolve(inside);
|
|
155
|
+
};
|
|
156
|
+
try {
|
|
157
|
+
const probeOptions = {
|
|
158
|
+
cwd,
|
|
159
|
+
env: sanitizeGitEnvironment(process.env),
|
|
160
|
+
stdio: "ignore",
|
|
161
|
+
shell: false
|
|
162
|
+
};
|
|
163
|
+
const probe = spawn("git", [...GIT_HARDENING_PREFIX, "rev-parse", "--git-dir"], withHidden(probeOptions));
|
|
164
|
+
const timer = setTimeout(() => {
|
|
165
|
+
probe.kill("SIGKILL");
|
|
166
|
+
done(true);
|
|
167
|
+
}, PROBE_TIMEOUT_MS);
|
|
168
|
+
timer.unref?.();
|
|
169
|
+
probe.on("error", () => {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
done(true);
|
|
172
|
+
});
|
|
173
|
+
probe.on("close", (code) => {
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
done(code === 0);
|
|
176
|
+
});
|
|
177
|
+
} catch {
|
|
178
|
+
done(true);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
141
182
|
function isPathContained(root, candidate) {
|
|
142
|
-
const relative =
|
|
143
|
-
return relative === "" || !
|
|
183
|
+
const relative = path3.relative(root, candidate);
|
|
184
|
+
return relative === "" || !path3.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path3.sep}`);
|
|
144
185
|
}
|
|
145
186
|
function isSelectableRepoRel(repoRel) {
|
|
146
187
|
if (repoRel === "") return true;
|
|
147
|
-
if (
|
|
148
|
-
const normalized =
|
|
149
|
-
return !normalized.startsWith("-") && normalized !== ".." && !normalized.startsWith(`..${
|
|
188
|
+
if (path3.isAbsolute(repoRel)) return false;
|
|
189
|
+
const normalized = path3.normalize(repoRel);
|
|
190
|
+
return !normalized.startsWith("-") && normalized !== ".." && !normalized.startsWith(`..${path3.sep}`);
|
|
150
191
|
}
|
|
151
192
|
|
|
152
193
|
// ../fleet-plugins/repository/server/git-marker.ts
|
|
153
194
|
var GITDIR_PREFIX = "gitdir:";
|
|
154
195
|
async function resolveContainedGitDir(repoDir, realTheaterPath) {
|
|
155
|
-
const markerPath =
|
|
196
|
+
const markerPath = path3.join(repoDir, ".git");
|
|
156
197
|
let marker;
|
|
157
198
|
try {
|
|
158
199
|
marker = await fs2.stat(markerPath);
|
|
@@ -171,7 +212,7 @@ async function resolveContainedGitDir(repoDir, realTheaterPath) {
|
|
|
171
212
|
if (!line) return null;
|
|
172
213
|
const target = line.trim().slice(GITDIR_PREFIX.length).trim();
|
|
173
214
|
if (!target) return null;
|
|
174
|
-
gitDir =
|
|
215
|
+
gitDir = path3.resolve(repoDir, target);
|
|
175
216
|
} else if (marker.isDirectory()) {
|
|
176
217
|
gitDir = markerPath;
|
|
177
218
|
} else {
|
|
@@ -186,7 +227,7 @@ async function resolveContainedGitDir(repoDir, realTheaterPath) {
|
|
|
186
227
|
if (!isPathContained(realTheaterPath, realGitDir)) return null;
|
|
187
228
|
let commonRaw = null;
|
|
188
229
|
try {
|
|
189
|
-
commonRaw = await fs2.readFile(
|
|
230
|
+
commonRaw = await fs2.readFile(path3.join(realGitDir, "commondir"), "utf8");
|
|
190
231
|
} catch {
|
|
191
232
|
}
|
|
192
233
|
if (commonRaw !== null) {
|
|
@@ -194,7 +235,7 @@ async function resolveContainedGitDir(repoDir, realTheaterPath) {
|
|
|
194
235
|
if (!commonTarget) return null;
|
|
195
236
|
let realCommonDir;
|
|
196
237
|
try {
|
|
197
|
-
realCommonDir = await fs2.realpath(
|
|
238
|
+
realCommonDir = await fs2.realpath(path3.resolve(realGitDir, commonTarget));
|
|
198
239
|
} catch {
|
|
199
240
|
return null;
|
|
200
241
|
}
|
|
@@ -266,9 +307,9 @@ var InvalidRepoError = class extends Error {
|
|
|
266
307
|
};
|
|
267
308
|
async function resolveGitCwd(theaterPath, repoRel = "") {
|
|
268
309
|
if (repoRel === void 0 || repoRel === "") return { gitCwd: theaterPath };
|
|
269
|
-
if (typeof repoRel !== "string" ||
|
|
310
|
+
if (typeof repoRel !== "string" || path3.isAbsolute(repoRel)) throw new InvalidRepoError("Repository path must be relative");
|
|
270
311
|
if (!isSelectableRepoRel(repoRel)) throw new InvalidRepoError("Repository path is invalid");
|
|
271
|
-
const resolved =
|
|
312
|
+
const resolved = path3.resolve(theaterPath, path3.normalize(repoRel));
|
|
272
313
|
if (!isPathContained(theaterPath, resolved)) throw new InvalidRepoError("Repository path escapes Theater");
|
|
273
314
|
let realTheater;
|
|
274
315
|
let realRepo;
|
|
@@ -399,12 +440,12 @@ async function handleRepositoryFile(req, res, ctx) {
|
|
|
399
440
|
}
|
|
400
441
|
const { gitCwd } = cwdResult;
|
|
401
442
|
const rawFilePath = body.filePath;
|
|
402
|
-
const resolvedFilePath =
|
|
403
|
-
if (!resolvedFilePath.startsWith(gitCwd +
|
|
443
|
+
const resolvedFilePath = path3.resolve(gitCwd, rawFilePath);
|
|
444
|
+
if (!resolvedFilePath.startsWith(gitCwd + path3.sep) && resolvedFilePath !== gitCwd) {
|
|
404
445
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
405
446
|
return;
|
|
406
447
|
}
|
|
407
|
-
const relativePath =
|
|
448
|
+
const relativePath = path3.normalize(rawFilePath);
|
|
408
449
|
if (relativePath.startsWith("..")) {
|
|
409
450
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
410
451
|
return;
|
|
@@ -422,7 +463,7 @@ async function handleRepositoryFile(req, res, ctx) {
|
|
|
422
463
|
ctx.host.http.writeJson(res, 404, { error: "file_not_found" });
|
|
423
464
|
return;
|
|
424
465
|
}
|
|
425
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
466
|
+
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
|
|
426
467
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
427
468
|
return;
|
|
428
469
|
}
|
|
@@ -438,7 +479,7 @@ async function handleRepositoryFile(req, res, ctx) {
|
|
|
438
479
|
fs2.realpath(gitCwd),
|
|
439
480
|
fs2.realpath(resolvedFilePath)
|
|
440
481
|
]);
|
|
441
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
482
|
+
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
|
|
442
483
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
443
484
|
return;
|
|
444
485
|
}
|
|
@@ -558,9 +599,9 @@ function isPlainObject3(value) {
|
|
|
558
599
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
559
600
|
}
|
|
560
601
|
function resolveLiteralFilePath(gitCwd, rawPath) {
|
|
561
|
-
const relativePath =
|
|
562
|
-
const resolved =
|
|
563
|
-
if (relativePath === ".." || relativePath.startsWith(".." +
|
|
602
|
+
const relativePath = path3.normalize(rawPath);
|
|
603
|
+
const resolved = path3.resolve(gitCwd, relativePath);
|
|
604
|
+
if (relativePath === ".." || relativePath.startsWith(".." + path3.sep) || !resolved.startsWith(gitCwd + path3.sep)) return null;
|
|
564
605
|
return relativePath;
|
|
565
606
|
}
|
|
566
607
|
function literalPathspec2(relativePath) {
|
|
@@ -746,9 +787,9 @@ function isPlainObject5(value) {
|
|
|
746
787
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
747
788
|
}
|
|
748
789
|
function resolveLiteralFilePath2(gitCwd, rawPath) {
|
|
749
|
-
const relativePath =
|
|
750
|
-
const resolved =
|
|
751
|
-
if (relativePath === ".." || relativePath.startsWith(".." +
|
|
790
|
+
const relativePath = path3.normalize(rawPath);
|
|
791
|
+
const resolved = path3.resolve(gitCwd, relativePath);
|
|
792
|
+
if (relativePath === ".." || relativePath.startsWith(".." + path3.sep) || !resolved.startsWith(gitCwd + path3.sep)) return null;
|
|
752
793
|
return relativePath;
|
|
753
794
|
}
|
|
754
795
|
function literalPathspec3(relativePath) {
|
|
@@ -849,8 +890,8 @@ async function resolveGitIdentity(gitCwd) {
|
|
|
849
890
|
runGit(["rev-parse", "--git-common-dir"], { cwd: gitCwd })
|
|
850
891
|
]);
|
|
851
892
|
const [gitDir, commonDir] = await Promise.all([
|
|
852
|
-
fs2.realpath(
|
|
853
|
-
fs2.realpath(
|
|
893
|
+
fs2.realpath(path3.resolve(gitCwd, gitDirResult.stdout.trim())),
|
|
894
|
+
fs2.realpath(path3.resolve(gitCwd, commonDirResult.stdout.trim()))
|
|
854
895
|
]);
|
|
855
896
|
return { gitDir, commonDir };
|
|
856
897
|
}
|
|
@@ -858,7 +899,7 @@ async function readLastFetchAt(gitDirs) {
|
|
|
858
899
|
let newest = null;
|
|
859
900
|
for (const gitDir of gitDirs) {
|
|
860
901
|
try {
|
|
861
|
-
const stats = await fs2.stat(
|
|
902
|
+
const stats = await fs2.stat(path3.join(gitDir, "FETCH_HEAD"));
|
|
862
903
|
if (stats.size === 0) continue;
|
|
863
904
|
if (!newest || stats.mtime.getTime() > newest.getTime()) newest = stats.mtime;
|
|
864
905
|
} catch (error) {
|
|
@@ -959,9 +1000,9 @@ async function fetchRepository(gitCwd) {
|
|
|
959
1000
|
async function fetchHeadCandidates(identity) {
|
|
960
1001
|
const candidates = /* @__PURE__ */ new Set([identity.gitDir, identity.commonDir]);
|
|
961
1002
|
try {
|
|
962
|
-
const worktreesDir =
|
|
1003
|
+
const worktreesDir = path3.join(identity.commonDir, "worktrees");
|
|
963
1004
|
for (const entry of await fs2.readdir(worktreesDir, { withFileTypes: true })) {
|
|
964
|
-
if (entry.isDirectory()) candidates.add(
|
|
1005
|
+
if (entry.isDirectory()) candidates.add(path3.join(worktreesDir, entry.name));
|
|
965
1006
|
}
|
|
966
1007
|
} catch (error) {
|
|
967
1008
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -1050,6 +1091,14 @@ async function handleRepositoryFetch(req, res, ctx) {
|
|
|
1050
1091
|
var WORKTREE_SHA_RE = /^[0-9a-f]{40}$/;
|
|
1051
1092
|
var CANONICAL_REF_RE = /^refs\/(?:heads|remotes|tags)\//;
|
|
1052
1093
|
var REF_METACHAR_RE = /[~^:?*\[\\\s\x00-\x1f\x7f]/;
|
|
1094
|
+
var LOG_PRETTY_FORMAT = "--pretty=format:%x1e%H%x00%h%x00%s%x00%an%x00%ar%x00%at%x00%D%x00%P%x00%<(8,trunc)%b";
|
|
1095
|
+
var LOG_FIELD_COUNT = 9;
|
|
1096
|
+
var FULL_HASH_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
1097
|
+
var LOG_ORDER_ARGS = { topo: "--topo-order", date: "--date-order" };
|
|
1098
|
+
function resolveLogOrder(requested) {
|
|
1099
|
+
if (requested === void 0) return "topo";
|
|
1100
|
+
return requested === "topo" || requested === "date" ? requested : null;
|
|
1101
|
+
}
|
|
1053
1102
|
function isPlainObject7(value) {
|
|
1054
1103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1055
1104
|
}
|
|
@@ -1073,9 +1122,10 @@ function parseLogOutput(stdout) {
|
|
|
1073
1122
|
const authorAt = Number.parseInt(fields[5] ?? "0", 10);
|
|
1074
1123
|
const refsRaw = fields[6] ?? "";
|
|
1075
1124
|
const parentsRaw = fields[7] ?? "";
|
|
1076
|
-
if (!fullHash) continue;
|
|
1125
|
+
if (fields.length < LOG_FIELD_COUNT || !FULL_HASH_RE.test(fullHash)) continue;
|
|
1077
1126
|
const refs = refsRaw.split(",").map((r) => r.trim()).filter(Boolean);
|
|
1078
1127
|
const parents = parentsRaw.split(" ").map((p) => p.trim()).filter(Boolean);
|
|
1128
|
+
const hasBody = (fields[8] ?? "").trim() !== "";
|
|
1079
1129
|
commits.push({
|
|
1080
1130
|
shortHash,
|
|
1081
1131
|
fullHash,
|
|
@@ -1085,7 +1135,8 @@ function parseLogOutput(stdout) {
|
|
|
1085
1135
|
authorAt: Number.isFinite(authorAt) ? authorAt : 0,
|
|
1086
1136
|
refs,
|
|
1087
1137
|
parents,
|
|
1088
|
-
onHead: true
|
|
1138
|
+
onHead: true,
|
|
1139
|
+
hasBody
|
|
1089
1140
|
});
|
|
1090
1141
|
}
|
|
1091
1142
|
return commits;
|
|
@@ -1184,6 +1235,11 @@ async function handleRepositoryLog(req, res, ctx) {
|
|
|
1184
1235
|
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
1185
1236
|
return;
|
|
1186
1237
|
}
|
|
1238
|
+
const order = resolveLogOrder(body.order);
|
|
1239
|
+
if (order === null) {
|
|
1240
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1187
1243
|
const limit = body.limit === void 0 ? 200 : body.limit;
|
|
1188
1244
|
const skip = body.skip === void 0 ? 0 : body.skip;
|
|
1189
1245
|
if (!Number.isInteger(limit) || typeof limit !== "number" || limit < 1 || limit > 500 || !Number.isInteger(skip) || typeof skip !== "number" || skip < 0) {
|
|
@@ -1224,13 +1280,14 @@ async function handleRepositoryLog(req, res, ctx) {
|
|
|
1224
1280
|
const [realGitCwd, realToplevel] = await Promise.all([normalizeWorktreePath(gitCwd), normalizeWorktreePath(currentWorktreePath)]);
|
|
1225
1281
|
const scopePathspec = realToplevel !== "" && realGitCwd === realToplevel ? [] : ["."];
|
|
1226
1282
|
const skipArg = skip > 0 ? [`--skip=${skip}`] : [];
|
|
1227
|
-
const
|
|
1283
|
+
const orderArg = LOG_ORDER_ARGS[order];
|
|
1284
|
+
const result = resolvedRef ? await runGit(["log", resolvedRef, orderArg, "-n", String(limit + 1), ...skipArg, "--decorate=full", LOG_PRETTY_FORMAT, "--", ...scopePathspec], { cwd: gitCwd }) : await runGit(
|
|
1228
1285
|
// --all은 refs/stash·refs/notes까지 그래프에 유입시키므로 브랜치/태그/원격 + 현재 HEAD + 워크트리 HEAD로 한정한다
|
|
1229
|
-
["log", "--branches", "--tags", "--remotes",
|
|
1286
|
+
["log", "--branches", "--tags", "--remotes", orderArg, "-n", String(limit + 1), ...skipArg, "--decorate=full", LOG_PRETTY_FORMAT, ...headRevs, ...worktreeRevs, "--", ...scopePathspec],
|
|
1230
1287
|
{ cwd: gitCwd }
|
|
1231
1288
|
);
|
|
1232
1289
|
const parsedCommits = parseLogOutput(result.stdout);
|
|
1233
|
-
const hasMore = parsedCommits.length > limit;
|
|
1290
|
+
const hasMore = parsedCommits.length > limit || result.truncated && parsedCommits.length > 0;
|
|
1234
1291
|
const commits = annotateHeadReachability(parsedCommits.slice(0, limit), headRevList);
|
|
1235
1292
|
ctx.host.http.writeJson(res, 200, { commits, checkouts, hasMore, ...result.truncated ? { truncated: true } : {} });
|
|
1236
1293
|
} catch (error) {
|
|
@@ -1377,18 +1434,18 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
1377
1434
|
if (repos.length >= cap) return true;
|
|
1378
1435
|
if (visits.remaining <= 0) return true;
|
|
1379
1436
|
visits.remaining -= 1;
|
|
1380
|
-
const relPath =
|
|
1437
|
+
const relPath = path3.relative(realTheaterPath, dir);
|
|
1381
1438
|
const gitDir = isSelectableRepoRel(relPath) ? await resolveContainedGitDir(dir, realTheaterPath) : null;
|
|
1382
1439
|
let linkedWorktree = false;
|
|
1383
1440
|
if (gitDir !== null) {
|
|
1384
1441
|
try {
|
|
1385
|
-
await fs2.access(
|
|
1442
|
+
await fs2.access(path3.join(gitDir, "commondir"));
|
|
1386
1443
|
linkedWorktree = true;
|
|
1387
1444
|
} catch {
|
|
1388
1445
|
}
|
|
1389
1446
|
}
|
|
1390
1447
|
if (gitDir !== null && !linkedWorktree) {
|
|
1391
|
-
repos.push({ relPath, name: relPath === "" ?
|
|
1448
|
+
repos.push({ relPath, name: relPath === "" ? path3.basename(realTheaterPath) : path3.basename(dir), repoDir: dir });
|
|
1392
1449
|
if (repos.length >= cap) return true;
|
|
1393
1450
|
}
|
|
1394
1451
|
if (currentDepth >= maxDepth) return false;
|
|
@@ -1399,7 +1456,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
1399
1456
|
while (dirent !== null) {
|
|
1400
1457
|
if (repos.length >= cap || visits.remaining <= 0) return true;
|
|
1401
1458
|
if (dirent.isDirectory() && dirent.name !== "node_modules" && dirent.name !== ".git") {
|
|
1402
|
-
const childPath =
|
|
1459
|
+
const childPath = path3.join(dir, dirent.name);
|
|
1403
1460
|
let realChildPath;
|
|
1404
1461
|
try {
|
|
1405
1462
|
realChildPath = await fs2.realpath(childPath);
|
|
@@ -1478,7 +1535,7 @@ async function handleRepositoryRepos(req, res, ctx) {
|
|
|
1478
1535
|
}
|
|
1479
1536
|
}
|
|
1480
1537
|
if (rootIsRepo) {
|
|
1481
|
-
repos.push({ relPath: "", name:
|
|
1538
|
+
repos.push({ relPath: "", name: path3.basename(theaterPath), branch: await resolveRepoBranch(theaterPath), kind: "root" });
|
|
1482
1539
|
seen.add("");
|
|
1483
1540
|
}
|
|
1484
1541
|
const scanned = [];
|
|
@@ -1637,12 +1694,12 @@ async function handleRepositoryWorktrees(req, res, ctx) {
|
|
|
1637
1694
|
continue;
|
|
1638
1695
|
}
|
|
1639
1696
|
if (!isPathContained(realTheaterPath, realWorktree)) continue;
|
|
1640
|
-
const relPath =
|
|
1697
|
+
const relPath = path3.relative(realTheaterPath, realWorktree);
|
|
1641
1698
|
if (!isSelectableRepoRel(relPath) || seen.has(relPath)) continue;
|
|
1642
1699
|
if (await resolveContainedGitDir(realWorktree, realTheaterPath) === null) continue;
|
|
1643
1700
|
worktrees.push({
|
|
1644
1701
|
relPath,
|
|
1645
|
-
name:
|
|
1702
|
+
name: path3.basename(realWorktree),
|
|
1646
1703
|
branch: worktree.branch ?? worktree.sha.slice(0, 7),
|
|
1647
1704
|
current: realWorktree === realGitCwd
|
|
1648
1705
|
});
|