@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.
- package/dist/cli.mjs +82 -63
- package/dist/client/assets/{_baseUniq-DEvADUqz.js → _baseUniq-Dvbxpxj-.js} +1 -1
- package/dist/client/assets/{arc-pB9oYw5d.js → arc-Cqerxs8I.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-D0gUBsh1.js → architectureDiagram-Q4EWVU46-gNhmCKnU.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-9NSt9xrC.js → blockDiagram-DXYQGD6D-CM5HRpKC.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-BS1EhMAN.js → c4Diagram-AHTNJAMY-DxLhQuvg.js} +1 -1
- package/dist/client/assets/channel-DKkQpLWI.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-BKkeF7mF.js → chunk-4BX2VUAB-HX6C_GAC.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-DRp3Dsap.js → chunk-4TB4RGXK-CoBQUhS4.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-De-sLT-H.js → chunk-55IACEB6-DWsWxGBm.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-ByYWvw6c.js → chunk-EDXVE4YY-BXNRu1M4.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BDpIWnNm.js → chunk-FMBD7UC4-DzKgP1_A.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-qxbtvneQ.js → chunk-OYMX7WX6-aqXdvKUD.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-BnkLN840.js → chunk-QZHKN3VN-BbMqaUb0.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-HfizEXdz.js → chunk-YZCP3GAM-D8Mo0nv8.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-CQurO5ix.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-CQurO5ix.js +1 -0
- package/dist/client/assets/clone-IodjsAGU.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-B4U_tiL9.js → cose-bilkent-S5V4N54A-BOuVjjXz.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-De3dSv-R.js → dagre-KV5264BT-BdUFzAj_.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-CZAF0Kro.js → diagram-5BDNPKRD-C0iFrsGo.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-BHyDlHAq.js → diagram-G4DWMVQ6-CGxGeQZt.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-CV93x0hu.js → diagram-MMDJMWI5-Dex5uqlA.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-CTRzVEQp.js → diagram-TYMM5635-O48PEpaS.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-xW-UvBlm.js → erDiagram-SMLLAGMA-B7PGw3YA.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-C21IECUy.js → flowDiagram-DWJPFMVM-CCXWJIMw.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-DVzOf8Xv.js → ganttDiagram-T4ZO3ILL-B26RghpR.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-B-NQC3Ae.js → gitGraphDiagram-UUTBAWPF-BJsjMn6A.js} +1 -1
- package/dist/client/assets/{graph-BDzdWbuy.js → graph-DuEL0RWH.js} +1 -1
- package/dist/client/assets/index-Dj775lUe.css +1 -0
- package/dist/client/assets/index-r6rDZQCs.js +451 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-2KCXFMZg.js → infoDiagram-42DDH7IO-D32dz1SM.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-D4Fvy3ms.js → ishikawaDiagram-UXIWVN3A-4YPQjln0.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-DkXZkeED.js → journeyDiagram-VCZTEJTY-BSvi22HM.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-CTrIp4rv.js → kanban-definition-6JOO6SKY-SozOXS7m.js} +1 -1
- package/dist/client/assets/{layout-DKdT-A2u.js → layout-CNXntIRV.js} +1 -1
- package/dist/client/assets/{linear-Dn21C3ab.js → linear-9RZPFtO4.js} +1 -1
- package/dist/client/assets/{mermaid.core-ho7gOyUr.js → mermaid.core-kwH-UxKY.js} +4 -4
- package/dist/client/assets/{min-DV-zxfLU.js → min-BdN2p8Q_.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-By8nfvJ0.js → mindmap-definition-QFDTVHPH-H-3dJmH9.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-B4ndzH1J.js → pieDiagram-DEJITSTG-CspzYvcs.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CiDb6b3-.js → quadrantDiagram-34T5L4WZ-D1C7e3-s.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-TTr1SzvX.js → requirementDiagram-MS252O5E-BJwKRtMi.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-CUyxk-WA.js → sankeyDiagram-XADWPNL6-7oc42Z3P.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-CyBxBNAZ.js → sequenceDiagram-FGHM5R23-TP5H_DnF.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-Cj4j5FFK.js → stateDiagram-FHFEXIEX-BWnrNgJY.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Cmp8tobq.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-MQzKGbxg.js → timeline-definition-GMOUNBTQ-xXFvCsrr.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-sG3K7NAZ.js → vennDiagram-DHZGUBPP-D5unm_iH.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-ID5rQjqH.js → wardley-RL74JXVD-B-eT02rs.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-AMajCCI5.js → wardleyDiagram-NUSXRM2D-BTQ1Yqmb.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-DUb36W9E.js → xychartDiagram-5P7HB3ND-Bialvg0l.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/file-explorer/routes.mjs +199 -38
- package/dist/fleet-plugins/repository/routes.mjs +405 -31
- package/dist/fleet-plugins/skills/routes.mjs +9 -6
- package/dist/fleet-plugins/terminal/routes.mjs +226 -88
- package/package.json +1 -1
- package/dist/client/assets/channel-BSgCfWXO.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-C3WhxxfZ.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-C3WhxxfZ.js +0 -1
- package/dist/client/assets/clone-DjOK69th.js +0 -1
- package/dist/client/assets/index-BDJVPQd1.css +0 -1
- package/dist/client/assets/index-CjBu8lNT.js +0 -451
- 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
|
|
5
|
-
import
|
|
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,
|
|
12
|
-
ctx.registerRouter(normalizePath(
|
|
11
|
+
function registerRouter(ctx, path8, handler) {
|
|
12
|
+
ctx.registerRouter(normalizePath(path8), handler);
|
|
13
13
|
}
|
|
14
|
-
function normalizePath(
|
|
15
|
-
if (
|
|
16
|
-
return
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
257
|
-
if (!resolvedFilePath.startsWith(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 =
|
|
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
|
-
|
|
273
|
-
|
|
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 +
|
|
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
|
-
|
|
293
|
-
|
|
397
|
+
fs2.realpath(gitCwd),
|
|
398
|
+
fs2.realpath(resolvedFilePath)
|
|
294
399
|
]);
|
|
295
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
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
|
-
|
|
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 =
|
|
407
|
-
const resolved =
|
|
408
|
-
if (relativePath === ".." || relativePath.startsWith(".." +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
669
|
+
return Promise.all(worktrees.map(async ({ worktreePath, ...checkout }) => ({
|
|
543
670
|
...checkout,
|
|
544
|
-
isCurrent: normalizedCurrentWorktreePath !== "" && await normalizeWorktreePath(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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: "
|
|
16411
|
-
{ modelId: "k3[1m]", name: "Kimi K3 [1M]", description: "Available on Allegretto and higher tiers (1M context)", effort: { supported: true, levels: ["max"], default: "
|
|
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) => {
|