@dotobokuri/fleet-console 1.40.0 → 1.41.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 (70) hide show
  1. package/README.md +2 -3
  2. package/dist/cli.d.ts +2 -1
  3. package/dist/cli.mjs +151 -134
  4. package/dist/client/assets/{_baseUniq-Bao5GOIa.js → _baseUniq-CImzMrtU.js} +1 -1
  5. package/dist/client/assets/{arc-DnhHMLy9.js → arc-CKb0rHjN.js} +1 -1
  6. package/dist/client/assets/{architectureDiagram-Q4EWVU46-DAbURr0a.js → architectureDiagram-Q4EWVU46-BaZipXSe.js} +1 -1
  7. package/dist/client/assets/{blockDiagram-DXYQGD6D-DE1raZQ6.js → blockDiagram-DXYQGD6D-D4sS9TjR.js} +1 -1
  8. package/dist/client/assets/{c4Diagram-AHTNJAMY-beLZlUB3.js → c4Diagram-AHTNJAMY-DvjpMsKx.js} +1 -1
  9. package/dist/client/assets/channel-dSuGhtYS.js +1 -0
  10. package/dist/client/assets/{chunk-4BX2VUAB-DEqdunZS.js → chunk-4BX2VUAB-B47BryhY.js} +1 -1
  11. package/dist/client/assets/{chunk-4TB4RGXK-D8i5nx4L.js → chunk-4TB4RGXK-DKIhSC52.js} +1 -1
  12. package/dist/client/assets/{chunk-55IACEB6-eM3-fH_e.js → chunk-55IACEB6-B3Gvpef7.js} +1 -1
  13. package/dist/client/assets/{chunk-EDXVE4YY-nQoyt_Pd.js → chunk-EDXVE4YY-C4gFtvU8.js} +1 -1
  14. package/dist/client/assets/{chunk-FMBD7UC4-D8Wq8CYj.js → chunk-FMBD7UC4-BADPieOa.js} +1 -1
  15. package/dist/client/assets/{chunk-OYMX7WX6-Cx4Wr2ZT.js → chunk-OYMX7WX6-BUdtWGUY.js} +1 -1
  16. package/dist/client/assets/{chunk-QZHKN3VN-BM2v7Krr.js → chunk-QZHKN3VN-CMxkeGnM.js} +1 -1
  17. package/dist/client/assets/{chunk-YZCP3GAM-zTniPxm7.js → chunk-YZCP3GAM-Br6N7t8d.js} +1 -1
  18. package/dist/client/assets/classDiagram-6PBFFD2Q-DEN4mh7x.js +1 -0
  19. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DEN4mh7x.js +1 -0
  20. package/dist/client/assets/clone-BbTnmCoq.js +1 -0
  21. package/dist/client/assets/{cose-bilkent-S5V4N54A-B5pl2A_p.js → cose-bilkent-S5V4N54A-D1Uw7i5_.js} +1 -1
  22. package/dist/client/assets/{dagre-KV5264BT-KHPgW9C2.js → dagre-KV5264BT-kop0bHkM.js} +1 -1
  23. package/dist/client/assets/{diagram-5BDNPKRD-DCv28c7S.js → diagram-5BDNPKRD-B9e41qNA.js} +1 -1
  24. package/dist/client/assets/{diagram-G4DWMVQ6-mT5WQ415.js → diagram-G4DWMVQ6-Wew8X03k.js} +1 -1
  25. package/dist/client/assets/{diagram-MMDJMWI5-iKYfjiRC.js → diagram-MMDJMWI5-DGBb5Oqa.js} +1 -1
  26. package/dist/client/assets/{diagram-TYMM5635-_uqBP7vH.js → diagram-TYMM5635-CiVNsAz6.js} +1 -1
  27. package/dist/client/assets/{erDiagram-SMLLAGMA-DxNN1WhN.js → erDiagram-SMLLAGMA-BdemYTgC.js} +1 -1
  28. package/dist/client/assets/{flowDiagram-DWJPFMVM-DGTsrBvM.js → flowDiagram-DWJPFMVM-CXC6yxhU.js} +1 -1
  29. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-C17hg4lr.js → ganttDiagram-T4ZO3ILL-SMUpaiUg.js} +1 -1
  30. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-CeJ25hAd.js → gitGraphDiagram-UUTBAWPF-UaFgTHEh.js} +1 -1
  31. package/dist/client/assets/{graph-BQvb-BfT.js → graph-Ch8F0gMI.js} +1 -1
  32. package/dist/client/assets/index-D38r12i5.css +1 -0
  33. package/dist/client/assets/index-DkTcJcbj.js +452 -0
  34. package/dist/client/assets/{infoDiagram-42DDH7IO-BctsofOZ.js → infoDiagram-42DDH7IO-BHBzw0bu.js} +1 -1
  35. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-PiPmxcg9.js → ishikawaDiagram-UXIWVN3A-CnDea8bw.js} +1 -1
  36. package/dist/client/assets/{journeyDiagram-VCZTEJTY-BwkjYq3n.js → journeyDiagram-VCZTEJTY-CI91m8BP.js} +1 -1
  37. package/dist/client/assets/{kanban-definition-6JOO6SKY-CSV4dYfF.js → kanban-definition-6JOO6SKY-D-rn-rG2.js} +1 -1
  38. package/dist/client/assets/{layout-DdReLJnP.js → layout-Cd9zpG94.js} +1 -1
  39. package/dist/client/assets/{linear-B54euy_k.js → linear-Cucr1ojd.js} +1 -1
  40. package/dist/client/assets/{mermaid.core-CF92I5mT.js → mermaid.core-G36P12JZ.js} +4 -4
  41. package/dist/client/assets/{min-BcETQ2qJ.js → min-CzfCLG7v.js} +1 -1
  42. package/dist/client/assets/{mindmap-definition-QFDTVHPH-CrmVPycO.js → mindmap-definition-QFDTVHPH-4e1VjBV7.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-DEJITSTG-B5d4jR0H.js → pieDiagram-DEJITSTG-m2CGbU51.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-DlgCNExa.js → quadrantDiagram-34T5L4WZ-BKNPGqMc.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-MS252O5E-CeuVib_X.js → requirementDiagram-MS252O5E-D2BMwnpz.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-XADWPNL6-Bzxv73ik.js → sankeyDiagram-XADWPNL6-Bm6hTqEh.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-FGHM5R23-B0Ondg0u.js → sequenceDiagram-FGHM5R23-Ds2FEGPj.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-FHFEXIEX-CuNHg5DJ.js → stateDiagram-FHFEXIEX-CrXn2Tb7.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BbzpXIlW.js +1 -0
  50. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DXcye6z7.js → timeline-definition-GMOUNBTQ-mw053fb5.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-DHZGUBPP-CL4rbpzL.js → vennDiagram-DHZGUBPP-CAws0Vt2.js} +1 -1
  52. package/dist/client/assets/{wardley-RL74JXVD-rVZBTA_R.js → wardley-RL74JXVD-DGad2cmz.js} +1 -1
  53. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-Cb8Arj1F.js → wardleyDiagram-NUSXRM2D-BlIyft8I.js} +1 -1
  54. package/dist/client/assets/{xychartDiagram-5P7HB3ND-C-qp4Say.js → xychartDiagram-5P7HB3ND-DHmSULAI.js} +1 -1
  55. package/dist/client/index.html +2 -2
  56. package/dist/fleet-plugins/file-explorer/routes.mjs +484 -56
  57. package/dist/fleet-plugins/ledger/routes.mjs +56 -47
  58. package/dist/fleet-plugins/quota/routes.mjs +19252 -118
  59. package/dist/fleet-plugins/repository/routes.mjs +274 -17
  60. package/dist/fleet-plugins/scuttlebutt/routes.mjs +6 -81
  61. package/dist/fleet-plugins/skills/routes.mjs +1 -46
  62. package/dist/fleet-plugins/terminal/routes.mjs +12090 -1060
  63. package/package.json +1 -1
  64. package/dist/client/assets/channel-Bn1wyUhs.js +0 -1
  65. package/dist/client/assets/classDiagram-6PBFFD2Q-DBE3Ix3c.js +0 -1
  66. package/dist/client/assets/classDiagram-v2-HSJHXN6E-DBE3Ix3c.js +0 -1
  67. package/dist/client/assets/clone-COL47Wy_.js +0 -1
  68. package/dist/client/assets/index-Ca6HkGPQ.css +0 -1
  69. package/dist/client/assets/index-Cr3Xq85X.js +0 -452
  70. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BVPbUfrG.js +0 -1
@@ -8,12 +8,12 @@ import fs2 from 'fs/promises';
8
8
  function definePlugin(definition) {
9
9
  return definition;
10
10
  }
11
- function registerRouter(ctx, path9, handler) {
12
- ctx.registerRouter(normalizePath(path9), handler);
11
+ function registerRouter(ctx, path10, handler) {
12
+ ctx.registerRouter(normalizePath(path10), handler);
13
13
  }
14
- function normalizePath(path9) {
15
- if (path9.length === 0 || path9 === "/") return "/";
16
- return path9;
14
+ function normalizePath(path10) {
15
+ if (path10.length === 0 || path10 === "/") return "/";
16
+ return path10;
17
17
  }
18
18
 
19
19
  // ../../packages/core-process/src/hidden.ts
@@ -36,9 +36,25 @@ var GitExecutorError = class extends Error {
36
36
  };
37
37
  var DEFAULT_TIMEOUT_MS = 3e4;
38
38
  var DEFAULT_MAX_BUFFER = 8 * 1024 * 1024;
39
+ var STDERR_MAX_BUFFER = 1024 * 1024;
40
+ var GIT_HARDENING_PREFIX = ["-c", "core.fsmonitor=false", "--no-optional-locks"];
41
+ function sanitizeGitEnvironment(environment) {
42
+ const sanitized = {};
43
+ for (const [key, value] of Object.entries(environment)) {
44
+ const normalizedKey = key.toUpperCase();
45
+ if (normalizedKey.startsWith("GIT_") || normalizedKey === "LC_ALL" || normalizedKey === "SSH_ASKPASS" || normalizedKey === "SSH_ASKPASS_REQUIRE") continue;
46
+ if (value !== void 0) sanitized[key] = value;
47
+ }
48
+ sanitized.GIT_OPTIONAL_LOCKS = "0";
49
+ sanitized.GIT_TERMINAL_PROMPT = "0";
50
+ sanitized.GIT_ALLOW_PROTOCOL = "ssh:git:http:https:file";
51
+ sanitized.LC_ALL = "C";
52
+ return sanitized;
53
+ }
39
54
  function runGit(args, opts) {
40
55
  const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
41
56
  const maxBuffer = opts.maxBuffer ?? DEFAULT_MAX_BUFFER;
57
+ const hardenedArgs = [...GIT_HARDENING_PREFIX, ...args];
42
58
  return new Promise((resolve, reject) => {
43
59
  if (opts.signal?.aborted) {
44
60
  reject(new GitExecutorError("timeout"));
@@ -46,7 +62,11 @@ function runGit(args, opts) {
46
62
  }
47
63
  let child;
48
64
  try {
49
- child = spawn("git", args, withHidden({ cwd: opts.cwd, shell: false }));
65
+ child = spawn("git", hardenedArgs, withHidden({
66
+ cwd: opts.cwd,
67
+ env: sanitizeGitEnvironment(process.env),
68
+ shell: false
69
+ }));
50
70
  } catch (error) {
51
71
  const code = error.code === "ENOENT" ? "git_unavailable" : "spawn_failed";
52
72
  reject(new GitExecutorError(code, String(error)));
@@ -55,7 +75,9 @@ function runGit(args, opts) {
55
75
  const stdoutChunks = [];
56
76
  const stderrChunks = [];
57
77
  let stdoutLen = 0;
78
+ let stderrLen = 0;
58
79
  let truncated = false;
80
+ let stderrTruncated = false;
59
81
  let timedOut = false;
60
82
  const abort = () => {
61
83
  timedOut = true;
@@ -83,7 +105,16 @@ function runGit(args, opts) {
83
105
  }
84
106
  });
85
107
  child.stderr.on("data", (chunk) => {
86
- stderrChunks.push(chunk);
108
+ if (stderrTruncated) return;
109
+ const remaining = STDERR_MAX_BUFFER - stderrLen;
110
+ if (chunk.length >= remaining) {
111
+ stderrChunks.push(chunk.subarray(0, remaining));
112
+ stderrLen += remaining;
113
+ stderrTruncated = true;
114
+ } else {
115
+ stderrChunks.push(chunk);
116
+ stderrLen += chunk.length;
117
+ }
87
118
  });
88
119
  child.on("error", (error) => {
89
120
  cleanup();
@@ -97,7 +128,7 @@ function runGit(args, opts) {
97
128
  if (code !== 0) {
98
129
  if (code !== null && (opts.allowExitCodes?.includes(code) ?? false)) {
99
130
  const stdout2 = Buffer.concat(stdoutChunks).toString("utf8");
100
- resolve({ stdout: stdout2, truncated });
131
+ resolve({ stdout: stdout2, stderr, truncated, stderrTruncated });
101
132
  return;
102
133
  }
103
134
  const isNoRepo = stderr.toLowerCase().includes("not a git repository");
@@ -105,7 +136,7 @@ function runGit(args, opts) {
105
136
  return;
106
137
  }
107
138
  const stdout = Buffer.concat(stdoutChunks).toString("utf8");
108
- resolve({ stdout, truncated });
139
+ resolve({ stdout, stderr, truncated, stderrTruncated });
109
140
  });
110
141
  });
111
142
  }
@@ -796,10 +827,232 @@ async function handleRepositoryCompareFile(req, res, ctx) {
796
827
  throw error;
797
828
  }
798
829
  }
830
+ var AUTO_FETCH_THROTTLE_MS = 3e5;
831
+ var autoFetchInFlight = /* @__PURE__ */ new Map();
832
+ var lastSuccessfulFetchAt = /* @__PURE__ */ new Map();
833
+ function isPlainObject6(value) {
834
+ return typeof value === "object" && value !== null && !Array.isArray(value);
835
+ }
836
+ function classifyFetchError(stderr) {
837
+ if (/permission denied|authentication failed|could not read username|terminal prompts disabled|repository not found/i.test(stderr)) {
838
+ return "auth_failed";
839
+ }
840
+ if (/could not resolve host|failed to connect|connection (refused|reset)|operation timed out|network is unreachable/i.test(stderr)) {
841
+ return "network";
842
+ }
843
+ if (/no remote repository specified|does not appear to be a git repository/i.test(stderr)) {
844
+ return "no_remote";
845
+ }
846
+ return null;
847
+ }
848
+ async function resolveGitIdentity(gitCwd) {
849
+ const [gitDirResult, commonDirResult] = await Promise.all([
850
+ runGit(["rev-parse", "--absolute-git-dir"], { cwd: gitCwd }),
851
+ runGit(["rev-parse", "--git-common-dir"], { cwd: gitCwd })
852
+ ]);
853
+ const [gitDir, commonDir] = await Promise.all([
854
+ fs2.realpath(path4.resolve(gitCwd, gitDirResult.stdout.trim())),
855
+ fs2.realpath(path4.resolve(gitCwd, commonDirResult.stdout.trim()))
856
+ ]);
857
+ return { gitDir, commonDir };
858
+ }
859
+ async function readLastFetchAt(gitDirs) {
860
+ let newest = null;
861
+ for (const gitDir of gitDirs) {
862
+ try {
863
+ const stats = await fs2.stat(path4.join(gitDir, "FETCH_HEAD"));
864
+ if (stats.size === 0) continue;
865
+ if (!newest || stats.mtime.getTime() > newest.getTime()) newest = stats.mtime;
866
+ } catch (error) {
867
+ if (error.code !== "ENOENT") throw error;
868
+ }
869
+ }
870
+ return newest;
871
+ }
872
+ function countFetchProgress(stderr, marker) {
873
+ return stderr.split(/\r?\n/).filter((line) => line.includes(marker)).length;
874
+ }
875
+ function countFetchUpdatedRefs(stderr) {
876
+ return stderr.split(/\r?\n/).filter(
877
+ (line) => line.includes(" -> ") && /\S+\.\.\S+/.test(line) && !line.includes("[deleted]") && !line.includes("[new ")
878
+ ).length;
879
+ }
880
+ var NoRemoteError = class extends Error {
881
+ };
882
+ var SAFE_CREDENTIAL_HELPERS = /* @__PURE__ */ new Set([
883
+ "cache",
884
+ "store",
885
+ "osxkeychain",
886
+ "wincred",
887
+ "libsecret",
888
+ "gnome-libsecret",
889
+ "manager",
890
+ "manager-core"
891
+ ]);
892
+ async function resolveCredentialHelperArgs(gitCwd) {
893
+ const raw = (await runGit(["config", "--get-all", "credential.helper"], { cwd: gitCwd, allowExitCodes: [1] })).stdout;
894
+ const helpers = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
895
+ const kept = helpers.filter((helper) => {
896
+ if (!/^[A-Za-z0-9-]+$/.test(helper)) return false;
897
+ return SAFE_CREDENTIAL_HELPERS.has(helper) || SAFE_CREDENTIAL_HELPERS.has(helper.replace(/^git-credential-/, ""));
898
+ });
899
+ return ["-c", "credential.helper=", ...kept.flatMap((helper) => ["-c", `credential.helper=${helper}`]), "-c", "core.askPass="];
900
+ }
901
+ async function resolveDefaultRemote(gitCwd) {
902
+ const branch = (await runGit(["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitCwd })).stdout.trim();
903
+ if (branch && branch !== "HEAD") {
904
+ const upstream = (await runGit(["config", "--get", `branch.${branch}.remote`], { cwd: gitCwd, allowExitCodes: [1] })).stdout.trim();
905
+ if (upstream && upstream !== ".") return upstream;
906
+ }
907
+ const remotes = (await runGit(["remote"], { cwd: gitCwd })).stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
908
+ if (remotes.includes("origin")) return "origin";
909
+ return remotes[0] ?? null;
910
+ }
911
+ async function readThrottleResult(gitDirs, commonDir) {
912
+ const fsNewest = await readLastFetchAt(gitDirs);
913
+ const recorded = lastSuccessfulFetchAt.get(commonDir);
914
+ let newest = fsNewest;
915
+ if (recorded !== void 0 && (!newest || recorded > newest.getTime())) newest = new Date(recorded);
916
+ if (!newest || Date.now() - newest.getTime() >= AUTO_FETCH_THROTTLE_MS) return null;
917
+ return { ok: true, skipped: "throttled", lastFetchAt: newest.toISOString() };
918
+ }
919
+ async function fetchRepository(gitCwd) {
920
+ const remote = await resolveDefaultRemote(gitCwd);
921
+ if (!remote) throw new NoRemoteError();
922
+ const credentialArgs = await resolveCredentialHelperArgs(gitCwd);
923
+ const result = await runGit([
924
+ "-c",
925
+ "core.sshCommand=ssh",
926
+ "-c",
927
+ // git 예약어 none만 프록시 우회다 — 다른 값은 프록시 "명령"으로 실행된다.
928
+ "core.gitProxy=none",
929
+ "-c",
930
+ // protocol.allow=user는 ext를 never에서 허용으로 뒤집는다(GIT_PROTOCOL_FROM_USER unset) —
931
+ // 기본 정책을 유지한 채 실행형 transport만 명시 차단한다.
932
+ "protocol.ext.allow=never",
933
+ "-c",
934
+ // ref transaction 훅(reference-transaction 등)의 zero-click 실행을 플랫폼 널 장치로 차단한다.
935
+ `core.hooksPath=${process.platform === "win32" ? "NUL" : "/dev/null"}`,
936
+ ...credentialArgs,
937
+ "fetch",
938
+ remote,
939
+ // 명시 refspec이 configured refspec을 대체한다 — prune의 파괴 범위를
940
+ // refs/remotes/<remote>/ 아래로 고정해 로컬 refs를 보호한다.
941
+ `+refs/heads/*:refs/remotes/${remote}/*`,
942
+ // repo config의 remote.<name>.uploadpack이 로컬 transport에서 명령 실행되므로 표준 명령을 강제한다.
943
+ "--upload-pack=git-upload-pack",
944
+ // 하드닝된 상위 fetch를 빠져나가는 재귀(중첩 네트워크+하위 upload-pack)를 끊는다.
945
+ "--no-recurse-submodules",
946
+ "--prune",
947
+ "--no-tags",
948
+ // fetch.pruneTags=true인 저장소가 --prune과 결합해 로컬 전용 태그를 지우는 것을 막는다.
949
+ "--no-prune-tags"
950
+ ], { cwd: gitCwd });
951
+ const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
952
+ return {
953
+ ok: true,
954
+ fetchedAt,
955
+ lastFetchAt: fetchedAt,
956
+ pruned: countFetchProgress(result.stderr, " - [deleted] "),
957
+ newRefs: countFetchProgress(result.stderr, " * [new branch] "),
958
+ updatedRefs: countFetchUpdatedRefs(result.stderr)
959
+ };
960
+ }
961
+ async function fetchHeadCandidates(identity) {
962
+ const candidates = /* @__PURE__ */ new Set([identity.gitDir, identity.commonDir]);
963
+ try {
964
+ const worktreesDir = path4.join(identity.commonDir, "worktrees");
965
+ for (const entry of await fs2.readdir(worktreesDir, { withFileTypes: true })) {
966
+ if (entry.isDirectory()) candidates.add(path4.join(worktreesDir, entry.name));
967
+ }
968
+ } catch (error) {
969
+ if (error.code !== "ENOENT") throw error;
970
+ }
971
+ return [...candidates];
972
+ }
973
+ async function runAutoFetch(gitCwd, identity) {
974
+ const throttled = await readThrottleResult(await fetchHeadCandidates(identity), identity.commonDir);
975
+ return throttled ?? fetchRepository(gitCwd);
976
+ }
977
+ async function fetchAutoSingleFlight(gitCwd, identity) {
978
+ const key = identity.commonDir;
979
+ const existing = autoFetchInFlight.get(key);
980
+ if (existing) {
981
+ await existing;
982
+ const throttled = await readThrottleResult(await fetchHeadCandidates(identity), identity.commonDir);
983
+ if (throttled) return throttled;
984
+ }
985
+ const operation = runAutoFetch(gitCwd, identity);
986
+ autoFetchInFlight.set(key, operation);
987
+ try {
988
+ return await operation;
989
+ } finally {
990
+ if (autoFetchInFlight.get(key) === operation) autoFetchInFlight.delete(key);
991
+ }
992
+ }
993
+ async function handleRepositoryFetch(req, res, ctx) {
994
+ if (req.method !== "POST") {
995
+ ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
996
+ return;
997
+ }
998
+ if (!ctx.host.security.isTerminalAuthorized(req)) {
999
+ ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
1000
+ return;
1001
+ }
1002
+ const body = await ctx.host.http.readJsonBody(req);
1003
+ if (!isPlainObject6(body) || "subPath" in body || typeof body.theaterId !== "string" || body.mode !== void 0 && body.mode !== "auto") {
1004
+ ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1005
+ return;
1006
+ }
1007
+ const theaterPath = ctx.host.paths.resolveTheaterPath(body.theaterId);
1008
+ if (!theaterPath) {
1009
+ ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
1010
+ return;
1011
+ }
1012
+ let gitCwd;
1013
+ try {
1014
+ ({ gitCwd } = await resolveGitCwd(theaterPath, body.repoRel));
1015
+ } catch (error) {
1016
+ if (error instanceof InvalidRepoError) {
1017
+ ctx.host.http.writeJson(res, 400, { error: error.code });
1018
+ return;
1019
+ }
1020
+ throw error;
1021
+ }
1022
+ try {
1023
+ const identity = await resolveGitIdentity(gitCwd);
1024
+ const result = body.mode === "auto" ? await fetchAutoSingleFlight(gitCwd, identity) : await (autoFetchInFlight.get(identity.commonDir) ?? fetchRepository(gitCwd));
1025
+ if ("fetchedAt" in result) lastSuccessfulFetchAt.set(identity.commonDir, Date.parse(result.fetchedAt));
1026
+ ctx.host.http.writeJson(res, 200, result);
1027
+ } catch (error) {
1028
+ if (error instanceof NoRemoteError) {
1029
+ ctx.host.http.writeJson(res, 422, { error: "no_remote" });
1030
+ return;
1031
+ }
1032
+ if (error instanceof GitExecutorError) {
1033
+ if (error.code === "timeout") {
1034
+ ctx.host.http.writeJson(res, 422, { error: "timeout" });
1035
+ return;
1036
+ }
1037
+ const classified = classifyFetchError(error.stderr);
1038
+ if (classified) {
1039
+ ctx.host.http.writeJson(res, 422, { error: classified });
1040
+ return;
1041
+ }
1042
+ if (error.code === "no_git_repo" || error.code === "git_unavailable") {
1043
+ ctx.host.http.writeJson(res, 422, { error: error.code });
1044
+ return;
1045
+ }
1046
+ ctx.host.http.writeJson(res, 500, { error: "git_failed" });
1047
+ return;
1048
+ }
1049
+ throw error;
1050
+ }
1051
+ }
799
1052
  var WORKTREE_SHA_RE = /^[0-9a-f]{40}$/;
800
1053
  var CANONICAL_REF_RE = /^refs\/(?:heads|remotes|tags)\//;
801
1054
  var REF_METACHAR_RE = /[~^:?*\[\\\s\x00-\x1f\x7f]/;
802
- function isPlainObject6(value) {
1055
+ function isPlainObject7(value) {
803
1056
  return typeof value === "object" && value !== null && !Array.isArray(value);
804
1057
  }
805
1058
  function isCanonicalRepositoryRef(ref) {
@@ -924,7 +1177,7 @@ async function handleRepositoryLog(req, res, ctx) {
924
1177
  return;
925
1178
  }
926
1179
  const body = await ctx.host.http.readJsonBody(req);
927
- if (!isPlainObject6(body) || "subPath" in body) {
1180
+ if (!isPlainObject7(body) || "subPath" in body) {
928
1181
  ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
929
1182
  return;
930
1183
  }
@@ -1092,7 +1345,7 @@ var HARD_CAP_DEPTH = 8;
1092
1345
  var NESTED_BRANCH_CAP = 64;
1093
1346
  var SCAN_VISIT_BUDGET = 1e4;
1094
1347
  var DEFAULT_DEPTH = 3;
1095
- function isPlainObject7(value) {
1348
+ function isPlainObject8(value) {
1096
1349
  return typeof value === "object" && value !== null && !Array.isArray(value);
1097
1350
  }
1098
1351
  async function resolveRepoBranch(repoDir) {
@@ -1181,7 +1434,7 @@ async function handleRepositoryRepos(req, res, ctx) {
1181
1434
  return;
1182
1435
  }
1183
1436
  const body = await ctx.host.http.readJsonBody(req);
1184
- if (!isPlainObject7(body) || typeof body.theaterId !== "string") {
1437
+ if (!isPlainObject8(body) || typeof body.theaterId !== "string") {
1185
1438
  ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1186
1439
  return;
1187
1440
  }
@@ -1260,7 +1513,7 @@ async function handleRepositorySearch(req, res, ctx) {
1260
1513
  return;
1261
1514
  }
1262
1515
  const body = await ctx.host.http.readJsonBody(req);
1263
- if (!isPlainObject8(body) || typeof body.theaterId !== "string" || typeof body.query !== "string" || body.query.trim() === "" || !Number.isInteger(body.limit) || body.limit < 1 || body.limit > 8 || "subPath" in body || "ref" in body) {
1516
+ if (!isPlainObject9(body) || typeof body.theaterId !== "string" || typeof body.query !== "string" || body.query.trim() === "" || !Number.isInteger(body.limit) || body.limit < 1 || body.limit > 8 || "subPath" in body || "ref" in body) {
1264
1517
  ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1265
1518
  return;
1266
1519
  }
@@ -1314,10 +1567,10 @@ async function handleRepositorySearch(req, res, ctx) {
1314
1567
  throw error;
1315
1568
  }
1316
1569
  }
1317
- function isPlainObject8(value) {
1570
+ function isPlainObject9(value) {
1318
1571
  return typeof value === "object" && value !== null && !Array.isArray(value);
1319
1572
  }
1320
- function isPlainObject9(value) {
1573
+ function isPlainObject10(value) {
1321
1574
  return typeof value === "object" && value !== null && !Array.isArray(value);
1322
1575
  }
1323
1576
  async function handleRepositoryWorktrees(req, res, ctx) {
@@ -1330,7 +1583,7 @@ async function handleRepositoryWorktrees(req, res, ctx) {
1330
1583
  return;
1331
1584
  }
1332
1585
  const body = await ctx.host.http.readJsonBody(req);
1333
- if (!isPlainObject9(body) || "subPath" in body || typeof body.theaterId !== "string") {
1586
+ if (!isPlainObject10(body) || "subPath" in body || typeof body.theaterId !== "string") {
1334
1587
  ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
1335
1588
  return;
1336
1589
  }
@@ -1411,6 +1664,10 @@ var routes_default = definePlugin({
1411
1664
  await handleRepositoryChanged(req, res, ctx);
1412
1665
  return true;
1413
1666
  });
1667
+ registerRouter(ctx, "fetch", async ({ req, res }) => {
1668
+ await handleRepositoryFetch(req, res, ctx);
1669
+ return true;
1670
+ });
1414
1671
  registerRouter(ctx, "file", async ({ req, res }) => {
1415
1672
  await handleRepositoryFile(req, res, ctx);
1416
1673
  return true;
@@ -18913,7 +18913,7 @@ var AcpConnection = class extends BaseConnection {
18913
18913
  }
18914
18914
  /** Claude 계열 브릿지인지 여부를 단일 판별 지점으로 유지합니다. */
18915
18915
  isClaudeBackend() {
18916
- return this.cliType === "claude" || this.cliType === "claude-kimi";
18916
+ return this.cliType === "claude";
18917
18917
  }
18918
18918
  /** Claude bridge 전용 system prompt replacement, strict-MCP와 effort `_meta`를 조립합니다. */
18919
18919
  buildClaudeSessionMeta(systemPrompt, strictMcp, effort) {
@@ -19379,22 +19379,6 @@ var models_default = {
19379
19379
  { modelId: "claude-opus-4-8[1m]", name: "Claude Opus 4.8 [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "high" } }
19380
19380
  ]
19381
19381
  },
19382
- "claude-kimi": {
19383
- name: "Kimi via Claude Code",
19384
- defaultModel: "kimi-for-coding",
19385
- models: [
19386
- { modelId: "kimi-for-coding", name: "Kimi for Coding", description: "Available on every Kimi Code membership tier (262K context)", effort: { supported: false }, contextWindow: 262144 },
19387
- { 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 },
19388
- { 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 },
19389
- { modelId: "kimi-for-coding-highspeed", name: "Kimi for Coding Highspeed", description: "Available on Allegretto and higher tiers (262K context)", effort: { supported: false }, contextWindow: 262144 }
19390
- ],
19391
- modelsMapping: {
19392
- haiku: "kimi-for-coding",
19393
- sonnet: "kimi-for-coding",
19394
- opus: "kimi-for-coding",
19395
- fable: "kimi-for-coding"
19396
- }
19397
- },
19398
19382
  codex: {
19399
19383
  name: "Codex",
19400
19384
  defaultModel: "gpt-5.6-sol",
@@ -19510,16 +19494,13 @@ var ModelEntrySchema = external_exports.object({
19510
19494
  });
19511
19495
  }
19512
19496
  });
19513
- var ModelsMappingSchema = external_exports.record(external_exports.string(), external_exports.string());
19514
19497
  var ProviderSchema = external_exports.object({
19515
19498
  /** 프로바이더 표시 이름 */
19516
19499
  name: external_exports.string(),
19517
19500
  /** 기본 모델 ID */
19518
19501
  defaultModel: external_exports.string(),
19519
19502
  /** 사용 가능한 모델 목록 */
19520
- models: external_exports.array(ModelEntrySchema).min(1),
19521
- /** modelId와 연관된 매핑 모델 (선택) */
19522
- modelsMapping: ModelsMappingSchema.optional()
19503
+ models: external_exports.array(ModelEntrySchema).min(1)
19523
19504
  }).check((ctx) => {
19524
19505
  const ids = new Set(ctx.value.models.map((m) => m.modelId));
19525
19506
  if (!ids.has(ctx.value.defaultModel)) {
@@ -19585,13 +19566,6 @@ function resolveCursorSpawnModel(modelId, effort) {
19585
19566
  }
19586
19567
  return model.spawnModelTemplate.replace("{effort}", resolvedLevel);
19587
19568
  }
19588
- function getProviderModelsMapping(cli) {
19589
- const provider = registry2.providers[cli];
19590
- if (!provider) {
19591
- throw new Error(`\uC54C \uC218 \uC5C6\uB294 \uD504\uB85C\uBC14\uC774\uB354: "${cli}"`);
19592
- }
19593
- return provider.modelsMapping ? structuredClone(provider.modelsMapping) : null;
19594
- }
19595
19569
  function findProviderModel(cli, modelId) {
19596
19570
  const provider = registry2.providers[cli];
19597
19571
  if (!provider) {
@@ -19623,32 +19597,6 @@ var CLI_BACKENDS = {
19623
19597
  usesNpxBridge: true,
19624
19598
  defaultMaxTokens: 16384
19625
19599
  },
19626
- "claude-kimi": {
19627
- id: "claude-kimi",
19628
- cliCommand: "claude",
19629
- protocol: "acp",
19630
- authRequired: true,
19631
- npxPackage: "@agentclientprotocol/claude-agent-acp@0.33.1",
19632
- modes: [
19633
- { id: "default", label: "Default" },
19634
- { id: "plan", label: "Plan" },
19635
- { id: "bypassPermissions", label: "YOLO" }
19636
- ],
19637
- supportsSessionClose: true,
19638
- supportsSessionLoad: true,
19639
- requiresModelAtSpawn: false,
19640
- usesNpxBridge: true,
19641
- defaultMaxTokens: 262144,
19642
- defaultEnv: {
19643
- ANTHROPIC_BASE_URL: "https://api.kimi.com/coding/",
19644
- ANTHROPIC_MODEL: "kimi-for-coding",
19645
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding",
19646
- ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding",
19647
- ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding",
19648
- CLAUDE_CODE_SUBAGENT_MODEL: "kimi-for-coding",
19649
- API_TIMEOUT_MS: "3000000"
19650
- }
19651
- },
19652
19600
  codex: {
19653
19601
  id: "codex",
19654
19602
  cliCommand: "codex",
@@ -19725,7 +19673,6 @@ function getBackendConfig(cli) {
19725
19673
  function getYoloModeId(cli) {
19726
19674
  switch (cli) {
19727
19675
  case "claude":
19728
- case "claude-kimi":
19729
19676
  return "bypassPermissions";
19730
19677
  case "cursor":
19731
19678
  return "agent";
@@ -19937,11 +19884,7 @@ var UnifiedClaudeAgentClient = class extends EventEmitter {
19937
19884
  firstPromptPending = null;
19938
19885
  currentEffort = null;
19939
19886
  detector = new CliDetector();
19940
- cliType;
19941
- constructor(cliType = "claude") {
19942
- super();
19943
- this.cliType = cliType;
19944
- }
19887
+ cliType = "claude";
19945
19888
  on(event, listener) {
19946
19889
  return super.on(event, listener);
19947
19890
  }
@@ -19957,31 +19900,14 @@ var UnifiedClaudeAgentClient = class extends EventEmitter {
19957
19900
  async connect(options) {
19958
19901
  await this.disconnect();
19959
19902
  const systemPromptMode = options.systemPromptMode ?? "prepend";
19960
- const requestedCli = options.cli ?? this.cliType;
19961
- if (systemPromptMode === "replace" && requestedCli !== "claude" && requestedCli !== "claude-kimi") {
19962
- throw new Error(
19963
- `system prompt replacement is not supported by the "${requestedCli}" backend`
19964
- );
19965
- }
19966
19903
  if (options.cli && options.cli !== this.cliType) {
19967
19904
  throw new Error(`UnifiedClaudeAgentClient\uB294 ${this.cliType} CLI\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.`);
19968
19905
  }
19969
19906
  const acpMcpServers = this.resolveMcpServers(options.mcpServers);
19970
19907
  const spawnConfig = createSpawnConfig(this.cliType, options);
19971
- const modelsMapping = getProviderModelsMapping(this.cliType);
19972
- const mappingEnv = {};
19973
- if (modelsMapping) {
19974
- for (const [modelId, mappedModel] of Object.entries(modelsMapping)) {
19975
- const envKey = `ANTHROPIC_DEFAULT_${modelId.toUpperCase()}_MODEL`;
19976
- mappingEnv[envKey] = mappedModel;
19977
- }
19978
- }
19979
19908
  const backendConfig = getBackendConfig(this.cliType);
19980
19909
  const defaultEnv = backendConfig.defaultEnv ?? {};
19981
- const cleanEnv = cleanEnvironment(process.env, { ...defaultEnv, ...mappingEnv, ...options.env });
19982
- if (this.cliType === "claude-kimi") {
19983
- delete cleanEnv.ANTHROPIC_AUTH_TOKEN;
19984
- }
19910
+ const cleanEnv = cleanEnvironment(process.env, { ...defaultEnv, ...options.env });
19985
19911
  if (defaultEnv.ANTHROPIC_BASE_URL && !cleanEnv.ANTHROPIC_API_KEY && !cleanEnv.ANTHROPIC_AUTH_TOKEN) {
19986
19912
  throw new Error(
19987
19913
  `[${this.cliType}] ANTHROPIC_API_KEY \uD658\uACBD\uBCC0\uC218\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. ${getProviderModels(this.cliType).name} API \uD0A4\uB97C \uC124\uC815\uD574\uC8FC\uC138\uC694.`
@@ -22132,8 +22058,7 @@ var UnifiedAgent = {
22132
22058
  createClient(cli) {
22133
22059
  switch (cli) {
22134
22060
  case "claude":
22135
- case "claude-kimi":
22136
- return new UnifiedClaudeAgentClient(cli);
22061
+ return new UnifiedClaudeAgentClient();
22137
22062
  case "codex":
22138
22063
  return new UnifiedCodexAgentClient();
22139
22064
  case "cursor":
@@ -22150,7 +22075,7 @@ var UnifiedAgent = {
22150
22075
  const preferred = await new CliDetector().getPreferred();
22151
22076
  if (!preferred) {
22152
22077
  throw new Error(
22153
- "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C CLI\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. claude, claude-kimi, codex, cursor \uC911 \uD558\uB098\uB97C \uC124\uCE58\uD574\uC8FC\uC138\uC694."
22078
+ "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C CLI\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. claude, codex, cursor \uC911 \uD558\uB098\uB97C \uC124\uCE58\uD574\uC8FC\uC138\uC694."
22154
22079
  );
22155
22080
  }
22156
22081
  return this.createClient(preferred.cli);
@@ -16402,22 +16402,6 @@ var models_default = {
16402
16402
  { modelId: "claude-opus-4-8[1m]", name: "Claude Opus 4.8 [1M]", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "high" } }
16403
16403
  ]
16404
16404
  },
16405
- "claude-kimi": {
16406
- name: "Kimi via Claude Code",
16407
- defaultModel: "kimi-for-coding",
16408
- models: [
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
- ],
16414
- modelsMapping: {
16415
- haiku: "kimi-for-coding",
16416
- sonnet: "kimi-for-coding",
16417
- opus: "kimi-for-coding",
16418
- fable: "kimi-for-coding"
16419
- }
16420
- },
16421
16405
  codex: {
16422
16406
  name: "Codex",
16423
16407
  defaultModel: "gpt-5.6-sol",
@@ -16533,16 +16517,13 @@ var ModelEntrySchema = external_exports.object({
16533
16517
  });
16534
16518
  }
16535
16519
  });
16536
- var ModelsMappingSchema = external_exports.record(external_exports.string(), external_exports.string());
16537
16520
  var ProviderSchema = external_exports.object({
16538
16521
  /** 프로바이더 표시 이름 */
16539
16522
  name: external_exports.string(),
16540
16523
  /** 기본 모델 ID */
16541
16524
  defaultModel: external_exports.string(),
16542
16525
  /** 사용 가능한 모델 목록 */
16543
- models: external_exports.array(ModelEntrySchema).min(1),
16544
- /** modelId와 연관된 매핑 모델 (선택) */
16545
- modelsMapping: ModelsMappingSchema.optional()
16526
+ models: external_exports.array(ModelEntrySchema).min(1)
16546
16527
  }).check((ctx) => {
16547
16528
  const ids = new Set(ctx.value.models.map((m) => m.modelId));
16548
16529
  if (!ids.has(ctx.value.defaultModel)) {
@@ -16593,32 +16574,6 @@ var CLI_BACKENDS = {
16593
16574
  usesNpxBridge: true,
16594
16575
  defaultMaxTokens: 16384
16595
16576
  },
16596
- "claude-kimi": {
16597
- id: "claude-kimi",
16598
- cliCommand: "claude",
16599
- protocol: "acp",
16600
- authRequired: true,
16601
- npxPackage: "@agentclientprotocol/claude-agent-acp@0.33.1",
16602
- modes: [
16603
- { id: "default", label: "Default" },
16604
- { id: "plan", label: "Plan" },
16605
- { id: "bypassPermissions", label: "YOLO" }
16606
- ],
16607
- supportsSessionClose: true,
16608
- supportsSessionLoad: true,
16609
- requiresModelAtSpawn: false,
16610
- usesNpxBridge: true,
16611
- defaultMaxTokens: 262144,
16612
- defaultEnv: {
16613
- ANTHROPIC_BASE_URL: "https://api.kimi.com/coding/",
16614
- ANTHROPIC_MODEL: "kimi-for-coding",
16615
- ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding",
16616
- ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding",
16617
- ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding",
16618
- CLAUDE_CODE_SUBAGENT_MODEL: "kimi-for-coding",
16619
- API_TIMEOUT_MS: "3000000"
16620
- }
16621
- },
16622
16577
  codex: {
16623
16578
  id: "codex",
16624
16579
  cliCommand: "codex",