kennel 0.2.44 → 0.2.46
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/core/api-client.d.ts +1 -1
- package/dist/core/api-client.js +69 -3
- package/dist/core/runtime.js +649 -140
- package/dist/llm/worker.js +11 -2
- package/dist/notes/command.js +20 -4
- package/dist/package/preview.d.ts +1 -1
- package/dist/package/preview.js +881 -540
- package/dist/site/hot-viewer.js +4 -4
- package/dist/site/server-plugins.js +468 -30
- package/dist/site/viewer.d.ts +0 -3
- package/dist/site/viewer.js +11 -41
- package/dist/tui/launcher.d.ts +11 -0
- package/dist/{site/terminal.js → tui/launcher.js} +60 -15
- package/package.json +2 -2
- package/web/assets/{abnfDiagram-VRR7QNED-ooA_TyhC.js → abnfDiagram-VRR7QNED-BOm3iBSE.js} +1 -1
- package/web/assets/{arc-DqQkY48J.js → arc-D_zfnjJW.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-DaXzRGP0.js → architectureDiagram-ZJ3FMSHR-CTXAbDtC.js} +1 -1
- package/web/assets/ask-tray-CT6DQRTC-DfTc3bnC.js +1 -0
- package/web/assets/{blockDiagram-677ZJIJ3-Br5Y_dbP.js → blockDiagram-677ZJIJ3-CcPWOAKO.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-CWIy0S0N.js → c4Diagram-LMCZKHZV-B_eNO2-f.js} +1 -1
- package/web/assets/channel-Dlb_u_Yk.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-8dxcuNwr.js → chunk-2Q5K7J3B-BtQX_f5i.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-DB27hz0Y.js → chunk-32BRIVSS-BBtfsm8_.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-CTNxGtbA.js → chunk-5VM5RSS4-CG0bf9ML.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-oKNLktOC.js → chunk-EX3LRPZG-DS2H7ilP.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-DeQNmWoS.js → chunk-JWPE2WC7-CGLMMKkr.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-BrW33Mrb.js → chunk-MOJQB5TN-C76lqDkU.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-DEkTU6j-.js → chunk-RYQCIY6F-ZY-EzC5l.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-BuDLgwdB.js → chunk-V7JOEXUC-sBv90hIh.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-BsNgrrKe.js → chunk-VR4S4FIN-jW9aFTp3.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-DjT1Bydl.js → chunk-XXDRQBXY-FPtE6zce.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-CinFzRGQ.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-CinFzRGQ.js +1 -0
- package/web/assets/client-5Q4VI7ZT-DYH5SwVp.js +52 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BtAklxrd.js → cose-bilkent-JH36ORCC-NdJGLzeJ.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-DabtZWZy.js → cynefin-VYW2F7L2-D-o_obcW.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-DVk8uQ8L.js → cynefinDiagram-TSTJHNR4-RA4QYrtB.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-CiAbBCor.js → dagre-VKFMJZFB-Bq4DfO16.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-DWp-OzJz.js → diagram-FQU43EPY-bPnACaba.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-YDllS_1P.js → diagram-G47NLZAW-cPca5hvp.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BH3l4yRj.js → diagram-NH7WQ7WH-DHyP34H8.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-CszBvxdr.js → diagram-OA4YK3LP-hVk7mHu1.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-bEhZeER6.js → diagram-WEI45ONY-Cd3AEJO3.js} +1 -1
- package/web/assets/document-view-VX2ZTQM5-CDdtofzz.js +24 -0
- package/web/assets/{ebnfDiagram-CCIWWBDH-DlpoUUO5.js → ebnfDiagram-CCIWWBDH-BxtDx2MR.js} +1 -1
- package/web/assets/{entity-search-page-YP4SPMBC-WryWZNPk.js → entity-search-page-RIYQYIJJ-IvZzjXvt.js} +5 -5
- package/web/assets/{erDiagram-Q63AITRT-DROgehIM.js → erDiagram-Q63AITRT-CtdOkrpo.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-63pYiBzl.js → flowDiagram-23GEKE2U-BDHfaJo6.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-tc34wKHJ.js → ganttDiagram-NO4QXBWP-BhV7JFJA.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-GOjqt583.js → gitGraphDiagram-IHSO6WYX-BVRBRDVk.js} +1 -1
- package/web/assets/index-BxCXNDe6.css +1 -0
- package/web/assets/index-Cl-kcrk3.js +502 -0
- package/web/assets/{infoDiagram-FWYZ7A6U-CD5BwkLQ.js → infoDiagram-FWYZ7A6U-BKuFQe6t.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-yIweY6wR.js → ishikawaDiagram-FXEZZL3T-lNbhlHEo.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-D3MeUGvn.js → journeyDiagram-5HDEW3XC-CoGf_pSm.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-CnZ5N79y.js → kanban-definition-HUTT4EX6-Cgp0WGrq.js} +1 -1
- package/web/assets/{linear-s5G6iOMz.js → linear-B3UMk7TK.js} +1 -1
- package/web/assets/{linked-session-header-DOEX4FYM-CtTOXcZn.js → linked-session-header-7BUTWROP-Coqn-Dz0.js} +1 -1
- package/web/assets/{mermaid.core-BpUKp9r_.js → mermaid.core-6bZ5bkAq.js} +4 -4
- package/web/assets/{mindmap-definition-LN4V7U3C-Bp2QH5sN.js → mindmap-definition-LN4V7U3C-BowQNgIG.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-lx1jDUmt.js → pegDiagram-2B236MQR-DrhWFvfI.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P--kf2YNbw.js → pieDiagram-ENE6RG2P-DNOg53mu.js} +1 -1
- package/web/assets/{plugin-host-modules-BKbxlu7U.js → plugin-host-modules-NvyHieUX.js} +26 -26
- package/web/assets/{project-search-page-TZG7AZBQ-3W40ayYn.js → project-search-page-O6YRLDGU-CFA4lf0c.js} +2 -2
- package/web/assets/{quadrantDiagram-ABIIQ3AL-Vr7zWQYV.js → quadrantDiagram-ABIIQ3AL-BOJ4UGbk.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-CqWD8KUC.js → railroadDiagram-RFXS5EU6-_UJ9_AEC.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-BHUBdOVh.js → requirementDiagram-TGXJPOKE-BFa61FSa.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-Cw_ZddWI.js → sankeyDiagram-HTMAVEWB-V1J9w6jl.js} +1 -1
- package/web/assets/{selection-popup-ABYH3A27-Gc-RFSWj.js → selection-popup-ZX4VUZ3A-Duh9LSP2.js} +4 -4
- package/web/assets/{sequenceDiagram-DBY2YBRQ-CyCbkAfM.js → sequenceDiagram-DBY2YBRQ-xdS7JpTx.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-BG6RM8rb.js → sizeCapture-X5ZJPWSS-DBULLaws.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-DKpZl5UU.js → stateDiagram-2N3HPSRC-DfBpQAQb.js} +1 -1
- package/web/assets/{stateDiagram-v2-6OUMAXLB-DHUy-0iS.js → stateDiagram-v2-6OUMAXLB-C-dC_PIX.js} +1 -1
- package/web/assets/{swimlanes-5IMT3BWC-D14uyHMH.js → swimlanes-5IMT3BWC-L-RTYuhP.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-BErklojq.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-f4WPhEXS.js → timeline-definition-FHXFAJF6-CAEbvdzk.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-lr4A8guw.js → vennDiagram-L72KCM5P-J10bFP8c.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-CR-Im585.js → wardleyDiagram-EHGQE667-Bm0goK-9.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DeNQfP7k.js → xychartDiagram-FW5EYKEG-BAH4TTVb.js} +1 -1
- package/web/index.html +2 -2
- package/dist/site/terminal.d.ts +0 -14
- package/web/assets/ask-tray-KUTWZ4MV-Ckn1y1Zw.js +0 -24
- package/web/assets/channel-BQxXBpeY.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-BT1TSwZs.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-BT1TSwZs.js +0 -1
- package/web/assets/index-Di6pguZz.css +0 -1
- package/web/assets/index-qmu99cjp.js +0 -493
- package/web/assets/swimlanesDiagram-G3AALYLV-C5riej52.js +0 -8
package/dist/core/runtime.js
CHANGED
|
@@ -17027,8 +17027,8 @@ var require__2020 = __commonJS(function(exports, module) {
|
|
|
17027
17027
|
import {
|
|
17028
17028
|
createServer
|
|
17029
17029
|
} from "node:http";
|
|
17030
|
-
import { readFile as
|
|
17031
|
-
import { extname as
|
|
17030
|
+
import { readFile as readFile12, realpath as realpath10, stat as stat13 } from "node:fs/promises";
|
|
17031
|
+
import { extname as extname8, isAbsolute as isAbsolute9, relative as relative14, resolve as resolve17 } from "node:path";
|
|
17032
17032
|
|
|
17033
17033
|
// packages/api/src/server/watcher.ts
|
|
17034
17034
|
import { existsSync, statSync, watch } from "node:fs";
|
|
@@ -21739,6 +21739,7 @@ var plugin = object({
|
|
|
21739
21739
|
icon: object({ paths: array(string2().min(1)).min(1) }).optional(),
|
|
21740
21740
|
id: string2(),
|
|
21741
21741
|
label: string2(),
|
|
21742
|
+
pages: array(object({ id: string2(), label: string2() })).optional(),
|
|
21742
21743
|
subpath: string2().optional()
|
|
21743
21744
|
});
|
|
21744
21745
|
var plugins = object({
|
|
@@ -21795,6 +21796,10 @@ var metadataQuerySchema = object({
|
|
|
21795
21796
|
sort: _enum(["alpha", "recent"]).optional(),
|
|
21796
21797
|
schema: string2().optional()
|
|
21797
21798
|
});
|
|
21799
|
+
var deleteNoteSchema = object({
|
|
21800
|
+
folder: string2(),
|
|
21801
|
+
path: string2()
|
|
21802
|
+
});
|
|
21798
21803
|
var documentEditSchema = object({
|
|
21799
21804
|
folder: string2(),
|
|
21800
21805
|
path: string2(),
|
|
@@ -21854,7 +21859,11 @@ var routes = {
|
|
|
21854
21859
|
path: "/api/health",
|
|
21855
21860
|
method: "get",
|
|
21856
21861
|
query: emptyQuery,
|
|
21857
|
-
response: object({
|
|
21862
|
+
response: object({
|
|
21863
|
+
viewer: string2(),
|
|
21864
|
+
root: string2(),
|
|
21865
|
+
kernelKey: string2()
|
|
21866
|
+
})
|
|
21858
21867
|
},
|
|
21859
21868
|
capabilities: {
|
|
21860
21869
|
path: "/api/capabilities",
|
|
@@ -21942,6 +21951,13 @@ var routes = {
|
|
|
21942
21951
|
path: "/api/open-path",
|
|
21943
21952
|
method: "get",
|
|
21944
21953
|
query: object({ path: string2() })
|
|
21954
|
+
},
|
|
21955
|
+
deleteNote: {
|
|
21956
|
+
path: "/api/delete-note",
|
|
21957
|
+
method: "post",
|
|
21958
|
+
query: emptyQuery,
|
|
21959
|
+
body: deleteNoteSchema,
|
|
21960
|
+
response: object({ deleted: literal(true) })
|
|
21945
21961
|
}
|
|
21946
21962
|
};
|
|
21947
21963
|
|
|
@@ -21993,17 +22009,17 @@ function sendContract(response, url, body, headers = {}) {
|
|
|
21993
22009
|
}
|
|
21994
22010
|
|
|
21995
22011
|
// packages/api/src/server/folder-metadata-plugin.ts
|
|
21996
|
-
import { realpathSync as
|
|
21997
|
-
import { readdir as
|
|
22012
|
+
import { realpathSync as realpathSync3 } from "node:fs";
|
|
22013
|
+
import { readdir as readdir7, readFile as readFile11, realpath as realpath9, stat as stat12 } from "node:fs/promises";
|
|
21998
22014
|
import { homedir as homedir4 } from "node:os";
|
|
21999
22015
|
import {
|
|
22000
|
-
basename as
|
|
22001
|
-
dirname as
|
|
22002
|
-
extname as
|
|
22003
|
-
isAbsolute as
|
|
22004
|
-
relative as
|
|
22005
|
-
resolve as
|
|
22006
|
-
sep as
|
|
22016
|
+
basename as basename8,
|
|
22017
|
+
dirname as dirname13,
|
|
22018
|
+
extname as extname7,
|
|
22019
|
+
isAbsolute as isAbsolute8,
|
|
22020
|
+
relative as relative13,
|
|
22021
|
+
resolve as resolve16,
|
|
22022
|
+
sep as sep11
|
|
22007
22023
|
} from "node:path";
|
|
22008
22024
|
|
|
22009
22025
|
// node_modules/.bun/@raggle-ai+barkdown@0.7.1+22117d1132b05fb8/node_modules/@raggle-ai/barkdown/dist/react.js
|
|
@@ -36708,7 +36724,11 @@ var run = promisify(execFile);
|
|
|
36708
36724
|
var REPOSITORY_ID_KEY = "kennel.repoId";
|
|
36709
36725
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
36710
36726
|
var markdownExtensions = new Set([".md", ".markdown"]);
|
|
36711
|
-
var caseInsensitiveHosts = new Set([
|
|
36727
|
+
var caseInsensitiveHosts = new Set([
|
|
36728
|
+
"github.com",
|
|
36729
|
+
"gitlab.com",
|
|
36730
|
+
"bitbucket.org"
|
|
36731
|
+
]);
|
|
36712
36732
|
class RepositoryNotesError extends Error {
|
|
36713
36733
|
code;
|
|
36714
36734
|
constructor(code, message) {
|
|
@@ -36789,7 +36809,12 @@ async function resolveCheckout(folder) {
|
|
|
36789
36809
|
}
|
|
36790
36810
|
let output;
|
|
36791
36811
|
try {
|
|
36792
|
-
output = await git([
|
|
36812
|
+
output = await git([
|
|
36813
|
+
"rev-parse",
|
|
36814
|
+
"--path-format=absolute",
|
|
36815
|
+
"--show-toplevel",
|
|
36816
|
+
"--git-common-dir"
|
|
36817
|
+
], start);
|
|
36793
36818
|
} catch (error) {
|
|
36794
36819
|
if (gitExitCode(error) === 128) {
|
|
36795
36820
|
throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
|
|
@@ -37119,23 +37144,55 @@ async function loadEntityWorkspace(root, query, selectedPath, currentWorkspaceRo
|
|
|
37119
37144
|
return { entities, query, rootPath, selected };
|
|
37120
37145
|
}
|
|
37121
37146
|
|
|
37147
|
+
// packages/api/src/server/api/delete-note.ts
|
|
37148
|
+
import { realpath as realpath5, stat as stat5, unlink as unlink2 } from "node:fs/promises";
|
|
37149
|
+
import { extname as extname3, isAbsolute as isAbsolute5, relative as relative8, resolve as resolve8, sep as sep5 } from "node:path";
|
|
37150
|
+
var markdownExtensions2 = new Set([".md", ".markdown"]);
|
|
37151
|
+
var deleteNoteRoute = {
|
|
37152
|
+
method: "POST",
|
|
37153
|
+
maxBodyBytes: 4 * 1024,
|
|
37154
|
+
async handle({ body, context, response }) {
|
|
37155
|
+
const parsed = deleteNoteSchema.safeParse(parseJsonBody(body));
|
|
37156
|
+
if (!parsed.success)
|
|
37157
|
+
throw apiError("bad-request", "Invalid delete request.");
|
|
37158
|
+
const folder = context.authorize(parsed.data.folder);
|
|
37159
|
+
if (!markdownExtensions2.has(extname3(parsed.data.path).toLowerCase()))
|
|
37160
|
+
throw apiError("bad-request", "Only Markdown notes can be deleted.");
|
|
37161
|
+
const target = await containedTarget(parsed.data.path, folder);
|
|
37162
|
+
const details = await stat5(target).catch(() => {
|
|
37163
|
+
return;
|
|
37164
|
+
});
|
|
37165
|
+
if (!details?.isFile() || details.nlink !== 1)
|
|
37166
|
+
throw apiError("forbidden", "Only regular, unlinked files can be deleted.");
|
|
37167
|
+
await unlink2(target);
|
|
37168
|
+
jsonResponse(response, 200, routes.deleteNote.response.parse({ deleted: true }));
|
|
37169
|
+
}
|
|
37170
|
+
};
|
|
37171
|
+
async function containedTarget(path, folder) {
|
|
37172
|
+
const target = await realpath5(resolve8(folder, path));
|
|
37173
|
+
const local = relative8(folder, target);
|
|
37174
|
+
if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
|
|
37175
|
+
throw apiError("forbidden", "The note must be inside its folder.");
|
|
37176
|
+
return target;
|
|
37177
|
+
}
|
|
37178
|
+
|
|
37122
37179
|
// packages/api/src/server/api/document-edit.ts
|
|
37123
37180
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
37124
37181
|
import {
|
|
37125
37182
|
readFile as readFile6,
|
|
37126
|
-
realpath as
|
|
37183
|
+
realpath as realpath6,
|
|
37127
37184
|
rename as rename3,
|
|
37128
|
-
stat as
|
|
37129
|
-
unlink as
|
|
37185
|
+
stat as stat6,
|
|
37186
|
+
unlink as unlink3,
|
|
37130
37187
|
writeFile as writeFile3
|
|
37131
37188
|
} from "node:fs/promises";
|
|
37132
37189
|
import {
|
|
37133
37190
|
dirname as dirname7,
|
|
37134
|
-
extname as
|
|
37135
|
-
isAbsolute as
|
|
37136
|
-
relative as
|
|
37137
|
-
resolve as
|
|
37138
|
-
sep as
|
|
37191
|
+
extname as extname4,
|
|
37192
|
+
isAbsolute as isAbsolute6,
|
|
37193
|
+
relative as relative9,
|
|
37194
|
+
resolve as resolve9,
|
|
37195
|
+
sep as sep6
|
|
37139
37196
|
} from "node:path";
|
|
37140
37197
|
|
|
37141
37198
|
// packages/api/src/shared/selection.ts
|
|
@@ -37151,12 +37208,12 @@ async function editDocument(folder, input) {
|
|
|
37151
37208
|
const { path, original, start, end, replacement } = input;
|
|
37152
37209
|
if (typeof path !== "string" || typeof original !== "string" || typeof replacement !== "string" || typeof start !== "number" || typeof end !== "number")
|
|
37153
37210
|
throw apiError("bad-request", "Invalid document edit.");
|
|
37154
|
-
if (![".md", ".markdown"].includes(
|
|
37211
|
+
if (![".md", ".markdown"].includes(extname4(path).toLowerCase()))
|
|
37155
37212
|
throw apiError("bad-request", "Only Markdown notes can be edited.");
|
|
37156
|
-
const root = await
|
|
37157
|
-
const target = await
|
|
37158
|
-
const local =
|
|
37159
|
-
if (!local || local === ".." || local.startsWith(`..${
|
|
37213
|
+
const root = await realpath6(folder);
|
|
37214
|
+
const target = await realpath6(resolve9(root, path));
|
|
37215
|
+
const local = relative9(root, target);
|
|
37216
|
+
if (!local || local === ".." || local.startsWith(`..${sep6}`) || isAbsolute6(local))
|
|
37160
37217
|
throw apiError("forbidden", "The document must be inside its folder.");
|
|
37161
37218
|
let content;
|
|
37162
37219
|
try {
|
|
@@ -37166,12 +37223,12 @@ async function editDocument(folder, input) {
|
|
|
37166
37223
|
}
|
|
37167
37224
|
const previous = pending.get(target) ?? Promise.resolve();
|
|
37168
37225
|
const operation = previous.catch(() => {}).then(async () => {
|
|
37169
|
-
const details = await
|
|
37226
|
+
const details = await stat6(target);
|
|
37170
37227
|
if (!details.isFile() || details.nlink !== 1)
|
|
37171
37228
|
throw apiError("forbidden", "Only regular, unlinked files can be edited.");
|
|
37172
37229
|
if (await readFile6(target, "utf8") !== original)
|
|
37173
37230
|
throw apiError("conflict", "This note changed on disk. Close this preview, reload the note, and try again.");
|
|
37174
|
-
const temporary =
|
|
37231
|
+
const temporary = resolve9(dirname7(target), `.kennel-edit-${randomUUID2()}.tmp`);
|
|
37175
37232
|
try {
|
|
37176
37233
|
await writeFile3(temporary, content, {
|
|
37177
37234
|
flag: "wx",
|
|
@@ -37181,7 +37238,7 @@ async function editDocument(folder, input) {
|
|
|
37181
37238
|
throw apiError("conflict", "This note changed on disk. Reload before applying an edit.");
|
|
37182
37239
|
await rename3(temporary, target);
|
|
37183
37240
|
} finally {
|
|
37184
|
-
await
|
|
37241
|
+
await unlink3(temporary).catch(() => {});
|
|
37185
37242
|
}
|
|
37186
37243
|
return content;
|
|
37187
37244
|
});
|
|
@@ -37202,7 +37259,7 @@ var documentEditRoute = {
|
|
|
37202
37259
|
throw apiError("bad-request", "Invalid document edit.");
|
|
37203
37260
|
const input = parsed.data;
|
|
37204
37261
|
const folder = context.authorize(input.folder);
|
|
37205
|
-
if (context.lockedFile && (typeof input.path !== "string" || await
|
|
37262
|
+
if (context.lockedFile && (typeof input.path !== "string" || await realpath6(resolve9(folder, input.path)) !== context.lockedFile))
|
|
37206
37263
|
throw apiError("forbidden", "Only the selected document can be edited.");
|
|
37207
37264
|
await editDocument(folder, input);
|
|
37208
37265
|
jsonResponse(response, 200, routes.documentEdit.response.parse({ saved: true }));
|
|
@@ -37211,7 +37268,7 @@ var documentEditRoute = {
|
|
|
37211
37268
|
|
|
37212
37269
|
// packages/api/src/server/api/create-agent-file.ts
|
|
37213
37270
|
import { writeFile as writeFile4 } from "node:fs/promises";
|
|
37214
|
-
import { resolve as
|
|
37271
|
+
import { resolve as resolve10 } from "node:path";
|
|
37215
37272
|
var createAgentFileRoute = {
|
|
37216
37273
|
method: "POST",
|
|
37217
37274
|
maxBodyBytes: 0,
|
|
@@ -37222,7 +37279,7 @@ var createAgentFileRoute = {
|
|
|
37222
37279
|
} catch (error) {
|
|
37223
37280
|
throw apiError("not-found", error instanceof Error ? error.message : "Folder not found");
|
|
37224
37281
|
}
|
|
37225
|
-
const target =
|
|
37282
|
+
const target = resolve10(folder, "AGENTS.md");
|
|
37226
37283
|
try {
|
|
37227
37284
|
await writeFile4(target, "", { flag: "wx" });
|
|
37228
37285
|
} catch (error) {
|
|
@@ -37274,8 +37331,8 @@ async function folderWebsite(folder) {
|
|
|
37274
37331
|
|
|
37275
37332
|
// packages/api/src/server/folder-icons.ts
|
|
37276
37333
|
import { createHash as createHash4 } from "node:crypto";
|
|
37277
|
-
import { readFile as readFile8, stat as
|
|
37278
|
-
import { extname as
|
|
37334
|
+
import { readFile as readFile8, stat as stat7 } from "node:fs/promises";
|
|
37335
|
+
import { extname as extname5 } from "node:path";
|
|
37279
37336
|
import { discoverFolderIcon } from "@raggle-ai/local";
|
|
37280
37337
|
var maxIconBytes2 = 4 * 1024 * 1024;
|
|
37281
37338
|
var iconTypes = new Map([
|
|
@@ -37312,8 +37369,8 @@ function iconUrl(folder, hash, mediaType) {
|
|
|
37312
37369
|
}
|
|
37313
37370
|
async function folderIcon(folder, assets, assetFiles, fallback) {
|
|
37314
37371
|
const icon = discoverFolderIcon(folder);
|
|
37315
|
-
const mediaType = icon ? iconTypes.get(
|
|
37316
|
-
const details = icon && mediaType ? await
|
|
37372
|
+
const mediaType = icon ? iconTypes.get(extname5(icon).toLowerCase()) : null;
|
|
37373
|
+
const details = icon && mediaType ? await stat7(icon).catch(() => {
|
|
37317
37374
|
return;
|
|
37318
37375
|
}) : undefined;
|
|
37319
37376
|
if (!icon || !mediaType || !details?.size || details.size > maxIconBytes2) {
|
|
@@ -37335,10 +37392,10 @@ async function searchIconDataUrl(icon) {
|
|
|
37335
37392
|
return;
|
|
37336
37393
|
if (icon.startsWith("data:"))
|
|
37337
37394
|
return icon;
|
|
37338
|
-
const mediaType = searchIconTypes.get(
|
|
37395
|
+
const mediaType = searchIconTypes.get(extname5(icon).toLowerCase());
|
|
37339
37396
|
if (!mediaType)
|
|
37340
37397
|
return;
|
|
37341
|
-
const details = await
|
|
37398
|
+
const details = await stat7(icon).catch(() => {
|
|
37342
37399
|
return;
|
|
37343
37400
|
});
|
|
37344
37401
|
if (!details?.size || details.size > maxSearchIconBytes)
|
|
@@ -37388,19 +37445,457 @@ async function workspaceBreadcrumbIcons(breadcrumbs, assetFiles, assets = {}) {
|
|
|
37388
37445
|
|
|
37389
37446
|
// packages/api/src/server/plugins.ts
|
|
37390
37447
|
import { pathToFileURL } from "node:url";
|
|
37391
|
-
import { existsSync as
|
|
37392
|
-
import { mkdir as mkdir3, readFile as
|
|
37448
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
37449
|
+
import { mkdir as mkdir3, readFile as readFile10, stat as stat11, writeFile as writeFile5 } from "node:fs/promises";
|
|
37450
|
+
import { dirname as dirname11, resolve as resolve15, sep as sep10 } from "node:path";
|
|
37451
|
+
|
|
37452
|
+
// packages/knl/src/server.ts
|
|
37453
|
+
import { stat as stat9 } from "node:fs/promises";
|
|
37454
|
+
import { join as join5 } from "node:path";
|
|
37455
|
+
|
|
37456
|
+
// packages/knl/src/knl.ts
|
|
37457
|
+
import { readdir as readdir5, stat as stat8 } from "node:fs/promises";
|
|
37458
|
+
import { basename as basename6, dirname as dirname10, relative as relative10, resolve as resolve13, sep as sep8 } from "node:path";
|
|
37459
|
+
import * as esbuild from "esbuild";
|
|
37460
|
+
|
|
37461
|
+
// packages/knl/src/folder-guard.ts
|
|
37462
|
+
import { existsSync as existsSync3, realpathSync as realpathSync2 } from "node:fs";
|
|
37463
|
+
import { realpath as realpath7 } from "node:fs/promises";
|
|
37393
37464
|
import { dirname as dirname8, resolve as resolve11, sep as sep7 } from "node:path";
|
|
37465
|
+
import { fileURLToPath } from "node:url";
|
|
37466
|
+
var knlHostResolveDir = dirname8(fileURLToPath(import.meta.url));
|
|
37467
|
+
var knlHostPackageFolders = (() => {
|
|
37468
|
+
const folders = [];
|
|
37469
|
+
let current = knlHostResolveDir;
|
|
37470
|
+
while (true) {
|
|
37471
|
+
const nodeModules = resolve11(current, "node_modules");
|
|
37472
|
+
if (existsSync3(nodeModules))
|
|
37473
|
+
folders.push(nodeModules);
|
|
37474
|
+
const parent = dirname8(current);
|
|
37475
|
+
if (parent === current)
|
|
37476
|
+
return folders;
|
|
37477
|
+
current = parent;
|
|
37478
|
+
}
|
|
37479
|
+
})();
|
|
37480
|
+
async function knlRealRoot(owner) {
|
|
37481
|
+
const expected = resolve11(await realpath7(owner), "knl");
|
|
37482
|
+
const real = await realpath7(resolve11(owner, "knl"));
|
|
37483
|
+
if (real !== expected)
|
|
37484
|
+
throw new Error("knl/ must be a folder, not a link to another place.");
|
|
37485
|
+
return real;
|
|
37486
|
+
}
|
|
37487
|
+
async function knlReadRoots(owner) {
|
|
37488
|
+
const packages = await Promise.all(knlHostPackageFolders.map((folder) => realpath7(folder).catch(() => {
|
|
37489
|
+
return;
|
|
37490
|
+
})));
|
|
37491
|
+
return [
|
|
37492
|
+
await knlRealRoot(owner),
|
|
37493
|
+
...packages.filter((folder) => Boolean(folder))
|
|
37494
|
+
];
|
|
37495
|
+
}
|
|
37496
|
+
function isKnlReadable(file, roots) {
|
|
37497
|
+
let real;
|
|
37498
|
+
try {
|
|
37499
|
+
real = realpathSync2(file);
|
|
37500
|
+
} catch {
|
|
37501
|
+
real = resolve11(file);
|
|
37502
|
+
}
|
|
37503
|
+
return roots.some((root) => real === root || real.startsWith(`${root}${sep7}`));
|
|
37504
|
+
}
|
|
37505
|
+
function knlReadError(file) {
|
|
37506
|
+
return `KNL pages can only use files in knl/ and installed packages: ${file}`;
|
|
37507
|
+
}
|
|
37508
|
+
function assertKnlReadable(file, roots) {
|
|
37509
|
+
if (!isKnlReadable(file, roots))
|
|
37510
|
+
throw new Error(knlReadError(file));
|
|
37511
|
+
}
|
|
37512
|
+
|
|
37513
|
+
// packages/knl/src/tailwind.ts
|
|
37514
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
37515
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
37516
|
+
import { dirname as dirname9, resolve as resolve12 } from "node:path";
|
|
37517
|
+
import { compile } from "@tailwindcss/node";
|
|
37518
|
+
import { Scanner } from "@tailwindcss/oxide";
|
|
37519
|
+
var require2 = createRequire2(import.meta.url);
|
|
37520
|
+
function starterKnlStyles() {
|
|
37521
|
+
return `@layer theme, utilities;
|
|
37522
|
+
@import "tailwindcss/theme.css" layer(theme) prefix(knl);
|
|
37523
|
+
@import "tailwindcss/utilities.css" layer(utilities) source(none);
|
|
37524
|
+
|
|
37525
|
+
@theme {
|
|
37526
|
+
--font-sans: system-ui, sans-serif;
|
|
37527
|
+
}
|
|
37528
|
+
`;
|
|
37529
|
+
}
|
|
37530
|
+
async function compileKnlCss(js, stylesheet, roots) {
|
|
37531
|
+
const base = dirname9(stylesheet);
|
|
37532
|
+
assertKnlReadable(stylesheet, roots);
|
|
37533
|
+
const css = await readFile9(stylesheet, "utf8").catch((error) => {
|
|
37534
|
+
if (error.code === "ENOENT")
|
|
37535
|
+
return starterKnlStyles();
|
|
37536
|
+
throw error;
|
|
37537
|
+
});
|
|
37538
|
+
const compiler = await compile(css, {
|
|
37539
|
+
base,
|
|
37540
|
+
from: stylesheet,
|
|
37541
|
+
onDependency: (path) => assertKnlReadable(path, roots),
|
|
37542
|
+
customJsResolver: async (id) => {
|
|
37543
|
+
throw new Error(`KNL does not load Tailwind plugins or configs (${id}).`);
|
|
37544
|
+
},
|
|
37545
|
+
customCssResolver: async (id) => id === "tailwindcss" || id.startsWith("tailwindcss/") ? require2.resolve(id) : undefined
|
|
37546
|
+
});
|
|
37547
|
+
const sources = [...compiler.sources];
|
|
37548
|
+
if (compiler.root !== "none") {
|
|
37549
|
+
sources.push({
|
|
37550
|
+
...compiler.root ?? { base, pattern: "**/*" },
|
|
37551
|
+
negated: false
|
|
37552
|
+
});
|
|
37553
|
+
}
|
|
37554
|
+
for (const source of sources) {
|
|
37555
|
+
if (source.negated)
|
|
37556
|
+
continue;
|
|
37557
|
+
const fixed = source.pattern.split("/");
|
|
37558
|
+
const glob = fixed.findIndex((part) => /[*?[{]/.test(part));
|
|
37559
|
+
assertKnlReadable(resolve12(source.base, fixed.slice(0, glob < 0 ? undefined : glob).join("/")), roots);
|
|
37560
|
+
}
|
|
37561
|
+
const scanner = new Scanner({ sources });
|
|
37562
|
+
const candidates = [
|
|
37563
|
+
...scanner.scan(),
|
|
37564
|
+
...scanner.scanFiles([{ content: js, extension: "js" }])
|
|
37565
|
+
];
|
|
37566
|
+
return compiler.build(candidates);
|
|
37567
|
+
}
|
|
37568
|
+
|
|
37569
|
+
// packages/knl/src/knl.ts
|
|
37570
|
+
var KNK_FOLDER = "knl";
|
|
37571
|
+
var MAX_SOURCE_BYTES = 512 * 1024;
|
|
37572
|
+
var MAX_JS_BYTES = 5 * 1024 * 1024;
|
|
37573
|
+
var MODULE_FILES = ["index.tsx", "index.ts"];
|
|
37574
|
+
function sourceFor(file, knlRoot, scanRoot, owner) {
|
|
37575
|
+
const id = toModuleId(file, knlRoot);
|
|
37576
|
+
return {
|
|
37577
|
+
id,
|
|
37578
|
+
file,
|
|
37579
|
+
relative: relative10(scanRoot, file).split(sep8).join("/"),
|
|
37580
|
+
folder: dirname10(file),
|
|
37581
|
+
owner
|
|
37582
|
+
};
|
|
37583
|
+
}
|
|
37584
|
+
function toModuleId(file, knlRoot) {
|
|
37585
|
+
const relativeKnl = relative10(knlRoot, file).split(sep8).join("/");
|
|
37586
|
+
return relativeKnl.replace(/\/?index\.(tsx|ts)$/, "") || "index";
|
|
37587
|
+
}
|
|
37588
|
+
async function collectKnlSources(scanRoot, owner) {
|
|
37589
|
+
const knlRoot = resolve13(scanRoot, KNK_FOLDER);
|
|
37590
|
+
const knlEntries = await readdir5(knlRoot, { withFileTypes: true }).catch(() => []);
|
|
37591
|
+
const sources = [];
|
|
37592
|
+
const addModule = async (folder) => {
|
|
37593
|
+
for (const candidate of MODULE_FILES) {
|
|
37594
|
+
const file = resolve13(folder, candidate);
|
|
37595
|
+
if (await stat8(file).then((d) => d.isFile()).catch(() => false)) {
|
|
37596
|
+
sources.push(sourceFor(file, knlRoot, scanRoot, owner));
|
|
37597
|
+
return;
|
|
37598
|
+
}
|
|
37599
|
+
}
|
|
37600
|
+
};
|
|
37601
|
+
await addModule(knlRoot);
|
|
37602
|
+
for (const entry of knlEntries) {
|
|
37603
|
+
if (!entry.isDirectory())
|
|
37604
|
+
continue;
|
|
37605
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
37606
|
+
continue;
|
|
37607
|
+
await addModule(resolve13(knlRoot, entry.name));
|
|
37608
|
+
}
|
|
37609
|
+
return sources.sort((left, right) => {
|
|
37610
|
+
const rank = (left.id === "index" ? 0 : 1) - (right.id === "index" ? 0 : 1);
|
|
37611
|
+
return rank !== 0 ? rank : left.id.localeCompare(right.id);
|
|
37612
|
+
});
|
|
37613
|
+
}
|
|
37614
|
+
async function loadKnlSources(viewRoot, linkedFolders = []) {
|
|
37615
|
+
const sources = [
|
|
37616
|
+
...await collectKnlSources(viewRoot, viewRoot),
|
|
37617
|
+
...(await Promise.all(linkedFolders.map(async (folder) => (await collectKnlSources(folder, folder)).map((source) => ({
|
|
37618
|
+
...source,
|
|
37619
|
+
id: `${basename6(folder)}:${source.id}`,
|
|
37620
|
+
relative: `${basename6(folder)}/${source.relative}`
|
|
37621
|
+
}))))).flat()
|
|
37622
|
+
];
|
|
37623
|
+
return { sources };
|
|
37624
|
+
}
|
|
37625
|
+
var MAX_CACHED_MODULES = 200;
|
|
37626
|
+
var compileCache = new Map;
|
|
37627
|
+
function knlNodePaths(knlRoot) {
|
|
37628
|
+
return [resolve13(knlRoot, "node_modules"), ...knlHostPackageFolders];
|
|
37629
|
+
}
|
|
37630
|
+
async function compileKnlSource(source) {
|
|
37631
|
+
const sourceBytes = (await stat8(source.file)).size;
|
|
37632
|
+
const tooLarge = (bytes, what) => ({
|
|
37633
|
+
id: source.id,
|
|
37634
|
+
relative: source.relative,
|
|
37635
|
+
js: "",
|
|
37636
|
+
css: "",
|
|
37637
|
+
issues: [
|
|
37638
|
+
{
|
|
37639
|
+
path: source.relative,
|
|
37640
|
+
message: `Module ${what} is too large (${bytes} bytes; limit ${MAX_SOURCE_BYTES}).`
|
|
37641
|
+
}
|
|
37642
|
+
]
|
|
37643
|
+
});
|
|
37644
|
+
if (sourceBytes > MAX_SOURCE_BYTES)
|
|
37645
|
+
return tooLarge(sourceBytes, "source");
|
|
37646
|
+
const cached = compileCache.get(source.file);
|
|
37647
|
+
const unchanged = cached && (await Promise.all(cached.dependencies.map(async ({ file, mtimeMs }) => (await stat8(file).catch(() => {
|
|
37648
|
+
return;
|
|
37649
|
+
}))?.mtimeMs === mtimeMs))).every(Boolean);
|
|
37650
|
+
const knlRoot = resolve13(source.owner, KNK_FOLDER);
|
|
37651
|
+
let issuePath = source.relative;
|
|
37652
|
+
try {
|
|
37653
|
+
const nodePaths = knlNodePaths(knlRoot);
|
|
37654
|
+
const roots = await knlReadRoots(source.owner);
|
|
37655
|
+
assertKnlReadable(source.file, roots);
|
|
37656
|
+
let js;
|
|
37657
|
+
if (cached && unchanged) {
|
|
37658
|
+
js = cached.js;
|
|
37659
|
+
} else {
|
|
37660
|
+
const result = await esbuild.build({
|
|
37661
|
+
absWorkingDir: source.folder,
|
|
37662
|
+
stdin: {
|
|
37663
|
+
contents: `import { createElement } from "react"
|
|
37664
|
+
import { createRoot } from "react-dom/client"
|
|
37665
|
+
import Component from ${JSON.stringify(source.file)}
|
|
37666
|
+
|
|
37667
|
+
globalThis.__knlMount = (container, context, onError) => {
|
|
37668
|
+
const root = createRoot(container, { onUncaughtError: onError })
|
|
37669
|
+
root.render(createElement(Component, context))
|
|
37670
|
+
}
|
|
37671
|
+
`,
|
|
37672
|
+
sourcefile: `${source.file}.mount.ts`,
|
|
37673
|
+
loader: "ts",
|
|
37674
|
+
resolveDir: source.folder
|
|
37675
|
+
},
|
|
37676
|
+
bundle: true,
|
|
37677
|
+
format: "iife",
|
|
37678
|
+
target: "es2022",
|
|
37679
|
+
jsx: "automatic",
|
|
37680
|
+
platform: "browser",
|
|
37681
|
+
write: false,
|
|
37682
|
+
minify: false,
|
|
37683
|
+
sourcemap: false,
|
|
37684
|
+
logLevel: "silent",
|
|
37685
|
+
metafile: true,
|
|
37686
|
+
nodePaths,
|
|
37687
|
+
plugins: [readGuard(roots)]
|
|
37688
|
+
});
|
|
37689
|
+
js = result.outputFiles?.[0]?.text ?? "";
|
|
37690
|
+
const files = new Set([
|
|
37691
|
+
resolve13(knlRoot, "package.json"),
|
|
37692
|
+
...Object.keys(result.metafile.inputs).map((file) => resolve13(source.folder, file)).filter((file) => file !== `${source.file}.mount.ts`)
|
|
37693
|
+
]);
|
|
37694
|
+
const dependencies = await Promise.all([...files].map(async (file) => ({
|
|
37695
|
+
file,
|
|
37696
|
+
mtimeMs: (await stat8(file).catch(() => {
|
|
37697
|
+
return;
|
|
37698
|
+
}))?.mtimeMs
|
|
37699
|
+
})));
|
|
37700
|
+
compileCache.delete(source.file);
|
|
37701
|
+
compileCache.set(source.file, { js, dependencies });
|
|
37702
|
+
if (compileCache.size > MAX_CACHED_MODULES)
|
|
37703
|
+
compileCache.delete(compileCache.keys().next().value);
|
|
37704
|
+
}
|
|
37705
|
+
const stylesheet = resolve13(knlRoot, "style.css");
|
|
37706
|
+
issuePath = relative10(source.owner, stylesheet).split(sep8).join("/");
|
|
37707
|
+
const css = await compileKnlCss(js, stylesheet, roots);
|
|
37708
|
+
issuePath = source.relative;
|
|
37709
|
+
if (js.length + css.length > MAX_JS_BYTES) {
|
|
37710
|
+
return {
|
|
37711
|
+
id: source.id,
|
|
37712
|
+
relative: source.relative,
|
|
37713
|
+
js: "",
|
|
37714
|
+
css: "",
|
|
37715
|
+
issues: [
|
|
37716
|
+
{
|
|
37717
|
+
path: source.relative,
|
|
37718
|
+
message: `Compiled module is too large (${js.length + css.length} bytes; limit ${MAX_JS_BYTES}).`
|
|
37719
|
+
}
|
|
37720
|
+
]
|
|
37721
|
+
};
|
|
37722
|
+
}
|
|
37723
|
+
return { id: source.id, relative: source.relative, js, css, issues: [] };
|
|
37724
|
+
} catch (error) {
|
|
37725
|
+
return {
|
|
37726
|
+
id: source.id,
|
|
37727
|
+
relative: source.relative,
|
|
37728
|
+
js: "",
|
|
37729
|
+
css: "",
|
|
37730
|
+
issues: knlBuildIssues(error, issuePath, source)
|
|
37731
|
+
};
|
|
37732
|
+
}
|
|
37733
|
+
}
|
|
37734
|
+
var guardResolve = Symbol("knl-guard");
|
|
37735
|
+
function readGuard(roots) {
|
|
37736
|
+
return {
|
|
37737
|
+
name: "knl-read-guard",
|
|
37738
|
+
setup(build) {
|
|
37739
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
37740
|
+
if (args.pluginData === guardResolve)
|
|
37741
|
+
return;
|
|
37742
|
+
const resolveFrom = (resolveDir) => build.resolve(args.path, {
|
|
37743
|
+
importer: args.importer,
|
|
37744
|
+
kind: args.kind,
|
|
37745
|
+
pluginData: guardResolve,
|
|
37746
|
+
resolveDir
|
|
37747
|
+
});
|
|
37748
|
+
const readable = (result) => result.namespace !== "file" || !result.path || isKnlReadable(result.path, roots);
|
|
37749
|
+
let result = await resolveFrom(args.resolveDir);
|
|
37750
|
+
const bare = !/^(?:\.{1,2}(?:\/|$)|\/)/.test(args.path);
|
|
37751
|
+
if (bare && result.errors.length === 0 && !readable(result))
|
|
37752
|
+
result = await resolveFrom(knlHostResolveDir);
|
|
37753
|
+
if (result.errors.length > 0)
|
|
37754
|
+
return { errors: result.errors };
|
|
37755
|
+
if (!readable(result))
|
|
37756
|
+
return { errors: [{ text: knlReadError(args.path) }] };
|
|
37757
|
+
return result;
|
|
37758
|
+
});
|
|
37759
|
+
}
|
|
37760
|
+
};
|
|
37761
|
+
}
|
|
37762
|
+
function knlBuildIssues(error, relativePath, source) {
|
|
37763
|
+
const failures = error?.errors;
|
|
37764
|
+
if (Array.isArray(failures) && failures.length > 0) {
|
|
37765
|
+
const first = failures[0];
|
|
37766
|
+
const file = first?.location?.file ? relative10(source.owner, resolve13(source.folder, first.location.file)).split(sep8).join("/") : relativePath;
|
|
37767
|
+
const location = first?.location ? `${file}:${first.location.line ?? "?"}` : relativePath;
|
|
37768
|
+
return [
|
|
37769
|
+
{ path: location, message: first?.text ?? "Module failed to compile." }
|
|
37770
|
+
];
|
|
37771
|
+
}
|
|
37772
|
+
return [
|
|
37773
|
+
{
|
|
37774
|
+
path: relativePath,
|
|
37775
|
+
message: error instanceof Error && error.message ? error.message : "Module failed to compile."
|
|
37776
|
+
}
|
|
37777
|
+
];
|
|
37778
|
+
}
|
|
37779
|
+
function starterKnlSource() {
|
|
37780
|
+
return `// knl/index.tsx — this folder's homepage (KNL folder web framework).
|
|
37781
|
+
// Every other page lives in its own folder: knl/<page>/index.tsx.
|
|
37782
|
+
import * as React from "react"
|
|
37783
|
+
|
|
37784
|
+
export default function Home() {
|
|
37785
|
+
return (
|
|
37786
|
+
<div className="knl:p-4 knl:font-sans">
|
|
37787
|
+
<h1 className="knl:m-0 knl:text-2xl">Home</h1>
|
|
37788
|
+
<p>Edit this file to build the homepage.</p>
|
|
37789
|
+
</div>
|
|
37790
|
+
)
|
|
37791
|
+
}
|
|
37792
|
+
`;
|
|
37793
|
+
}
|
|
37794
|
+
|
|
37795
|
+
// packages/knl/src/manifest.ts
|
|
37796
|
+
var knlManifest = {
|
|
37797
|
+
id: "knl",
|
|
37798
|
+
label: "KNL"
|
|
37799
|
+
};
|
|
37800
|
+
|
|
37801
|
+
// packages/knl/src/server.ts
|
|
37802
|
+
function createKnlPlugin() {
|
|
37803
|
+
return {
|
|
37804
|
+
...knlManifest,
|
|
37805
|
+
async detect(context) {
|
|
37806
|
+
const hasKnlFolder = await stat9(join5(context.folder, KNK_FOLDER)).then((entry) => entry.isDirectory()).catch(() => false);
|
|
37807
|
+
if (!hasKnlFolder)
|
|
37808
|
+
return { status: "unavailable" };
|
|
37809
|
+
const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
|
|
37810
|
+
if (!sources.some((source) => source.id === "index"))
|
|
37811
|
+
return { status: "unavailable" };
|
|
37812
|
+
return {
|
|
37813
|
+
count: sources.length,
|
|
37814
|
+
pages: sources.filter((source) => source.id !== "index" && !source.id.endsWith(":index")).map((source) => ({
|
|
37815
|
+
id: source.id,
|
|
37816
|
+
label: source.id.replaceAll(/[-_]/g, " ").replace(/^./, (letter) => letter.toUpperCase())
|
|
37817
|
+
})),
|
|
37818
|
+
status: "available"
|
|
37819
|
+
};
|
|
37820
|
+
},
|
|
37821
|
+
async handle(context, subpath) {
|
|
37822
|
+
const { sources } = await loadKnlSources(context.folder, context.linkedFolders ?? []);
|
|
37823
|
+
const folder = context.folder;
|
|
37824
|
+
if (sources.length === 0) {
|
|
37825
|
+
return { kind: "available", data: { folder, status: "unavailable" } };
|
|
37826
|
+
}
|
|
37827
|
+
const requested = subpath.trim() || "index";
|
|
37828
|
+
const selected = sources.filter((source) => source.id === requested);
|
|
37829
|
+
if (selected.length === 0) {
|
|
37830
|
+
return {
|
|
37831
|
+
kind: "available",
|
|
37832
|
+
data: {
|
|
37833
|
+
folder,
|
|
37834
|
+
status: "error",
|
|
37835
|
+
message: `KNL page not found: ${requested}`
|
|
37836
|
+
}
|
|
37837
|
+
};
|
|
37838
|
+
}
|
|
37839
|
+
const compiled = await Promise.all(selected.map(compileKnlSource));
|
|
37840
|
+
const entries = compiled.map((result) => {
|
|
37841
|
+
const source = sources.find((item) => item.id === result.id);
|
|
37842
|
+
return {
|
|
37843
|
+
id: result.id,
|
|
37844
|
+
relative: result.relative,
|
|
37845
|
+
...result.js ? { js: result.js } : {},
|
|
37846
|
+
css: result.css,
|
|
37847
|
+
...result.issues.length > 0 ? { issues: result.issues } : {},
|
|
37848
|
+
owner: source?.owner ?? folder
|
|
37849
|
+
};
|
|
37850
|
+
});
|
|
37851
|
+
const failing = entries.filter((entry) => entry.issues?.length);
|
|
37852
|
+
if (failing.length > 0) {
|
|
37853
|
+
return {
|
|
37854
|
+
kind: "available",
|
|
37855
|
+
data: {
|
|
37856
|
+
folder,
|
|
37857
|
+
status: "error",
|
|
37858
|
+
entries,
|
|
37859
|
+
issues: failing[0]?.issues
|
|
37860
|
+
}
|
|
37861
|
+
};
|
|
37862
|
+
}
|
|
37863
|
+
return {
|
|
37864
|
+
kind: "available",
|
|
37865
|
+
data: {
|
|
37866
|
+
folder,
|
|
37867
|
+
status: "available",
|
|
37868
|
+
entries
|
|
37869
|
+
}
|
|
37870
|
+
};
|
|
37871
|
+
},
|
|
37872
|
+
async init() {
|
|
37873
|
+
return {
|
|
37874
|
+
files: [
|
|
37875
|
+
{ path: "knl/index.tsx", content: starterKnlSource() },
|
|
37876
|
+
{ path: "knl/style.css", content: starterKnlStyles() },
|
|
37877
|
+
{
|
|
37878
|
+
path: "knl/package.json",
|
|
37879
|
+
content: `${JSON.stringify({ private: true, type: "module" }, null, 2)}
|
|
37880
|
+
`
|
|
37881
|
+
},
|
|
37882
|
+
{ path: "knl/.gitignore", content: `node_modules/
|
|
37883
|
+
` }
|
|
37884
|
+
]
|
|
37885
|
+
};
|
|
37886
|
+
}
|
|
37887
|
+
};
|
|
37888
|
+
}
|
|
37394
37889
|
|
|
37395
37890
|
// packages/notes/src/server.ts
|
|
37396
|
-
import { readdir as
|
|
37891
|
+
import { readdir as readdir6, realpath as realpath8, stat as stat10 } from "node:fs/promises";
|
|
37397
37892
|
import {
|
|
37398
|
-
basename as
|
|
37399
|
-
extname as
|
|
37400
|
-
isAbsolute as
|
|
37401
|
-
relative as
|
|
37402
|
-
resolve as
|
|
37403
|
-
sep as
|
|
37893
|
+
basename as basename7,
|
|
37894
|
+
extname as extname6,
|
|
37895
|
+
isAbsolute as isAbsolute7,
|
|
37896
|
+
relative as relative11,
|
|
37897
|
+
resolve as resolve14,
|
|
37898
|
+
sep as sep9
|
|
37404
37899
|
} from "node:path";
|
|
37405
37900
|
|
|
37406
37901
|
// packages/notes/src/manifest.ts
|
|
@@ -37417,40 +37912,40 @@ var notesManifest = {
|
|
|
37417
37912
|
};
|
|
37418
37913
|
|
|
37419
37914
|
// packages/notes/src/server.ts
|
|
37420
|
-
var
|
|
37915
|
+
var markdownExtensions3 = new Set([".md", ".markdown"]);
|
|
37421
37916
|
var llmNoteLimit = 20;
|
|
37422
37917
|
async function listMarkdownFiles(folder) {
|
|
37423
37918
|
const files = [];
|
|
37424
37919
|
const visit = async (current) => {
|
|
37425
|
-
const entries = await
|
|
37920
|
+
const entries = await readdir6(current, { withFileTypes: true }).catch(() => []);
|
|
37426
37921
|
for (const entry of entries) {
|
|
37427
37922
|
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
37428
37923
|
continue;
|
|
37429
|
-
const path =
|
|
37924
|
+
const path = resolve14(current, entry.name);
|
|
37430
37925
|
if (entry.isDirectory()) {
|
|
37431
37926
|
await visit(path);
|
|
37432
37927
|
continue;
|
|
37433
37928
|
}
|
|
37434
|
-
if (!entry.isFile() || !
|
|
37929
|
+
if (!entry.isFile() || !markdownExtensions3.has(extname6(entry.name))) {
|
|
37435
37930
|
continue;
|
|
37436
37931
|
}
|
|
37437
|
-
const details = await
|
|
37932
|
+
const details = await stat10(path).catch(() => {
|
|
37438
37933
|
return;
|
|
37439
37934
|
});
|
|
37440
37935
|
files.push({
|
|
37441
37936
|
activityAt: details?.mtimeMs ?? 0,
|
|
37442
|
-
name:
|
|
37443
|
-
relativePath:
|
|
37937
|
+
name: basename7(entry.name, extname6(entry.name)),
|
|
37938
|
+
relativePath: relative11(folder, path).split(sep9).join("/")
|
|
37444
37939
|
});
|
|
37445
37940
|
}
|
|
37446
37941
|
};
|
|
37447
37942
|
await visit(folder);
|
|
37448
37943
|
return files.sort((left, right) => right.activityAt - left.activityAt);
|
|
37449
37944
|
}
|
|
37450
|
-
var canonical = (path) =>
|
|
37945
|
+
var canonical = (path) => realpath8(path).catch(() => path);
|
|
37451
37946
|
function inside5(path, root) {
|
|
37452
|
-
const local =
|
|
37453
|
-
return local === "" || !local.startsWith("..") && !
|
|
37947
|
+
const local = relative11(root, path);
|
|
37948
|
+
return local === "" || !local.startsWith("..") && !isAbsolute7(local);
|
|
37454
37949
|
}
|
|
37455
37950
|
async function withoutManaged(groups, managedRoots) {
|
|
37456
37951
|
if (!managedRoots.length)
|
|
@@ -37461,7 +37956,7 @@ async function withoutManaged(groups, managedRoots) {
|
|
|
37461
37956
|
return { ...group, files: [] };
|
|
37462
37957
|
}
|
|
37463
37958
|
const files = await Promise.all(group.files.map(async (file) => {
|
|
37464
|
-
const real = await canonical(
|
|
37959
|
+
const real = await canonical(resolve14(group.folder, file.relativePath));
|
|
37465
37960
|
return roots.some((root) => inside5(real, root)) ? undefined : file;
|
|
37466
37961
|
}));
|
|
37467
37962
|
return { ...group, files: files.filter((file) => file !== undefined) };
|
|
@@ -37511,7 +38006,7 @@ async function notesLlmSections({
|
|
|
37511
38006
|
continue;
|
|
37512
38007
|
seen.add(identity);
|
|
37513
38008
|
const files = await listMarkdownFiles(source);
|
|
37514
|
-
const paths = files.map((file) =>
|
|
38009
|
+
const paths = files.map((file) => resolve14(source, file.relativePath));
|
|
37515
38010
|
groups.push({ folder: source, paths });
|
|
37516
38011
|
files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
|
|
37517
38012
|
}
|
|
@@ -37583,14 +38078,14 @@ function parsePluginRegistryConfig(content) {
|
|
|
37583
38078
|
};
|
|
37584
38079
|
}
|
|
37585
38080
|
async function nearestKennelConfig(folder) {
|
|
37586
|
-
for (let current = folder;; current =
|
|
37587
|
-
const candidate =
|
|
37588
|
-
const details = await
|
|
38081
|
+
for (let current = folder;; current = dirname11(current)) {
|
|
38082
|
+
const candidate = resolve15(current, "kennel.json");
|
|
38083
|
+
const details = await stat11(candidate).catch(() => {
|
|
37589
38084
|
return;
|
|
37590
38085
|
});
|
|
37591
38086
|
if (details?.isFile())
|
|
37592
38087
|
return candidate;
|
|
37593
|
-
if (
|
|
38088
|
+
if (dirname11(current) === current)
|
|
37594
38089
|
return;
|
|
37595
38090
|
}
|
|
37596
38091
|
}
|
|
@@ -37599,13 +38094,13 @@ async function folderPluginRegistryConfig(folder) {
|
|
|
37599
38094
|
if (!path)
|
|
37600
38095
|
return defaultPluginRegistryConfig;
|
|
37601
38096
|
try {
|
|
37602
|
-
return parsePluginRegistryConfig(await
|
|
38097
|
+
return parsePluginRegistryConfig(await readFile10(path, "utf8"));
|
|
37603
38098
|
} catch {
|
|
37604
38099
|
return defaultPluginRegistryConfig;
|
|
37605
38100
|
}
|
|
37606
38101
|
}
|
|
37607
38102
|
function createFolderServerPlugins() {
|
|
37608
|
-
return [createNotesPlugin()];
|
|
38103
|
+
return [createNotesPlugin(), createKnlPlugin()];
|
|
37609
38104
|
}
|
|
37610
38105
|
function folderServerRegistry() {
|
|
37611
38106
|
return new Map(createFolderServerPlugins().map((plugin) => [plugin.id, plugin]));
|
|
@@ -37647,7 +38142,7 @@ async function folderServerRegistryAsync() {
|
|
|
37647
38142
|
...server,
|
|
37648
38143
|
async detect(context) {
|
|
37649
38144
|
const detection = await server.detect(context);
|
|
37650
|
-
if (detection.status === "unavailable" &&
|
|
38145
|
+
if (detection.status === "unavailable" && existsSync4(resolve15(context.folder, entry.trigger))) {
|
|
37651
38146
|
return { status: "available" };
|
|
37652
38147
|
}
|
|
37653
38148
|
return detection;
|
|
@@ -37690,10 +38185,10 @@ import { execFile as execFile4 } from "node:child_process";
|
|
|
37690
38185
|
|
|
37691
38186
|
// packages/api/src/server/linked-folder-discovery.ts
|
|
37692
38187
|
import { execFile as execFile3 } from "node:child_process";
|
|
37693
|
-
import { existsSync as
|
|
37694
|
-
import { createRequire as
|
|
37695
|
-
import { dirname as
|
|
37696
|
-
import { fileURLToPath } from "node:url";
|
|
38188
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
38189
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
38190
|
+
import { dirname as dirname12, join as join6 } from "node:path";
|
|
38191
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
37697
38192
|
var script = `
|
|
37698
38193
|
const local = require(process.argv[1]);
|
|
37699
38194
|
const { stat } = require('node:fs/promises');
|
|
@@ -37710,24 +38205,24 @@ const { stat } = require('node:fs/promises');
|
|
|
37710
38205
|
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
37711
38206
|
`;
|
|
37712
38207
|
function resolveLocalModule(from = import.meta.url) {
|
|
37713
|
-
const require2 =
|
|
38208
|
+
const require2 = createRequire3(from);
|
|
37714
38209
|
const candidates = [];
|
|
37715
38210
|
try {
|
|
37716
38211
|
candidates.push(require2.resolve("@raggle-ai/local"));
|
|
37717
38212
|
} catch {}
|
|
37718
|
-
let directory =
|
|
37719
|
-
while (
|
|
37720
|
-
if (
|
|
38213
|
+
let directory = dirname12(fileURLToPath2(from));
|
|
38214
|
+
while (dirname12(directory) !== directory) {
|
|
38215
|
+
if (existsSync5(join6(directory, "raggle.json"))) {
|
|
37721
38216
|
for (const candidate of [
|
|
37722
|
-
|
|
37723
|
-
|
|
38217
|
+
join6(dirname12(directory), "raggle-ai-scout", "packages", "local"),
|
|
38218
|
+
join6(directory, "..", "..", "raggle-ai-scout", "packages", "local")
|
|
37724
38219
|
]) {
|
|
37725
38220
|
if (!candidates.includes(candidate))
|
|
37726
38221
|
candidates.push(candidate);
|
|
37727
38222
|
}
|
|
37728
38223
|
break;
|
|
37729
38224
|
}
|
|
37730
|
-
directory =
|
|
38225
|
+
directory = dirname12(directory);
|
|
37731
38226
|
}
|
|
37732
38227
|
for (const candidate of candidates) {
|
|
37733
38228
|
try {
|
|
@@ -37736,7 +38231,7 @@ function resolveLocalModule(from = import.meta.url) {
|
|
|
37736
38231
|
return candidate;
|
|
37737
38232
|
} catch {}
|
|
37738
38233
|
}
|
|
37739
|
-
return candidates[0] ??
|
|
38234
|
+
return candidates[0] ?? join6(dirname12(fileURLToPath2(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
|
|
37740
38235
|
}
|
|
37741
38236
|
function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
|
|
37742
38237
|
return new Promise((resolve, reject) => {
|
|
@@ -38088,13 +38583,13 @@ var metadataExtensions = new Set([
|
|
|
38088
38583
|
".txt"
|
|
38089
38584
|
]);
|
|
38090
38585
|
function metadataFile(path, root) {
|
|
38091
|
-
const candidate =
|
|
38092
|
-
if (!candidate || candidate.startsWith("..") ||
|
|
38586
|
+
const candidate = relative13(root, path);
|
|
38587
|
+
if (!candidate || candidate.startsWith("..") || isAbsolute8(candidate))
|
|
38093
38588
|
return false;
|
|
38094
|
-
const parts = candidate.split(
|
|
38589
|
+
const parts = candidate.split(sep11);
|
|
38095
38590
|
if (parts.some((part) => part === "node_modules"))
|
|
38096
38591
|
return false;
|
|
38097
|
-
return metadataExtensions.has(
|
|
38592
|
+
return metadataExtensions.has(extname7(candidate).toLowerCase());
|
|
38098
38593
|
}
|
|
38099
38594
|
async function configuredAiInputModule(folder) {
|
|
38100
38595
|
const config = folderWorkspace(folder).config;
|
|
@@ -38102,11 +38597,11 @@ async function configuredAiInputModule(folder) {
|
|
|
38102
38597
|
return typeof module === "string" && module.trim() ? module.trim() : null;
|
|
38103
38598
|
}
|
|
38104
38599
|
function folderPath(value, root, lockedRoot) {
|
|
38105
|
-
const folder =
|
|
38600
|
+
const folder = realpathSync3(resolve16(value ?? root));
|
|
38106
38601
|
if (!lockedRoot)
|
|
38107
38602
|
return folder;
|
|
38108
|
-
const path =
|
|
38109
|
-
if (!path || !path.startsWith("..") && !
|
|
38603
|
+
const path = relative13(lockedRoot, folder);
|
|
38604
|
+
if (!path || !path.startsWith("..") && !isAbsolute8(path))
|
|
38110
38605
|
return folder;
|
|
38111
38606
|
throw new Error(`Folder is outside the viewer root: ${folder}`);
|
|
38112
38607
|
}
|
|
@@ -38177,15 +38672,15 @@ function unique(values) {
|
|
|
38177
38672
|
return [...new Set(values)];
|
|
38178
38673
|
}
|
|
38179
38674
|
function isMachineRoot(folder) {
|
|
38180
|
-
return folder === homedir4() ||
|
|
38675
|
+
return folder === homedir4() || dirname13(folder) === folder;
|
|
38181
38676
|
}
|
|
38182
38677
|
async function shallowMetadata(folder) {
|
|
38183
|
-
const entries = await
|
|
38678
|
+
const entries = await readdir7(folder, { withFileTypes: true });
|
|
38184
38679
|
const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
|
|
38185
|
-
const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(
|
|
38186
|
-
const path =
|
|
38187
|
-
const details = await
|
|
38188
|
-
const content = await
|
|
38680
|
+
const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname7(entry.name).toLowerCase())).map(async (entry) => {
|
|
38681
|
+
const path = resolve16(folder, entry.name);
|
|
38682
|
+
const details = await stat12(path);
|
|
38683
|
+
const content = await readFile11(path, "utf8").catch(() => "");
|
|
38189
38684
|
const title = markdownMetadata(content).title;
|
|
38190
38685
|
return {
|
|
38191
38686
|
activityAt: details.mtimeMs,
|
|
@@ -38198,10 +38693,10 @@ async function shallowMetadata(folder) {
|
|
|
38198
38693
|
};
|
|
38199
38694
|
}));
|
|
38200
38695
|
const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
38201
|
-
id: createHash5("sha1").update(
|
|
38696
|
+
id: createHash5("sha1").update(resolve16(folder, entry.name)).digest("hex"),
|
|
38202
38697
|
name: entry.name,
|
|
38203
38698
|
relativePath: entry.name,
|
|
38204
|
-
worktree:
|
|
38699
|
+
worktree: resolve16(folder, entry.name)
|
|
38205
38700
|
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
38206
38701
|
return {
|
|
38207
38702
|
changes: { items: [] },
|
|
@@ -38227,8 +38722,8 @@ async function loadFolderData(folder) {
|
|
|
38227
38722
|
items: projects.map((project) => ({
|
|
38228
38723
|
id: project.id,
|
|
38229
38724
|
worktree: project.worktree,
|
|
38230
|
-
name: project.name ??
|
|
38231
|
-
relativePath:
|
|
38725
|
+
name: project.name ?? basename8(project.worktree),
|
|
38726
|
+
relativePath: relative13(folder, project.worktree)
|
|
38232
38727
|
}))
|
|
38233
38728
|
}
|
|
38234
38729
|
};
|
|
@@ -38236,10 +38731,10 @@ async function loadFolderData(folder) {
|
|
|
38236
38731
|
async function notesFiles(folder, files, origin) {
|
|
38237
38732
|
return {
|
|
38238
38733
|
items: await Promise.all(files.map(async (file) => {
|
|
38239
|
-
const path = file.path ?? (file.relativePath ?
|
|
38734
|
+
const path = file.path ?? (file.relativePath ? resolve16(folder, file.relativePath) : undefined);
|
|
38240
38735
|
if (!path)
|
|
38241
38736
|
return origin ? { ...file, folder: origin } : file;
|
|
38242
|
-
const content = [".markdown", ".md"].includes(
|
|
38737
|
+
const content = [".markdown", ".md"].includes(extname7(path).toLowerCase()) ? await readFile11(path, "utf8").catch(() => "") : "";
|
|
38243
38738
|
const title = content ? markdownMetadata(content).title : undefined;
|
|
38244
38739
|
return {
|
|
38245
38740
|
...file,
|
|
@@ -38253,7 +38748,7 @@ async function notesFiles(folder, files, origin) {
|
|
|
38253
38748
|
async function managedNotesFiles(directory) {
|
|
38254
38749
|
const notes = await listManagedNotes(directory);
|
|
38255
38750
|
return Promise.all(notes.map(async (note) => {
|
|
38256
|
-
const content = await
|
|
38751
|
+
const content = await readFile11(note.path, "utf8").catch(() => "");
|
|
38257
38752
|
const title = content ? markdownMetadata(content).title : undefined;
|
|
38258
38753
|
return {
|
|
38259
38754
|
...note,
|
|
@@ -38263,10 +38758,10 @@ async function managedNotesFiles(directory) {
|
|
|
38263
38758
|
};
|
|
38264
38759
|
}));
|
|
38265
38760
|
}
|
|
38266
|
-
var canonicalPath = (path) =>
|
|
38761
|
+
var canonicalPath = (path) => realpath9(path).catch(() => path);
|
|
38267
38762
|
function insideFolder(path, root) {
|
|
38268
|
-
const local =
|
|
38269
|
-
return local === "" || !local.startsWith("..") && !
|
|
38763
|
+
const local = relative13(root, path);
|
|
38764
|
+
return local === "" || !local.startsWith("..") && !isAbsolute8(local);
|
|
38270
38765
|
}
|
|
38271
38766
|
async function withoutManagedFiles(items, managedRoot) {
|
|
38272
38767
|
if (!managedRoot)
|
|
@@ -38297,16 +38792,16 @@ async function localProjectSearchCache(cache) {
|
|
|
38297
38792
|
function mediaTarget(folder, file, lockedTarget) {
|
|
38298
38793
|
if (!file)
|
|
38299
38794
|
return;
|
|
38300
|
-
const rootReal =
|
|
38301
|
-
const path =
|
|
38795
|
+
const rootReal = realpathSync3(resolve16(folder));
|
|
38796
|
+
const path = resolve16(folder, file);
|
|
38302
38797
|
let pathReal;
|
|
38303
38798
|
try {
|
|
38304
|
-
pathReal =
|
|
38799
|
+
pathReal = realpathSync3(path);
|
|
38305
38800
|
} catch {
|
|
38306
38801
|
return;
|
|
38307
38802
|
}
|
|
38308
|
-
const rel =
|
|
38309
|
-
if (rel.startsWith("..") ||
|
|
38803
|
+
const rel = relative13(rootReal, pathReal);
|
|
38804
|
+
if (rel.startsWith("..") || isAbsolute8(rel))
|
|
38310
38805
|
return;
|
|
38311
38806
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
38312
38807
|
return;
|
|
@@ -38345,16 +38840,16 @@ function documentError(response) {
|
|
|
38345
38840
|
function documentTarget(folder, file, lockedTarget) {
|
|
38346
38841
|
if (!file)
|
|
38347
38842
|
return;
|
|
38348
|
-
const rootReal =
|
|
38349
|
-
const path =
|
|
38843
|
+
const rootReal = realpathSync3(resolve16(folder));
|
|
38844
|
+
const path = resolve16(folder, file);
|
|
38350
38845
|
let pathReal;
|
|
38351
38846
|
try {
|
|
38352
|
-
pathReal =
|
|
38847
|
+
pathReal = realpathSync3(path);
|
|
38353
38848
|
} catch {
|
|
38354
38849
|
return;
|
|
38355
38850
|
}
|
|
38356
|
-
const rel =
|
|
38357
|
-
if (rel.startsWith("..") ||
|
|
38851
|
+
const rel = relative13(rootReal, pathReal);
|
|
38852
|
+
if (rel.startsWith("..") || isAbsolute8(rel))
|
|
38358
38853
|
return;
|
|
38359
38854
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
38360
38855
|
return;
|
|
@@ -38411,7 +38906,8 @@ async function pluginAvailability(registry, folder, linkedFolders, configWarning
|
|
|
38411
38906
|
error,
|
|
38412
38907
|
icon: plugin.icon,
|
|
38413
38908
|
id: plugin.id,
|
|
38414
|
-
label: plugin.label
|
|
38909
|
+
label: plugin.label,
|
|
38910
|
+
...detection.status === "available" && detection.pages ? { pages: detection.pages } : {}
|
|
38415
38911
|
});
|
|
38416
38912
|
}
|
|
38417
38913
|
} catch {}
|
|
@@ -38426,8 +38922,8 @@ function createFolderApi({
|
|
|
38426
38922
|
remoteAccess = false,
|
|
38427
38923
|
lockedFile
|
|
38428
38924
|
}) {
|
|
38429
|
-
let lockedRoot = restrictToRoot ?
|
|
38430
|
-
const lockedTarget = lockedFile ?
|
|
38925
|
+
let lockedRoot = restrictToRoot ? realpathSync3(root) : undefined;
|
|
38926
|
+
const lockedTarget = lockedFile ? realpathSync3(lockedFile) : undefined;
|
|
38431
38927
|
let basePlugins;
|
|
38432
38928
|
let baseExternalApiHandlers;
|
|
38433
38929
|
const assetFiles = new Map;
|
|
@@ -38465,7 +38961,7 @@ function createFolderApi({
|
|
|
38465
38961
|
const serverHost = server.host;
|
|
38466
38962
|
const remote = remoteAccess || serverHost === true || typeof serverHost === "string" && !isLoopbackServerHost(serverHost);
|
|
38467
38963
|
if (remote)
|
|
38468
|
-
lockedRoot = lockedRoot ??
|
|
38964
|
+
lockedRoot = lockedRoot ?? realpathSync3(root);
|
|
38469
38965
|
const collectionServer = createSchemaCollectionServer(server, {
|
|
38470
38966
|
boundary: lockedRoot,
|
|
38471
38967
|
lockedTarget
|
|
@@ -38491,7 +38987,7 @@ function createFolderApi({
|
|
|
38491
38987
|
if (!folder)
|
|
38492
38988
|
return;
|
|
38493
38989
|
server.emit({
|
|
38494
|
-
data: { path:
|
|
38990
|
+
data: { path: relative13(folder, path) },
|
|
38495
38991
|
event: "kennel:metadata-changed",
|
|
38496
38992
|
type: "custom"
|
|
38497
38993
|
});
|
|
@@ -38574,7 +39070,7 @@ function createFolderApi({
|
|
|
38574
39070
|
"Content-Type": asset.mediaType,
|
|
38575
39071
|
ETag: `"${hash}"`
|
|
38576
39072
|
});
|
|
38577
|
-
response.end(await
|
|
39073
|
+
response.end(await readFile11(asset.path));
|
|
38578
39074
|
} catch (error) {
|
|
38579
39075
|
response.writeHead(404, {
|
|
38580
39076
|
"Content-Type": "text/plain; charset=utf-8"
|
|
@@ -38646,14 +39142,14 @@ function createFolderApi({
|
|
|
38646
39142
|
}
|
|
38647
39143
|
if (url.pathname === "/api/documents") {
|
|
38648
39144
|
const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
|
|
38649
|
-
const target = lockedTarget ? documentTarget(folder,
|
|
39145
|
+
const target = lockedTarget ? documentTarget(folder, relative13(folder, lockedTarget), lockedTarget) : undefined;
|
|
38650
39146
|
const dataset = lockedTarget ? {
|
|
38651
39147
|
root: folder,
|
|
38652
39148
|
documents: target ? [
|
|
38653
39149
|
{
|
|
38654
|
-
path:
|
|
39150
|
+
path: relative13(folder, target.path),
|
|
38655
39151
|
kind: target.kind,
|
|
38656
|
-
content: previewTextKind(target.kind) ? await
|
|
39152
|
+
content: previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : ""
|
|
38657
39153
|
}
|
|
38658
39154
|
] : []
|
|
38659
39155
|
} : await readFolder(folder);
|
|
@@ -38669,7 +39165,7 @@ function createFolderApi({
|
|
|
38669
39165
|
documentError(response);
|
|
38670
39166
|
return;
|
|
38671
39167
|
}
|
|
38672
|
-
const details = await
|
|
39168
|
+
const details = await stat12(target.path);
|
|
38673
39169
|
if (!details.isFile()) {
|
|
38674
39170
|
documentError(response);
|
|
38675
39171
|
return;
|
|
@@ -38681,16 +39177,16 @@ function createFolderApi({
|
|
|
38681
39177
|
return;
|
|
38682
39178
|
}
|
|
38683
39179
|
watch(folder);
|
|
38684
|
-
const content = previewTextKind(target.kind) ? await
|
|
39180
|
+
const content = previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : "";
|
|
38685
39181
|
sendContract(response, url, {
|
|
38686
39182
|
content,
|
|
38687
39183
|
kind: target.kind,
|
|
38688
|
-
path:
|
|
39184
|
+
path: relative13(target.root, target.path),
|
|
38689
39185
|
root: folder,
|
|
38690
39186
|
...previewTextKind(target.kind) ? {} : {
|
|
38691
39187
|
rawUrl: `/api/raw?${new URLSearchParams({
|
|
38692
39188
|
path: folder,
|
|
38693
|
-
file:
|
|
39189
|
+
file: relative13(target.root, target.path)
|
|
38694
39190
|
})}`
|
|
38695
39191
|
}
|
|
38696
39192
|
}, {
|
|
@@ -38710,7 +39206,7 @@ function createFolderApi({
|
|
|
38710
39206
|
documentError(response);
|
|
38711
39207
|
return;
|
|
38712
39208
|
}
|
|
38713
|
-
const details = await
|
|
39209
|
+
const details = await stat12(target.path);
|
|
38714
39210
|
if (!details.isFile()) {
|
|
38715
39211
|
documentError(response);
|
|
38716
39212
|
return;
|
|
@@ -38731,10 +39227,10 @@ function createFolderApi({
|
|
|
38731
39227
|
ETag: etag,
|
|
38732
39228
|
"X-Content-Type-Options": "nosniff",
|
|
38733
39229
|
...download ? {
|
|
38734
|
-
"Content-Disposition": `attachment; filename="${
|
|
39230
|
+
"Content-Disposition": `attachment; filename="${basename8(target.path)}"`
|
|
38735
39231
|
} : {}
|
|
38736
39232
|
});
|
|
38737
|
-
response.end(await
|
|
39233
|
+
response.end(await readFile11(target.path));
|
|
38738
39234
|
} catch (error) {
|
|
38739
39235
|
documentError(response);
|
|
38740
39236
|
}
|
|
@@ -38748,7 +39244,7 @@ function createFolderApi({
|
|
|
38748
39244
|
documentError(response);
|
|
38749
39245
|
return;
|
|
38750
39246
|
}
|
|
38751
|
-
const details = await
|
|
39247
|
+
const details = await stat12(target.path);
|
|
38752
39248
|
if (!details.isFile()) {
|
|
38753
39249
|
documentError(response);
|
|
38754
39250
|
return;
|
|
@@ -38760,7 +39256,7 @@ function createFolderApi({
|
|
|
38760
39256
|
return;
|
|
38761
39257
|
}
|
|
38762
39258
|
watch(folder);
|
|
38763
|
-
const data = await
|
|
39259
|
+
const data = await readFile11(target.path);
|
|
38764
39260
|
const baseHeaders = {
|
|
38765
39261
|
"Accept-Ranges": "bytes",
|
|
38766
39262
|
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
@@ -38869,6 +39365,19 @@ function createFolderApi({
|
|
|
38869
39365
|
}))
|
|
38870
39366
|
return;
|
|
38871
39367
|
}
|
|
39368
|
+
if (url.pathname === "/api/delete-note") {
|
|
39369
|
+
if (remote) {
|
|
39370
|
+
response.writeHead(403, {
|
|
39371
|
+
"Content-Type": "text/plain; charset=utf-8"
|
|
39372
|
+
});
|
|
39373
|
+
response.end("Host actions are disabled for remote access");
|
|
39374
|
+
return;
|
|
39375
|
+
}
|
|
39376
|
+
await runApiRoute(request, response, url, deleteNoteRoute, {
|
|
39377
|
+
authorize: (folder) => folderPath(folder, root, lockedRoot)
|
|
39378
|
+
});
|
|
39379
|
+
return;
|
|
39380
|
+
}
|
|
38872
39381
|
if (url.pathname === "/api/create-agent-file") {
|
|
38873
39382
|
if (remote) {
|
|
38874
39383
|
response.writeHead(403, {
|
|
@@ -38923,7 +39432,7 @@ function createFolderApi({
|
|
|
38923
39432
|
workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
|
|
38924
39433
|
const files = [
|
|
38925
39434
|
workspace.configPath,
|
|
38926
|
-
|
|
39435
|
+
resolve16(workspace.root, "raggle.json")
|
|
38927
39436
|
].filter((path) => Boolean(path));
|
|
38928
39437
|
for (const path of files)
|
|
38929
39438
|
contexts.add(path);
|
|
@@ -39082,36 +39591,36 @@ var contentTypes = {
|
|
|
39082
39591
|
".ttf": "font/ttf"
|
|
39083
39592
|
};
|
|
39084
39593
|
async function sendFile(request, response, directory, pathname, fallback) {
|
|
39085
|
-
const root = await
|
|
39594
|
+
const root = await realpath10(directory);
|
|
39086
39595
|
const inside = (path) => {
|
|
39087
|
-
const local =
|
|
39088
|
-
return local !== ".." && !local.startsWith("../") && !
|
|
39596
|
+
const local = relative14(root, path);
|
|
39597
|
+
return local !== ".." && !local.startsWith("../") && !isAbsolute9(local);
|
|
39089
39598
|
};
|
|
39090
|
-
let target =
|
|
39599
|
+
let target = resolve17(root, `.${decodeURIComponent(pathname)}`);
|
|
39091
39600
|
if (!inside(target)) {
|
|
39092
39601
|
response.writeHead(403);
|
|
39093
39602
|
response.end();
|
|
39094
39603
|
return;
|
|
39095
39604
|
}
|
|
39096
|
-
if (!(await
|
|
39605
|
+
if (!(await stat13(target).catch(() => null))?.isFile()) {
|
|
39097
39606
|
if (!fallback) {
|
|
39098
39607
|
response.writeHead(404);
|
|
39099
39608
|
response.end();
|
|
39100
39609
|
return;
|
|
39101
39610
|
}
|
|
39102
|
-
target =
|
|
39611
|
+
target = resolve17(root, fallback);
|
|
39103
39612
|
}
|
|
39104
|
-
target = await
|
|
39613
|
+
target = await realpath10(target);
|
|
39105
39614
|
if (!inside(target)) {
|
|
39106
39615
|
response.writeHead(403);
|
|
39107
39616
|
response.end();
|
|
39108
39617
|
return;
|
|
39109
39618
|
}
|
|
39110
39619
|
response.writeHead(200, {
|
|
39111
|
-
"Content-Type": contentTypes[
|
|
39620
|
+
"Content-Type": contentTypes[extname8(target)] ?? "application/octet-stream",
|
|
39112
39621
|
"X-Content-Type-Options": "nosniff"
|
|
39113
39622
|
});
|
|
39114
|
-
response.end(request.method === "HEAD" ? undefined : await
|
|
39623
|
+
response.end(request.method === "HEAD" ? undefined : await readFile12(target));
|
|
39115
39624
|
}
|
|
39116
39625
|
function createKennelApi(options) {
|
|
39117
39626
|
const remote = options.remoteAccess || !isLoopbackServerHost(options.host);
|