@dotobokuri/fleet-console 1.69.0 → 1.71.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 (69) hide show
  1. package/dist/cli.mjs +38 -4
  2. package/dist/client/assets/{_baseUniq-BPBd-J9P.js → _baseUniq--GYoaiDn.js} +1 -1
  3. package/dist/client/assets/{arc-spHqy4X3.js → arc-ioTPutEb.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-7CHqKfxu.js → architectureDiagram-Q4EWVU46-DLfH6cdt.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-YasFDgl_.js → blockDiagram-DXYQGD6D-ZM6X_dlD.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-CcoPJq56.js → c4Diagram-AHTNJAMY-DDPTnZGS.js} +1 -1
  7. package/dist/client/assets/channel-BvAoE0IP.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-MYBJn9BE.js → chunk-4BX2VUAB-0ShpcAto.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-C1wg6GR_.js → chunk-4TB4RGXK-CvMAl5bA.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-BoufMT1O.js → chunk-55IACEB6-CgV8t6TS.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-BzAhAIl3.js → chunk-EDXVE4YY-DQkbYjs9.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-yVrLsKzP.js → chunk-FMBD7UC4-D4Pm3UM-.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-YiiKaqRQ.js → chunk-OYMX7WX6-BrWIgOQ6.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-CqWQlGHT.js → chunk-QZHKN3VN-BC-GEuDl.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-9yLOmU5k.js → chunk-YZCP3GAM-Dst--QPc.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-BeVFxENS.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-BeVFxENS.js +1 -0
  18. package/dist/client/assets/clone-TnYsvJuQ.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-XkTuSq7v.js → cose-bilkent-S5V4N54A-D87Ru6Nw.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-DyzXIEAe.js → dagre-KV5264BT-CUG8AIPS.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-DF24YlCa.js → diagram-5BDNPKRD-DrhhXUdy.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-CVhcj9RK.js → diagram-G4DWMVQ6-DgGQFVlD.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-C-kGKk4K.js → diagram-MMDJMWI5-DEyFyzNg.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-mr285WbR.js → diagram-TYMM5635-ari8AKd5.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-BtEx-XlU.js → erDiagram-SMLLAGMA-Aywt-vx8.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-DuWy3xzN.js → flowDiagram-DWJPFMVM-FO46vDh_.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-B2-xEikU.js → ganttDiagram-T4ZO3ILL-C0m3r6C5.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-EM17u_t7.js → gitGraphDiagram-UUTBAWPF-CTUKGfIc.js} +1 -1
  29. package/dist/client/assets/{graph-xuE12FIq.js → graph-PzhNFXt_.js} +1 -1
  30. package/dist/client/assets/index-Bcc3Exng.css +1 -0
  31. package/dist/client/assets/index-DkOT93yC.js +467 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-B3MF4XtW.js → infoDiagram-42DDH7IO-CFry6eYn.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DoQ2mGyh.js → ishikawaDiagram-UXIWVN3A-BBJvVjTh.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-GeR9sFH-.js → journeyDiagram-VCZTEJTY-glHJeITG.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-DrrYMuvL.js → kanban-definition-6JOO6SKY-Dp7IjAmS.js} +1 -1
  36. package/dist/client/assets/{layout-DH63A-3u.js → layout-PhtOMDDE.js} +1 -1
  37. package/dist/client/assets/{linear-BIjE6zB1.js → linear-B3pjuRWl.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-yrrC2Lqo.js → mermaid.core-DDurtDWt.js} +4 -4
  39. package/dist/client/assets/{min-CR-H8E6U.js → min-ZS3oLuaq.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BzSC43J-.js → mindmap-definition-QFDTVHPH-D67iQI4K.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-BdghNtCc.js → pieDiagram-DEJITSTG-B95nftHb.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-s_zN2epw.js → quadrantDiagram-34T5L4WZ-BbBVDSwm.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-DuiGG6Tx.js → requirementDiagram-MS252O5E-Dbtcs04o.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-Bp0JBk2g.js → sankeyDiagram-XADWPNL6-BuqmxYyX.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-AzQ3M31V.js → sequenceDiagram-FGHM5R23-DmLihX3U.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-BGgm44ev.js → stateDiagram-FHFEXIEX-D3Z_iYXj.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BbUn16lr.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-UqfsiBJY.js → timeline-definition-GMOUNBTQ-witmATQr.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-PsCGF9nR.js → vennDiagram-DHZGUBPP-BDI4z0F-.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-BDQbnh-d.js → wardley-RL74JXVD-oYmBD5eO.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CtVA1JMs.js → wardleyDiagram-NUSXRM2D-DhnyRjSq.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-BsLlZWbc.js → xychartDiagram-5P7HB3ND-DUY-bU_6.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/file-explorer/routes.mjs +138 -96
  55. package/dist/fleet-plugins/ledger/routes.mjs +33 -2
  56. package/dist/fleet-plugins/quota/routes.mjs +33 -2
  57. package/dist/fleet-plugins/repository/routes.mjs +41 -5
  58. package/dist/fleet-plugins/scuttlebutt/routes.mjs +34 -2
  59. package/dist/fleet-plugins/skills/routes.mjs +126 -60
  60. package/dist/fleet-plugins/terminal/routes.mjs +1482 -786
  61. package/dist/fleet.mjs +1462 -705
  62. package/package.json +1 -1
  63. package/dist/client/assets/channel-DfEjadrw.js +0 -1
  64. package/dist/client/assets/classDiagram-6PBFFD2Q-D9vQdv0q.js +0 -1
  65. package/dist/client/assets/classDiagram-v2-HSJHXN6E-D9vQdv0q.js +0 -1
  66. package/dist/client/assets/clone-Ck2x5JX2.js +0 -1
  67. package/dist/client/assets/index-C5EOZGlC.js +0 -466
  68. package/dist/client/assets/index-CHHE3nPw.css +0 -1
  69. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CZDbnuN-.js +0 -1
@@ -276,7 +276,8 @@ async function readFileForTheater(theaterPath, relativePath) {
276
276
  content: chunk.toString("utf8"),
277
277
  lang: detectLang(realResolved),
278
278
  truncated: true,
279
- sizeBytes: stat.size
279
+ sizeBytes: stat.size,
280
+ mtimeMs: stat.mtimeMs
280
281
  };
281
282
  }
282
283
  const buffer = await fs2.promises.readFile(realResolved);
@@ -285,7 +286,8 @@ async function readFileForTheater(theaterPath, relativePath) {
285
286
  relativePath: path5.relative(realRoot, realResolved),
286
287
  content: buffer.toString("utf8"),
287
288
  lang: detectLang(realResolved),
288
- sizeBytes: stat.size
289
+ sizeBytes: stat.size,
290
+ mtimeMs: stat.mtimeMs
289
291
  };
290
292
  }
291
293
  function isWithinRoot(resolved, root) {
@@ -672,96 +674,28 @@ async function statDirectory(targetPath, stat) {
672
674
  async function collectContentsEntries(targetPath, theaterPath, opendir, stat, entries, hiddenVcs) {
673
675
  const directory = await openDirectory(targetPath, opendir);
674
676
  try {
675
- const pending = [];
676
- let truncated = false;
677
- const accept = (entry) => {
678
- if (entry === null) return false;
679
- if ("vcsInternal" in entry) {
680
- hiddenVcs.push(entry.vcsInternal);
681
- return false;
682
- }
683
- if (entries.length >= DIRECTORY_ENTRY_CAP) return true;
684
- entries.push(entry);
685
- return false;
686
- };
687
- const flushPending = async () => {
688
- while (pending.length > 0) {
689
- if (entries.length >= DIRECTORY_ENTRY_CAP) {
690
- pending.length = 0;
691
- return true;
692
- }
693
- const remaining = DIRECTORY_ENTRY_CAP - entries.length;
694
- const batch = pending.splice(0, Math.min(CLASSIFY_CONCURRENCY, remaining));
695
- const classified = await Promise.all(
696
- batch.map((item) => toContentsEntry(targetPath, theaterPath, item, stat))
697
- );
698
- let overflowed = false;
699
- for (const entry of classified) {
700
- if (overflowed) {
701
- if (entry && "vcsInternal" in entry) hiddenVcs.push(entry.vcsInternal);
702
- continue;
703
- }
704
- if (accept(entry)) overflowed = true;
705
- }
706
- if (overflowed) {
707
- pending.length = 0;
708
- return true;
709
- }
710
- }
711
- return false;
712
- };
713
- const finishCheapTail = async (first) => {
714
- pending.length = 0;
715
- let current = first;
716
- let omitted = false;
717
- while (current !== null) {
718
- if (VCS_INTERNAL_NAMES.has(current.name)) hiddenVcs.push(current.name);
719
- else {
720
- omitted = true;
721
- break;
722
- }
723
- current = await directory.read();
724
- }
725
- return omitted;
726
- };
727
- let dirent = await directory.read();
728
- while (dirent !== null) {
729
- if (VCS_INTERNAL_NAMES.has(dirent.name)) {
730
- hiddenVcs.push(dirent.name);
731
- dirent = await directory.read();
732
- continue;
733
- }
734
- if (entries.length >= DIRECTORY_ENTRY_CAP) {
735
- truncated = await finishCheapTail(dirent);
736
- dirent = null;
737
- break;
738
- }
739
- if (dirent.isDirectory() || dirent.isFile()) {
740
- if (pending.length > 0 && await flushPending()) {
741
- truncated = true;
742
- await finishCheapTail(dirent);
743
- dirent = null;
744
- break;
745
- }
746
- const rel = path5.relative(theaterPath, path5.join(targetPath, dirent.name));
747
- if (accept({ name: dirent.name, relativePath: rel, kind: dirent.isDirectory() ? "dir" : "file" })) {
748
- truncated = true;
749
- await finishCheapTail(await directory.read());
750
- dirent = null;
751
- break;
752
- }
753
- } else {
754
- pending.push(dirent);
755
- if (pending.length >= CLASSIFY_CONCURRENCY && await flushPending()) {
756
- truncated = true;
757
- await finishCheapTail(await directory.read());
758
- dirent = null;
759
- break;
677
+ const cheap = [];
678
+ for (let dirent = await directory.read(); dirent !== null; dirent = await directory.read()) {
679
+ if (VCS_INTERNAL_NAMES.has(dirent.name)) hiddenVcs.push(dirent.name);
680
+ else cheap.push(dirent);
681
+ }
682
+ cheap.sort((left, right) => left.name.localeCompare(right.name));
683
+ const truncated = cheap.length > DIRECTORY_ENTRY_CAP;
684
+ const kept = truncated ? cheap.slice(0, DIRECTORY_ENTRY_CAP) : cheap;
685
+ for (let start = 0; start < kept.length; start += CLASSIFY_CONCURRENCY) {
686
+ const batch = kept.slice(start, start + CLASSIFY_CONCURRENCY);
687
+ const classified = await Promise.all(
688
+ batch.map((item) => toContentsEntry(targetPath, theaterPath, item, stat))
689
+ );
690
+ for (const entry of classified) {
691
+ if (entry === null) continue;
692
+ if ("vcsInternal" in entry) {
693
+ hiddenVcs.push(entry.vcsInternal);
694
+ continue;
760
695
  }
696
+ entries.push(entry);
761
697
  }
762
- dirent = await directory.read();
763
698
  }
764
- if (!truncated && await flushPending()) truncated = true;
765
699
  return truncated;
766
700
  } catch (error) {
767
701
  throw mapFolderBrowserFsError(error);
@@ -969,16 +903,54 @@ function mapGitStatusFsError(error) {
969
903
  }
970
904
  var SEARCH_DIRECTORY_CAP = 500;
971
905
  var SEARCH_ENTRY_CAP = 25e3;
972
- async function searchTheaterFiles(theaterPath, query, limit) {
906
+ var SEARCH_LIMIT_MAX = 200;
907
+ var SEARCH_IGNORED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([
908
+ "node_modules",
909
+ ".pnpm",
910
+ ".yarn",
911
+ "bower_components",
912
+ "dist",
913
+ "build",
914
+ "out",
915
+ "coverage",
916
+ "vendor",
917
+ "target",
918
+ ".next",
919
+ ".nuxt",
920
+ ".turbo",
921
+ ".cache",
922
+ ".parcel-cache",
923
+ ".svelte-kit",
924
+ ".angular",
925
+ ".venv",
926
+ "__pycache__",
927
+ ".gradle",
928
+ ".terraform"
929
+ ]);
930
+ async function searchTheaterFiles(theaterPath, query, limit, options = {}) {
973
931
  const realRoot = await fsp.realpath(theaterPath);
974
- const tokens = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
932
+ const tokens = tokenizeSearchQuery(query);
975
933
  const pending = [{ absolutePath: realRoot, relativePath: "" }];
976
934
  const visited = /* @__PURE__ */ new Set();
977
935
  const matches = [];
978
936
  let directoryCount = 0;
979
937
  let entryCount = 0;
980
938
  let walkCapped = false;
939
+ let ignoredSkipped = false;
940
+ const signal = options.signal;
941
+ const honorIgnoreList = options.includeIgnored !== true;
942
+ const allowedKinds = options.kinds;
943
+ const includeHidden = options.includeHidden !== false;
944
+ const accepts = (relativePath, kind) => {
945
+ if (allowedKinds && !allowedKinds.includes(kind)) return false;
946
+ if (!includeHidden && relativePath.split("/").some((segment) => segment.startsWith("."))) return false;
947
+ return true;
948
+ };
949
+ if (tokens.length === 0) return { files: [], totalMatches: 0, ignoredSkipped: false };
981
950
  while (pending.length > 0) {
951
+ if (signal?.aborted) {
952
+ return { files: [], totalMatches: 0, ignoredSkipped };
953
+ }
982
954
  if (directoryCount >= SEARCH_DIRECTORY_CAP || entryCount >= SEARCH_ENTRY_CAP) {
983
955
  walkCapped = true;
984
956
  break;
@@ -995,17 +967,27 @@ async function searchTheaterFiles(theaterPath, query, limit) {
995
967
  }
996
968
  entries.sort((left, right) => left.name.localeCompare(right.name));
997
969
  for (const entry of entries) {
970
+ if (signal?.aborted) {
971
+ return { files: [], totalMatches: 0, ignoredSkipped };
972
+ }
998
973
  if (entryCount >= SEARCH_ENTRY_CAP) {
999
974
  walkCapped = true;
1000
975
  break;
1001
976
  }
1002
- entryCount += 1;
1003
977
  if (VCS_INTERNAL_NAMES.has(entry.name)) continue;
978
+ if (!includeHidden && entry.name.startsWith(".")) continue;
979
+ entryCount += 1;
1004
980
  const relativePath = directory.relativePath ? `${directory.relativePath}/${entry.name}` : entry.name;
1005
981
  const absolutePath = path5.join(directory.absolutePath, entry.name);
982
+ const ignoredName = honorIgnoreList && SEARCH_IGNORED_DIRECTORY_NAMES.has(entry.name);
1006
983
  let kind = entry.isDirectory() ? "dir" : entry.isFile() ? "file" : null;
1007
984
  let realPath = absolutePath;
1008
985
  if (entry.isSymbolicLink()) {
986
+ if (ignoredName) {
987
+ ignoredSkipped = true;
988
+ if (accepts(relativePath, "dir") && pathMatchesSearchTokens(relativePath, tokens)) matches.push({ relativePath, kind: "dir" });
989
+ continue;
990
+ }
1009
991
  try {
1010
992
  realPath = await fsp.realpath(absolutePath);
1011
993
  if (!isContained(realRoot, realPath)) continue;
@@ -1017,19 +999,28 @@ async function searchTheaterFiles(theaterPath, query, limit) {
1017
999
  }
1018
1000
  }
1019
1001
  if (kind === "dir") {
1002
+ if (accepts(relativePath, "dir") && pathMatchesSearchTokens(relativePath, tokens)) matches.push({ relativePath, kind: "dir" });
1003
+ if (ignoredName) {
1004
+ ignoredSkipped = true;
1005
+ continue;
1006
+ }
1007
+ if (!includeHidden && entry.name.startsWith(".")) continue;
1020
1008
  if (!visited.has(realPath)) pending.push({ absolutePath: realPath, relativePath });
1021
1009
  continue;
1022
1010
  }
1023
1011
  if (kind !== "file") continue;
1024
- const low = relativePath.toLocaleLowerCase();
1025
- if (tokens.every((token) => low.includes(token))) matches.push({ relativePath });
1012
+ if (accepts(relativePath, "file") && pathMatchesSearchTokens(relativePath, tokens)) matches.push({ relativePath, kind: "file" });
1026
1013
  }
1027
1014
  if (walkCapped) break;
1028
1015
  }
1016
+ if (signal?.aborted) {
1017
+ return { files: [], totalMatches: 0, ignoredSkipped };
1018
+ }
1029
1019
  const sorted = matches.sort((left, right) => compareFileSearchItem(left, right, query));
1030
1020
  return {
1031
1021
  files: sorted.slice(0, limit),
1032
1022
  totalMatches: sorted.length,
1023
+ ignoredSkipped,
1033
1024
  ...walkCapped ? { walkCapped: true } : {}
1034
1025
  };
1035
1026
  }
@@ -1043,26 +1034,77 @@ async function handleFilesSearch(req, res, ctx) {
1043
1034
  return;
1044
1035
  }
1045
1036
  const body = await ctx.host.http.readJsonBody(req);
1046
- if (!isPlainObject(body) || typeof body.theaterId !== "string" || typeof body.query !== "string" || body.query.trim() === "" || !Number.isInteger(body.limit) || body.limit < 1 || body.limit > 8) {
1037
+ if (!isPlainObject(body) || typeof body.theaterId !== "string" || typeof body.query !== "string" || body.query.trim() === "" || !Number.isInteger(body.limit) || body.limit < 1 || body.limit > SEARCH_LIMIT_MAX) {
1047
1038
  ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1048
1039
  return;
1049
1040
  }
1041
+ const kinds = parseSearchKinds(body.kinds);
1042
+ if (kinds === "invalid") {
1043
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1044
+ return;
1045
+ }
1046
+ const includeHidden = body.includeHidden !== false;
1050
1047
  const theaterPath = ctx.host.paths.resolveTheaterPath(body.theaterId);
1051
1048
  if (!theaterPath) {
1052
1049
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
1053
1050
  return;
1054
1051
  }
1052
+ const abort = new AbortController();
1053
+ const onClose = () => {
1054
+ if (!res.writableEnded) abort.abort();
1055
+ };
1056
+ if (typeof res.on === "function") res.on("close", onClose);
1057
+ const detach = () => {
1058
+ if (typeof res.off === "function") res.off("close", onClose);
1059
+ };
1055
1060
  try {
1056
- const outcome = await searchTheaterFiles(theaterPath, body.query, body.limit);
1061
+ if (abort.signal.aborted) return;
1062
+ let outcome = await searchTheaterFiles(theaterPath, body.query, body.limit, {
1063
+ signal: abort.signal,
1064
+ ...kinds ? { kinds } : {},
1065
+ includeHidden
1066
+ });
1067
+ if (abort.signal.aborted) return;
1068
+ if (outcome.totalMatches === 0 && outcome.ignoredSkipped && !outcome.walkCapped) {
1069
+ const fallback = await searchTheaterFiles(theaterPath, body.query, body.limit, {
1070
+ signal: abort.signal,
1071
+ includeIgnored: true,
1072
+ ...kinds ? { kinds } : {},
1073
+ includeHidden
1074
+ });
1075
+ if (abort.signal.aborted) return;
1076
+ if (fallback.totalMatches > 0) outcome = { ...fallback, ignoredSkipped: false };
1077
+ }
1057
1078
  ctx.host.http.writeJson(res, 200, {
1058
1079
  files: outcome.files,
1059
1080
  totalMatches: outcome.totalMatches,
1081
+ ignoredSkipped: outcome.ignoredSkipped,
1060
1082
  ...outcome.walkCapped ? { walkCapped: true } : {}
1061
1083
  });
1062
1084
  } catch {
1085
+ if (abort.signal.aborted) return;
1063
1086
  ctx.host.http.writeJson(res, 500, { error: "search_failed" });
1087
+ } finally {
1088
+ detach();
1064
1089
  }
1065
1090
  }
1091
+ function parseSearchKinds(raw) {
1092
+ if (raw === void 0) return null;
1093
+ if (!Array.isArray(raw) || raw.length === 0) return "invalid";
1094
+ const kinds = [];
1095
+ for (const value of raw) {
1096
+ if (value !== "file" && value !== "dir") return "invalid";
1097
+ if (!kinds.includes(value)) kinds.push(value);
1098
+ }
1099
+ return kinds;
1100
+ }
1101
+ function tokenizeSearchQuery(query) {
1102
+ return query.trim().toLocaleLowerCase().split(/[\s/]+/).filter(Boolean);
1103
+ }
1104
+ function pathMatchesSearchTokens(relativePath, tokens) {
1105
+ const low = relativePath.toLocaleLowerCase();
1106
+ return tokens.every((token) => low.includes(token));
1107
+ }
1066
1108
  function compareFileSearchItem(left, right, query) {
1067
1109
  const lowQuery = query.trim().toLocaleLowerCase();
1068
1110
  const leftName = left.relativePath.split("/").at(-1)?.toLocaleLowerCase() ?? "";
@@ -18080,7 +18080,7 @@ var benchmarks_default = {
18080
18080
  };
18081
18081
  var models_default = {
18082
18082
  version: 1,
18083
- updatedAt: "2026-08-20T00:00:00Z",
18083
+ updatedAt: "2026-08-21T00:00:00Z",
18084
18084
  providers: {
18085
18085
  codex: {
18086
18086
  name: "Codex",
@@ -18797,7 +18797,7 @@ var models_default = {
18797
18797
  opencode: {
18798
18798
  name: "OpenCode",
18799
18799
  defaultModel: "minimax-m3",
18800
- source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only",
18800
+ source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only; ox-alpha-free and muse-spark-1.2-contributor added 2026-08-21 from the same live list, each wire chosen by a streaming probe on that date (ox-alpha-free frames natively on /chat/completions and returns reasoning_content deltas; muse-spark-1.2-contributor frames natively on /responses and echoes its own reasoning block) and each effort ladder taken from the upstream's own rejection message (ox-alpha-free: low/high/max; muse-spark-1.2-contributor: none/minimal/low/medium/high/xhigh, so max is refused); their contextWindow comes from models.dev api.json opencode-go entries (2026-08-21). muse-spark-1.2-contributor's Responses backend accepts only tool_choice auto: named function choices, required, and none are refused with a 400 (2026-08-21), so a caller that forces a tool cannot use it",
18801
18801
  models: [
18802
18802
  {
18803
18803
  modelId: "minimax-m3",
@@ -18845,6 +18845,22 @@ var models_default = {
18845
18845
  },
18846
18846
  benchmarkKey: "grok-4.5"
18847
18847
  },
18848
+ {
18849
+ modelId: "muse-spark-1.2-contributor",
18850
+ name: "Muse-Spark-1.2-Contributor",
18851
+ capabilityClass: "flagship",
18852
+ wire: "responses",
18853
+ contextWindow: 1048576,
18854
+ effort: {
18855
+ supported: true,
18856
+ levels: [
18857
+ "low",
18858
+ "medium",
18859
+ "high",
18860
+ "xhigh"
18861
+ ]
18862
+ }
18863
+ },
18848
18864
  {
18849
18865
  modelId: "deepseek-v4-flash",
18850
18866
  name: "DeepSeek-V4-Flash",
@@ -18895,6 +18911,21 @@ var models_default = {
18895
18911
  capabilityClass: "flagship",
18896
18912
  wire: "chat-completions",
18897
18913
  contextWindow: 256e3
18914
+ },
18915
+ {
18916
+ modelId: "ox-alpha-free",
18917
+ name: "Ox-Alpha-Free",
18918
+ capabilityClass: "flagship",
18919
+ wire: "chat-completions",
18920
+ contextWindow: 1e6,
18921
+ effort: {
18922
+ supported: true,
18923
+ levels: [
18924
+ "low",
18925
+ "high",
18926
+ "max"
18927
+ ]
18928
+ }
18898
18929
  }
18899
18930
  ]
18900
18931
  },
@@ -18032,7 +18032,7 @@ var benchmarks_default = {
18032
18032
  };
18033
18033
  var models_default = {
18034
18034
  version: 1,
18035
- updatedAt: "2026-08-20T00:00:00Z",
18035
+ updatedAt: "2026-08-21T00:00:00Z",
18036
18036
  providers: {
18037
18037
  codex: {
18038
18038
  name: "Codex",
@@ -18749,7 +18749,7 @@ var models_default = {
18749
18749
  opencode: {
18750
18750
  name: "OpenCode",
18751
18751
  defaultModel: "minimax-m3",
18752
- source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only",
18752
+ source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only; ox-alpha-free and muse-spark-1.2-contributor added 2026-08-21 from the same live list, each wire chosen by a streaming probe on that date (ox-alpha-free frames natively on /chat/completions and returns reasoning_content deltas; muse-spark-1.2-contributor frames natively on /responses and echoes its own reasoning block) and each effort ladder taken from the upstream's own rejection message (ox-alpha-free: low/high/max; muse-spark-1.2-contributor: none/minimal/low/medium/high/xhigh, so max is refused); their contextWindow comes from models.dev api.json opencode-go entries (2026-08-21). muse-spark-1.2-contributor's Responses backend accepts only tool_choice auto: named function choices, required, and none are refused with a 400 (2026-08-21), so a caller that forces a tool cannot use it",
18753
18753
  models: [
18754
18754
  {
18755
18755
  modelId: "minimax-m3",
@@ -18797,6 +18797,22 @@ var models_default = {
18797
18797
  },
18798
18798
  benchmarkKey: "grok-4.5"
18799
18799
  },
18800
+ {
18801
+ modelId: "muse-spark-1.2-contributor",
18802
+ name: "Muse-Spark-1.2-Contributor",
18803
+ capabilityClass: "flagship",
18804
+ wire: "responses",
18805
+ contextWindow: 1048576,
18806
+ effort: {
18807
+ supported: true,
18808
+ levels: [
18809
+ "low",
18810
+ "medium",
18811
+ "high",
18812
+ "xhigh"
18813
+ ]
18814
+ }
18815
+ },
18800
18816
  {
18801
18817
  modelId: "deepseek-v4-flash",
18802
18818
  name: "DeepSeek-V4-Flash",
@@ -18847,6 +18863,21 @@ var models_default = {
18847
18863
  capabilityClass: "flagship",
18848
18864
  wire: "chat-completions",
18849
18865
  contextWindow: 256e3
18866
+ },
18867
+ {
18868
+ modelId: "ox-alpha-free",
18869
+ name: "Ox-Alpha-Free",
18870
+ capabilityClass: "flagship",
18871
+ wire: "chat-completions",
18872
+ contextWindow: 1e6,
18873
+ effort: {
18874
+ supported: true,
18875
+ levels: [
18876
+ "low",
18877
+ "high",
18878
+ "max"
18879
+ ]
18880
+ }
18850
18881
  }
18851
18882
  ]
18852
18883
  },
@@ -1671,6 +1671,37 @@ function writeRemoteFailure(res, ctx, error, classify) {
1671
1671
  }
1672
1672
  throw error;
1673
1673
  }
1674
+ async function countCommits(gitCwd, range) {
1675
+ try {
1676
+ const raw = (await runGit(["rev-list", "--count", range], { cwd: gitCwd })).stdout.trim();
1677
+ const count = Number.parseInt(raw, 10);
1678
+ return Number.isInteger(count) && count >= 0 ? count : null;
1679
+ } catch {
1680
+ return null;
1681
+ }
1682
+ }
1683
+ function parsePushOutcome(stdout, branch) {
1684
+ for (const line of stdout.split("\n")) {
1685
+ const fields = line.split(" ");
1686
+ if (fields.length < 3) continue;
1687
+ const [, refPair, summary] = fields;
1688
+ if (!refPair || !summary) continue;
1689
+ if (refPair.split(":")[1] !== `refs/heads/${branch}`) continue;
1690
+ const trimmed = summary.trim();
1691
+ const moved = /^([0-9a-f]{7,40})\.\.([0-9a-f]{7,40})$/.exec(trimmed);
1692
+ if (moved) return { kind: "moved", from: moved[1], to: moved[2] };
1693
+ return trimmed === "[up to date]" ? { kind: "up_to_date" } : null;
1694
+ }
1695
+ return null;
1696
+ }
1697
+ async function headSha(gitCwd) {
1698
+ try {
1699
+ const sha = (await runGit(["rev-parse", "HEAD"], { cwd: gitCwd })).stdout.trim();
1700
+ return /^[0-9a-f]{40}$/.test(sha) ? sha : null;
1701
+ } catch {
1702
+ return null;
1703
+ }
1704
+ }
1674
1705
  async function handleRepositoryPush(req, res, ctx) {
1675
1706
  const remoteCtx = await resolveRemoteContext(req, res, ctx);
1676
1707
  if (!remoteCtx) return;
@@ -1680,10 +1711,11 @@ async function handleRepositoryPush(req, res, ctx) {
1680
1711
  const remote = upstreamRemote && upstreamRemote !== "." ? upstreamRemote : await resolveDefaultRemote(gitCwd);
1681
1712
  if (!remote) throw new NoRemoteError();
1682
1713
  const credentialArgs = await resolveCredentialHelperArgs(gitCwd);
1683
- await runGit([
1714
+ const pushed = await runGit([
1684
1715
  ...NETWORK_HARDENING_ARGS,
1685
1716
  ...credentialArgs,
1686
1717
  "push",
1718
+ "--porcelain",
1687
1719
  // repo config의 remote.<name>.receivepack이 로컬 transport에서 명령 실행되므로 표준 명령을 강제한다.
1688
1720
  "--receive-pack=git-receive-pack",
1689
1721
  "--no-recurse-submodules",
@@ -1691,7 +1723,9 @@ async function handleRepositoryPush(req, res, ctx) {
1691
1723
  remote,
1692
1724
  `refs/heads/${branch}:refs/heads/${branch}`
1693
1725
  ], { cwd: gitCwd, timeoutMs: REMOTE_TIMEOUT_MS });
1694
- ctx.host.http.writeJson(res, 200, { ok: true, remote, branch });
1726
+ const outcome = parsePushOutcome(pushed.stdout, branch);
1727
+ const sent = outcome === null ? null : outcome.kind === "up_to_date" ? 0 : await countCommits(gitCwd, `${outcome.from}..${outcome.to}`);
1728
+ ctx.host.http.writeJson(res, 200, { ok: true, remote, branch, ...sent === null ? {} : { sent } });
1695
1729
  } catch (error) {
1696
1730
  writeRemoteFailure(res, ctx, error, classifyPushError);
1697
1731
  }
@@ -1707,6 +1741,7 @@ async function handleRepositoryPull(req, res, ctx) {
1707
1741
  return;
1708
1742
  }
1709
1743
  const credentialArgs = await resolveCredentialHelperArgs(gitCwd);
1744
+ const before = await headSha(gitCwd);
1710
1745
  await runGit([
1711
1746
  ...NETWORK_HARDENING_ARGS,
1712
1747
  ...credentialArgs,
@@ -1716,7 +1751,8 @@ async function handleRepositoryPull(req, res, ctx) {
1716
1751
  "--upload-pack=git-upload-pack",
1717
1752
  "--no-recurse-submodules"
1718
1753
  ], { cwd: gitCwd, timeoutMs: REMOTE_TIMEOUT_MS });
1719
- ctx.host.http.writeJson(res, 200, { ok: true, remote: upstreamRemote, branch });
1754
+ const received = before === null ? null : await countCommits(gitCwd, `${before}..HEAD`);
1755
+ ctx.host.http.writeJson(res, 200, { ok: true, remote: upstreamRemote, branch, ...received === null ? {} : { received } });
1720
1756
  } catch (error) {
1721
1757
  writeRemoteFailure(res, ctx, error, classifyPullError);
1722
1758
  }
@@ -2435,7 +2471,7 @@ async function handleRepositoryWorkstate(req, res, ctx) {
2435
2471
  ]);
2436
2472
  const gitDir = path3.resolve(gitCwd, gitDirResult.stdout.trim());
2437
2473
  const headBranch = headResult.stdout.trim() || null;
2438
- const headSha = headShaResult.stdout.trim() || null;
2474
+ const headSha2 = headShaResult.stdout.trim() || null;
2439
2475
  const [indexLock, merge, rebaseMerge, rebaseApply, cherryPick] = await Promise.all([
2440
2476
  fileExists(path3.join(gitDir, "index.lock")),
2441
2477
  fileExists(path3.join(gitDir, "MERGE_HEAD")),
@@ -2476,7 +2512,7 @@ async function handleRepositoryWorkstate(req, res, ctx) {
2476
2512
  }
2477
2513
  if (isPathContained(realGitCwd, realCwd)) stationedOperations.push({ id: operation.id, title: operation.title });
2478
2514
  }
2479
- const payload = { indexLock, inProgress, headBranch, headSha, upstream, ahead, behind, stationedOperations };
2515
+ const payload = { indexLock, inProgress, headBranch, headSha: headSha2, upstream, ahead, behind, stationedOperations };
2480
2516
  ctx.host.http.writeJson(res, 200, payload);
2481
2517
  } catch (error) {
2482
2518
  if (error instanceof GitExecutorError) {
@@ -17709,7 +17709,7 @@ var benchmarks_default = {
17709
17709
  };
17710
17710
  var models_default = {
17711
17711
  version: 1,
17712
- updatedAt: "2026-08-20T00:00:00Z",
17712
+ updatedAt: "2026-08-21T00:00:00Z",
17713
17713
  providers: {
17714
17714
  codex: {
17715
17715
  name: "Codex",
@@ -18426,7 +18426,7 @@ var models_default = {
18426
18426
  opencode: {
18427
18427
  name: "OpenCode",
18428
18428
  defaultModel: "minimax-m3",
18429
- source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only",
18429
+ source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only; ox-alpha-free and muse-spark-1.2-contributor added 2026-08-21 from the same live list, each wire chosen by a streaming probe on that date (ox-alpha-free frames natively on /chat/completions and returns reasoning_content deltas; muse-spark-1.2-contributor frames natively on /responses and echoes its own reasoning block) and each effort ladder taken from the upstream's own rejection message (ox-alpha-free: low/high/max; muse-spark-1.2-contributor: none/minimal/low/medium/high/xhigh, so max is refused); their contextWindow comes from models.dev api.json opencode-go entries (2026-08-21). muse-spark-1.2-contributor's Responses backend accepts only tool_choice auto: named function choices, required, and none are refused with a 400 (2026-08-21), so a caller that forces a tool cannot use it",
18430
18430
  models: [
18431
18431
  {
18432
18432
  modelId: "minimax-m3",
@@ -18474,6 +18474,22 @@ var models_default = {
18474
18474
  },
18475
18475
  benchmarkKey: "grok-4.5"
18476
18476
  },
18477
+ {
18478
+ modelId: "muse-spark-1.2-contributor",
18479
+ name: "Muse-Spark-1.2-Contributor",
18480
+ capabilityClass: "flagship",
18481
+ wire: "responses",
18482
+ contextWindow: 1048576,
18483
+ effort: {
18484
+ supported: true,
18485
+ levels: [
18486
+ "low",
18487
+ "medium",
18488
+ "high",
18489
+ "xhigh"
18490
+ ]
18491
+ }
18492
+ },
18477
18493
  {
18478
18494
  modelId: "deepseek-v4-flash",
18479
18495
  name: "DeepSeek-V4-Flash",
@@ -18524,6 +18540,21 @@ var models_default = {
18524
18540
  capabilityClass: "flagship",
18525
18541
  wire: "chat-completions",
18526
18542
  contextWindow: 256e3
18543
+ },
18544
+ {
18545
+ modelId: "ox-alpha-free",
18546
+ name: "Ox-Alpha-Free",
18547
+ capabilityClass: "flagship",
18548
+ wire: "chat-completions",
18549
+ contextWindow: 1e6,
18550
+ effort: {
18551
+ supported: true,
18552
+ levels: [
18553
+ "low",
18554
+ "high",
18555
+ "max"
18556
+ ]
18557
+ }
18527
18558
  }
18528
18559
  ]
18529
18560
  },
@@ -20028,6 +20059,7 @@ function claudeGatewayLaunchEnv(inherited, options) {
20028
20059
  env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
20029
20060
  env.ENABLE_TOOL_SEARCH = "true";
20030
20061
  env.CLAUDE_CODE_AUTO_COMPACT_WINDOW ??= "1000000";
20062
+ env.CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH ??= "1";
20031
20063
  delete env.ANTHROPIC_AUTH_TOKEN;
20032
20064
  delete env.ANTHROPIC_API_KEY;
20033
20065
  delete env.ANTHROPIC_MODEL;