@dotobokuri/fleet-console 1.18.0 → 1.19.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/AGENTS.md +6 -5
- package/dist/cli.d.ts +13 -12
- package/dist/cli.mjs +143 -175
- package/dist/client/assets/SymbolsNerdFontMono-Regular-CoUzE12l.woff2 +0 -0
- package/dist/client/assets/{_baseUniq-DFEDX4NY.js → _baseUniq-C4_iQlZS.js} +1 -1
- package/dist/client/assets/{arc-CGwMrR6o.js → arc-BBAWxYi5.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-BqLyvIOK.js → architectureDiagram-Q4EWVU46-_oQqhexV.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-DoiPMLdv.js → blockDiagram-DXYQGD6D-3xT9VFiD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-DfGxDkah.js → c4Diagram-AHTNJAMY-S-IMcMW2.js} +1 -1
- package/dist/client/assets/channel-5tV1napa.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-3zXv74oY.js → chunk-4BX2VUAB-tS9Ys0P9.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-MkXUmjUE.js → chunk-4TB4RGXK-YZgAG7SW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-mhkgqlI_.js → chunk-55IACEB6-m-CtqHoO.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-B0_ZDihs.js → chunk-EDXVE4YY-yoymxaEV.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BQAuE4pL.js → chunk-FMBD7UC4-REUkrQAG.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-CY8Eyj9X.js → chunk-OYMX7WX6-DzMuL105.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CaZoMwKH.js → chunk-QZHKN3VN-DXFFll8-.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-DV1YOEUH.js → chunk-YZCP3GAM-CmY7W-gg.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-WDM904ap.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-WDM904ap.js +1 -0
- package/dist/client/assets/clone-BJg9p0cC.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-Cy3v0C_N.js → cose-bilkent-S5V4N54A-BupE-a40.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-CGJ6x_VR.js → dagre-KV5264BT-Jn67LxMS.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-MyyXeHKQ.js → diagram-5BDNPKRD-BAeFEORH.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-D52Q2132.js → diagram-G4DWMVQ6-CnZrDABY.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-DCypusCr.js → diagram-MMDJMWI5-DefG-kXU.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-c5UBqE9-.js → diagram-TYMM5635-BPkXKB-Q.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-BlF0Lxy7.js → erDiagram-SMLLAGMA-Bi4jlild.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-lo8xyzfx.js → flowDiagram-DWJPFMVM-BoZyLO7h.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-BXVZMpon.js → ganttDiagram-T4ZO3ILL-LgzSNjrh.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-DB1A6uYk.js → gitGraphDiagram-UUTBAWPF-B4fH-hPT.js} +1 -1
- package/dist/client/assets/{graph-Ck2AX1p2.js → graph-D3ZAYMLG.js} +1 -1
- package/dist/client/assets/{index-Cf9MmpwN.css → index-DctP_CJP.css} +1 -1
- package/dist/client/assets/index-vgEn_EkG.js +404 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-NEVE42fw.js → infoDiagram-42DDH7IO-pENdunXd.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-MbFr_8pL.js → ishikawaDiagram-UXIWVN3A-WakLHKte.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-Sq2ja3q8.js → journeyDiagram-VCZTEJTY-CrqXKtzk.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-C63qPhtK.js → kanban-definition-6JOO6SKY-DdSC9eJ-.js} +1 -1
- package/dist/client/assets/{layout-CFEuVj4N.js → layout-BI1Oko_a.js} +1 -1
- package/dist/client/assets/{linear-B626G8Op.js → linear-KUjZtfMe.js} +1 -1
- package/dist/client/assets/{mermaid.core-BT7SVKBR.js → mermaid.core-DNlIf4_3.js} +4 -4
- package/dist/client/assets/{min-MbEOApMw.js → min-DCAecveI.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-DMpht5Mg.js → mindmap-definition-QFDTVHPH-BSCyQA-4.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-B4XS0haZ.js → pieDiagram-DEJITSTG-C_1I63z5.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CReo5Ajb.js → quadrantDiagram-34T5L4WZ-CtqeYPcA.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-A50aqG-K.js → requirementDiagram-MS252O5E-Cpakdg-s.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-471VOdlW.js → sankeyDiagram-XADWPNL6-CK_eMH3e.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-BAvOyUC-.js → sequenceDiagram-FGHM5R23-D2YygHqF.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-gCtZrvR-.js → stateDiagram-FHFEXIEX-DEDhnbhU.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-ji50PgJQ.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-CTHf-uxq.js → timeline-definition-GMOUNBTQ-Brgny70W.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-5gvXi0dO.js → vennDiagram-DHZGUBPP-B1I5E8Hy.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-XR-qQTWZ.js → wardley-RL74JXVD-P0VCYfKI.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-EwPsMHdt.js → wardleyDiagram-NUSXRM2D-DhYC5Sus.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-CePwuUb7.js → xychartDiagram-5P7HB3ND-ByJX99rJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/diff/routes.mjs +207 -34
- package/dist/fleet-plugins/file-explorer/routes.mjs +1 -1
- package/dist/fleet-plugins/skills/routes.mjs +101 -83
- package/dist/fleet-plugins/terminal/routes.mjs +556 -325
- package/package.json +1 -1
- package/dist/client/assets/channel-DlT8zCVq.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-B-UN9f9r.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-B-UN9f9r.js +0 -1
- package/dist/client/assets/clone-CDPRFCO0.js +0 -1
- package/dist/client/assets/index-DWgndVkX.js +0 -404
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-Bdb0YU66.js +0 -1
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from 'fs/promises';
|
|
3
|
-
import path2 from 'path';
|
|
4
2
|
import { spawn } from 'child_process';
|
|
3
|
+
import 'fs';
|
|
4
|
+
import path3 from 'path';
|
|
5
|
+
import fs from 'fs/promises';
|
|
5
6
|
|
|
6
7
|
// sdk/plugin/node.ts
|
|
7
8
|
function definePlugin(definition) {
|
|
8
9
|
return definition;
|
|
9
10
|
}
|
|
10
|
-
function registerRouter(ctx,
|
|
11
|
-
ctx.registerRouter(normalizePath(
|
|
11
|
+
function registerRouter(ctx, path4, handler) {
|
|
12
|
+
ctx.registerRouter(normalizePath(path4), handler);
|
|
12
13
|
}
|
|
13
|
-
function normalizePath(
|
|
14
|
-
if (
|
|
15
|
-
return
|
|
14
|
+
function normalizePath(path4) {
|
|
15
|
+
if (path4.length === 0 || path4 === "/") return "/";
|
|
16
|
+
return path4;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ../../packages/core-process/src/hidden.ts
|
|
20
|
+
function withHidden(options) {
|
|
21
|
+
return { ...options ?? {}, windowsHide: true };
|
|
16
22
|
}
|
|
23
|
+
|
|
24
|
+
// ../fleet-plugins/diff/server/git-executor.ts
|
|
17
25
|
var GitExecutorError = class extends Error {
|
|
18
26
|
code;
|
|
19
27
|
exitCode;
|
|
@@ -34,7 +42,7 @@ function runGit(args, opts) {
|
|
|
34
42
|
return new Promise((resolve, reject) => {
|
|
35
43
|
let child;
|
|
36
44
|
try {
|
|
37
|
-
child = spawn("git", args, { cwd: opts.cwd, shell: false
|
|
45
|
+
child = spawn("git", args, withHidden({ cwd: opts.cwd, shell: false }));
|
|
38
46
|
} catch (error) {
|
|
39
47
|
const code = error.code === "ENOENT" ? "git_unavailable" : "spawn_failed";
|
|
40
48
|
reject(new GitExecutorError(code, String(error)));
|
|
@@ -89,8 +97,6 @@ function runGit(args, opts) {
|
|
|
89
97
|
});
|
|
90
98
|
});
|
|
91
99
|
}
|
|
92
|
-
|
|
93
|
-
// ../fleet-plugins/diff/server/diff.ts
|
|
94
100
|
function isPlainObject(value) {
|
|
95
101
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
96
102
|
}
|
|
@@ -141,9 +147,9 @@ function isNoHeadError(error) {
|
|
|
141
147
|
}
|
|
142
148
|
async function resolveGitCwd(theaterPath, rawSubPath) {
|
|
143
149
|
if (rawSubPath === "") return { gitCwd: theaterPath };
|
|
144
|
-
const resolvedSub =
|
|
145
|
-
if (!resolvedSub.startsWith(theaterPath +
|
|
146
|
-
if (
|
|
150
|
+
const resolvedSub = path3.resolve(theaterPath, rawSubPath);
|
|
151
|
+
if (!resolvedSub.startsWith(theaterPath + path3.sep) && resolvedSub !== theaterPath) return null;
|
|
152
|
+
if (path3.normalize(rawSubPath).startsWith("..")) return null;
|
|
147
153
|
let realTheater;
|
|
148
154
|
let realSub;
|
|
149
155
|
try {
|
|
@@ -154,7 +160,7 @@ async function resolveGitCwd(theaterPath, rawSubPath) {
|
|
|
154
160
|
} catch {
|
|
155
161
|
return null;
|
|
156
162
|
}
|
|
157
|
-
const norm = realTheater.endsWith(
|
|
163
|
+
const norm = realTheater.endsWith(path3.sep) ? realTheater : realTheater + path3.sep;
|
|
158
164
|
if (realSub !== realTheater && !realSub.startsWith(norm)) return null;
|
|
159
165
|
return { gitCwd: resolvedSub };
|
|
160
166
|
}
|
|
@@ -263,12 +269,12 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
263
269
|
}
|
|
264
270
|
const { gitCwd } = cwdResult;
|
|
265
271
|
const rawFilePath = body.filePath;
|
|
266
|
-
const resolvedFilePath =
|
|
267
|
-
if (!resolvedFilePath.startsWith(gitCwd +
|
|
272
|
+
const resolvedFilePath = path3.resolve(gitCwd, rawFilePath);
|
|
273
|
+
if (!resolvedFilePath.startsWith(gitCwd + path3.sep) && resolvedFilePath !== gitCwd) {
|
|
268
274
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
269
275
|
return;
|
|
270
276
|
}
|
|
271
|
-
const relativePath =
|
|
277
|
+
const relativePath = path3.normalize(rawFilePath);
|
|
272
278
|
if (relativePath.startsWith("..")) {
|
|
273
279
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
274
280
|
return;
|
|
@@ -286,7 +292,7 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
286
292
|
ctx.host.http.writeJson(res, 404, { error: "file_not_found" });
|
|
287
293
|
return;
|
|
288
294
|
}
|
|
289
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
295
|
+
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
|
|
290
296
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
291
297
|
return;
|
|
292
298
|
}
|
|
@@ -302,7 +308,7 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
302
308
|
fs.realpath(gitCwd),
|
|
303
309
|
fs.realpath(resolvedFilePath)
|
|
304
310
|
]);
|
|
305
|
-
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd +
|
|
311
|
+
if (realFile !== realGitCwd && !realFile.startsWith(realGitCwd + path3.sep)) {
|
|
306
312
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
307
313
|
return;
|
|
308
314
|
}
|
|
@@ -328,10 +334,169 @@ async function handleDiffFile(req, res, ctx) {
|
|
|
328
334
|
throw error;
|
|
329
335
|
}
|
|
330
336
|
}
|
|
337
|
+
|
|
338
|
+
// ../fleet-plugins/diff/server/commit.ts
|
|
339
|
+
var REF_RE = /^[0-9a-f]{7,40}$/i;
|
|
340
|
+
function isPlainObject2(value) {
|
|
341
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
342
|
+
}
|
|
343
|
+
async function handleDiffCommit(req, res, ctx) {
|
|
344
|
+
if (req.method !== "POST") {
|
|
345
|
+
ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!ctx.host.security.isTerminalAuthorized(req)) {
|
|
349
|
+
ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const body = await ctx.host.http.readJsonBody(req);
|
|
353
|
+
if (!isPlainObject2(body)) {
|
|
354
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const theaterId = body.theaterId;
|
|
358
|
+
if (typeof theaterId !== "string") {
|
|
359
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const ref = body.ref;
|
|
363
|
+
if (typeof ref !== "string" || !REF_RE.test(ref)) {
|
|
364
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_ref" });
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const theaterPath = ctx.host.paths.resolveTheaterPath(theaterId);
|
|
368
|
+
if (!theaterPath) {
|
|
369
|
+
ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const rawSubPath = typeof body.subPath === "string" ? body.subPath : "";
|
|
373
|
+
const cwdResult = await resolveGitCwd(theaterPath, rawSubPath);
|
|
374
|
+
if (!cwdResult) {
|
|
375
|
+
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const { gitCwd } = cwdResult;
|
|
379
|
+
try {
|
|
380
|
+
const result = await runGit(["show", ref, "--unified=3", "--format="], { cwd: gitCwd });
|
|
381
|
+
ctx.host.http.writeJson(res, 200, { content: result.stdout, ...result.truncated ? { truncated: true } : {} });
|
|
382
|
+
} catch (error) {
|
|
383
|
+
if (error instanceof GitExecutorError) {
|
|
384
|
+
if (error.code === "no_git_repo" || error.code === "git_unavailable") {
|
|
385
|
+
ctx.host.http.writeJson(res, 422, { error: error.code });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
ctx.host.http.writeJson(res, 500, { error: "git_failed" });
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ../fleet-plugins/diff/server/log.ts
|
|
396
|
+
function isPlainObject3(value) {
|
|
397
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
398
|
+
}
|
|
399
|
+
function parseLogOutput(stdout) {
|
|
400
|
+
if (!stdout.trim()) return [];
|
|
401
|
+
const commits = [];
|
|
402
|
+
for (const chunk of stdout.split("")) {
|
|
403
|
+
const trimmed = chunk.trimStart();
|
|
404
|
+
if (!trimmed) continue;
|
|
405
|
+
const lines = trimmed.split("\n");
|
|
406
|
+
const firstLine = lines[0] ?? "";
|
|
407
|
+
const fields = firstLine.split("\0");
|
|
408
|
+
const fullHash = fields[0] ?? "";
|
|
409
|
+
const shortHash = fields[1] ?? "";
|
|
410
|
+
const subject = fields[2] ?? "";
|
|
411
|
+
const authorName = fields[3] ?? "";
|
|
412
|
+
const relTime = fields[4] ?? "";
|
|
413
|
+
const refsRaw = fields[5] ?? "";
|
|
414
|
+
const parentsRaw = fields[6] ?? "";
|
|
415
|
+
if (!fullHash) continue;
|
|
416
|
+
const refs = refsRaw.split(",").map((r) => r.trim()).filter(Boolean);
|
|
417
|
+
const parents = parentsRaw.split(" ").map((p) => p.trim()).filter(Boolean);
|
|
418
|
+
let additions = 0;
|
|
419
|
+
let deletions = 0;
|
|
420
|
+
for (const line of lines.slice(1)) {
|
|
421
|
+
if (!line.trim()) continue;
|
|
422
|
+
const parts = line.split(" ");
|
|
423
|
+
if (parts.length < 2) continue;
|
|
424
|
+
const a = parseInt(parts[0] ?? "0", 10);
|
|
425
|
+
const d = parseInt(parts[1] ?? "0", 10);
|
|
426
|
+
if (!isNaN(a)) additions += a;
|
|
427
|
+
if (!isNaN(d)) deletions += d;
|
|
428
|
+
}
|
|
429
|
+
commits.push({ shortHash, fullHash, subject, authorName, relTime, refs, parents, additions, deletions });
|
|
430
|
+
}
|
|
431
|
+
return commits;
|
|
432
|
+
}
|
|
433
|
+
function isNoHeadError2(error) {
|
|
434
|
+
if (!(error instanceof GitExecutorError)) return false;
|
|
435
|
+
if (error.code !== "non_zero_exit") return false;
|
|
436
|
+
return error.stderr.includes("unknown revision") || error.stderr.includes("bad revision");
|
|
437
|
+
}
|
|
438
|
+
async function handleDiffLog(req, res, ctx) {
|
|
439
|
+
if (req.method !== "POST") {
|
|
440
|
+
ctx.host.http.writeJson(res, 405, { error: "Method not allowed" });
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (!ctx.host.security.isTerminalAuthorized(req)) {
|
|
444
|
+
ctx.host.http.writeJson(res, 401, { error: "unauthorized" });
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const body = await ctx.host.http.readJsonBody(req);
|
|
448
|
+
if (!isPlainObject3(body)) {
|
|
449
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const theaterId = body.theaterId;
|
|
453
|
+
if (typeof theaterId !== "string") {
|
|
454
|
+
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const theaterPath = ctx.host.paths.resolveTheaterPath(theaterId);
|
|
458
|
+
if (!theaterPath) {
|
|
459
|
+
ctx.host.http.writeJson(res, 404, { error: "theater_not_found" });
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const rawSubPath = typeof body.subPath === "string" ? body.subPath : "";
|
|
463
|
+
const cwdResult = await resolveGitCwd(theaterPath, rawSubPath);
|
|
464
|
+
if (!cwdResult) {
|
|
465
|
+
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const { gitCwd } = cwdResult;
|
|
469
|
+
try {
|
|
470
|
+
const result = await runGit(
|
|
471
|
+
["log", "-n", "50", "--numstat", "--pretty=format:%x1e%H%x00%h%x00%s%x00%an%x00%ar%x00%D%x00%P"],
|
|
472
|
+
{ cwd: gitCwd }
|
|
473
|
+
);
|
|
474
|
+
const commits = parseLogOutput(result.stdout);
|
|
475
|
+
ctx.host.http.writeJson(res, 200, { commits, ...result.truncated ? { truncated: true } : {} });
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (error instanceof GitExecutorError) {
|
|
478
|
+
if (error.code === "no_git_repo") {
|
|
479
|
+
ctx.host.http.writeJson(res, 200, { commits: [] });
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (error.code === "git_unavailable") {
|
|
483
|
+
ctx.host.http.writeJson(res, 422, { error: error.code });
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (isNoHeadError2(error)) {
|
|
487
|
+
ctx.host.http.writeJson(res, 200, { commits: [] });
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
ctx.host.http.writeJson(res, 500, { error: "git_failed" });
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
throw error;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
331
496
|
var REPOS_CAP = 200;
|
|
332
497
|
var HARD_CAP_DEPTH = 8;
|
|
333
498
|
var DEFAULT_DEPTH = 3;
|
|
334
|
-
function
|
|
499
|
+
function isPlainObject4(value) {
|
|
335
500
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
336
501
|
}
|
|
337
502
|
async function resolveRepoBranch(repoDir) {
|
|
@@ -351,13 +516,13 @@ async function detectWorktreeParent(dir, gitFileContent) {
|
|
|
351
516
|
const match = gitFileContent.trim().match(/^gitdir:\s*(.+)$/m);
|
|
352
517
|
if (!match) return null;
|
|
353
518
|
const rawPointer = match[1].trim();
|
|
354
|
-
const resolvedGitdir =
|
|
355
|
-
|
|
519
|
+
const resolvedGitdir = path3.normalize(
|
|
520
|
+
path3.isAbsolute(rawPointer) ? rawPointer : path3.resolve(dir, rawPointer)
|
|
356
521
|
);
|
|
357
|
-
if (resolvedGitdir.includes(`${
|
|
522
|
+
if (resolvedGitdir.includes(`${path3.sep}modules${path3.sep}`)) {
|
|
358
523
|
return null;
|
|
359
524
|
}
|
|
360
|
-
const worktreesSegment = `${
|
|
525
|
+
const worktreesSegment = `${path3.sep}.git${path3.sep}worktrees${path3.sep}`;
|
|
361
526
|
const wtIdx = resolvedGitdir.indexOf(worktreesSegment);
|
|
362
527
|
if (wtIdx === -1) {
|
|
363
528
|
return null;
|
|
@@ -367,17 +532,17 @@ async function detectWorktreeParent(dir, gitFileContent) {
|
|
|
367
532
|
try {
|
|
368
533
|
parentAbs = await fs.realpath(parentAbsRaw);
|
|
369
534
|
} catch {
|
|
370
|
-
parentAbs =
|
|
535
|
+
parentAbs = path3.normalize(parentAbsRaw);
|
|
371
536
|
}
|
|
372
537
|
return { isWorktree: true, parentAbs };
|
|
373
538
|
}
|
|
374
539
|
async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDepth, repos, cap = REPOS_CAP) {
|
|
375
540
|
if (repos.length >= cap) return true;
|
|
376
|
-
const gitPath =
|
|
541
|
+
const gitPath = path3.join(dir, ".git");
|
|
377
542
|
try {
|
|
378
543
|
const gitStat = await fs.stat(gitPath);
|
|
379
|
-
const relPath =
|
|
380
|
-
const name = relPath === "" ?
|
|
544
|
+
const relPath = path3.relative(theaterPath, dir);
|
|
545
|
+
const name = relPath === "" ? path3.basename(theaterPath) : path3.basename(dir);
|
|
381
546
|
const branch = await resolveRepoBranch(dir);
|
|
382
547
|
if (gitStat.isFile()) {
|
|
383
548
|
const content = await fs.readFile(gitPath, "utf8");
|
|
@@ -407,7 +572,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
407
572
|
dirent = await dirHandle.read();
|
|
408
573
|
continue;
|
|
409
574
|
}
|
|
410
|
-
const childPath =
|
|
575
|
+
const childPath = path3.join(dir, childName);
|
|
411
576
|
let realChildPath;
|
|
412
577
|
try {
|
|
413
578
|
realChildPath = await fs.realpath(childPath);
|
|
@@ -415,7 +580,7 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
415
580
|
dirent = await dirHandle.read();
|
|
416
581
|
continue;
|
|
417
582
|
}
|
|
418
|
-
const normalizedReal = realTheaterPath.endsWith(
|
|
583
|
+
const normalizedReal = realTheaterPath.endsWith(path3.sep) ? realTheaterPath : realTheaterPath + path3.sep;
|
|
419
584
|
if (realChildPath !== realTheaterPath && !realChildPath.startsWith(normalizedReal)) {
|
|
420
585
|
dirent = await dirHandle.read();
|
|
421
586
|
continue;
|
|
@@ -442,15 +607,15 @@ async function scanRepos(theaterPath, realTheaterPath, dir, currentDepth, maxDep
|
|
|
442
607
|
function resolveWorktreeParents(raw, realTheaterPath) {
|
|
443
608
|
const absToRelPath = /* @__PURE__ */ new Map();
|
|
444
609
|
for (const entry of raw) {
|
|
445
|
-
const absDir =
|
|
446
|
-
entry.relPath === "" ? realTheaterPath :
|
|
610
|
+
const absDir = path3.normalize(
|
|
611
|
+
entry.relPath === "" ? realTheaterPath : path3.join(realTheaterPath, entry.relPath)
|
|
447
612
|
);
|
|
448
613
|
absToRelPath.set(absDir, entry.relPath);
|
|
449
614
|
}
|
|
450
615
|
return raw.map((entry) => {
|
|
451
616
|
const { _wtParentAbs, ...rest } = entry;
|
|
452
617
|
if (_wtParentAbs && entry.isWorktree) {
|
|
453
|
-
const normalizedParent =
|
|
618
|
+
const normalizedParent = path3.normalize(_wtParentAbs);
|
|
454
619
|
const parentRelPath = absToRelPath.get(normalizedParent);
|
|
455
620
|
if (parentRelPath !== void 0) {
|
|
456
621
|
return { ...rest, worktreeOf: parentRelPath };
|
|
@@ -469,7 +634,7 @@ async function handleDiffRepos(req, res, ctx) {
|
|
|
469
634
|
return;
|
|
470
635
|
}
|
|
471
636
|
const body = await ctx.host.http.readJsonBody(req);
|
|
472
|
-
if (!
|
|
637
|
+
if (!isPlainObject4(body)) {
|
|
473
638
|
ctx.host.http.writeJson(res, 400, { error: "invalid_request" });
|
|
474
639
|
return;
|
|
475
640
|
}
|
|
@@ -520,6 +685,14 @@ var routes_default = definePlugin({
|
|
|
520
685
|
await handleDiffFile(req, res, ctx);
|
|
521
686
|
return true;
|
|
522
687
|
});
|
|
688
|
+
registerRouter(ctx, "log", async ({ req, res }) => {
|
|
689
|
+
await handleDiffLog(req, res, ctx);
|
|
690
|
+
return true;
|
|
691
|
+
});
|
|
692
|
+
registerRouter(ctx, "commit", async ({ req, res }) => {
|
|
693
|
+
await handleDiffCommit(req, res, ctx);
|
|
694
|
+
return true;
|
|
695
|
+
});
|
|
523
696
|
}
|
|
524
697
|
});
|
|
525
698
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import path from 'path';
|
|
3
3
|
import { execFile } from 'child_process';
|
|
4
4
|
import fs3 from 'fs/promises';
|
|
5
5
|
import os2 from 'os';
|
|
6
6
|
import crypto4 from 'crypto';
|
|
7
7
|
import 'http';
|
|
8
8
|
import 'stream';
|
|
9
|
-
import { existsSync } from 'fs';
|
|
9
|
+
import { existsSync, accessSync, constants } from 'fs';
|
|
10
10
|
|
|
11
11
|
var __defProp = Object.defineProperty;
|
|
12
12
|
var __export = (target, all) => {
|
|
@@ -15932,6 +15932,93 @@ external_exports.union([
|
|
|
15932
15932
|
id: zRequestId
|
|
15933
15933
|
})
|
|
15934
15934
|
]);
|
|
15935
|
+
var DEFAULT_WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
15936
|
+
var WINDOWS_PATH_SEPARATOR = ";";
|
|
15937
|
+
var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat"]);
|
|
15938
|
+
var CMD_EXPANSION_SENSITIVE_PATTERN = /[%^]/;
|
|
15939
|
+
function resolvePathBinary(command, env, options = {}) {
|
|
15940
|
+
const resolved = findBinaryPath(command, env, options);
|
|
15941
|
+
return resolved ? wrapWindowsShim(resolved, env, options.platform ?? process.platform) : void 0;
|
|
15942
|
+
}
|
|
15943
|
+
function findBinaryPath(command, env, options = {}) {
|
|
15944
|
+
const platform = options.platform ?? process.platform;
|
|
15945
|
+
const isWindows2 = platform === "win32";
|
|
15946
|
+
const pathValue = isWindows2 ? env.Path ?? env.PATH ?? "" : env.PATH ?? "";
|
|
15947
|
+
const pathExts = isWindows2 ? parsePathExt(env.PATHEXT) : [""];
|
|
15948
|
+
return findOnPath(command, pathValue, pathExts, platform);
|
|
15949
|
+
}
|
|
15950
|
+
function wrapWindowsShim(resolved, env, platform) {
|
|
15951
|
+
if (platform !== "win32" || !WINDOWS_SHIM_EXTENSIONS.has(path.extname(resolved).toLowerCase())) {
|
|
15952
|
+
return { bin: resolved, prefixArgs: [] };
|
|
15953
|
+
}
|
|
15954
|
+
rejectCmdExpansionSensitiveShim(resolved);
|
|
15955
|
+
return {
|
|
15956
|
+
bin: env.ComSpec ?? "cmd.exe",
|
|
15957
|
+
prefixArgs: ["/d", "/s", "/c", "call", forceWindowsArgQuoting(resolved)]
|
|
15958
|
+
};
|
|
15959
|
+
}
|
|
15960
|
+
function rejectCmdExpansionSensitiveShim(resolved) {
|
|
15961
|
+
if (CMD_EXPANSION_SENSITIVE_PATTERN.test(resolved)) {
|
|
15962
|
+
throw new Error(`Refusing to run Windows shim path with cmd.exe expansion-sensitive characters (% or ^): ${resolved}`);
|
|
15963
|
+
}
|
|
15964
|
+
}
|
|
15965
|
+
function forceWindowsArgQuoting(value) {
|
|
15966
|
+
return `${value} `;
|
|
15967
|
+
}
|
|
15968
|
+
function findOnPath(bin, pathValue, pathExts, platform) {
|
|
15969
|
+
const pathSeparator = platform === "win32" ? WINDOWS_PATH_SEPARATOR : path.delimiter;
|
|
15970
|
+
if (hasPathSeparator(bin, platform) || path.isAbsolute(bin)) {
|
|
15971
|
+
return resolveWithExtensions(bin, pathExts, platform);
|
|
15972
|
+
}
|
|
15973
|
+
for (const entry of pathValue.split(pathSeparator)) {
|
|
15974
|
+
const searchDir = entry.length === 0 ? "." : entry;
|
|
15975
|
+
const candidate = resolveWithExtensions(path.join(searchDir, bin), pathExts, platform);
|
|
15976
|
+
if (candidate) {
|
|
15977
|
+
return candidate;
|
|
15978
|
+
}
|
|
15979
|
+
}
|
|
15980
|
+
return void 0;
|
|
15981
|
+
}
|
|
15982
|
+
function resolveWithExtensions(candidate, pathExts, platform) {
|
|
15983
|
+
if (platform !== "win32" || path.extname(candidate).length > 0) {
|
|
15984
|
+
return isAccessibleExecutable(candidate, platform) ? candidate : void 0;
|
|
15985
|
+
}
|
|
15986
|
+
for (const ext of pathExts) {
|
|
15987
|
+
if (ext.length === 0) {
|
|
15988
|
+
continue;
|
|
15989
|
+
}
|
|
15990
|
+
const withExt = `${candidate}${ext}`;
|
|
15991
|
+
if (existsSync(withExt)) {
|
|
15992
|
+
return withExt;
|
|
15993
|
+
}
|
|
15994
|
+
}
|
|
15995
|
+
return isAccessibleExecutable(candidate, platform) ? candidate : void 0;
|
|
15996
|
+
}
|
|
15997
|
+
function isAccessibleExecutable(candidate, platform) {
|
|
15998
|
+
if (!existsSync(candidate)) return false;
|
|
15999
|
+
if (platform === "win32") return true;
|
|
16000
|
+
try {
|
|
16001
|
+
accessSync(candidate, constants.X_OK);
|
|
16002
|
+
return true;
|
|
16003
|
+
} catch {
|
|
16004
|
+
return false;
|
|
16005
|
+
}
|
|
16006
|
+
}
|
|
16007
|
+
function hasPathSeparator(value, platform) {
|
|
16008
|
+
if (value.includes("/")) {
|
|
16009
|
+
return true;
|
|
16010
|
+
}
|
|
16011
|
+
return platform === "win32" && value.includes("\\");
|
|
16012
|
+
}
|
|
16013
|
+
function parsePathExt(pathExt) {
|
|
16014
|
+
const raw = pathExt && pathExt.trim().length > 0 ? pathExt : DEFAULT_WINDOWS_PATHEXT;
|
|
16015
|
+
return raw.split(WINDOWS_PATH_SEPARATOR).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
16016
|
+
}
|
|
16017
|
+
|
|
16018
|
+
// ../../packages/core-process/src/hidden.ts
|
|
16019
|
+
function withHidden(options) {
|
|
16020
|
+
return { ...options ?? {}, windowsHide: true };
|
|
16021
|
+
}
|
|
15935
16022
|
|
|
15936
16023
|
// ../../packages/core-unified-agent/models.json
|
|
15937
16024
|
var models_default = {
|
|
@@ -15939,7 +16026,7 @@ var models_default = {
|
|
|
15939
16026
|
updatedAt: "2026-07-04T00:00:00Z",
|
|
15940
16027
|
providers: {
|
|
15941
16028
|
claude: {
|
|
15942
|
-
name: "Claude Code
|
|
16029
|
+
name: "Claude Code",
|
|
15943
16030
|
defaultModel: "opus[1m]",
|
|
15944
16031
|
models: [
|
|
15945
16032
|
{ modelId: "haiku", name: "Claude Haiku", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "low" } },
|
|
@@ -15952,7 +16039,7 @@ var models_default = {
|
|
|
15952
16039
|
]
|
|
15953
16040
|
},
|
|
15954
16041
|
codex: {
|
|
15955
|
-
name: "
|
|
16042
|
+
name: "Codex",
|
|
15956
16043
|
defaultModel: "gpt-5.4",
|
|
15957
16044
|
models: [
|
|
15958
16045
|
{ modelId: "gpt-5.4", name: "GPT-5.4", effort: { supported: true, levels: ["low", "medium", "high", "xhigh"], default: "high" } },
|
|
@@ -16160,75 +16247,6 @@ Object.entries(CLI_BACKENDS).map(([id, backend]) => ({
|
|
|
16160
16247
|
|
|
16161
16248
|
// ../../packages/core-unified-agent/src/client/UnifiedCodexAgentClient.ts
|
|
16162
16249
|
process.platform === "win32";
|
|
16163
|
-
var DEFAULT_WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
16164
|
-
var WINDOWS_PATH_SEPARATOR = ";";
|
|
16165
|
-
var WINDOWS_SHIM_EXTENSIONS = /* @__PURE__ */ new Set([".cmd", ".bat"]);
|
|
16166
|
-
var CMD_EXPANSION_SENSITIVE_PATTERN = /[%^]/;
|
|
16167
|
-
function resolvePathBinary(command, env, options = {}) {
|
|
16168
|
-
const platform = options.platform ?? process.platform;
|
|
16169
|
-
const isWindows2 = platform === "win32";
|
|
16170
|
-
const pathValue = isWindows2 ? env.Path ?? env.PATH ?? "" : env.PATH ?? "";
|
|
16171
|
-
const pathExts = isWindows2 ? parsePathExt(env.PATHEXT) : [""];
|
|
16172
|
-
const resolved = findOnPath(command, pathValue, pathExts, platform);
|
|
16173
|
-
return resolved ? wrapWindowsShim(resolved, env, platform) : void 0;
|
|
16174
|
-
}
|
|
16175
|
-
function wrapWindowsShim(resolved, env, platform) {
|
|
16176
|
-
if (platform !== "win32" || !WINDOWS_SHIM_EXTENSIONS.has(path2.extname(resolved).toLowerCase())) {
|
|
16177
|
-
return { bin: resolved, prefixArgs: [] };
|
|
16178
|
-
}
|
|
16179
|
-
rejectCmdExpansionSensitiveShim(resolved);
|
|
16180
|
-
return {
|
|
16181
|
-
bin: env.ComSpec ?? "cmd.exe",
|
|
16182
|
-
prefixArgs: ["/d", "/s", "/c", "call", forceWindowsArgQuoting(resolved)]
|
|
16183
|
-
};
|
|
16184
|
-
}
|
|
16185
|
-
function rejectCmdExpansionSensitiveShim(resolved) {
|
|
16186
|
-
if (CMD_EXPANSION_SENSITIVE_PATTERN.test(resolved)) {
|
|
16187
|
-
throw new Error(`Refusing to run Windows shim path with cmd.exe expansion-sensitive characters (% or ^): ${resolved}`);
|
|
16188
|
-
}
|
|
16189
|
-
}
|
|
16190
|
-
function forceWindowsArgQuoting(value) {
|
|
16191
|
-
return `${value} `;
|
|
16192
|
-
}
|
|
16193
|
-
function findOnPath(bin, pathValue, pathExts, platform) {
|
|
16194
|
-
const pathSeparator = platform === "win32" ? WINDOWS_PATH_SEPARATOR : path2.delimiter;
|
|
16195
|
-
if (hasPathSeparator(bin, platform) || path2.isAbsolute(bin)) {
|
|
16196
|
-
return resolveWithExtensions(bin, pathExts, platform);
|
|
16197
|
-
}
|
|
16198
|
-
for (const entry of pathValue.split(pathSeparator)) {
|
|
16199
|
-
const searchDir = entry.length === 0 ? "." : entry;
|
|
16200
|
-
const candidate = resolveWithExtensions(path2.join(searchDir, bin), pathExts, platform);
|
|
16201
|
-
if (candidate) {
|
|
16202
|
-
return candidate;
|
|
16203
|
-
}
|
|
16204
|
-
}
|
|
16205
|
-
return void 0;
|
|
16206
|
-
}
|
|
16207
|
-
function resolveWithExtensions(candidate, pathExts, platform) {
|
|
16208
|
-
if (platform !== "win32" || path2.extname(candidate).length > 0) {
|
|
16209
|
-
return existsSync(candidate) ? candidate : void 0;
|
|
16210
|
-
}
|
|
16211
|
-
for (const ext of pathExts) {
|
|
16212
|
-
if (ext.length === 0) {
|
|
16213
|
-
continue;
|
|
16214
|
-
}
|
|
16215
|
-
const withExt = `${candidate}${ext}`;
|
|
16216
|
-
if (existsSync(withExt)) {
|
|
16217
|
-
return withExt;
|
|
16218
|
-
}
|
|
16219
|
-
}
|
|
16220
|
-
return existsSync(candidate) ? candidate : void 0;
|
|
16221
|
-
}
|
|
16222
|
-
function hasPathSeparator(value, platform) {
|
|
16223
|
-
if (value.includes("/")) {
|
|
16224
|
-
return true;
|
|
16225
|
-
}
|
|
16226
|
-
return platform === "win32" && value.includes("\\");
|
|
16227
|
-
}
|
|
16228
|
-
function parsePathExt(pathExt) {
|
|
16229
|
-
const raw = pathExt && pathExt.trim().length > 0 ? pathExt : DEFAULT_WINDOWS_PATHEXT;
|
|
16230
|
-
return raw.split(WINDOWS_PATH_SEPARATOR).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
16231
|
-
}
|
|
16232
16250
|
|
|
16233
16251
|
// ../fleet-plugins/skills/server/cli.ts
|
|
16234
16252
|
var SKILLS_VERSION = "1.5.14";
|
|
@@ -16267,7 +16285,7 @@ async function runNpmInstall(cliHome) {
|
|
|
16267
16285
|
file2,
|
|
16268
16286
|
args,
|
|
16269
16287
|
// windowsHide: GUI 콘솔에서 하위 프로세스(cmd.exe 심 래퍼) 콘솔 창이 순간 표시되는 것을 막는다.
|
|
16270
|
-
{ shell: false,
|
|
16288
|
+
withHidden({ shell: false, timeout: BOOTSTRAP_TIMEOUT_MS })
|
|
16271
16289
|
);
|
|
16272
16290
|
child.on("close", (code) => {
|
|
16273
16291
|
if (code === 0) resolve();
|
|
@@ -16280,8 +16298,8 @@ async function ensureCliMjs(cliHome, onBootstrap) {
|
|
|
16280
16298
|
if (_cliMjsPath) return _cliMjsPath;
|
|
16281
16299
|
if (_bootstrapPromise) return _bootstrapPromise;
|
|
16282
16300
|
_bootstrapPromise = (async () => {
|
|
16283
|
-
const mjsPath =
|
|
16284
|
-
const pkgPath =
|
|
16301
|
+
const mjsPath = path.join(cliHome, "node_modules", SKILLS_PACKAGE, "bin", "cli.mjs");
|
|
16302
|
+
const pkgPath = path.join(cliHome, "node_modules", SKILLS_PACKAGE, "package.json");
|
|
16285
16303
|
let needsInstall = true;
|
|
16286
16304
|
try {
|
|
16287
16305
|
const raw = await fs3.readFile(pkgPath, "utf-8");
|
|
@@ -16310,7 +16328,7 @@ function createDefaultExecutor(cliHome) {
|
|
|
16310
16328
|
process.execPath,
|
|
16311
16329
|
[mjsPath, ...args],
|
|
16312
16330
|
// windowsHide: GUI 콘솔에서 하위 node.exe 콘솔 창이 순간 표시되는 것을 막는다.
|
|
16313
|
-
{ shell: false,
|
|
16331
|
+
withHidden({ shell: false, cwd, timeout, maxBuffer: 10 * 1024 * 1024 })
|
|
16314
16332
|
);
|
|
16315
16333
|
const stdoutParts = [];
|
|
16316
16334
|
const stderrParts = [];
|
|
@@ -16493,7 +16511,7 @@ var CLI_TIMEOUT_MS = 12e4;
|
|
|
16493
16511
|
async function readSkillSources(cwd) {
|
|
16494
16512
|
const sources = /* @__PURE__ */ new Map();
|
|
16495
16513
|
try {
|
|
16496
|
-
const raw = await fs3.readFile(
|
|
16514
|
+
const raw = await fs3.readFile(path.join(cwd, "skills-lock.json"), "utf-8");
|
|
16497
16515
|
const parsed = JSON.parse(raw);
|
|
16498
16516
|
if (typeof parsed === "object" && parsed !== null) {
|
|
16499
16517
|
for (const [name, entry] of Object.entries(parsed)) {
|
|
@@ -16504,7 +16522,7 @@ async function readSkillSources(cwd) {
|
|
|
16504
16522
|
} catch {
|
|
16505
16523
|
}
|
|
16506
16524
|
try {
|
|
16507
|
-
const raw = await fs3.readFile(
|
|
16525
|
+
const raw = await fs3.readFile(path.join(cwd, ".agents", ".skill-lock.json"), "utf-8");
|
|
16508
16526
|
const parsed = JSON.parse(raw);
|
|
16509
16527
|
if (typeof parsed === "object" && parsed !== null) {
|
|
16510
16528
|
for (const [name, entry] of Object.entries(parsed)) {
|
|
@@ -16869,7 +16887,7 @@ async function handleInstalledFile(req, res, ctx, executor) {
|
|
|
16869
16887
|
return;
|
|
16870
16888
|
}
|
|
16871
16889
|
const skillRoot = entry.path;
|
|
16872
|
-
const skillMdPath =
|
|
16890
|
+
const skillMdPath = path.join(skillRoot, "SKILL.md");
|
|
16873
16891
|
const [realAllowedRoot, realRoot, realMd] = await Promise.all([
|
|
16874
16892
|
// CLI가 보고한 skillRoot 자체도 신뢰하지 않는다 — scope의 정당한 상위 경계
|
|
16875
16893
|
// (project=theater 루트, global=홈)를 벗어나면 읽지 않는다. `.agents/skills`로
|
|
@@ -16882,11 +16900,11 @@ async function handleInstalledFile(req, res, ctx, executor) {
|
|
|
16882
16900
|
ctx.host.http.writeJson(res, 404, { error: "skill_not_found" });
|
|
16883
16901
|
return;
|
|
16884
16902
|
}
|
|
16885
|
-
if (realRoot !== realAllowedRoot && !realRoot.startsWith(realAllowedRoot +
|
|
16903
|
+
if (realRoot !== realAllowedRoot && !realRoot.startsWith(realAllowedRoot + path.sep)) {
|
|
16886
16904
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
16887
16905
|
return;
|
|
16888
16906
|
}
|
|
16889
|
-
if (realMd !== realRoot && !realMd.startsWith(realRoot +
|
|
16907
|
+
if (realMd !== realRoot && !realMd.startsWith(realRoot + path.sep)) {
|
|
16890
16908
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
16891
16909
|
return;
|
|
16892
16910
|
}
|
|
@@ -16901,7 +16919,7 @@ async function handleInstalledFile(req, res, ctx, executor) {
|
|
|
16901
16919
|
var routes_default = definePlugin({
|
|
16902
16920
|
id: "skills",
|
|
16903
16921
|
register(ctx) {
|
|
16904
|
-
const cliHome =
|
|
16922
|
+
const cliHome = path.join(ctx.host.paths.pluginDataDir("skills"), "cli");
|
|
16905
16923
|
const executor = createDefaultExecutor(cliHome);
|
|
16906
16924
|
registerRouter(ctx, "list", async ({ req, res }) => {
|
|
16907
16925
|
await handleList(req, res, ctx, executor);
|