@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.
Files changed (73) hide show
  1. package/AGENTS.md +10 -5
  2. package/README.md +10 -8
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.mjs +8507 -8365
  5. package/dist/client/assets/{_baseUniq-CXYajthv.js → _baseUniq-CYpcPDgB.js} +1 -1
  6. package/dist/client/assets/{arc-CN2U0RJi.js → arc-BuehZLUz.js} +1 -1
  7. package/dist/client/assets/{architectureDiagram-Q4EWVU46-KMYgwTU-.js → architectureDiagram-Q4EWVU46-Bqn18zpp.js} +1 -1
  8. package/dist/client/assets/{blockDiagram-DXYQGD6D-B2n_Ujhi.js → blockDiagram-DXYQGD6D-DmtLdC53.js} +1 -1
  9. package/dist/client/assets/{c4Diagram-AHTNJAMY-BmExz-H8.js → c4Diagram-AHTNJAMY-BEBhZ5GG.js} +1 -1
  10. package/dist/client/assets/channel-BQlgz3sc.js +1 -0
  11. package/dist/client/assets/{chunk-4BX2VUAB-WxXjYCDb.js → chunk-4BX2VUAB-B8PizWjA.js} +1 -1
  12. package/dist/client/assets/{chunk-4TB4RGXK-CoSmkS4d.js → chunk-4TB4RGXK-C1ofR8zt.js} +1 -1
  13. package/dist/client/assets/{chunk-55IACEB6-BQ6DMyRz.js → chunk-55IACEB6-CbKQ3d6C.js} +1 -1
  14. package/dist/client/assets/{chunk-EDXVE4YY-DEhxxADi.js → chunk-EDXVE4YY-DU-bru4J.js} +1 -1
  15. package/dist/client/assets/{chunk-FMBD7UC4-BUW_vTgY.js → chunk-FMBD7UC4-DD9GbN05.js} +1 -1
  16. package/dist/client/assets/{chunk-OYMX7WX6-xqJeGGdu.js → chunk-OYMX7WX6-DZKa6Wtf.js} +1 -1
  17. package/dist/client/assets/{chunk-QZHKN3VN-DPXuiqFZ.js → chunk-QZHKN3VN-fK2o17Jf.js} +1 -1
  18. package/dist/client/assets/{chunk-YZCP3GAM-CJBloo9Q.js → chunk-YZCP3GAM-z-7p86Bc.js} +1 -1
  19. package/dist/client/assets/classDiagram-6PBFFD2Q-CQs4cWly.js +1 -0
  20. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CQs4cWly.js +1 -0
  21. package/dist/client/assets/clone-B5bOHdOg.js +1 -0
  22. package/dist/client/assets/{cose-bilkent-S5V4N54A-C7eVojQK.js → cose-bilkent-S5V4N54A-x5L9kNFv.js} +1 -1
  23. package/dist/client/assets/{dagre-KV5264BT-DYPawRSP.js → dagre-KV5264BT-944maqqT.js} +1 -1
  24. package/dist/client/assets/{diagram-5BDNPKRD-9nbdU3Kr.js → diagram-5BDNPKRD-PV4GQ8C6.js} +1 -1
  25. package/dist/client/assets/{diagram-G4DWMVQ6-DtSzEakz.js → diagram-G4DWMVQ6-GcoFZYg0.js} +1 -1
  26. package/dist/client/assets/{diagram-MMDJMWI5-2FX1CuCi.js → diagram-MMDJMWI5-8cIlnSEJ.js} +1 -1
  27. package/dist/client/assets/{diagram-TYMM5635-D1aBD1Sv.js → diagram-TYMM5635-BlIMSlQ5.js} +1 -1
  28. package/dist/client/assets/{erDiagram-SMLLAGMA-AK79bFYW.js → erDiagram-SMLLAGMA-KjlbhDy3.js} +1 -1
  29. package/dist/client/assets/{flowDiagram-DWJPFMVM-C1ApmkLT.js → flowDiagram-DWJPFMVM-DqYNn08z.js} +1 -1
  30. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-CQvFUGlj.js → ganttDiagram-T4ZO3ILL-BplrlRlU.js} +1 -1
  31. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-C-Vh-bKl.js → gitGraphDiagram-UUTBAWPF-CISgtHAu.js} +1 -1
  32. package/dist/client/assets/{graph-DeEdzsTp.js → graph-DaTRgbvC.js} +1 -1
  33. package/dist/client/assets/index-DVQ5EO_a.css +1 -0
  34. package/dist/client/assets/index-alR6ElCr.js +479 -0
  35. package/dist/client/assets/{infoDiagram-42DDH7IO-B_0wD24T.js → infoDiagram-42DDH7IO-BbFeDUi-.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DgGbx2eb.js → ishikawaDiagram-UXIWVN3A-BQTo9ri4.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-VCZTEJTY-BdQqxMsJ.js → journeyDiagram-VCZTEJTY-CiRkOhp8.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-6JOO6SKY-DU8AwVYp.js → kanban-definition-6JOO6SKY-IIn_MSzy.js} +1 -1
  39. package/dist/client/assets/{layout-BF-OB3sH.js → layout-Cr58D4FS.js} +1 -1
  40. package/dist/client/assets/{linear-Cd22n3Ol.js → linear-DNaIPcgl.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-CV1wZraa.js → mermaid.core-Dq_ukAsT.js} +4 -4
  42. package/dist/client/assets/{min-Avezts6s.js → min-DpLyTgPW.js} +1 -1
  43. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BiYy2OKo.js → mindmap-definition-QFDTVHPH-C2DLYGq2.js} +1 -1
  44. package/dist/client/assets/{pieDiagram-DEJITSTG-Bg9O56EU.js → pieDiagram-DEJITSTG-C3Cxmz-n.js} +1 -1
  45. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-B9NeeqFq.js → quadrantDiagram-34T5L4WZ-CEdmiODY.js} +1 -1
  46. package/dist/client/assets/{requirementDiagram-MS252O5E-DV1FugSk.js → requirementDiagram-MS252O5E-B-Vj0Wyw.js} +1 -1
  47. package/dist/client/assets/{sankeyDiagram-XADWPNL6-BvIHcJf7.js → sankeyDiagram-XADWPNL6-CyvEd7TA.js} +1 -1
  48. package/dist/client/assets/{sequenceDiagram-FGHM5R23-BCQ2xDy1.js → sequenceDiagram-FGHM5R23-B2MiPHOT.js} +1 -1
  49. package/dist/client/assets/{stateDiagram-FHFEXIEX-BpK4LvW_.js → stateDiagram-FHFEXIEX-Dsa-VSCx.js} +1 -1
  50. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BKHWSGsO.js +1 -0
  51. package/dist/client/assets/{timeline-definition-GMOUNBTQ-BEwHf_wL.js → timeline-definition-GMOUNBTQ-B9p0xkPR.js} +1 -1
  52. package/dist/client/assets/{vennDiagram-DHZGUBPP-Cd0iA5Vu.js → vennDiagram-DHZGUBPP-DeU4CNd8.js} +1 -1
  53. package/dist/client/assets/{wardley-RL74JXVD-cubfgt6B.js → wardley-RL74JXVD-BQyG38I_.js} +1 -1
  54. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-DTdlOJlu.js → wardleyDiagram-NUSXRM2D-DjrdDkEs.js} +1 -1
  55. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BguY4oGj.js → xychartDiagram-5P7HB3ND-CG0KUfn_.js} +1 -1
  56. package/dist/client/index.html +2 -2
  57. package/dist/desktop-protocol.mjs +3 -3
  58. package/dist/fleet-plugins/file-explorer/routes.mjs +4 -4
  59. package/dist/fleet-plugins/ledger/routes.mjs +61 -16510
  60. package/dist/fleet-plugins/quota/routes.mjs +2864 -2483
  61. package/dist/fleet-plugins/repository/routes.mjs +107 -50
  62. package/dist/fleet-plugins/scuttlebutt/routes.mjs +4955 -6895
  63. package/dist/fleet-plugins/skills/routes.mjs +75 -16523
  64. package/dist/fleet-plugins/terminal/routes.mjs +24129 -34156
  65. package/dist/fleet.mjs +54671 -0
  66. package/package.json +6 -4
  67. package/dist/client/assets/channel-7u-T-USm.js +0 -1
  68. package/dist/client/assets/classDiagram-6PBFFD2Q-CdK4kqo5.js +0 -1
  69. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CdK4kqo5.js +0 -1
  70. package/dist/client/assets/clone-BJX0Y00S.js +0 -1
  71. package/dist/client/assets/index-PYKMOCH4.css +0 -1
  72. package/dist/client/assets/index-rFuEoKaA.js +0 -482
  73. 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 path4 from 'path';
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, path10, handler) {
12
- ctx.registerRouter(normalizePath(path10), handler);
10
+ function registerRouter(ctx, path9, handler) {
11
+ ctx.registerRouter(normalizePath(path9), handler);
13
12
  }
14
- function normalizePath(path10) {
15
- if (path10.length === 0 || path10 === "/") return "/";
16
- return path10;
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
- const isNoRepo = stderr.toLowerCase().includes("not a git repository");
133
- reject(new GitExecutorError(isNoRepo ? "no_git_repo" : "non_zero_exit", stderr, code ?? void 0));
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 = path4.relative(root, candidate);
143
- return relative === "" || !path4.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path4.sep}`);
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 (path4.isAbsolute(repoRel)) return false;
148
- const normalized = path4.normalize(repoRel);
149
- return !normalized.startsWith("-") && normalized !== ".." && !normalized.startsWith(`..${path4.sep}`);
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 = path4.join(repoDir, ".git");
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 = path4.resolve(repoDir, target);
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(path4.join(realGitDir, "commondir"), "utf8");
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(path4.resolve(realGitDir, commonTarget));
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" || path4.isAbsolute(repoRel)) throw new InvalidRepoError("Repository path must be relative");
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 = path4.resolve(theaterPath, path4.normalize(repoRel));
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 = path4.resolve(gitCwd, rawFilePath);
403
- if (!resolvedFilePath.startsWith(gitCwd + path4.sep) && resolvedFilePath !== 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 = path4.normalize(rawFilePath);
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 + path4.sep)) {
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 + path4.sep)) {
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 = path4.normalize(rawPath);
562
- const resolved = path4.resolve(gitCwd, relativePath);
563
- if (relativePath === ".." || relativePath.startsWith(".." + path4.sep) || !resolved.startsWith(gitCwd + path4.sep)) return null;
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 = path4.normalize(rawPath);
750
- const resolved = path4.resolve(gitCwd, relativePath);
751
- if (relativePath === ".." || relativePath.startsWith(".." + path4.sep) || !resolved.startsWith(gitCwd + path4.sep)) return null;
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(path4.resolve(gitCwd, gitDirResult.stdout.trim())),
853
- fs2.realpath(path4.resolve(gitCwd, commonDirResult.stdout.trim()))
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(path4.join(gitDir, "FETCH_HEAD"));
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 = path4.join(identity.commonDir, "worktrees");
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(path4.join(worktreesDir, entry.name));
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 result = resolvedRef ? await runGit(["log", resolvedRef, "--date-order", "-n", String(limit + 1), ...skipArg, "--decorate=full", "--pretty=format:%x1e%H%x00%h%x00%s%x00%an%x00%ar%x00%at%x00%D%x00%P", "--", ...scopePathspec], { cwd: gitCwd }) : await runGit(
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", "--date-order", "-n", String(limit + 1), ...skipArg, "--decorate=full", "--pretty=format:%x1e%H%x00%h%x00%s%x00%an%x00%ar%x00%at%x00%D%x00%P", ...headRevs, ...worktreeRevs, "--", ...scopePathspec],
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 = path4.relative(realTheaterPath, dir);
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(path4.join(gitDir, "commondir"));
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 === "" ? path4.basename(realTheaterPath) : path4.basename(dir), repoDir: dir });
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 = path4.join(dir, dirent.name);
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: path4.basename(theaterPath), branch: await resolveRepoBranch(theaterPath), kind: "root" });
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 = path4.relative(realTheaterPath, realWorktree);
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: path4.basename(realWorktree),
1702
+ name: path3.basename(realWorktree),
1646
1703
  branch: worktree.branch ?? worktree.sha.slice(0, 7),
1647
1704
  current: realWorktree === realGitCwd
1648
1705
  });