@dotobokuri/fleet-console 1.29.0 → 1.30.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 (65) hide show
  1. package/dist/cli.mjs +82 -63
  2. package/dist/client/assets/{_baseUniq-DEvADUqz.js → _baseUniq-Dvbxpxj-.js} +1 -1
  3. package/dist/client/assets/{arc-pB9oYw5d.js → arc-Cqerxs8I.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-D0gUBsh1.js → architectureDiagram-Q4EWVU46-gNhmCKnU.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-9NSt9xrC.js → blockDiagram-DXYQGD6D-CM5HRpKC.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-BS1EhMAN.js → c4Diagram-AHTNJAMY-DxLhQuvg.js} +1 -1
  7. package/dist/client/assets/channel-DKkQpLWI.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-BKkeF7mF.js → chunk-4BX2VUAB-HX6C_GAC.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-DRp3Dsap.js → chunk-4TB4RGXK-CoBQUhS4.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-De-sLT-H.js → chunk-55IACEB6-DWsWxGBm.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-ByYWvw6c.js → chunk-EDXVE4YY-BXNRu1M4.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-BDpIWnNm.js → chunk-FMBD7UC4-DzKgP1_A.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-qxbtvneQ.js → chunk-OYMX7WX6-aqXdvKUD.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-BnkLN840.js → chunk-QZHKN3VN-BbMqaUb0.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-HfizEXdz.js → chunk-YZCP3GAM-D8Mo0nv8.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-CQurO5ix.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CQurO5ix.js +1 -0
  18. package/dist/client/assets/clone-IodjsAGU.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-B4U_tiL9.js → cose-bilkent-S5V4N54A-BOuVjjXz.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-De3dSv-R.js → dagre-KV5264BT-BdUFzAj_.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-CZAF0Kro.js → diagram-5BDNPKRD-C0iFrsGo.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-BHyDlHAq.js → diagram-G4DWMVQ6-CGxGeQZt.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-CV93x0hu.js → diagram-MMDJMWI5-Dex5uqlA.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-CTRzVEQp.js → diagram-TYMM5635-O48PEpaS.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-xW-UvBlm.js → erDiagram-SMLLAGMA-B7PGw3YA.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-C21IECUy.js → flowDiagram-DWJPFMVM-CCXWJIMw.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-DVzOf8Xv.js → ganttDiagram-T4ZO3ILL-B26RghpR.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-B-NQC3Ae.js → gitGraphDiagram-UUTBAWPF-BJsjMn6A.js} +1 -1
  29. package/dist/client/assets/{graph-BDzdWbuy.js → graph-DuEL0RWH.js} +1 -1
  30. package/dist/client/assets/index-Dj775lUe.css +1 -0
  31. package/dist/client/assets/index-r6rDZQCs.js +451 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-2KCXFMZg.js → infoDiagram-42DDH7IO-D32dz1SM.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-D4Fvy3ms.js → ishikawaDiagram-UXIWVN3A-4YPQjln0.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-DkXZkeED.js → journeyDiagram-VCZTEJTY-BSvi22HM.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-CTrIp4rv.js → kanban-definition-6JOO6SKY-SozOXS7m.js} +1 -1
  36. package/dist/client/assets/{layout-DKdT-A2u.js → layout-CNXntIRV.js} +1 -1
  37. package/dist/client/assets/{linear-Dn21C3ab.js → linear-9RZPFtO4.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-ho7gOyUr.js → mermaid.core-kwH-UxKY.js} +4 -4
  39. package/dist/client/assets/{min-DV-zxfLU.js → min-BdN2p8Q_.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-By8nfvJ0.js → mindmap-definition-QFDTVHPH-H-3dJmH9.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-B4ndzH1J.js → pieDiagram-DEJITSTG-CspzYvcs.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CiDb6b3-.js → quadrantDiagram-34T5L4WZ-D1C7e3-s.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-TTr1SzvX.js → requirementDiagram-MS252O5E-BJwKRtMi.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-CUyxk-WA.js → sankeyDiagram-XADWPNL6-7oc42Z3P.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-CyBxBNAZ.js → sequenceDiagram-FGHM5R23-TP5H_DnF.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-Cj4j5FFK.js → stateDiagram-FHFEXIEX-BWnrNgJY.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Cmp8tobq.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-MQzKGbxg.js → timeline-definition-GMOUNBTQ-xXFvCsrr.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-sG3K7NAZ.js → vennDiagram-DHZGUBPP-D5unm_iH.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-ID5rQjqH.js → wardley-RL74JXVD-B-eT02rs.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-AMajCCI5.js → wardleyDiagram-NUSXRM2D-BTQ1Yqmb.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-DUb36W9E.js → xychartDiagram-5P7HB3ND-Bialvg0l.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/file-explorer/routes.mjs +199 -38
  55. package/dist/fleet-plugins/repository/routes.mjs +405 -31
  56. package/dist/fleet-plugins/skills/routes.mjs +9 -6
  57. package/dist/fleet-plugins/terminal/routes.mjs +226 -88
  58. package/package.json +1 -1
  59. package/dist/client/assets/channel-BSgCfWXO.js +0 -1
  60. package/dist/client/assets/classDiagram-6PBFFD2Q-C3WhxxfZ.js +0 -1
  61. package/dist/client/assets/classDiagram-v2-HSJHXN6E-C3WhxxfZ.js +0 -1
  62. package/dist/client/assets/clone-DjOK69th.js +0 -1
  63. package/dist/client/assets/index-BDJVPQd1.css +0 -1
  64. package/dist/client/assets/index-CjBu8lNT.js +0 -451
  65. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CWhJmDZj.js +0 -1
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from 'child_process';
3
3
  import 'fs';
4
- import path2 from 'path';
5
- import fs from 'fs/promises';
4
+ import path4 from 'path';
5
+ import fs2 from 'fs/promises';
6
6
 
7
7
  // sdk/plugin/node.ts
8
8
  function definePlugin(definition) {
9
9
  return definition;
10
10
  }
11
- function registerRouter(ctx, path4, handler) {
12
- ctx.registerRouter(normalizePath(path4), handler);
11
+ function registerRouter(ctx, path8, handler) {
12
+ ctx.registerRouter(normalizePath(path8), handler);
13
13
  }
14
- function normalizePath(path4) {
15
- if (path4.length === 0 || path4 === "/") return "/";
16
- return path4;
14
+ function normalizePath(path8) {
15
+ if (path8.length === 0 || path8 === "/") return "/";
16
+ return path8;
17
17
  }
18
18
 
19
19
  // ../../packages/core-process/src/hidden.ts
@@ -97,6 +97,72 @@ function runGit(args, opts) {
97
97
  });
98
98
  });
99
99
  }
100
+ function isPathContained(root, candidate) {
101
+ const relative = path4.relative(root, candidate);
102
+ return relative === "" || !path4.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path4.sep}`);
103
+ }
104
+ function isSelectableRepoRel(repoRel) {
105
+ if (repoRel === "") return true;
106
+ if (path4.isAbsolute(repoRel)) return false;
107
+ const normalized = path4.normalize(repoRel);
108
+ return !normalized.startsWith("-") && normalized !== ".." && !normalized.startsWith(`..${path4.sep}`);
109
+ }
110
+
111
+ // ../fleet-plugins/repository/server/git-marker.ts
112
+ var GITDIR_PREFIX = "gitdir:";
113
+ async function resolveContainedGitDir(repoDir, realTheaterPath) {
114
+ const markerPath = path4.join(repoDir, ".git");
115
+ let marker;
116
+ try {
117
+ marker = await fs2.stat(markerPath);
118
+ } catch {
119
+ return null;
120
+ }
121
+ let gitDir;
122
+ if (marker.isFile()) {
123
+ let content;
124
+ try {
125
+ content = await fs2.readFile(markerPath, "utf8");
126
+ } catch {
127
+ return null;
128
+ }
129
+ const line = content.split("\n").find((entry) => entry.trim().startsWith(GITDIR_PREFIX));
130
+ if (!line) return null;
131
+ const target = line.trim().slice(GITDIR_PREFIX.length).trim();
132
+ if (!target) return null;
133
+ gitDir = path4.resolve(repoDir, target);
134
+ } else if (marker.isDirectory()) {
135
+ gitDir = markerPath;
136
+ } else {
137
+ return null;
138
+ }
139
+ let realGitDir;
140
+ try {
141
+ realGitDir = await fs2.realpath(gitDir);
142
+ } catch {
143
+ return null;
144
+ }
145
+ if (!isPathContained(realTheaterPath, realGitDir)) return null;
146
+ let commonRaw = null;
147
+ try {
148
+ commonRaw = await fs2.readFile(path4.join(realGitDir, "commondir"), "utf8");
149
+ } catch {
150
+ }
151
+ if (commonRaw !== null) {
152
+ const commonTarget = commonRaw.trim();
153
+ if (!commonTarget) return null;
154
+ let realCommonDir;
155
+ try {
156
+ realCommonDir = await fs2.realpath(path4.resolve(realGitDir, commonTarget));
157
+ } catch {
158
+ return null;
159
+ }
160
+ if (!isPathContained(realTheaterPath, realCommonDir)) return null;
161
+ }
162
+ return realGitDir;
163
+ }
164
+
165
+ // ../fleet-plugins/repository/server/diff.ts
100
166
  function isPlainObject(value) {
101
167
  return typeof value === "object" && value !== null && !Array.isArray(value);
102
168
  }
@@ -154,8 +220,29 @@ function isNoHeadError(error) {
154
220
  if (error.code !== "non_zero_exit") return false;
155
221
  return error.stderr.includes("unknown revision") || error.stderr.includes("bad revision");
156
222
  }
157
- function resolveGitCwd(theaterPath) {
158
- return { gitCwd: theaterPath };
223
+ var InvalidRepoError = class extends Error {
224
+ code = "invalid_repo";
225
+ };
226
+ async function resolveGitCwd(theaterPath, repoRel = "") {
227
+ if (repoRel === void 0 || repoRel === "") return { gitCwd: theaterPath };
228
+ if (typeof repoRel !== "string" || path4.isAbsolute(repoRel)) throw new InvalidRepoError("Repository path must be relative");
229
+ if (!isSelectableRepoRel(repoRel)) throw new InvalidRepoError("Repository path is invalid");
230
+ const resolved = path4.resolve(theaterPath, path4.normalize(repoRel));
231
+ if (!isPathContained(theaterPath, resolved)) throw new InvalidRepoError("Repository path escapes Theater");
232
+ let realTheater;
233
+ let realRepo;
234
+ try {
235
+ [realTheater, realRepo] = await Promise.all([fs2.realpath(theaterPath), fs2.realpath(resolved)]);
236
+ } catch {
237
+ throw new InvalidRepoError("Repository path does not exist");
238
+ }
239
+ if (!isPathContained(realTheater, realRepo)) {
240
+ throw new InvalidRepoError("Repository path escapes Theater");
241
+ }
242
+ if (await resolveContainedGitDir(realRepo, realTheater) === null) {
243
+ throw new InvalidRepoError("Repository marker does not exist");
244
+ }
245
+ return { gitCwd: realRepo };
159
246
  }
160
247
  async function handleRepositoryChanged(req, res, ctx) {
161
248
  if (req.method !== "POST") {
@@ -181,7 +268,16 @@ async function handleRepositoryChanged(req, res, ctx) {
181
268
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
182
269
  return;
183
270
  }
184
- const cwdResult = resolveGitCwd(theaterPath);
271
+ let cwdResult;
272
+ try {
273
+ cwdResult = await resolveGitCwd(theaterPath, body.repoRel);
274
+ } catch (error) {
275
+ if (error instanceof InvalidRepoError) {
276
+ ctx.host.http.writeJson(res, 400, { error: error.code });
277
+ return;
278
+ }
279
+ throw error;
280
+ }
185
281
  const { gitCwd } = cwdResult;
186
282
  try {
187
283
  await ensureGitRepository(gitCwd);
@@ -250,15 +346,24 @@ async function handleRepositoryFile(req, res, ctx) {
250
346
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
251
347
  return;
252
348
  }
253
- const cwdResult = resolveGitCwd(theaterPath);
349
+ let cwdResult;
350
+ try {
351
+ cwdResult = await resolveGitCwd(theaterPath, body.repoRel);
352
+ } catch (error) {
353
+ if (error instanceof InvalidRepoError) {
354
+ ctx.host.http.writeJson(res, 400, { error: error.code });
355
+ return;
356
+ }
357
+ throw error;
358
+ }
254
359
  const { gitCwd } = cwdResult;
255
360
  const rawFilePath = body.filePath;
256
- const resolvedFilePath = path2.resolve(gitCwd, rawFilePath);
257
- if (!resolvedFilePath.startsWith(gitCwd + path2.sep) && resolvedFilePath !== gitCwd) {
361
+ const resolvedFilePath = path4.resolve(gitCwd, rawFilePath);
362
+ if (!resolvedFilePath.startsWith(gitCwd + path4.sep) && resolvedFilePath !== gitCwd) {
258
363
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
259
364
  return;
260
365
  }
261
- const relativePath = path2.normalize(rawFilePath);
366
+ const relativePath = path4.normalize(rawFilePath);
262
367
  if (relativePath.startsWith("..")) {
263
368
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
264
369
  return;
@@ -269,14 +374,14 @@ async function handleRepositoryFile(req, res, ctx) {
269
374
  let realFile;
270
375
  try {
271
376
  [realGitCwd, realFile] = await Promise.all([
272
- fs.realpath(gitCwd),
273
- fs.realpath(resolvedFilePath)
377
+ fs2.realpath(gitCwd),
378
+ fs2.realpath(resolvedFilePath)
274
379
  ]);
275
380
  } catch {
276
381
  ctx.host.http.writeJson(res, 404, { error: "file_not_found" });
277
382
  return;
278
383
  }
279
- if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path2.sep)) {
384
+ if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path4.sep)) {
280
385
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
281
386
  return;
282
387
  }
@@ -289,10 +394,10 @@ async function handleRepositoryFile(req, res, ctx) {
289
394
  }
290
395
  try {
291
396
  const [realGitCwd, realFile] = await Promise.all([
292
- fs.realpath(gitCwd),
293
- fs.realpath(resolvedFilePath)
397
+ fs2.realpath(gitCwd),
398
+ fs2.realpath(resolvedFilePath)
294
399
  ]);
295
- if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path2.sep)) {
400
+ if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path4.sep)) {
296
401
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
297
402
  return;
298
403
  }
@@ -369,7 +474,16 @@ async function handleRepositoryCommit(req, res, ctx) {
369
474
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
370
475
  return;
371
476
  }
372
- const cwdResult = resolveGitCwd(theaterPath);
477
+ let cwdResult;
478
+ try {
479
+ cwdResult = await resolveGitCwd(theaterPath, body.repoRel);
480
+ } catch (error) {
481
+ if (error instanceof InvalidRepoError) {
482
+ ctx.host.http.writeJson(res, 400, { error: error.code });
483
+ return;
484
+ }
485
+ throw error;
486
+ }
373
487
  const { gitCwd } = cwdResult;
374
488
  try {
375
489
  const [metaResult, nameStatusResult, numstatResult] = await Promise.all([
@@ -403,9 +517,9 @@ function isPlainObject3(value) {
403
517
  return typeof value === "object" && value !== null && !Array.isArray(value);
404
518
  }
405
519
  function resolveLiteralFilePath(gitCwd, rawPath) {
406
- const relativePath = path2.normalize(rawPath);
407
- const resolved = path2.resolve(gitCwd, relativePath);
408
- if (relativePath === ".." || relativePath.startsWith(".." + path2.sep) || !resolved.startsWith(gitCwd + path2.sep)) return null;
520
+ const relativePath = path4.normalize(rawPath);
521
+ const resolved = path4.resolve(gitCwd, relativePath);
522
+ if (relativePath === ".." || relativePath.startsWith(".." + path4.sep) || !resolved.startsWith(gitCwd + path4.sep)) return null;
409
523
  return relativePath;
410
524
  }
411
525
  function literalPathspec2(relativePath) {
@@ -442,7 +556,16 @@ async function handleRepositoryCommitFile(req, res, ctx) {
442
556
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
443
557
  return;
444
558
  }
445
- const cwdResult = resolveGitCwd(theaterPath);
559
+ let cwdResult;
560
+ try {
561
+ cwdResult = await resolveGitCwd(theaterPath, body.repoRel);
562
+ } catch (error) {
563
+ if (error instanceof InvalidRepoError) {
564
+ ctx.host.http.writeJson(res, 400, { error: error.code });
565
+ return;
566
+ }
567
+ throw error;
568
+ }
446
569
  const relativePath = resolveLiteralFilePath(cwdResult.gitCwd, body.filePath);
447
570
  const oldPath = typeof body.oldPath === "string" ? resolveLiteralFilePath(cwdResult.gitCwd, body.oldPath) : void 0;
448
571
  if (!relativePath || body.oldPath !== void 0 && !oldPath) {
@@ -507,7 +630,7 @@ function parseLogOutput(stdout) {
507
630
  }
508
631
  return commits;
509
632
  }
510
- async function parseWorktreePorcelain(stdout, currentWorktreePath) {
633
+ function parseWorktreePorcelainEntries(stdout) {
511
634
  const worktrees = [];
512
635
  let worktreePath = null;
513
636
  let sha = "";
@@ -538,10 +661,14 @@ async function parseWorktreePorcelain(stdout, currentWorktreePath) {
538
661
  }
539
662
  }
540
663
  pushCurrent();
664
+ return worktrees;
665
+ }
666
+ async function parseWorktreePorcelain(stdout, currentWorktreePath) {
667
+ const worktrees = parseWorktreePorcelainEntries(stdout);
541
668
  const normalizedCurrentWorktreePath = await normalizeWorktreePath(currentWorktreePath);
542
- return Promise.all(worktrees.map(async ({ worktreePath: worktreePath2, ...checkout }) => ({
669
+ return Promise.all(worktrees.map(async ({ worktreePath, ...checkout }) => ({
543
670
  ...checkout,
544
- isCurrent: normalizedCurrentWorktreePath !== "" && await normalizeWorktreePath(worktreePath2) === normalizedCurrentWorktreePath
671
+ isCurrent: normalizedCurrentWorktreePath !== "" && await normalizeWorktreePath(worktreePath) === normalizedCurrentWorktreePath
545
672
  })));
546
673
  }
547
674
  function annotateHeadReachability(commits, revListStdout) {
@@ -557,7 +684,7 @@ function isNoHeadError2(error) {
557
684
  async function normalizeWorktreePath(worktreePath) {
558
685
  if (!worktreePath) return "";
559
686
  try {
560
- return await fs.realpath(worktreePath);
687
+ return await fs2.realpath(worktreePath);
561
688
  } catch {
562
689
  return worktreePath;
563
690
  }
@@ -602,7 +729,16 @@ async function handleRepositoryLog(req, res, ctx) {
602
729
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
603
730
  return;
604
731
  }
605
- const cwdResult = resolveGitCwd(theaterPath);
732
+ let cwdResult;
733
+ try {
734
+ cwdResult = await resolveGitCwd(theaterPath, body.repoRel);
735
+ } catch (error) {
736
+ if (error instanceof InvalidRepoError) {
737
+ ctx.host.http.writeJson(res, 400, { error: error.code });
738
+ return;
739
+ }
740
+ throw error;
741
+ }
606
742
  const { gitCwd } = cwdResult;
607
743
  const requestedRef = body.ref;
608
744
  if (requestedRef !== void 0 && (typeof requestedRef !== "string" || !isCanonicalRepositoryRef(requestedRef))) {
@@ -691,7 +827,16 @@ async function handleRepositoryRefs(req, res, ctx) {
691
827
  ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
692
828
  return;
693
829
  }
694
- const resolved = resolveGitCwd(theaterPath);
830
+ let resolved;
831
+ try {
832
+ resolved = await resolveGitCwd(theaterPath, body.repoRel);
833
+ } catch (error) {
834
+ if (error instanceof InvalidRepoError) {
835
+ ctx.host.http.writeJson(res, 400, { error: error.code });
836
+ return;
837
+ }
838
+ throw error;
839
+ }
695
840
  try {
696
841
  const [head, local, remote, tags, stashes] = await Promise.all([
697
842
  runGit(["symbolic-ref", "--quiet", "HEAD"], { cwd: resolved.gitCwd, allowExitCodes: [1] }),
@@ -722,11 +867,240 @@ async function handleRepositoryRefs(req, res, ctx) {
722
867
  ctx.host.http.writeJson(res, 500, { error: "git_failed" });
723
868
  }
724
869
  }
870
+ var REPOS_CAP = 200;
871
+ var HARD_CAP_DEPTH = 8;
872
+ var NESTED_BRANCH_CAP = 64;
873
+ var SCAN_VISIT_BUDGET = 1e4;
874
+ var DEFAULT_DEPTH = 3;
875
+ function isPlainObject5(value) {
876
+ return typeof value === "object" && value !== null && !Array.isArray(value);
877
+ }
878
+ async function resolveRepoBranch(repoDir) {
879
+ try {
880
+ const result = await runGit(["status", "--porcelain=v2", "--branch", "--untracked-files=no"], { cwd: repoDir });
881
+ const branch = result.stdout.split("\n").find((line) => line.startsWith("# branch.head "))?.slice(14).trim() ?? "";
882
+ const oid = result.stdout.split("\n").find((line) => line.startsWith("# branch.oid "))?.slice(13).trim() ?? "";
883
+ return branch === "(detached)" ? oid.slice(0, 7) : branch;
884
+ } catch {
885
+ return "";
886
+ }
887
+ }
888
+ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDepth, repos, cap = REPOS_CAP, visits = { remaining: SCAN_VISIT_BUDGET }) {
889
+ if (repos.length >= cap) return true;
890
+ if (visits.remaining <= 0) return true;
891
+ visits.remaining -= 1;
892
+ const relPath = path4.relative(realTheaterPath, dir);
893
+ const gitDir = isSelectableRepoRel(relPath) ? await resolveContainedGitDir(dir, realTheaterPath) : null;
894
+ let linkedWorktree = false;
895
+ if (gitDir !== null) {
896
+ try {
897
+ await fs2.access(path4.join(gitDir, "commondir"));
898
+ linkedWorktree = true;
899
+ } catch {
900
+ }
901
+ }
902
+ if (gitDir !== null && !linkedWorktree) {
903
+ repos.push({ relPath, name: relPath === "" ? path4.basename(realTheaterPath) : path4.basename(dir), repoDir: dir });
904
+ if (repos.length >= cap) return true;
905
+ }
906
+ if (currentDepth >= maxDepth) return false;
907
+ let dirHandle = null;
908
+ try {
909
+ dirHandle = await fs2.opendir(dir);
910
+ let dirent = await dirHandle.read();
911
+ while (dirent !== null) {
912
+ if (repos.length >= cap || visits.remaining <= 0) return true;
913
+ if (dirent.isDirectory() && dirent.name !== "node_modules" && dirent.name !== ".git") {
914
+ const childPath = path4.join(dir, dirent.name);
915
+ let realChildPath;
916
+ try {
917
+ realChildPath = await fs2.realpath(childPath);
918
+ } catch {
919
+ dirent = await dirHandle.read();
920
+ continue;
921
+ }
922
+ if (isPathContained(realTheaterPath, realChildPath)) {
923
+ const truncated = await scanRepos(realTheaterPath, realTheaterPath, realChildPath, currentDepth + 1, maxDepth, repos, cap, visits);
924
+ if (truncated) return true;
925
+ }
926
+ }
927
+ dirent = await dirHandle.read();
928
+ }
929
+ return false;
930
+ } catch {
931
+ return false;
932
+ } finally {
933
+ if (dirHandle) {
934
+ try {
935
+ await dirHandle.close();
936
+ } catch {
937
+ }
938
+ }
939
+ }
940
+ }
941
+ async function resolveNestedRepoCandidates(candidates, availableSlots, resolver = resolveRepoBranch) {
942
+ const resolved = [];
943
+ const selected = candidates.slice(0, Math.max(0, availableSlots));
944
+ for (const [index, candidate] of selected.entries()) {
945
+ resolved.push({
946
+ relPath: candidate.relPath,
947
+ name: candidate.name,
948
+ branch: index < NESTED_BRANCH_CAP ? await resolver(candidate.repoDir) : "",
949
+ kind: "nested"
950
+ });
951
+ }
952
+ return resolved;
953
+ }
954
+ async function handleRepositoryRepos(req, res, ctx) {
955
+ if (req.method !== "POST") {
956
+ ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
957
+ return;
958
+ }
959
+ if (!ctx.host.security.isTerminalAuthorized(req)) {
960
+ ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
961
+ return;
962
+ }
963
+ const body = await ctx.host.http.readJsonBody(req);
964
+ if (!isPlainObject5(body) || typeof body.theaterId !== "string") {
965
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
966
+ return;
967
+ }
968
+ const theaterPath = ctx.host.paths.resolveTheaterPath(body.theaterId);
969
+ if (!theaterPath) {
970
+ ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
971
+ return;
972
+ }
973
+ let realTheaterPath;
974
+ try {
975
+ realTheaterPath = await fs2.realpath(theaterPath);
976
+ } catch {
977
+ ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
978
+ return;
979
+ }
980
+ const maxDepth = typeof body.maxDepth === "number" && Number.isFinite(body.maxDepth) ? Math.max(1, Math.min(HARD_CAP_DEPTH, Math.floor(body.maxDepth))) : DEFAULT_DEPTH;
981
+ const repos = [];
982
+ const seen = /* @__PURE__ */ new Set();
983
+ let truncated = false;
984
+ let rootIsRepo = await resolveContainedGitDir(realTheaterPath, realTheaterPath) !== null;
985
+ if (!rootIsRepo) {
986
+ try {
987
+ const inside = await runGit(["rev-parse", "--is-inside-work-tree"], { cwd: theaterPath });
988
+ rootIsRepo = inside.stdout.trim() === "true";
989
+ } catch {
990
+ }
991
+ }
992
+ if (rootIsRepo) {
993
+ repos.push({ relPath: "", name: path4.basename(theaterPath), branch: await resolveRepoBranch(theaterPath), kind: "root" });
994
+ seen.add("");
995
+ }
996
+ const scanned = [];
997
+ const scanCap = Math.max(0, REPOS_CAP - repos.length);
998
+ truncated = await scanRepos(realTheaterPath, realTheaterPath, realTheaterPath, 0, maxDepth, scanned, scanCap) || truncated;
999
+ const unseenNested = scanned.filter((candidate) => !seen.has(candidate.relPath));
1000
+ const remainingSlots = Math.max(0, REPOS_CAP - repos.length);
1001
+ if (unseenNested.length > remainingSlots) truncated = true;
1002
+ const nestedCandidates = await resolveNestedRepoCandidates(unseenNested, remainingSlots);
1003
+ for (const candidate of nestedCandidates) {
1004
+ if (repos.length >= REPOS_CAP) {
1005
+ truncated = true;
1006
+ break;
1007
+ }
1008
+ repos.push(candidate);
1009
+ seen.add(candidate.relPath);
1010
+ }
1011
+ const result = { repos, ...truncated ? { truncated: true } : {} };
1012
+ ctx.host.http.writeJson(res, 200, result);
1013
+ }
1014
+ function isPlainObject6(value) {
1015
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1016
+ }
1017
+ async function handleRepositoryWorktrees(req, res, ctx) {
1018
+ if (req.method !== "POST") {
1019
+ ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
1020
+ return;
1021
+ }
1022
+ if (!ctx.host.security.isTerminalAuthorized(req)) {
1023
+ ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
1024
+ return;
1025
+ }
1026
+ const body = await ctx.host.http.readJsonBody(req);
1027
+ if (!isPlainObject6(body) || "subPath" in body || typeof body.theaterId !== "string") {
1028
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1029
+ return;
1030
+ }
1031
+ const theaterPath = ctx.host.paths.resolveTheaterPath(body.theaterId);
1032
+ if (!theaterPath) {
1033
+ ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
1034
+ return;
1035
+ }
1036
+ let gitCwd;
1037
+ try {
1038
+ ({ gitCwd } = await resolveGitCwd(theaterPath, body.repoRel));
1039
+ } catch (error) {
1040
+ if (error instanceof InvalidRepoError) {
1041
+ ctx.host.http.writeJson(res, 400, { error: error.code });
1042
+ return;
1043
+ }
1044
+ throw error;
1045
+ }
1046
+ let realTheaterPath;
1047
+ let realGitCwd;
1048
+ try {
1049
+ [realTheaterPath, realGitCwd] = await Promise.all([fs2.realpath(theaterPath), fs2.realpath(gitCwd)]);
1050
+ } catch {
1051
+ ctx.host.http.writeJson(res, 400, { error: "invalid_repo" });
1052
+ return;
1053
+ }
1054
+ try {
1055
+ const result = await runGit(["worktree", "list", "--porcelain"], { cwd: gitCwd });
1056
+ const worktrees = [];
1057
+ const seen = /* @__PURE__ */ new Set();
1058
+ for (const worktree of parseWorktreePorcelainEntries(result.stdout)) {
1059
+ let realWorktree;
1060
+ try {
1061
+ realWorktree = await fs2.realpath(worktree.worktreePath);
1062
+ } catch {
1063
+ continue;
1064
+ }
1065
+ if (!isPathContained(realTheaterPath, realWorktree)) continue;
1066
+ const relPath = path4.relative(realTheaterPath, realWorktree);
1067
+ if (!isSelectableRepoRel(relPath) || seen.has(relPath)) continue;
1068
+ if (await resolveContainedGitDir(realWorktree, realTheaterPath) === null) continue;
1069
+ worktrees.push({
1070
+ relPath,
1071
+ name: path4.basename(realWorktree),
1072
+ branch: worktree.branch ?? worktree.sha.slice(0, 7),
1073
+ current: realWorktree === realGitCwd
1074
+ });
1075
+ seen.add(relPath);
1076
+ }
1077
+ const payload = { worktrees };
1078
+ ctx.host.http.writeJson(res, 200, payload);
1079
+ } catch (error) {
1080
+ if (error instanceof GitExecutorError) {
1081
+ if (error.code === "no_git_repo" || error.code === "git_unavailable") {
1082
+ ctx.host.http.writeJson(res, 422, { error: error.code });
1083
+ return;
1084
+ }
1085
+ ctx.host.http.writeJson(res, 500, { error: "git_failed" });
1086
+ return;
1087
+ }
1088
+ throw error;
1089
+ }
1090
+ }
725
1091
 
726
1092
  // ../fleet-plugins/repository/routes.ts
727
1093
  var routes_default = definePlugin({
728
1094
  id: "repository",
729
1095
  register(ctx) {
1096
+ registerRouter(ctx, "repos", async ({ req, res }) => {
1097
+ await handleRepositoryRepos(req, res, ctx);
1098
+ return true;
1099
+ });
1100
+ registerRouter(ctx, "worktrees", async ({ req, res }) => {
1101
+ await handleRepositoryWorktrees(req, res, ctx);
1102
+ return true;
1103
+ });
730
1104
  registerRouter(ctx, "changed", async ({ req, res }) => {
731
1105
  await handleRepositoryChanged(req, res, ctx);
732
1106
  return true;
@@ -16387,7 +16387,7 @@ function withHidden(options) {
16387
16387
  // ../../packages/core-unified-agent/models.json
16388
16388
  var models_default = {
16389
16389
  version: 1,
16390
- updatedAt: "2026-07-10T00:00:00Z",
16390
+ updatedAt: "2026-07-20T00:00:00Z",
16391
16391
  providers: {
16392
16392
  claude: {
16393
16393
  name: "Claude Code",
@@ -16406,15 +16406,16 @@ var models_default = {
16406
16406
  name: "Kimi via Claude Code",
16407
16407
  defaultModel: "kimi-for-coding",
16408
16408
  models: [
16409
- { modelId: "kimi-for-coding", name: "Kimi for Coding", description: "Available on every Kimi Code membership tier (262K context)", effort: { supported: false } },
16410
- { modelId: "k3", name: "Kimi K3", description: "Available on Moderato and higher tiers (262K context)", effort: { supported: true, levels: ["max"], default: "max" } },
16411
- { modelId: "k3[1m]", name: "Kimi K3 [1M]", description: "Available on Allegretto and higher tiers (1M context)", effort: { supported: true, levels: ["max"], default: "max" } },
16412
- { modelId: "kimi-for-coding-highspeed", name: "Kimi for Coding Highspeed", description: "Available on Allegretto and higher tiers (262K context)", effort: { supported: false } }
16409
+ { modelId: "kimi-for-coding", name: "Kimi for Coding", description: "Available on every Kimi Code membership tier (262K context)", effort: { supported: false }, contextWindow: 262144 },
16410
+ { modelId: "k3", name: "Kimi K3", description: "Available on Moderato and higher tiers (262K context)", effort: { supported: true, levels: ["low", "high", "max"], default: "high" }, contextWindow: 262144 },
16411
+ { modelId: "k3[1m]", name: "Kimi K3 [1M]", description: "Available on Allegretto and higher tiers (1M context)", effort: { supported: true, levels: ["low", "high", "max"], default: "high" }, contextWindow: 1048576 },
16412
+ { modelId: "kimi-for-coding-highspeed", name: "Kimi for Coding Highspeed", description: "Available on Allegretto and higher tiers (262K context)", effort: { supported: false }, contextWindow: 262144 }
16413
16413
  ],
16414
16414
  modelsMapping: {
16415
16415
  haiku: "kimi-for-coding",
16416
16416
  sonnet: "kimi-for-coding",
16417
- opus: "kimi-for-coding"
16417
+ opus: "kimi-for-coding",
16418
+ fable: "kimi-for-coding"
16418
16419
  }
16419
16420
  },
16420
16421
  codex: {
@@ -16488,6 +16489,8 @@ var ModelEntrySchema = external_exports.object({
16488
16489
  description: external_exports.string().optional(),
16489
16490
  /** spawn 시 실제 CLI 모델 ID를 조립해야 하는 경우의 템플릿 */
16490
16491
  spawnModelTemplate: external_exports.string().optional(),
16492
+ /** 모델의 컨텍스트 윈도우 크기 (토큰) */
16493
+ contextWindow: external_exports.number().int().positive().optional(),
16491
16494
  /** 모델별 effort 설정 */
16492
16495
  effort: EffortSchema
16493
16496
  }).check((ctx) => {