@dotobokuri/fleet-console 1.69.0 → 1.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +38 -4
- package/dist/client/assets/{_baseUniq-BPBd-J9P.js → _baseUniq--GYoaiDn.js} +1 -1
- package/dist/client/assets/{arc-spHqy4X3.js → arc-ioTPutEb.js} +1 -1
- package/dist/client/assets/{architectureDiagram-Q4EWVU46-7CHqKfxu.js → architectureDiagram-Q4EWVU46-DLfH6cdt.js} +1 -1
- package/dist/client/assets/{blockDiagram-DXYQGD6D-YasFDgl_.js → blockDiagram-DXYQGD6D-ZM6X_dlD.js} +1 -1
- package/dist/client/assets/{c4Diagram-AHTNJAMY-CcoPJq56.js → c4Diagram-AHTNJAMY-DDPTnZGS.js} +1 -1
- package/dist/client/assets/channel-BvAoE0IP.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-MYBJn9BE.js → chunk-4BX2VUAB-0ShpcAto.js} +1 -1
- package/dist/client/assets/{chunk-4TB4RGXK-C1wg6GR_.js → chunk-4TB4RGXK-CvMAl5bA.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-BoufMT1O.js → chunk-55IACEB6-CgV8t6TS.js} +1 -1
- package/dist/client/assets/{chunk-EDXVE4YY-BzAhAIl3.js → chunk-EDXVE4YY-DQkbYjs9.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-yVrLsKzP.js → chunk-FMBD7UC4-D4Pm3UM-.js} +1 -1
- package/dist/client/assets/{chunk-OYMX7WX6-YiiKaqRQ.js → chunk-OYMX7WX6-BrWIgOQ6.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CqWQlGHT.js → chunk-QZHKN3VN-BC-GEuDl.js} +1 -1
- package/dist/client/assets/{chunk-YZCP3GAM-9yLOmU5k.js → chunk-YZCP3GAM-Dst--QPc.js} +1 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-BeVFxENS.js +1 -0
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-BeVFxENS.js +1 -0
- package/dist/client/assets/clone-TnYsvJuQ.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-XkTuSq7v.js → cose-bilkent-S5V4N54A-D87Ru6Nw.js} +1 -1
- package/dist/client/assets/{dagre-KV5264BT-DyzXIEAe.js → dagre-KV5264BT-CUG8AIPS.js} +1 -1
- package/dist/client/assets/{diagram-5BDNPKRD-DF24YlCa.js → diagram-5BDNPKRD-DrhhXUdy.js} +1 -1
- package/dist/client/assets/{diagram-G4DWMVQ6-CVhcj9RK.js → diagram-G4DWMVQ6-DgGQFVlD.js} +1 -1
- package/dist/client/assets/{diagram-MMDJMWI5-C-kGKk4K.js → diagram-MMDJMWI5-DEyFyzNg.js} +1 -1
- package/dist/client/assets/{diagram-TYMM5635-mr285WbR.js → diagram-TYMM5635-ari8AKd5.js} +1 -1
- package/dist/client/assets/{erDiagram-SMLLAGMA-BtEx-XlU.js → erDiagram-SMLLAGMA-Aywt-vx8.js} +1 -1
- package/dist/client/assets/{flowDiagram-DWJPFMVM-DuWy3xzN.js → flowDiagram-DWJPFMVM-FO46vDh_.js} +1 -1
- package/dist/client/assets/{ganttDiagram-T4ZO3ILL-B2-xEikU.js → ganttDiagram-T4ZO3ILL-C0m3r6C5.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-EM17u_t7.js → gitGraphDiagram-UUTBAWPF-CTUKGfIc.js} +1 -1
- package/dist/client/assets/{graph-xuE12FIq.js → graph-PzhNFXt_.js} +1 -1
- package/dist/client/assets/index-Bcc3Exng.css +1 -0
- package/dist/client/assets/index-DkOT93yC.js +467 -0
- package/dist/client/assets/{infoDiagram-42DDH7IO-B3MF4XtW.js → infoDiagram-42DDH7IO-CFry6eYn.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-DoQ2mGyh.js → ishikawaDiagram-UXIWVN3A-BBJvVjTh.js} +1 -1
- package/dist/client/assets/{journeyDiagram-VCZTEJTY-GeR9sFH-.js → journeyDiagram-VCZTEJTY-glHJeITG.js} +1 -1
- package/dist/client/assets/{kanban-definition-6JOO6SKY-DrrYMuvL.js → kanban-definition-6JOO6SKY-Dp7IjAmS.js} +1 -1
- package/dist/client/assets/{layout-DH63A-3u.js → layout-PhtOMDDE.js} +1 -1
- package/dist/client/assets/{linear-BIjE6zB1.js → linear-B3pjuRWl.js} +1 -1
- package/dist/client/assets/{mermaid.core-yrrC2Lqo.js → mermaid.core-DDurtDWt.js} +4 -4
- package/dist/client/assets/{min-CR-H8E6U.js → min-ZS3oLuaq.js} +1 -1
- package/dist/client/assets/{mindmap-definition-QFDTVHPH-BzSC43J-.js → mindmap-definition-QFDTVHPH-D67iQI4K.js} +1 -1
- package/dist/client/assets/{pieDiagram-DEJITSTG-BdghNtCc.js → pieDiagram-DEJITSTG-B95nftHb.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-34T5L4WZ-s_zN2epw.js → quadrantDiagram-34T5L4WZ-BbBVDSwm.js} +1 -1
- package/dist/client/assets/{requirementDiagram-MS252O5E-DuiGG6Tx.js → requirementDiagram-MS252O5E-Dbtcs04o.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-XADWPNL6-Bp0JBk2g.js → sankeyDiagram-XADWPNL6-BuqmxYyX.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-FGHM5R23-AzQ3M31V.js → sequenceDiagram-FGHM5R23-DmLihX3U.js} +1 -1
- package/dist/client/assets/{stateDiagram-FHFEXIEX-BGgm44ev.js → stateDiagram-FHFEXIEX-D3Z_iYXj.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-BbUn16lr.js +1 -0
- package/dist/client/assets/{timeline-definition-GMOUNBTQ-UqfsiBJY.js → timeline-definition-GMOUNBTQ-witmATQr.js} +1 -1
- package/dist/client/assets/{vennDiagram-DHZGUBPP-PsCGF9nR.js → vennDiagram-DHZGUBPP-BDI4z0F-.js} +1 -1
- package/dist/client/assets/{wardley-RL74JXVD-BDQbnh-d.js → wardley-RL74JXVD-oYmBD5eO.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-NUSXRM2D-CtVA1JMs.js → wardleyDiagram-NUSXRM2D-DhnyRjSq.js} +1 -1
- package/dist/client/assets/{xychartDiagram-5P7HB3ND-BsLlZWbc.js → xychartDiagram-5P7HB3ND-DUY-bU_6.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/fleet-plugins/file-explorer/routes.mjs +138 -96
- package/dist/fleet-plugins/ledger/routes.mjs +33 -2
- package/dist/fleet-plugins/quota/routes.mjs +33 -2
- package/dist/fleet-plugins/repository/routes.mjs +41 -5
- package/dist/fleet-plugins/scuttlebutt/routes.mjs +34 -2
- package/dist/fleet-plugins/skills/routes.mjs +126 -60
- package/dist/fleet-plugins/terminal/routes.mjs +1482 -786
- package/dist/fleet.mjs +1462 -705
- package/package.json +1 -1
- package/dist/client/assets/channel-DfEjadrw.js +0 -1
- package/dist/client/assets/classDiagram-6PBFFD2Q-D9vQdv0q.js +0 -1
- package/dist/client/assets/classDiagram-v2-HSJHXN6E-D9vQdv0q.js +0 -1
- package/dist/client/assets/clone-Ck2x5JX2.js +0 -1
- package/dist/client/assets/index-C5EOZGlC.js +0 -466
- package/dist/client/assets/index-CHHE3nPw.css +0 -1
- package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-CZDbnuN-.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFile } from 'node:child_process';
|
|
4
|
-
import
|
|
4
|
+
import fs4 from 'node:fs/promises';
|
|
5
5
|
import os2 from 'node:os';
|
|
6
6
|
import { existsSync, accessSync, constants } from 'node:fs';
|
|
7
7
|
import crypto from 'node:crypto';
|
|
@@ -10,16 +10,16 @@ import crypto from 'node:crypto';
|
|
|
10
10
|
function definePlugin(definition) {
|
|
11
11
|
return definition;
|
|
12
12
|
}
|
|
13
|
-
function registerRouter(ctx,
|
|
13
|
+
function registerRouter(ctx, path7, handler, catalog) {
|
|
14
14
|
if (catalog) {
|
|
15
|
-
ctx.registerRouter(normalizePath(
|
|
15
|
+
ctx.registerRouter(normalizePath(path7), handler, catalog);
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
ctx.registerRouter(normalizePath(
|
|
18
|
+
ctx.registerRouter(normalizePath(path7), handler);
|
|
19
19
|
}
|
|
20
|
-
function normalizePath(
|
|
21
|
-
if (
|
|
22
|
-
return
|
|
20
|
+
function normalizePath(path7) {
|
|
21
|
+
if (path7.length === 0 || path7 === "/") return "/";
|
|
22
|
+
return path7;
|
|
23
23
|
}
|
|
24
24
|
var DEFAULT_WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
25
25
|
var WINDOWS_PATH_SEPARATOR = ";";
|
|
@@ -161,14 +161,14 @@ async function ensureCliMjs(cliHome, onBootstrap) {
|
|
|
161
161
|
const pkgPath = path.join(cliHome, "node_modules", SKILLS_PACKAGE, "package.json");
|
|
162
162
|
let needsInstall = true;
|
|
163
163
|
try {
|
|
164
|
-
const raw = await
|
|
164
|
+
const raw = await fs4.readFile(pkgPath, "utf-8");
|
|
165
165
|
const pkg = JSON.parse(raw);
|
|
166
166
|
if (pkg.version === SKILLS_VERSION) needsInstall = false;
|
|
167
167
|
} catch {
|
|
168
168
|
}
|
|
169
169
|
if (needsInstall) {
|
|
170
170
|
onBootstrap?.(WARMUP_LINE);
|
|
171
|
-
await
|
|
171
|
+
await fs4.mkdir(cliHome, { recursive: true });
|
|
172
172
|
await runNpmInstall(cliHome);
|
|
173
173
|
}
|
|
174
174
|
_cliMjsPath = mjsPath;
|
|
@@ -293,6 +293,55 @@ function getJobResult(id, cursor) {
|
|
|
293
293
|
exitCode: job.exitCode
|
|
294
294
|
};
|
|
295
295
|
}
|
|
296
|
+
var HEAD_BYTES = 8192;
|
|
297
|
+
var MAX_DESCRIPTION = 500;
|
|
298
|
+
function parseSkillDescription(head) {
|
|
299
|
+
const text = head.charCodeAt(0) === 65279 ? head.slice(1) : head;
|
|
300
|
+
if (!/^---\r?\n/.test(text)) return void 0;
|
|
301
|
+
const body = text.slice(text.indexOf("\n") + 1);
|
|
302
|
+
const endIndex = body.search(/^---\s*$/m);
|
|
303
|
+
if (endIndex === -1) return void 0;
|
|
304
|
+
const lines = body.slice(0, endIndex).split(/\r?\n/);
|
|
305
|
+
const startLine = lines.findIndex((line) => /^description\s*:/.test(line));
|
|
306
|
+
if (startLine === -1) return void 0;
|
|
307
|
+
const first = (lines[startLine] ?? "").replace(/^description\s*:\s*/, "");
|
|
308
|
+
if (/^[>|]/.test(first.trim())) return void 0;
|
|
309
|
+
const parts = [first.trim()];
|
|
310
|
+
for (let i = startLine + 1; i < lines.length; i += 1) {
|
|
311
|
+
const line = lines[i] ?? "";
|
|
312
|
+
if (!/^\s+\S/.test(line)) break;
|
|
313
|
+
parts.push(line.trim());
|
|
314
|
+
}
|
|
315
|
+
let value = parts.filter(Boolean).join(" ").trim();
|
|
316
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length > 1 || value.startsWith("'") && value.endsWith("'") && value.length > 1) {
|
|
317
|
+
value = value.slice(1, -1).trim();
|
|
318
|
+
}
|
|
319
|
+
if (!value) return void 0;
|
|
320
|
+
return value.length > MAX_DESCRIPTION ? value.slice(0, MAX_DESCRIPTION) : value;
|
|
321
|
+
}
|
|
322
|
+
async function readSkillDescription(skillRoot, allowedRoot) {
|
|
323
|
+
let handle;
|
|
324
|
+
try {
|
|
325
|
+
const [realAllowedRoot, realRoot] = await Promise.all([
|
|
326
|
+
fs4.realpath(allowedRoot),
|
|
327
|
+
fs4.realpath(skillRoot)
|
|
328
|
+
]);
|
|
329
|
+
if (realRoot !== realAllowedRoot && !realRoot.startsWith(realAllowedRoot + path.sep)) {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
const realMd = await fs4.realpath(path.join(realRoot, "SKILL.md"));
|
|
333
|
+
if (!realMd.startsWith(realRoot + path.sep)) return void 0;
|
|
334
|
+
handle = await fs4.open(realMd, "r");
|
|
335
|
+
const buffer = Buffer.alloc(HEAD_BYTES);
|
|
336
|
+
const { bytesRead } = await handle.read(buffer, 0, HEAD_BYTES, 0);
|
|
337
|
+
return parseSkillDescription(buffer.subarray(0, bytesRead).toString("utf-8"));
|
|
338
|
+
} catch {
|
|
339
|
+
return void 0;
|
|
340
|
+
} finally {
|
|
341
|
+
await handle?.close().catch(() => {
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
296
345
|
var ProjectPathError = class extends Error {
|
|
297
346
|
code;
|
|
298
347
|
constructor(code) {
|
|
@@ -305,12 +354,12 @@ async function resolveProjectCwd(theaterRoot) {
|
|
|
305
354
|
const lexicalRoot = path.resolve(theaterRoot);
|
|
306
355
|
let realRoot;
|
|
307
356
|
try {
|
|
308
|
-
realRoot = await
|
|
357
|
+
realRoot = await fs4.realpath(lexicalRoot);
|
|
309
358
|
} catch (error) {
|
|
310
359
|
throw mapFilesystemError(error);
|
|
311
360
|
}
|
|
312
361
|
try {
|
|
313
|
-
if (!(await
|
|
362
|
+
if (!(await fs4.stat(realRoot)).isDirectory()) {
|
|
314
363
|
throw new ProjectPathError("invalid_path");
|
|
315
364
|
}
|
|
316
365
|
} catch (error) {
|
|
@@ -402,30 +451,47 @@ var CLI_TIMEOUT_MS = 12e4;
|
|
|
402
451
|
var USERINFO_URL_RE = /\b[a-z][a-z0-9+.-]*:\/\/[^\s/@]+@[^\s]+/gi;
|
|
403
452
|
var TOKEN_URL_PARAM_RE = /([?&](?:access_?token|token|api_?key|apikey|auth(?:orization)?|password|secret|credential)=)[^&#\s]*/gi;
|
|
404
453
|
var installedSkillsByTheater = /* @__PURE__ */ new Map();
|
|
454
|
+
function isRecord(value) {
|
|
455
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
456
|
+
}
|
|
457
|
+
function hasSourceEntry(table) {
|
|
458
|
+
return Object.values(table).some((entry) => isRecord(entry) && typeof entry["source"] === "string");
|
|
459
|
+
}
|
|
460
|
+
function isLockShape(parsed) {
|
|
461
|
+
if (!isRecord(parsed)) return false;
|
|
462
|
+
if ("skills" in parsed) return isRecord(parsed["skills"]);
|
|
463
|
+
return hasSourceEntry(parsed);
|
|
464
|
+
}
|
|
465
|
+
function collectLockSources(lock, sources) {
|
|
466
|
+
const table = isRecord(lock.skills) ? lock.skills : lock;
|
|
467
|
+
for (const [name, entry] of Object.entries(table)) {
|
|
468
|
+
if (isRecord(entry) && typeof entry["source"] === "string") {
|
|
469
|
+
sources.set(name, entry["source"]);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
405
473
|
async function readSkillSources(cwd) {
|
|
406
474
|
const sources = /* @__PURE__ */ new Map();
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
475
|
+
for (const candidate of [
|
|
476
|
+
path.join(cwd, "skills-lock.json"),
|
|
477
|
+
path.join(cwd, ".agents", ".skill-lock.json")
|
|
478
|
+
]) {
|
|
479
|
+
let raw;
|
|
480
|
+
try {
|
|
481
|
+
raw = await fs4.readFile(candidate, "utf-8");
|
|
482
|
+
} catch {
|
|
483
|
+
continue;
|
|
415
484
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
422
|
-
for (const [name, entry] of Object.entries(parsed)) {
|
|
423
|
-
if (entry && typeof entry.source === "string") sources.set(name, entry.source);
|
|
485
|
+
try {
|
|
486
|
+
const parsed = JSON.parse(raw);
|
|
487
|
+
if (isLockShape(parsed)) {
|
|
488
|
+
collectLockSources(parsed, sources);
|
|
489
|
+
return { sources, lockRead: true };
|
|
424
490
|
}
|
|
491
|
+
} catch {
|
|
425
492
|
}
|
|
426
|
-
} catch {
|
|
427
493
|
}
|
|
428
|
-
return sources;
|
|
494
|
+
return { sources, lockRead: false };
|
|
429
495
|
}
|
|
430
496
|
async function runListCommand(args, cwd, executor) {
|
|
431
497
|
const result = await executor(args, { cwd, timeout: CLI_TIMEOUT_MS });
|
|
@@ -488,42 +554,42 @@ function spawnJobAsync(jobId, args, cwd, redactionPaths, executor) {
|
|
|
488
554
|
}).then((result) => finishJob(jobId, result.exitCode)).catch(() => finishJob(jobId, 1));
|
|
489
555
|
});
|
|
490
556
|
}
|
|
557
|
+
async function toListItems(raw, scope, lock, allowedRoot) {
|
|
558
|
+
return Promise.all(raw.map(async (entry) => {
|
|
559
|
+
const description = entry.path ? await readSkillDescription(entry.path, allowedRoot) : void 0;
|
|
560
|
+
const source = lock.sources.get(entry.name);
|
|
561
|
+
const unmanaged = !source && lock.lockRead;
|
|
562
|
+
return {
|
|
563
|
+
name: entry.name,
|
|
564
|
+
scope,
|
|
565
|
+
agents: entry.agents,
|
|
566
|
+
...source ? { source } : {},
|
|
567
|
+
...unmanaged ? { unmanaged: true } : {},
|
|
568
|
+
...description ? { description } : {},
|
|
569
|
+
displayPath: buildDisplayPath(scope, entry.name)
|
|
570
|
+
};
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
491
573
|
async function listInstalledSkills(projectCwd, executor) {
|
|
574
|
+
const homeDir = os2.homedir();
|
|
492
575
|
if (projectCwd) {
|
|
493
576
|
const [projectRaw, globalRaw2, projectSources, globalSources2] = await Promise.all([
|
|
494
577
|
runListCommand(["list", "--json"], projectCwd, executor),
|
|
495
|
-
runListCommand(["list", "-g", "--json"],
|
|
578
|
+
runListCommand(["list", "-g", "--json"], homeDir, executor),
|
|
496
579
|
readSkillSources(projectCwd),
|
|
497
|
-
readSkillSources(
|
|
580
|
+
readSkillSources(homeDir)
|
|
581
|
+
]);
|
|
582
|
+
const [projectItems, globalItems] = await Promise.all([
|
|
583
|
+
toListItems(projectRaw, "project", projectSources, projectCwd),
|
|
584
|
+
toListItems(globalRaw2, "global", globalSources2, homeDir)
|
|
498
585
|
]);
|
|
499
|
-
return [
|
|
500
|
-
...projectRaw.map((entry) => ({
|
|
501
|
-
name: entry.name,
|
|
502
|
-
scope: "project",
|
|
503
|
-
agents: entry.agents,
|
|
504
|
-
source: projectSources.get(entry.name),
|
|
505
|
-
displayPath: buildDisplayPath("project", entry.name)
|
|
506
|
-
})),
|
|
507
|
-
...globalRaw2.map((entry) => ({
|
|
508
|
-
name: entry.name,
|
|
509
|
-
scope: "global",
|
|
510
|
-
agents: entry.agents,
|
|
511
|
-
source: globalSources2.get(entry.name),
|
|
512
|
-
displayPath: buildDisplayPath("global", entry.name)
|
|
513
|
-
}))
|
|
514
|
-
];
|
|
586
|
+
return [...projectItems, ...globalItems];
|
|
515
587
|
}
|
|
516
588
|
const [globalRaw, globalSources] = await Promise.all([
|
|
517
|
-
runListCommand(["list", "-g", "--json"],
|
|
518
|
-
readSkillSources(
|
|
589
|
+
runListCommand(["list", "-g", "--json"], homeDir, executor),
|
|
590
|
+
readSkillSources(homeDir)
|
|
519
591
|
]);
|
|
520
|
-
return globalRaw
|
|
521
|
-
name: entry.name,
|
|
522
|
-
scope: "global",
|
|
523
|
-
agents: entry.agents,
|
|
524
|
-
source: globalSources.get(entry.name),
|
|
525
|
-
displayPath: buildDisplayPath("global", entry.name)
|
|
526
|
-
}));
|
|
592
|
+
return toListItems(globalRaw, "global", globalSources, homeDir);
|
|
527
593
|
}
|
|
528
594
|
async function handleList(req, res, ctx, executor) {
|
|
529
595
|
if (req.method !== "GET") {
|
|
@@ -900,9 +966,9 @@ async function handleInstalledFile(req, res, ctx, executor) {
|
|
|
900
966
|
// CLI가 보고한 skillRoot 자체도 신뢰하지 않는다 — scope의 정당한 상위 경계
|
|
901
967
|
// (project=theater 루트, global=홈)를 벗어나면 읽지 않는다. `.agents/skills`로
|
|
902
968
|
// 고정하지 않는 이유: claude 단독 설치는 `.claude/skills` 아래에 놓인다.
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
969
|
+
fs4.realpath(cwd),
|
|
970
|
+
fs4.realpath(skillRoot),
|
|
971
|
+
fs4.realpath(skillMdPath).catch(() => null)
|
|
906
972
|
]);
|
|
907
973
|
if (!realMd) {
|
|
908
974
|
ctx.host.http.writeJson(res, 404, { error: "skill_not_found" });
|
|
@@ -916,7 +982,7 @@ async function handleInstalledFile(req, res, ctx, executor) {
|
|
|
916
982
|
ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
|
|
917
983
|
return;
|
|
918
984
|
}
|
|
919
|
-
const markdown = await
|
|
985
|
+
const markdown = await fs4.readFile(realMd, "utf-8");
|
|
920
986
|
ctx.host.http.writeJson(res, 200, { markdown });
|
|
921
987
|
} catch {
|
|
922
988
|
ctx.host.http.writeJson(res, 502, { error: "read_failed" });
|