kennel 0.2.32 → 0.2.33
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/dispatch.js +5 -0
- package/dist/cli/usage.js +5 -0
- package/dist/core/runtime.js +458 -166
- package/dist/llm/command.js +10 -3
- package/dist/llm/worker.js +297 -32
- package/dist/notes/command.d.ts +1 -0
- package/dist/notes/command.js +556 -0
- package/dist/package/preview.js +481 -188
- package/dist/site/server-plugins.js +51 -10
- package/package.json +3 -2
- package/schemas/config.schema.json +11 -0
- package/web/assets/{abnfDiagram-VRR7QNED-DPZyna8O.js → abnfDiagram-VRR7QNED-CpP1VHKI.js} +1 -1
- package/web/assets/{arc-BAcU9y_V.js → arc-kL4AxfvR.js} +1 -1
- package/web/assets/{architectureDiagram-ZJ3FMSHR-D5yd7Cag.js → architectureDiagram-ZJ3FMSHR-BWaRktRJ.js} +1 -1
- package/web/assets/{ask-tray-PMT6VVXN-C4dJH21Q.js → ask-tray-PMT6VVXN-FqPDpwtw.js} +3 -3
- package/web/assets/{blockDiagram-677ZJIJ3-C7LSGyxV.js → blockDiagram-677ZJIJ3-B07uVoPU.js} +1 -1
- package/web/assets/{c4Diagram-LMCZKHZV-BW2KfoQr.js → c4Diagram-LMCZKHZV-uvoaQC09.js} +1 -1
- package/web/assets/channel-Djmf1Ik1.js +1 -0
- package/web/assets/{chunk-2Q5K7J3B-4Og6VAFG.js → chunk-2Q5K7J3B-BH3SaHqk.js} +1 -1
- package/web/assets/{chunk-32BRIVSS-BYOowQl5.js → chunk-32BRIVSS-SPD8Eraw.js} +1 -1
- package/web/assets/{chunk-5VM5RSS4-C1m-lq64.js → chunk-5VM5RSS4-DNeOgOuZ.js} +1 -1
- package/web/assets/{chunk-EX3LRPZG-B2hriRpJ.js → chunk-EX3LRPZG-SJQ-vKGz.js} +1 -1
- package/web/assets/{chunk-JWPE2WC7-C6RsnzVA.js → chunk-JWPE2WC7-Dy_9wZ2Y.js} +1 -1
- package/web/assets/{chunk-MOJQB5TN-yKbMmUWf.js → chunk-MOJQB5TN-CuIx80Vn.js} +1 -1
- package/web/assets/{chunk-RYQCIY6F-BOokm3Qc.js → chunk-RYQCIY6F-DDG61RKR.js} +1 -1
- package/web/assets/{chunk-V7JOEXUC-CKW-H5An.js → chunk-V7JOEXUC-7OMVhaoH.js} +1 -1
- package/web/assets/{chunk-VR4S4FIN-BU5pFrup.js → chunk-VR4S4FIN-Y-HNaSmV.js} +1 -1
- package/web/assets/{chunk-XXDRQBXY-DYtdSJHU.js → chunk-XXDRQBXY-CtG0MPgC.js} +1 -1
- package/web/assets/classDiagram-OUVF2IWQ-hlDS9RWf.js +1 -0
- package/web/assets/classDiagram-v2-EOCWNBFH-hlDS9RWf.js +1 -0
- package/web/assets/{cose-bilkent-JH36ORCC-BZeXtC6E.js → cose-bilkent-JH36ORCC-BZaPMYcl.js} +1 -1
- package/web/assets/{cynefin-VYW2F7L2-BSDVyzMI.js → cynefin-VYW2F7L2-DxAlRHA1.js} +1 -1
- package/web/assets/{cynefinDiagram-TSTJHNR4-CReoDBYo.js → cynefinDiagram-TSTJHNR4-gjgzmagS.js} +1 -1
- package/web/assets/{dagre-VKFMJZFB-CBLLzMxd.js → dagre-VKFMJZFB-DvLPeYuw.js} +1 -1
- package/web/assets/{diagram-FQU43EPY-BcGWiZXZ.js → diagram-FQU43EPY-443LFnzG.js} +1 -1
- package/web/assets/{diagram-G47NLZAW-BXUYZA9N.js → diagram-G47NLZAW-CqfUewot.js} +1 -1
- package/web/assets/{diagram-NH7WQ7WH-BrvrZs7N.js → diagram-NH7WQ7WH-D-FElUGW.js} +1 -1
- package/web/assets/{diagram-OA4YK3LP-7lnVcjXL.js → diagram-OA4YK3LP-DbidUAp5.js} +1 -1
- package/web/assets/{diagram-WEI45ONY-s58_bMFf.js → diagram-WEI45ONY-CPLeckCZ.js} +1 -1
- package/web/assets/{ebnfDiagram-CCIWWBDH-BemWecoD.js → ebnfDiagram-CCIWWBDH-DR0rqZzN.js} +1 -1
- package/web/assets/{entity-search-page-G6QEQNXT--sKP5dP3.js → entity-search-page-G6QEQNXT-CnG8d-at.js} +1 -1
- package/web/assets/{erDiagram-Q63AITRT-ClVJddch.js → erDiagram-Q63AITRT-DMXv_8Tj.js} +1 -1
- package/web/assets/{flowDiagram-23GEKE2U-xzUJu-i_.js → flowDiagram-23GEKE2U-DnbQjZs0.js} +1 -1
- package/web/assets/{ganttDiagram-NO4QXBWP-CXQmtF5g.js → ganttDiagram-NO4QXBWP-DBKIrjj8.js} +1 -1
- package/web/assets/{gitGraphDiagram-IHSO6WYX-DCdljcLl.js → gitGraphDiagram-IHSO6WYX-BqqxKWT_.js} +1 -1
- package/web/assets/{index-CtTx1o2d.js → index-Bd3ghcW-.js} +5 -5
- package/web/assets/{index-vjrhIAJn.css → index-Dt8OzQCe.css} +1 -1
- package/web/assets/{infoDiagram-FWYZ7A6U-B11SJYSK.js → infoDiagram-FWYZ7A6U-Ba6Fs17y.js} +1 -1
- package/web/assets/{ishikawaDiagram-FXEZZL3T-B2CCGlAm.js → ishikawaDiagram-FXEZZL3T-D3BOtFjy.js} +1 -1
- package/web/assets/{journeyDiagram-5HDEW3XC-CBPETzOk.js → journeyDiagram-5HDEW3XC-MbzFPQm8.js} +1 -1
- package/web/assets/{kanban-definition-HUTT4EX6-unhQ3RPE.js → kanban-definition-HUTT4EX6-CurBmwu1.js} +1 -1
- package/web/assets/{linear-DuP_pG51.js → linear-CQIr4JjI.js} +1 -1
- package/web/assets/{linked-session-header-M7WY6IGF-DwlNCJlj.js → linked-session-header-M7WY6IGF-BBgN8zTq.js} +1 -1
- package/web/assets/{mermaid.core-BlYNzkpn.js → mermaid.core-bE6Vl3bZ.js} +4 -4
- package/web/assets/{mindmap-definition-LN4V7U3C-CtKHN6O1.js → mindmap-definition-LN4V7U3C-C1a46OGU.js} +1 -1
- package/web/assets/{pegDiagram-2B236MQR-CRyz6XWY.js → pegDiagram-2B236MQR-DZxMOxek.js} +1 -1
- package/web/assets/{pieDiagram-ENE6RG2P-BXfkPm3Q.js → pieDiagram-ENE6RG2P-BN0XOKLi.js} +1 -1
- package/web/assets/{plugin-host-modules-5NQgs5Cq.js → plugin-host-modules-CaRD5_5q.js} +1 -1
- package/web/assets/{project-search-dialog-HJFGW4DG-DPly4OiB.js → project-search-dialog-HJFGW4DG-h_dHeh7g.js} +1 -1
- package/web/assets/{project-search-page-NKQEBGPF-Cc6HV7BN.js → project-search-page-NKQEBGPF-2AzjjLEC.js} +1 -1
- package/web/assets/{quadrantDiagram-ABIIQ3AL-Drb_qWex.js → quadrantDiagram-ABIIQ3AL-C6IegTP-.js} +1 -1
- package/web/assets/{railroadDiagram-RFXS5EU6-DCKk2Pix.js → railroadDiagram-RFXS5EU6-bsjyAl-q.js} +1 -1
- package/web/assets/{requirementDiagram-TGXJPOKE-6WwZwcpD.js → requirementDiagram-TGXJPOKE-Cl8EZWy7.js} +1 -1
- package/web/assets/{sankeyDiagram-HTMAVEWB-DGGk3WCE.js → sankeyDiagram-HTMAVEWB-DhK4rRux.js} +1 -1
- package/web/assets/{selection-popup-532EHNCZ-yPhTrk7L.js → selection-popup-532EHNCZ-E2iQ2gQQ.js} +1 -1
- package/web/assets/{sequenceDiagram-DBY2YBRQ-a4tGi3DH.js → sequenceDiagram-DBY2YBRQ-DQTaPSD6.js} +1 -1
- package/web/assets/{sizeCapture-X5ZJPWSS-CHehBmy9.js → sizeCapture-X5ZJPWSS-k77bZ1zg.js} +1 -1
- package/web/assets/{stateDiagram-2N3HPSRC-Bn9GSWgV.js → stateDiagram-2N3HPSRC-Cv_5WuZJ.js} +1 -1
- package/web/assets/{stateDiagram-v2-6OUMAXLB-DI7fdBd7.js → stateDiagram-v2-6OUMAXLB-DJ1Lrskh.js} +1 -1
- package/web/assets/{swimlanes-5IMT3BWC-BX0WRzfR.js → swimlanes-5IMT3BWC-gBtAjG3z.js} +2 -2
- package/web/assets/swimlanesDiagram-G3AALYLV-DF1K9voG.js +8 -0
- package/web/assets/{timeline-definition-FHXFAJF6-9Ni2kIzq.js → timeline-definition-FHXFAJF6-CFjnM1Qq.js} +1 -1
- package/web/assets/{vennDiagram-L72KCM5P-rfBSNh3U.js → vennDiagram-L72KCM5P-CeMWrThY.js} +1 -1
- package/web/assets/{wardleyDiagram-EHGQE667-CzziImCC.js → wardleyDiagram-EHGQE667-R-ZaAOAI.js} +1 -1
- package/web/assets/{xychartDiagram-FW5EYKEG-DTLI4h5R.js → xychartDiagram-FW5EYKEG-BFXoMXiN.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/channel-um92dqpm.js +0 -1
- package/web/assets/classDiagram-OUVF2IWQ-h7sFEJBg.js +0 -1
- package/web/assets/classDiagram-v2-EOCWNBFH-h7sFEJBg.js +0 -1
- package/web/assets/swimlanesDiagram-G3AALYLV-OGHC9nqi.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 extname7, isAbsolute as
|
|
17030
|
+
import { readFile as readFile11, realpath as realpath8, stat as stat10 } from "node:fs/promises";
|
|
17031
|
+
import { extname as extname7, isAbsolute as isAbsolute8, relative as relative12, resolve as resolve13 } from "node:path";
|
|
17032
17032
|
|
|
17033
17033
|
// packages/api/src/server/watcher.ts
|
|
17034
17034
|
import { existsSync, statSync, watch } from "node:fs";
|
|
@@ -21993,16 +21993,16 @@ function sendContract(response, url, body, headers = {}) {
|
|
|
21993
21993
|
|
|
21994
21994
|
// packages/api/src/server/folder-metadata-plugin.ts
|
|
21995
21995
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
21996
|
-
import { readdir as
|
|
21997
|
-
import { homedir as
|
|
21996
|
+
import { readdir as readdir6, readFile as readFile10, realpath as realpath7, stat as stat9 } from "node:fs/promises";
|
|
21997
|
+
import { homedir as homedir4 } from "node:os";
|
|
21998
21998
|
import {
|
|
21999
|
-
basename as
|
|
22000
|
-
dirname as
|
|
21999
|
+
basename as basename7,
|
|
22000
|
+
dirname as dirname10,
|
|
22001
22001
|
extname as extname6,
|
|
22002
|
-
isAbsolute as
|
|
22003
|
-
relative as
|
|
22004
|
-
resolve as
|
|
22005
|
-
sep as
|
|
22002
|
+
isAbsolute as isAbsolute7,
|
|
22003
|
+
relative as relative11,
|
|
22004
|
+
resolve as resolve12,
|
|
22005
|
+
sep as sep8
|
|
22006
22006
|
} from "node:path";
|
|
22007
22007
|
|
|
22008
22008
|
// node_modules/.bun/@raggle-ai+barkdown@0.7.1+22117d1132b05fb8/node_modules/@raggle-ai/barkdown/dist/react.js
|
|
@@ -35561,7 +35561,7 @@ async function readFolder(root) {
|
|
|
35561
35561
|
}
|
|
35562
35562
|
|
|
35563
35563
|
// packages/api/src/server/folder-metadata-plugin.ts
|
|
35564
|
-
import { execFile as
|
|
35564
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
35565
35565
|
|
|
35566
35566
|
// packages/plugins/src/endpoint.ts
|
|
35567
35567
|
var statusByCode = {
|
|
@@ -36688,19 +36688,215 @@ function createSchemaCollectionServer(host, options = {}) {
|
|
|
36688
36688
|
// packages/api/src/server/folder-metadata-plugin.ts
|
|
36689
36689
|
import local from "@raggle-ai/local";
|
|
36690
36690
|
|
|
36691
|
-
// packages/
|
|
36692
|
-
import {
|
|
36691
|
+
// packages/notes/src/store.ts
|
|
36692
|
+
import { execFile } from "node:child_process";
|
|
36693
|
+
import {
|
|
36694
|
+
mkdir as mkdir2,
|
|
36695
|
+
readdir as readdir3,
|
|
36696
|
+
readFile as readFile4,
|
|
36697
|
+
realpath as realpath3,
|
|
36698
|
+
rename as rename2,
|
|
36699
|
+
rm,
|
|
36700
|
+
stat as stat3,
|
|
36701
|
+
writeFile as writeFile2
|
|
36702
|
+
} from "node:fs/promises";
|
|
36693
36703
|
import { homedir as homedir2 } from "node:os";
|
|
36704
|
+
import { basename as basename4, dirname as dirname5, join as join2, relative as relative6, resolve as resolve6, sep as sep4 } from "node:path";
|
|
36705
|
+
import { promisify } from "node:util";
|
|
36706
|
+
var run = promisify(execFile);
|
|
36707
|
+
var REPOSITORY_ID_KEY = "kennel.repoId";
|
|
36708
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
36709
|
+
var markdownExtensions = new Set([".md", ".markdown"]);
|
|
36710
|
+
var caseInsensitiveHosts = new Set(["github.com", "gitlab.com", "bitbucket.org"]);
|
|
36711
|
+
class RepositoryNotesError extends Error {
|
|
36712
|
+
code;
|
|
36713
|
+
constructor(code, message) {
|
|
36714
|
+
super(message);
|
|
36715
|
+
this.name = "RepositoryNotesError";
|
|
36716
|
+
this.code = code;
|
|
36717
|
+
}
|
|
36718
|
+
}
|
|
36719
|
+
function kennelConfigHome() {
|
|
36720
|
+
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
36721
|
+
return xdg ? xdg : join2(homedir2(), ".config");
|
|
36722
|
+
}
|
|
36723
|
+
function kennelConfigFile() {
|
|
36724
|
+
return join2(kennelConfigHome(), "kennel", "config.json");
|
|
36725
|
+
}
|
|
36726
|
+
function repositoryNotesRoot() {
|
|
36727
|
+
return join2(kennelConfigHome(), "kennel", "repos");
|
|
36728
|
+
}
|
|
36729
|
+
function isRepositoryId(value) {
|
|
36730
|
+
return typeof value === "string" && UUID.test(value);
|
|
36731
|
+
}
|
|
36732
|
+
function managedNotesDirectory(id) {
|
|
36733
|
+
if (!isRepositoryId(id)) {
|
|
36734
|
+
throw new RepositoryNotesError("invalid-id", `Invalid repository id: ${id}`);
|
|
36735
|
+
}
|
|
36736
|
+
return join2(repositoryNotesRoot(), id);
|
|
36737
|
+
}
|
|
36738
|
+
async function readRepositoryNotesSettings() {
|
|
36739
|
+
const file = kennelConfigFile();
|
|
36740
|
+
let text;
|
|
36741
|
+
try {
|
|
36742
|
+
text = await readFile4(file, "utf8");
|
|
36743
|
+
} catch (error) {
|
|
36744
|
+
if (error.code === "ENOENT") {
|
|
36745
|
+
return { enabled: false };
|
|
36746
|
+
}
|
|
36747
|
+
throw new RepositoryNotesError("config-invalid", `Cannot read ${file}: ${errorMessage(error)}`);
|
|
36748
|
+
}
|
|
36749
|
+
let value;
|
|
36750
|
+
try {
|
|
36751
|
+
value = JSON.parse(text);
|
|
36752
|
+
} catch (error) {
|
|
36753
|
+
throw new RepositoryNotesError("config-invalid", `Invalid JSON in ${file}: ${errorMessage(error)}`);
|
|
36754
|
+
}
|
|
36755
|
+
if (!isRecord(value)) {
|
|
36756
|
+
throw new RepositoryNotesError("config-invalid", `${file} must hold an object`);
|
|
36757
|
+
}
|
|
36758
|
+
const section = value.repositoryNotes;
|
|
36759
|
+
if (section === undefined)
|
|
36760
|
+
return { enabled: false };
|
|
36761
|
+
if (!isRecord(section) || section.enabled !== undefined && typeof section.enabled !== "boolean") {
|
|
36762
|
+
throw new RepositoryNotesError("config-invalid", `${file}: repositoryNotes must be an object with a boolean "enabled" field`);
|
|
36763
|
+
}
|
|
36764
|
+
return { enabled: section.enabled === true };
|
|
36765
|
+
}
|
|
36766
|
+
async function git(args, cwd) {
|
|
36767
|
+
const { stdout } = await run("git", args, {
|
|
36768
|
+
cwd,
|
|
36769
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" },
|
|
36770
|
+
maxBuffer: 1024 * 1024
|
|
36771
|
+
});
|
|
36772
|
+
return stdout.replace(/\r?\n$/u, "");
|
|
36773
|
+
}
|
|
36774
|
+
function gitExitCode(error) {
|
|
36775
|
+
const code = error.code;
|
|
36776
|
+
return typeof code === "number" ? code : undefined;
|
|
36777
|
+
}
|
|
36778
|
+
async function resolveCheckout(folder) {
|
|
36779
|
+
let start;
|
|
36780
|
+
try {
|
|
36781
|
+
start = await realpath3(resolve6(folder));
|
|
36782
|
+
if (!(await stat3(start)).isDirectory())
|
|
36783
|
+
start = dirname5(start);
|
|
36784
|
+
} catch {
|
|
36785
|
+
throw new RepositoryNotesError("not-git", `Folder does not exist: ${folder}`);
|
|
36786
|
+
}
|
|
36787
|
+
let output;
|
|
36788
|
+
try {
|
|
36789
|
+
output = await git(["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-common-dir"], start);
|
|
36790
|
+
} catch (error) {
|
|
36791
|
+
if (gitExitCode(error) === 128) {
|
|
36792
|
+
throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
|
|
36793
|
+
}
|
|
36794
|
+
throw error;
|
|
36795
|
+
}
|
|
36796
|
+
const [top, common] = output.split(/\r?\n/u);
|
|
36797
|
+
if (!top || !common) {
|
|
36798
|
+
throw new RepositoryNotesError("not-git", `Not a Git work tree: ${start}`);
|
|
36799
|
+
}
|
|
36800
|
+
const root = await realpath3(top);
|
|
36801
|
+
const commonDir = await realpath3(common);
|
|
36802
|
+
return { root, commonDir, configFile: join2(commonDir, "config") };
|
|
36803
|
+
}
|
|
36804
|
+
async function readBinding(checkout) {
|
|
36805
|
+
let value;
|
|
36806
|
+
try {
|
|
36807
|
+
value = await git(["config", "--file", checkout.configFile, "--get", REPOSITORY_ID_KEY], checkout.root);
|
|
36808
|
+
} catch (error) {
|
|
36809
|
+
if (gitExitCode(error) === 1)
|
|
36810
|
+
return;
|
|
36811
|
+
throw error;
|
|
36812
|
+
}
|
|
36813
|
+
const id = value.trim();
|
|
36814
|
+
if (!id)
|
|
36815
|
+
return;
|
|
36816
|
+
if (!isRepositoryId(id)) {
|
|
36817
|
+
throw new RepositoryNotesError("invalid-binding", `${checkout.configFile} has an invalid ${REPOSITORY_ID_KEY} value. Remove it or run "kennel notes new-identity".`);
|
|
36818
|
+
}
|
|
36819
|
+
return id;
|
|
36820
|
+
}
|
|
36821
|
+
function isRecord(value) {
|
|
36822
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
36823
|
+
}
|
|
36824
|
+
function errorMessage(error) {
|
|
36825
|
+
return error instanceof Error ? error.message : String(error);
|
|
36826
|
+
}
|
|
36827
|
+
async function lookupRepositoryNotes(folder) {
|
|
36828
|
+
let checkout;
|
|
36829
|
+
try {
|
|
36830
|
+
checkout = await resolveCheckout(folder);
|
|
36831
|
+
} catch (error) {
|
|
36832
|
+
if (error instanceof RepositoryNotesError && error.code === "not-git") {
|
|
36833
|
+
return { status: "not-git", folder };
|
|
36834
|
+
}
|
|
36835
|
+
throw error;
|
|
36836
|
+
}
|
|
36837
|
+
const id = await readBinding(checkout);
|
|
36838
|
+
if (!id)
|
|
36839
|
+
return { status: "unregistered", checkout, root: checkout.root };
|
|
36840
|
+
return {
|
|
36841
|
+
status: "registered",
|
|
36842
|
+
checkout,
|
|
36843
|
+
id,
|
|
36844
|
+
notesDirectory: managedNotesDirectory(id),
|
|
36845
|
+
root: checkout.root
|
|
36846
|
+
};
|
|
36847
|
+
}
|
|
36848
|
+
async function listManagedNotes(directory) {
|
|
36849
|
+
const notes = [];
|
|
36850
|
+
const visit = async (current) => {
|
|
36851
|
+
let entries;
|
|
36852
|
+
try {
|
|
36853
|
+
entries = await readdir3(current, { withFileTypes: true });
|
|
36854
|
+
} catch (error) {
|
|
36855
|
+
if (error.code === "ENOENT")
|
|
36856
|
+
return;
|
|
36857
|
+
throw error;
|
|
36858
|
+
}
|
|
36859
|
+
for (const entry of entries) {
|
|
36860
|
+
if (entry.name.startsWith(".") || entry.isSymbolicLink())
|
|
36861
|
+
continue;
|
|
36862
|
+
const path = join2(current, entry.name);
|
|
36863
|
+
if (entry.isDirectory()) {
|
|
36864
|
+
await visit(path);
|
|
36865
|
+
continue;
|
|
36866
|
+
}
|
|
36867
|
+
if (!entry.isFile())
|
|
36868
|
+
continue;
|
|
36869
|
+
const dot = entry.name.lastIndexOf(".");
|
|
36870
|
+
const extension = dot === -1 ? "" : entry.name.slice(dot).toLowerCase();
|
|
36871
|
+
if (!markdownExtensions.has(extension))
|
|
36872
|
+
continue;
|
|
36873
|
+
const details = await stat3(path);
|
|
36874
|
+
notes.push({
|
|
36875
|
+
activityAt: details.mtimeMs,
|
|
36876
|
+
name: dot === -1 ? entry.name : entry.name.slice(0, dot),
|
|
36877
|
+
path,
|
|
36878
|
+
relativePath: relative6(directory, path).split(sep4).join("/"),
|
|
36879
|
+
size: details.size
|
|
36880
|
+
});
|
|
36881
|
+
}
|
|
36882
|
+
};
|
|
36883
|
+
await visit(directory);
|
|
36884
|
+
return notes.sort((left, right) => right.activityAt - left.activityAt);
|
|
36885
|
+
}
|
|
36886
|
+
|
|
36887
|
+
// packages/api/src/server/entity-folder.ts
|
|
36888
|
+
import { readdir as readdir4, readFile as readFile5, realpath as realpath4, stat as stat4 } from "node:fs/promises";
|
|
36889
|
+
import { homedir as homedir3 } from "node:os";
|
|
36694
36890
|
import {
|
|
36695
|
-
basename as
|
|
36696
|
-
dirname as
|
|
36891
|
+
basename as basename5,
|
|
36892
|
+
dirname as dirname6,
|
|
36697
36893
|
extname as extname2,
|
|
36698
36894
|
isAbsolute as isAbsolute4,
|
|
36699
|
-
join as
|
|
36700
|
-
relative as
|
|
36701
|
-
resolve as
|
|
36895
|
+
join as join3,
|
|
36896
|
+
relative as relative7,
|
|
36897
|
+
resolve as resolve7
|
|
36702
36898
|
} from "node:path";
|
|
36703
|
-
var CONFIG_FILE =
|
|
36899
|
+
var CONFIG_FILE = join3("kennel", "config.json");
|
|
36704
36900
|
var IGNORED_DIRECTORIES = new Set([
|
|
36705
36901
|
".git",
|
|
36706
36902
|
".gtl",
|
|
@@ -36741,11 +36937,11 @@ function paragraph(body) {
|
|
|
36741
36937
|
}
|
|
36742
36938
|
function configPath() {
|
|
36743
36939
|
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
36744
|
-
return xdg ?
|
|
36940
|
+
return xdg ? join3(xdg, CONFIG_FILE) : join3(homedir3(), ".config", CONFIG_FILE);
|
|
36745
36941
|
}
|
|
36746
36942
|
async function readConfig(path) {
|
|
36747
36943
|
try {
|
|
36748
|
-
return parseConfig(await
|
|
36944
|
+
return parseConfig(await readFile5(path, "utf8"));
|
|
36749
36945
|
} catch (error) {
|
|
36750
36946
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
|
|
36751
36947
|
return null;
|
|
@@ -36762,6 +36958,8 @@ function parseConfig(source) {
|
|
|
36762
36958
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
36763
36959
|
throw new Error("Invalid Kennel entity root config");
|
|
36764
36960
|
const roots = value.allowedRoots;
|
|
36961
|
+
if (roots === undefined)
|
|
36962
|
+
return {};
|
|
36765
36963
|
if (!Array.isArray(roots))
|
|
36766
36964
|
throw new Error("Invalid Kennel entity root config");
|
|
36767
36965
|
const allowedRoots = roots.map((root) => {
|
|
@@ -36773,12 +36971,12 @@ function parseConfig(source) {
|
|
|
36773
36971
|
}
|
|
36774
36972
|
async function allowedRoots(currentWorkspaceRoot) {
|
|
36775
36973
|
const config = await readConfig(configPath());
|
|
36776
|
-
if (config)
|
|
36974
|
+
if (config?.allowedRoots)
|
|
36777
36975
|
return config.allowedRoots;
|
|
36778
36976
|
return currentWorkspaceRoot?.trim() ? [currentWorkspaceRoot] : [];
|
|
36779
36977
|
}
|
|
36780
36978
|
function inside4(root, path) {
|
|
36781
|
-
const local =
|
|
36979
|
+
const local = relative7(root, path);
|
|
36782
36980
|
return local === "" || !local.startsWith("..") && !isAbsolute4(local);
|
|
36783
36981
|
}
|
|
36784
36982
|
async function normalizeRoot(rootPath, currentWorkspaceRoot) {
|
|
@@ -36786,13 +36984,13 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
|
|
|
36786
36984
|
const fallback = currentWorkspaceRoot?.trim();
|
|
36787
36985
|
if (!trimmed && !fallback)
|
|
36788
36986
|
return null;
|
|
36789
|
-
const requested =
|
|
36790
|
-
const resolved = await
|
|
36791
|
-
const info = await
|
|
36987
|
+
const requested = resolve7(trimmed || fallback);
|
|
36988
|
+
const resolved = await realpath4(requested);
|
|
36989
|
+
const info = await stat4(resolved);
|
|
36792
36990
|
if (!info.isDirectory())
|
|
36793
36991
|
throw new Error(`${resolved} is not a folder`);
|
|
36794
36992
|
const roots = await allowedRoots(currentWorkspaceRoot);
|
|
36795
|
-
const realAllowedRoots = await Promise.all(roots.map((root) =>
|
|
36993
|
+
const realAllowedRoots = await Promise.all(roots.map((root) => realpath4(root)));
|
|
36796
36994
|
const allowed = realAllowedRoots.some((root) => inside4(root, resolved));
|
|
36797
36995
|
if (!allowed)
|
|
36798
36996
|
throw new Error("Entity root is outside the configured roots");
|
|
@@ -36801,11 +36999,11 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
|
|
|
36801
36999
|
async function markdownFiles(folderPath) {
|
|
36802
37000
|
const files = [];
|
|
36803
37001
|
async function walk(currentPath) {
|
|
36804
|
-
const entries = await
|
|
37002
|
+
const entries = await readdir4(currentPath, { withFileTypes: true });
|
|
36805
37003
|
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
36806
37004
|
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
|
|
36807
37005
|
continue;
|
|
36808
|
-
const entryPath =
|
|
37006
|
+
const entryPath = join3(currentPath, entry.name);
|
|
36809
37007
|
if (entry.isDirectory()) {
|
|
36810
37008
|
if (entryPath !== folderPath && await hasIndex(entryPath))
|
|
36811
37009
|
continue;
|
|
@@ -36820,36 +37018,36 @@ async function markdownFiles(folderPath) {
|
|
|
36820
37018
|
return files;
|
|
36821
37019
|
}
|
|
36822
37020
|
async function hasIndex(folderPath) {
|
|
36823
|
-
return
|
|
37021
|
+
return stat4(join3(folderPath, "index.md")).then((value) => value.isFile()).catch(() => false);
|
|
36824
37022
|
}
|
|
36825
37023
|
async function schemaPath(folderPath, rootPath) {
|
|
36826
37024
|
let current = folderPath;
|
|
36827
37025
|
while (current === rootPath || current.startsWith(`${rootPath}/`)) {
|
|
36828
37026
|
for (const name of ["schema.json", "_schema.json"]) {
|
|
36829
|
-
const candidate =
|
|
36830
|
-
if (await
|
|
37027
|
+
const candidate = join3(current, name);
|
|
37028
|
+
if (await stat4(candidate).then((value) => value.isFile()).catch(() => false))
|
|
36831
37029
|
return candidate;
|
|
36832
37030
|
}
|
|
36833
37031
|
if (current === rootPath)
|
|
36834
37032
|
return null;
|
|
36835
|
-
current =
|
|
37033
|
+
current = dirname6(current);
|
|
36836
37034
|
}
|
|
36837
37035
|
return null;
|
|
36838
37036
|
}
|
|
36839
37037
|
async function readEntity(folderPath, rootPath) {
|
|
36840
|
-
const indexPath =
|
|
36841
|
-
const index = parseMarkdown(await
|
|
37038
|
+
const indexPath = join3(folderPath, "index.md");
|
|
37039
|
+
const index = parseMarkdown(await readFile5(indexPath, "utf8"));
|
|
36842
37040
|
const files = await markdownFiles(folderPath);
|
|
36843
37041
|
const documents = await Promise.all(files.map(async (filePath) => {
|
|
36844
|
-
const document2 = parseMarkdown(await
|
|
37042
|
+
const document2 = parseMarkdown(await readFile5(filePath, "utf8"));
|
|
36845
37043
|
return {
|
|
36846
37044
|
content: document2.body,
|
|
36847
|
-
path:
|
|
36848
|
-
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ??
|
|
37045
|
+
path: relative7(folderPath, filePath),
|
|
37046
|
+
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename5(filePath, extname2(filePath))
|
|
36849
37047
|
};
|
|
36850
37048
|
}));
|
|
36851
|
-
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ??
|
|
36852
|
-
const relativePath =
|
|
37049
|
+
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename5(folderPath);
|
|
37050
|
+
const relativePath = relative7(rootPath, folderPath) || ".";
|
|
36853
37051
|
return {
|
|
36854
37052
|
aliases: stringValues(index.frontmatter.aliases),
|
|
36855
37053
|
content: [
|
|
@@ -36877,11 +37075,11 @@ async function discover(rootPath) {
|
|
|
36877
37075
|
async function walk(folderPath) {
|
|
36878
37076
|
if (await hasIndex(folderPath))
|
|
36879
37077
|
folders.push(folderPath);
|
|
36880
|
-
const entries = await
|
|
37078
|
+
const entries = await readdir4(folderPath, { withFileTypes: true });
|
|
36881
37079
|
for (const entry of entries) {
|
|
36882
37080
|
if (!entry.isDirectory() || entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
|
|
36883
37081
|
continue;
|
|
36884
|
-
await walk(
|
|
37082
|
+
await walk(join3(folderPath, entry.name));
|
|
36885
37083
|
}
|
|
36886
37084
|
}
|
|
36887
37085
|
await walk(rootPath);
|
|
@@ -36921,20 +37119,20 @@ async function loadEntityWorkspace(root, query, selectedPath, currentWorkspaceRo
|
|
|
36921
37119
|
// packages/api/src/server/api/document-edit.ts
|
|
36922
37120
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
36923
37121
|
import {
|
|
36924
|
-
readFile as
|
|
36925
|
-
realpath as
|
|
36926
|
-
rename as
|
|
36927
|
-
stat as
|
|
37122
|
+
readFile as readFile6,
|
|
37123
|
+
realpath as realpath5,
|
|
37124
|
+
rename as rename3,
|
|
37125
|
+
stat as stat5,
|
|
36928
37126
|
unlink as unlink2,
|
|
36929
|
-
writeFile as
|
|
37127
|
+
writeFile as writeFile3
|
|
36930
37128
|
} from "node:fs/promises";
|
|
36931
37129
|
import {
|
|
36932
|
-
dirname as
|
|
37130
|
+
dirname as dirname7,
|
|
36933
37131
|
extname as extname3,
|
|
36934
37132
|
isAbsolute as isAbsolute5,
|
|
36935
|
-
relative as
|
|
36936
|
-
resolve as
|
|
36937
|
-
sep as
|
|
37133
|
+
relative as relative8,
|
|
37134
|
+
resolve as resolve8,
|
|
37135
|
+
sep as sep5
|
|
36938
37136
|
} from "node:path";
|
|
36939
37137
|
|
|
36940
37138
|
// packages/api/src/shared/selection.ts
|
|
@@ -36952,10 +37150,10 @@ async function editDocument(folder, input) {
|
|
|
36952
37150
|
throw apiError("bad-request", "Invalid document edit.");
|
|
36953
37151
|
if (![".md", ".markdown"].includes(extname3(path).toLowerCase()))
|
|
36954
37152
|
throw apiError("bad-request", "Only Markdown notes can be edited.");
|
|
36955
|
-
const root = await
|
|
36956
|
-
const target = await
|
|
36957
|
-
const local =
|
|
36958
|
-
if (!local || local === ".." || local.startsWith(`..${
|
|
37153
|
+
const root = await realpath5(folder);
|
|
37154
|
+
const target = await realpath5(resolve8(root, path));
|
|
37155
|
+
const local = relative8(root, target);
|
|
37156
|
+
if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
|
|
36959
37157
|
throw apiError("forbidden", "The document must be inside its folder.");
|
|
36960
37158
|
let content;
|
|
36961
37159
|
try {
|
|
@@ -36965,20 +37163,20 @@ async function editDocument(folder, input) {
|
|
|
36965
37163
|
}
|
|
36966
37164
|
const previous = pending.get(target) ?? Promise.resolve();
|
|
36967
37165
|
const operation = previous.catch(() => {}).then(async () => {
|
|
36968
|
-
const details = await
|
|
37166
|
+
const details = await stat5(target);
|
|
36969
37167
|
if (!details.isFile() || details.nlink !== 1)
|
|
36970
37168
|
throw apiError("forbidden", "Only regular, unlinked files can be edited.");
|
|
36971
|
-
if (await
|
|
37169
|
+
if (await readFile6(target, "utf8") !== original)
|
|
36972
37170
|
throw apiError("conflict", "This note changed on disk. Close this preview, reload the note, and try again.");
|
|
36973
|
-
const temporary =
|
|
37171
|
+
const temporary = resolve8(dirname7(target), `.kennel-edit-${randomUUID2()}.tmp`);
|
|
36974
37172
|
try {
|
|
36975
|
-
await
|
|
37173
|
+
await writeFile3(temporary, content, {
|
|
36976
37174
|
flag: "wx",
|
|
36977
37175
|
mode: details.mode & 511
|
|
36978
37176
|
});
|
|
36979
|
-
if (await
|
|
37177
|
+
if (await readFile6(target, "utf8") !== original)
|
|
36980
37178
|
throw apiError("conflict", "This note changed on disk. Reload before applying an edit.");
|
|
36981
|
-
await
|
|
37179
|
+
await rename3(temporary, target);
|
|
36982
37180
|
} finally {
|
|
36983
37181
|
await unlink2(temporary).catch(() => {});
|
|
36984
37182
|
}
|
|
@@ -37001,7 +37199,7 @@ var documentEditRoute = {
|
|
|
37001
37199
|
throw apiError("bad-request", "Invalid document edit.");
|
|
37002
37200
|
const input = parsed.data;
|
|
37003
37201
|
const folder = context.authorize(input.folder);
|
|
37004
|
-
if (context.lockedFile && (typeof input.path !== "string" || await
|
|
37202
|
+
if (context.lockedFile && (typeof input.path !== "string" || await realpath5(resolve8(folder, input.path)) !== context.lockedFile))
|
|
37005
37203
|
throw apiError("forbidden", "Only the selected document can be edited.");
|
|
37006
37204
|
await editDocument(folder, input);
|
|
37007
37205
|
jsonResponse(response, 200, routes.documentEdit.response.parse({ saved: true }));
|
|
@@ -37009,8 +37207,8 @@ var documentEditRoute = {
|
|
|
37009
37207
|
};
|
|
37010
37208
|
|
|
37011
37209
|
// packages/api/src/server/api/create-agent-file.ts
|
|
37012
|
-
import { writeFile as
|
|
37013
|
-
import { resolve as
|
|
37210
|
+
import { writeFile as writeFile4 } from "node:fs/promises";
|
|
37211
|
+
import { resolve as resolve9 } from "node:path";
|
|
37014
37212
|
var createAgentFileRoute = {
|
|
37015
37213
|
method: "POST",
|
|
37016
37214
|
maxBodyBytes: 0,
|
|
@@ -37021,9 +37219,9 @@ var createAgentFileRoute = {
|
|
|
37021
37219
|
} catch (error) {
|
|
37022
37220
|
throw apiError("not-found", error instanceof Error ? error.message : "Folder not found");
|
|
37023
37221
|
}
|
|
37024
|
-
const target =
|
|
37222
|
+
const target = resolve9(folder, "AGENTS.md");
|
|
37025
37223
|
try {
|
|
37026
|
-
await
|
|
37224
|
+
await writeFile4(target, "", { flag: "wx" });
|
|
37027
37225
|
} catch (error) {
|
|
37028
37226
|
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
37029
37227
|
throw apiError("conflict", "AGENTS.md already exists in this folder");
|
|
@@ -37035,7 +37233,7 @@ var createAgentFileRoute = {
|
|
|
37035
37233
|
};
|
|
37036
37234
|
|
|
37037
37235
|
// packages/api/src/server/api/open-path.ts
|
|
37038
|
-
import { execFile } from "node:child_process";
|
|
37236
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
37039
37237
|
var openPathRoute = {
|
|
37040
37238
|
method: "GET",
|
|
37041
37239
|
async handle({ context, query, response }) {
|
|
@@ -37046,7 +37244,7 @@ var openPathRoute = {
|
|
|
37046
37244
|
throw apiError("not-found", error instanceof Error ? error.message : "Path not found");
|
|
37047
37245
|
}
|
|
37048
37246
|
await new Promise((done, fail) => {
|
|
37049
|
-
|
|
37247
|
+
execFile2("/usr/bin/open", [target], (error) => {
|
|
37050
37248
|
if (error)
|
|
37051
37249
|
fail(apiError("failed", "Could not open path"));
|
|
37052
37250
|
else
|
|
@@ -37059,11 +37257,11 @@ var openPathRoute = {
|
|
|
37059
37257
|
};
|
|
37060
37258
|
|
|
37061
37259
|
// packages/api/src/server/folder-favicon.ts
|
|
37062
|
-
import { readFile as
|
|
37063
|
-
import { join as
|
|
37260
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
37261
|
+
import { join as join4 } from "node:path";
|
|
37064
37262
|
async function folderWebsite(folder) {
|
|
37065
37263
|
for (const name of ["AGENTS.md", "README.md"]) {
|
|
37066
|
-
const content = await
|
|
37264
|
+
const content = await readFile7(join4(folder, name), "utf8").catch(() => "");
|
|
37067
37265
|
const entry = markdownMetadata(content).entries.find(([key]) => key === "website");
|
|
37068
37266
|
if (entry)
|
|
37069
37267
|
return websiteHost(entry[1]);
|
|
@@ -37073,7 +37271,7 @@ async function folderWebsite(folder) {
|
|
|
37073
37271
|
|
|
37074
37272
|
// packages/api/src/server/folder-icons.ts
|
|
37075
37273
|
import { createHash as createHash4 } from "node:crypto";
|
|
37076
|
-
import { readFile as
|
|
37274
|
+
import { readFile as readFile8, stat as stat6 } from "node:fs/promises";
|
|
37077
37275
|
import { extname as extname4 } from "node:path";
|
|
37078
37276
|
import { discoverFolderIcon } from "@raggle-ai/local";
|
|
37079
37277
|
var maxIconBytes2 = 4 * 1024 * 1024;
|
|
@@ -37112,13 +37310,13 @@ function iconUrl(folder, hash, mediaType) {
|
|
|
37112
37310
|
async function folderIcon(folder, assets, assetFiles, fallback) {
|
|
37113
37311
|
const icon = discoverFolderIcon(folder);
|
|
37114
37312
|
const mediaType = icon ? iconTypes.get(extname4(icon).toLowerCase()) : null;
|
|
37115
|
-
const details = icon && mediaType ? await
|
|
37313
|
+
const details = icon && mediaType ? await stat6(icon).catch(() => {
|
|
37116
37314
|
return;
|
|
37117
37315
|
}) : undefined;
|
|
37118
37316
|
if (!icon || !mediaType || !details?.size || details.size > maxIconBytes2) {
|
|
37119
37317
|
return fallback ? await fallback(folder, assets) : undefined;
|
|
37120
37318
|
}
|
|
37121
|
-
const data = await
|
|
37319
|
+
const data = await readFile8(icon);
|
|
37122
37320
|
const hash = assetKey(data);
|
|
37123
37321
|
const key = `local:${hash}`;
|
|
37124
37322
|
assetFiles.set(hash, { mediaType, path: icon });
|
|
@@ -37137,12 +37335,12 @@ async function searchIconDataUrl(icon) {
|
|
|
37137
37335
|
const mediaType = searchIconTypes.get(extname4(icon).toLowerCase());
|
|
37138
37336
|
if (!mediaType)
|
|
37139
37337
|
return;
|
|
37140
|
-
const details = await
|
|
37338
|
+
const details = await stat6(icon).catch(() => {
|
|
37141
37339
|
return;
|
|
37142
37340
|
});
|
|
37143
37341
|
if (!details?.size || details.size > maxSearchIconBytes)
|
|
37144
37342
|
return;
|
|
37145
|
-
return `data:${mediaType};base64,${(await
|
|
37343
|
+
return `data:${mediaType};base64,${(await readFile8(icon)).toString("base64")}`;
|
|
37146
37344
|
}
|
|
37147
37345
|
async function faviconAsset(folderName, assets) {
|
|
37148
37346
|
if (process.env.KENNEL_EXTERNAL_ICONS !== "1")
|
|
@@ -37188,12 +37386,19 @@ async function workspaceBreadcrumbIcons(breadcrumbs, assetFiles, assets = {}) {
|
|
|
37188
37386
|
// packages/api/src/server/plugins.ts
|
|
37189
37387
|
import { pathToFileURL } from "node:url";
|
|
37190
37388
|
import { existsSync as existsSync3 } from "node:fs";
|
|
37191
|
-
import { mkdir as
|
|
37192
|
-
import { dirname as
|
|
37389
|
+
import { mkdir as mkdir3, readFile as readFile9, stat as stat8, writeFile as writeFile5 } from "node:fs/promises";
|
|
37390
|
+
import { dirname as dirname8, resolve as resolve11, sep as sep7 } from "node:path";
|
|
37193
37391
|
|
|
37194
37392
|
// packages/notes/src/server.ts
|
|
37195
|
-
import { readdir as
|
|
37196
|
-
import {
|
|
37393
|
+
import { readdir as readdir5, realpath as realpath6, stat as stat7 } from "node:fs/promises";
|
|
37394
|
+
import {
|
|
37395
|
+
basename as basename6,
|
|
37396
|
+
extname as extname5,
|
|
37397
|
+
isAbsolute as isAbsolute6,
|
|
37398
|
+
relative as relative9,
|
|
37399
|
+
resolve as resolve10,
|
|
37400
|
+
sep as sep6
|
|
37401
|
+
} from "node:path";
|
|
37197
37402
|
|
|
37198
37403
|
// packages/notes/src/manifest.ts
|
|
37199
37404
|
var notesManifest = {
|
|
@@ -37209,47 +37414,71 @@ var notesManifest = {
|
|
|
37209
37414
|
};
|
|
37210
37415
|
|
|
37211
37416
|
// packages/notes/src/server.ts
|
|
37212
|
-
var
|
|
37417
|
+
var markdownExtensions2 = new Set([".md", ".markdown"]);
|
|
37213
37418
|
var llmNoteLimit = 20;
|
|
37214
37419
|
async function listMarkdownFiles(folder) {
|
|
37215
37420
|
const files = [];
|
|
37216
37421
|
const visit = async (current) => {
|
|
37217
|
-
const entries = await
|
|
37422
|
+
const entries = await readdir5(current, { withFileTypes: true }).catch(() => []);
|
|
37218
37423
|
for (const entry of entries) {
|
|
37219
37424
|
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
37220
37425
|
continue;
|
|
37221
|
-
const path =
|
|
37426
|
+
const path = resolve10(current, entry.name);
|
|
37222
37427
|
if (entry.isDirectory()) {
|
|
37223
37428
|
await visit(path);
|
|
37224
37429
|
continue;
|
|
37225
37430
|
}
|
|
37226
|
-
if (!entry.isFile() || !
|
|
37431
|
+
if (!entry.isFile() || !markdownExtensions2.has(extname5(entry.name))) {
|
|
37227
37432
|
continue;
|
|
37228
37433
|
}
|
|
37229
|
-
const details = await
|
|
37434
|
+
const details = await stat7(path).catch(() => {
|
|
37230
37435
|
return;
|
|
37231
37436
|
});
|
|
37232
37437
|
files.push({
|
|
37233
37438
|
activityAt: details?.mtimeMs ?? 0,
|
|
37234
|
-
name:
|
|
37235
|
-
relativePath:
|
|
37439
|
+
name: basename6(entry.name, extname5(entry.name)),
|
|
37440
|
+
relativePath: relative9(folder, path).split(sep6).join("/")
|
|
37236
37441
|
});
|
|
37237
37442
|
}
|
|
37238
37443
|
};
|
|
37239
37444
|
await visit(folder);
|
|
37240
37445
|
return files.sort((left, right) => right.activityAt - left.activityAt);
|
|
37241
37446
|
}
|
|
37242
|
-
|
|
37447
|
+
var canonical = (path) => realpath6(path).catch(() => path);
|
|
37448
|
+
function inside5(path, root) {
|
|
37449
|
+
const local = relative9(root, path);
|
|
37450
|
+
return local === "" || !local.startsWith("..") && !isAbsolute6(local);
|
|
37451
|
+
}
|
|
37452
|
+
async function withoutManaged(groups, managedRoots) {
|
|
37453
|
+
if (!managedRoots.length)
|
|
37454
|
+
return groups;
|
|
37455
|
+
const roots = await Promise.all(managedRoots.map(canonical));
|
|
37456
|
+
return Promise.all(groups.map(async (group) => {
|
|
37457
|
+
if (roots.includes(await canonical(group.folder))) {
|
|
37458
|
+
return { ...group, files: [] };
|
|
37459
|
+
}
|
|
37460
|
+
const files = await Promise.all(group.files.map(async (file) => {
|
|
37461
|
+
const real = await canonical(resolve10(group.folder, file.relativePath));
|
|
37462
|
+
return roots.some((root) => inside5(real, root)) ? undefined : file;
|
|
37463
|
+
}));
|
|
37464
|
+
return { ...group, files: files.filter((file) => file !== undefined) };
|
|
37465
|
+
}));
|
|
37466
|
+
}
|
|
37467
|
+
async function loadNotes(folder, linkedFolders = [], managedFolders = []) {
|
|
37243
37468
|
const localFiles = await listMarkdownFiles(folder);
|
|
37244
|
-
const
|
|
37245
|
-
files: await listMarkdownFiles(
|
|
37246
|
-
folder:
|
|
37469
|
+
const listGroups = (folders) => Promise.all(folders.map(async (source) => ({
|
|
37470
|
+
files: await listMarkdownFiles(source),
|
|
37471
|
+
folder: source
|
|
37247
37472
|
})));
|
|
37473
|
+
const [managed, linked] = await Promise.all([
|
|
37474
|
+
listGroups(managedFolders),
|
|
37475
|
+
listGroups(linkedFolders).then((groups) => withoutManaged(groups, managedFolders))
|
|
37476
|
+
]);
|
|
37248
37477
|
return {
|
|
37249
37478
|
files: {
|
|
37250
37479
|
items: [
|
|
37251
37480
|
...localFiles,
|
|
37252
|
-
...linked.flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
|
|
37481
|
+
...[...managed, ...linked].flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
|
|
37253
37482
|
]
|
|
37254
37483
|
},
|
|
37255
37484
|
folder,
|
|
@@ -37266,14 +37495,20 @@ function codeSpan(text) {
|
|
|
37266
37495
|
async function notesLlmSections({
|
|
37267
37496
|
folder,
|
|
37268
37497
|
linkedFolders = [],
|
|
37498
|
+
managedFolders = [],
|
|
37269
37499
|
signal
|
|
37270
37500
|
}) {
|
|
37271
37501
|
const groups = [];
|
|
37272
37502
|
const notes = [];
|
|
37273
|
-
|
|
37503
|
+
const seen = new Set;
|
|
37504
|
+
for (const source of [folder, ...managedFolders, ...linkedFolders]) {
|
|
37274
37505
|
signal.throwIfAborted();
|
|
37506
|
+
const identity = await canonical(source);
|
|
37507
|
+
if (seen.has(identity))
|
|
37508
|
+
continue;
|
|
37509
|
+
seen.add(identity);
|
|
37275
37510
|
const files = await listMarkdownFiles(source);
|
|
37276
|
-
const paths = files.map((file) =>
|
|
37511
|
+
const paths = files.map((file) => resolve10(source, file.relativePath));
|
|
37277
37512
|
groups.push({ folder: source, paths });
|
|
37278
37513
|
files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
|
|
37279
37514
|
}
|
|
@@ -37306,9 +37541,13 @@ function createNotesPlugin() {
|
|
|
37306
37541
|
},
|
|
37307
37542
|
async handle({
|
|
37308
37543
|
folder,
|
|
37309
|
-
linkedFolders
|
|
37544
|
+
linkedFolders,
|
|
37545
|
+
managedFolders
|
|
37310
37546
|
}) {
|
|
37311
|
-
return {
|
|
37547
|
+
return {
|
|
37548
|
+
data: await loadNotes(folder, linkedFolders, managedFolders),
|
|
37549
|
+
kind: "available"
|
|
37550
|
+
};
|
|
37312
37551
|
},
|
|
37313
37552
|
llm: notesLlmSections
|
|
37314
37553
|
};
|
|
@@ -37341,14 +37580,14 @@ function parsePluginRegistryConfig(content) {
|
|
|
37341
37580
|
};
|
|
37342
37581
|
}
|
|
37343
37582
|
async function nearestKennelConfig(folder) {
|
|
37344
|
-
for (let current = folder;; current =
|
|
37345
|
-
const candidate =
|
|
37346
|
-
const details = await
|
|
37583
|
+
for (let current = folder;; current = dirname8(current)) {
|
|
37584
|
+
const candidate = resolve11(current, "kennel.json");
|
|
37585
|
+
const details = await stat8(candidate).catch(() => {
|
|
37347
37586
|
return;
|
|
37348
37587
|
});
|
|
37349
37588
|
if (details?.isFile())
|
|
37350
37589
|
return candidate;
|
|
37351
|
-
if (
|
|
37590
|
+
if (dirname8(current) === current)
|
|
37352
37591
|
return;
|
|
37353
37592
|
}
|
|
37354
37593
|
}
|
|
@@ -37357,7 +37596,7 @@ async function folderPluginRegistryConfig(folder) {
|
|
|
37357
37596
|
if (!path)
|
|
37358
37597
|
return defaultPluginRegistryConfig;
|
|
37359
37598
|
try {
|
|
37360
|
-
return parsePluginRegistryConfig(await
|
|
37599
|
+
return parsePluginRegistryConfig(await readFile9(path, "utf8"));
|
|
37361
37600
|
} catch {
|
|
37362
37601
|
return defaultPluginRegistryConfig;
|
|
37363
37602
|
}
|
|
@@ -37405,7 +37644,7 @@ async function folderServerRegistryAsync() {
|
|
|
37405
37644
|
...server,
|
|
37406
37645
|
async detect(context) {
|
|
37407
37646
|
const detection = await server.detect(context);
|
|
37408
|
-
if (detection.status === "unavailable" && existsSync3(
|
|
37647
|
+
if (detection.status === "unavailable" && existsSync3(resolve11(context.folder, entry.trigger))) {
|
|
37409
37648
|
return { status: "available" };
|
|
37410
37649
|
}
|
|
37411
37650
|
return detection;
|
|
@@ -37444,13 +37683,13 @@ async function externalApiHandlers() {
|
|
|
37444
37683
|
}
|
|
37445
37684
|
|
|
37446
37685
|
// packages/api/src/server/project-discovery.ts
|
|
37447
|
-
import { execFile as
|
|
37686
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
37448
37687
|
|
|
37449
37688
|
// packages/api/src/server/linked-folder-discovery.ts
|
|
37450
|
-
import { execFile as
|
|
37689
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
37451
37690
|
import { existsSync as existsSync4 } from "node:fs";
|
|
37452
37691
|
import { createRequire as createRequire2 } from "node:module";
|
|
37453
|
-
import { dirname as
|
|
37692
|
+
import { dirname as dirname9, join as join5 } from "node:path";
|
|
37454
37693
|
import { fileURLToPath } from "node:url";
|
|
37455
37694
|
var script = `
|
|
37456
37695
|
const local = require(process.argv[1]);
|
|
@@ -37473,19 +37712,19 @@ function resolveLocalModule(from = import.meta.url) {
|
|
|
37473
37712
|
try {
|
|
37474
37713
|
candidates.push(require2.resolve("@raggle-ai/local"));
|
|
37475
37714
|
} catch {}
|
|
37476
|
-
let directory =
|
|
37477
|
-
while (
|
|
37478
|
-
if (existsSync4(
|
|
37715
|
+
let directory = dirname9(fileURLToPath(from));
|
|
37716
|
+
while (dirname9(directory) !== directory) {
|
|
37717
|
+
if (existsSync4(join5(directory, "raggle.json"))) {
|
|
37479
37718
|
for (const candidate of [
|
|
37480
|
-
|
|
37481
|
-
|
|
37719
|
+
join5(dirname9(directory), "raggle-ai-scout", "packages", "local"),
|
|
37720
|
+
join5(directory, "..", "..", "raggle-ai-scout", "packages", "local")
|
|
37482
37721
|
]) {
|
|
37483
37722
|
if (!candidates.includes(candidate))
|
|
37484
37723
|
candidates.push(candidate);
|
|
37485
37724
|
}
|
|
37486
37725
|
break;
|
|
37487
37726
|
}
|
|
37488
|
-
directory =
|
|
37727
|
+
directory = dirname9(directory);
|
|
37489
37728
|
}
|
|
37490
37729
|
for (const candidate of candidates) {
|
|
37491
37730
|
try {
|
|
@@ -37494,11 +37733,11 @@ function resolveLocalModule(from = import.meta.url) {
|
|
|
37494
37733
|
return candidate;
|
|
37495
37734
|
} catch {}
|
|
37496
37735
|
}
|
|
37497
|
-
return candidates[0] ??
|
|
37736
|
+
return candidates[0] ?? join5(dirname9(fileURLToPath(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
|
|
37498
37737
|
}
|
|
37499
37738
|
function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
|
|
37500
37739
|
return new Promise((resolve, reject) => {
|
|
37501
|
-
|
|
37740
|
+
execFile3(process.execPath, ["-e", script, modulePath, folder], {
|
|
37502
37741
|
signal,
|
|
37503
37742
|
timeout: 60000,
|
|
37504
37743
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -37589,7 +37828,7 @@ local.discoverProjectsUnderFolder({folder}).then(projects => {
|
|
|
37589
37828
|
`;
|
|
37590
37829
|
function discoverProjects(folder, signal) {
|
|
37591
37830
|
return new Promise((resolve, reject) => {
|
|
37592
|
-
|
|
37831
|
+
execFile4(process.execPath, ["-e", script2, resolveLocalModule(), folder], {
|
|
37593
37832
|
signal,
|
|
37594
37833
|
timeout: 60000,
|
|
37595
37834
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -37846,10 +38085,10 @@ var metadataExtensions = new Set([
|
|
|
37846
38085
|
".txt"
|
|
37847
38086
|
]);
|
|
37848
38087
|
function metadataFile(path, root) {
|
|
37849
|
-
const candidate =
|
|
37850
|
-
if (!candidate || candidate.startsWith("..") ||
|
|
38088
|
+
const candidate = relative11(root, path);
|
|
38089
|
+
if (!candidate || candidate.startsWith("..") || isAbsolute7(candidate))
|
|
37851
38090
|
return false;
|
|
37852
|
-
const parts = candidate.split(
|
|
38091
|
+
const parts = candidate.split(sep8);
|
|
37853
38092
|
if (parts.some((part) => part === "node_modules"))
|
|
37854
38093
|
return false;
|
|
37855
38094
|
return metadataExtensions.has(extname6(candidate).toLowerCase());
|
|
@@ -37860,11 +38099,11 @@ async function configuredAiInputModule(folder) {
|
|
|
37860
38099
|
return typeof module === "string" && module.trim() ? module.trim() : null;
|
|
37861
38100
|
}
|
|
37862
38101
|
function folderPath(value, root, lockedRoot) {
|
|
37863
|
-
const folder = realpathSync2(
|
|
38102
|
+
const folder = realpathSync2(resolve12(value ?? root));
|
|
37864
38103
|
if (!lockedRoot)
|
|
37865
38104
|
return folder;
|
|
37866
|
-
const path =
|
|
37867
|
-
if (!path || !path.startsWith("..") && !
|
|
38105
|
+
const path = relative11(lockedRoot, folder);
|
|
38106
|
+
if (!path || !path.startsWith("..") && !isAbsolute7(path))
|
|
37868
38107
|
return folder;
|
|
37869
38108
|
throw new Error(`Folder is outside the viewer root: ${folder}`);
|
|
37870
38109
|
}
|
|
@@ -37935,15 +38174,15 @@ function unique(values) {
|
|
|
37935
38174
|
return [...new Set(values)];
|
|
37936
38175
|
}
|
|
37937
38176
|
function isMachineRoot(folder) {
|
|
37938
|
-
return folder ===
|
|
38177
|
+
return folder === homedir4() || dirname10(folder) === folder;
|
|
37939
38178
|
}
|
|
37940
38179
|
async function shallowMetadata(folder) {
|
|
37941
|
-
const entries = await
|
|
38180
|
+
const entries = await readdir6(folder, { withFileTypes: true });
|
|
37942
38181
|
const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
|
|
37943
38182
|
const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname6(entry.name).toLowerCase())).map(async (entry) => {
|
|
37944
|
-
const path =
|
|
37945
|
-
const details = await
|
|
37946
|
-
const content = await
|
|
38183
|
+
const path = resolve12(folder, entry.name);
|
|
38184
|
+
const details = await stat9(path);
|
|
38185
|
+
const content = await readFile10(path, "utf8").catch(() => "");
|
|
37947
38186
|
const title = markdownMetadata(content).title;
|
|
37948
38187
|
return {
|
|
37949
38188
|
activityAt: details.mtimeMs,
|
|
@@ -37956,10 +38195,10 @@ async function shallowMetadata(folder) {
|
|
|
37956
38195
|
};
|
|
37957
38196
|
}));
|
|
37958
38197
|
const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
37959
|
-
id: createHash5("sha1").update(
|
|
38198
|
+
id: createHash5("sha1").update(resolve12(folder, entry.name)).digest("hex"),
|
|
37960
38199
|
name: entry.name,
|
|
37961
38200
|
relativePath: entry.name,
|
|
37962
|
-
worktree:
|
|
38201
|
+
worktree: resolve12(folder, entry.name)
|
|
37963
38202
|
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
37964
38203
|
return {
|
|
37965
38204
|
changes: { items: [] },
|
|
@@ -37985,8 +38224,8 @@ async function loadFolderData(folder) {
|
|
|
37985
38224
|
items: projects.map((project) => ({
|
|
37986
38225
|
id: project.id,
|
|
37987
38226
|
worktree: project.worktree,
|
|
37988
|
-
name: project.name ??
|
|
37989
|
-
relativePath:
|
|
38227
|
+
name: project.name ?? basename7(project.worktree),
|
|
38228
|
+
relativePath: relative11(folder, project.worktree)
|
|
37990
38229
|
}))
|
|
37991
38230
|
}
|
|
37992
38231
|
};
|
|
@@ -37994,10 +38233,10 @@ async function loadFolderData(folder) {
|
|
|
37994
38233
|
async function notesFiles(folder, files, origin) {
|
|
37995
38234
|
return {
|
|
37996
38235
|
items: await Promise.all(files.map(async (file) => {
|
|
37997
|
-
const path = file.path ?? (file.relativePath ?
|
|
38236
|
+
const path = file.path ?? (file.relativePath ? resolve12(folder, file.relativePath) : undefined);
|
|
37998
38237
|
if (!path)
|
|
37999
38238
|
return origin ? { ...file, folder: origin } : file;
|
|
38000
|
-
const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await
|
|
38239
|
+
const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile10(path, "utf8").catch(() => "") : "";
|
|
38001
38240
|
const title = content ? markdownMetadata(content).title : undefined;
|
|
38002
38241
|
return {
|
|
38003
38242
|
...file,
|
|
@@ -38008,6 +38247,31 @@ async function notesFiles(folder, files, origin) {
|
|
|
38008
38247
|
}))
|
|
38009
38248
|
};
|
|
38010
38249
|
}
|
|
38250
|
+
async function managedNotesFiles(directory) {
|
|
38251
|
+
const notes = await listManagedNotes(directory);
|
|
38252
|
+
return Promise.all(notes.map(async (note) => {
|
|
38253
|
+
const content = await readFile10(note.path, "utf8").catch(() => "");
|
|
38254
|
+
const title = content ? markdownMetadata(content).title : undefined;
|
|
38255
|
+
return {
|
|
38256
|
+
...note,
|
|
38257
|
+
folder: directory,
|
|
38258
|
+
name: title ?? note.name,
|
|
38259
|
+
openHref: openPathHref(note.path)
|
|
38260
|
+
};
|
|
38261
|
+
}));
|
|
38262
|
+
}
|
|
38263
|
+
var canonicalPath = (path) => realpath7(path).catch(() => path);
|
|
38264
|
+
function insideFolder(path, root) {
|
|
38265
|
+
const local = relative11(root, path);
|
|
38266
|
+
return local === "" || !local.startsWith("..") && !isAbsolute7(local);
|
|
38267
|
+
}
|
|
38268
|
+
async function withoutManagedFiles(items, managedRoot) {
|
|
38269
|
+
if (!managedRoot)
|
|
38270
|
+
return items;
|
|
38271
|
+
const root = await canonicalPath(managedRoot);
|
|
38272
|
+
const outside = await Promise.all(items.map(async (item) => !item.path || !insideFolder(await canonicalPath(item.path), root)));
|
|
38273
|
+
return items.filter((_, index) => outside[index]);
|
|
38274
|
+
}
|
|
38011
38275
|
async function localProjectSearchCache(cache) {
|
|
38012
38276
|
if (cache && Date.now() - cache.loadedAt < projectSearchCacheMs)
|
|
38013
38277
|
return cache;
|
|
@@ -38030,16 +38294,16 @@ async function localProjectSearchCache(cache) {
|
|
|
38030
38294
|
function mediaTarget(folder, file, lockedTarget) {
|
|
38031
38295
|
if (!file)
|
|
38032
38296
|
return;
|
|
38033
|
-
const rootReal = realpathSync2(
|
|
38034
|
-
const path =
|
|
38297
|
+
const rootReal = realpathSync2(resolve12(folder));
|
|
38298
|
+
const path = resolve12(folder, file);
|
|
38035
38299
|
let pathReal;
|
|
38036
38300
|
try {
|
|
38037
38301
|
pathReal = realpathSync2(path);
|
|
38038
38302
|
} catch {
|
|
38039
38303
|
return;
|
|
38040
38304
|
}
|
|
38041
|
-
const rel =
|
|
38042
|
-
if (rel.startsWith("..") ||
|
|
38305
|
+
const rel = relative11(rootReal, pathReal);
|
|
38306
|
+
if (rel.startsWith("..") || isAbsolute7(rel))
|
|
38043
38307
|
return;
|
|
38044
38308
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
38045
38309
|
return;
|
|
@@ -38078,16 +38342,16 @@ function documentError(response) {
|
|
|
38078
38342
|
function documentTarget(folder, file, lockedTarget) {
|
|
38079
38343
|
if (!file)
|
|
38080
38344
|
return;
|
|
38081
|
-
const rootReal = realpathSync2(
|
|
38082
|
-
const path =
|
|
38345
|
+
const rootReal = realpathSync2(resolve12(folder));
|
|
38346
|
+
const path = resolve12(folder, file);
|
|
38083
38347
|
let pathReal;
|
|
38084
38348
|
try {
|
|
38085
38349
|
pathReal = realpathSync2(path);
|
|
38086
38350
|
} catch {
|
|
38087
38351
|
return;
|
|
38088
38352
|
}
|
|
38089
|
-
const rel =
|
|
38090
|
-
if (rel.startsWith("..") ||
|
|
38353
|
+
const rel = relative11(rootReal, pathReal);
|
|
38354
|
+
if (rel.startsWith("..") || isAbsolute7(rel))
|
|
38091
38355
|
return;
|
|
38092
38356
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
38093
38357
|
return;
|
|
@@ -38165,6 +38429,18 @@ function createFolderApi({
|
|
|
38165
38429
|
const linkedDiscovery = createLinkedFolderDiscovery(linkedFolderLoader, (folder) => discoveryChanged(folder));
|
|
38166
38430
|
const linkedState = (folder) => lockedRoot || isMachineRoot(folder) ? { folders: [], status: "ready" } : linkedDiscovery.read(folder);
|
|
38167
38431
|
const resolveLinkedFolders = (folder) => lockedRoot || isMachineRoot(folder) ? Promise.resolve([]) : linkedDiscovery.folders(folder);
|
|
38432
|
+
const managedNotesFolder = async (folder) => {
|
|
38433
|
+
if (lockedRoot || lockedTarget || isMachineRoot(folder))
|
|
38434
|
+
return;
|
|
38435
|
+
try {
|
|
38436
|
+
if (!(await readRepositoryNotesSettings()).enabled)
|
|
38437
|
+
return;
|
|
38438
|
+
const lookup = await lookupRepositoryNotes(folder);
|
|
38439
|
+
return lookup.status === "registered" ? lookup.notesDirectory : undefined;
|
|
38440
|
+
} catch {
|
|
38441
|
+
return;
|
|
38442
|
+
}
|
|
38443
|
+
};
|
|
38168
38444
|
const favicon = faviconService();
|
|
38169
38445
|
return {
|
|
38170
38446
|
name: "kennel-folder-metadata",
|
|
@@ -38207,7 +38483,7 @@ function createFolderApi({
|
|
|
38207
38483
|
if (!folder)
|
|
38208
38484
|
return;
|
|
38209
38485
|
server.emit({
|
|
38210
|
-
data: { path:
|
|
38486
|
+
data: { path: relative11(folder, path) },
|
|
38211
38487
|
event: "kennel:metadata-changed",
|
|
38212
38488
|
type: "custom"
|
|
38213
38489
|
});
|
|
@@ -38290,7 +38566,7 @@ function createFolderApi({
|
|
|
38290
38566
|
"Content-Type": asset.mediaType,
|
|
38291
38567
|
ETag: `"${hash}"`
|
|
38292
38568
|
});
|
|
38293
|
-
response.end(await
|
|
38569
|
+
response.end(await readFile10(asset.path));
|
|
38294
38570
|
} catch (error) {
|
|
38295
38571
|
response.writeHead(404, {
|
|
38296
38572
|
"Content-Type": "text/plain; charset=utf-8"
|
|
@@ -38355,21 +38631,21 @@ function createFolderApi({
|
|
|
38355
38631
|
}
|
|
38356
38632
|
const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
|
|
38357
38633
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
38358
|
-
await new Promise((done, fail) =>
|
|
38634
|
+
await new Promise((done, fail) => execFile5(command, [folder], (error) => error ? fail(error) : done()));
|
|
38359
38635
|
response.writeHead(204);
|
|
38360
38636
|
response.end();
|
|
38361
38637
|
return;
|
|
38362
38638
|
}
|
|
38363
38639
|
if (url.pathname === "/api/documents") {
|
|
38364
38640
|
const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
|
|
38365
|
-
const target = lockedTarget ? documentTarget(folder,
|
|
38641
|
+
const target = lockedTarget ? documentTarget(folder, relative11(folder, lockedTarget), lockedTarget) : undefined;
|
|
38366
38642
|
const dataset = lockedTarget ? {
|
|
38367
38643
|
root: folder,
|
|
38368
38644
|
documents: target ? [
|
|
38369
38645
|
{
|
|
38370
|
-
path:
|
|
38646
|
+
path: relative11(folder, target.path),
|
|
38371
38647
|
kind: target.kind,
|
|
38372
|
-
content: previewTextKind(target.kind) ? await
|
|
38648
|
+
content: previewTextKind(target.kind) ? await readFile10(target.path, "utf8") : ""
|
|
38373
38649
|
}
|
|
38374
38650
|
] : []
|
|
38375
38651
|
} : await readFolder(folder);
|
|
@@ -38385,7 +38661,7 @@ function createFolderApi({
|
|
|
38385
38661
|
documentError(response);
|
|
38386
38662
|
return;
|
|
38387
38663
|
}
|
|
38388
|
-
const details = await
|
|
38664
|
+
const details = await stat9(target.path);
|
|
38389
38665
|
if (!details.isFile()) {
|
|
38390
38666
|
documentError(response);
|
|
38391
38667
|
return;
|
|
@@ -38397,16 +38673,16 @@ function createFolderApi({
|
|
|
38397
38673
|
return;
|
|
38398
38674
|
}
|
|
38399
38675
|
watch(folder);
|
|
38400
|
-
const content = previewTextKind(target.kind) ? await
|
|
38676
|
+
const content = previewTextKind(target.kind) ? await readFile10(target.path, "utf8") : "";
|
|
38401
38677
|
sendContract(response, url, {
|
|
38402
38678
|
content,
|
|
38403
38679
|
kind: target.kind,
|
|
38404
|
-
path:
|
|
38680
|
+
path: relative11(target.root, target.path),
|
|
38405
38681
|
root: folder,
|
|
38406
38682
|
...previewTextKind(target.kind) ? {} : {
|
|
38407
38683
|
rawUrl: `/api/raw?${new URLSearchParams({
|
|
38408
38684
|
path: folder,
|
|
38409
|
-
file:
|
|
38685
|
+
file: relative11(target.root, target.path)
|
|
38410
38686
|
})}`
|
|
38411
38687
|
}
|
|
38412
38688
|
}, {
|
|
@@ -38426,7 +38702,7 @@ function createFolderApi({
|
|
|
38426
38702
|
documentError(response);
|
|
38427
38703
|
return;
|
|
38428
38704
|
}
|
|
38429
|
-
const details = await
|
|
38705
|
+
const details = await stat9(target.path);
|
|
38430
38706
|
if (!details.isFile()) {
|
|
38431
38707
|
documentError(response);
|
|
38432
38708
|
return;
|
|
@@ -38447,10 +38723,10 @@ function createFolderApi({
|
|
|
38447
38723
|
ETag: etag,
|
|
38448
38724
|
"X-Content-Type-Options": "nosniff",
|
|
38449
38725
|
...download ? {
|
|
38450
|
-
"Content-Disposition": `attachment; filename="${
|
|
38726
|
+
"Content-Disposition": `attachment; filename="${basename7(target.path)}"`
|
|
38451
38727
|
} : {}
|
|
38452
38728
|
});
|
|
38453
|
-
response.end(await
|
|
38729
|
+
response.end(await readFile10(target.path));
|
|
38454
38730
|
} catch (error) {
|
|
38455
38731
|
documentError(response);
|
|
38456
38732
|
}
|
|
@@ -38464,7 +38740,7 @@ function createFolderApi({
|
|
|
38464
38740
|
documentError(response);
|
|
38465
38741
|
return;
|
|
38466
38742
|
}
|
|
38467
|
-
const details = await
|
|
38743
|
+
const details = await stat9(target.path);
|
|
38468
38744
|
if (!details.isFile()) {
|
|
38469
38745
|
documentError(response);
|
|
38470
38746
|
return;
|
|
@@ -38476,7 +38752,7 @@ function createFolderApi({
|
|
|
38476
38752
|
return;
|
|
38477
38753
|
}
|
|
38478
38754
|
watch(folder);
|
|
38479
|
-
const data = await
|
|
38755
|
+
const data = await readFile10(target.path);
|
|
38480
38756
|
const baseHeaders = {
|
|
38481
38757
|
"Accept-Ranges": "bytes",
|
|
38482
38758
|
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
@@ -38530,9 +38806,13 @@ function createFolderApi({
|
|
|
38530
38806
|
if (!lockedRoot && !isMachineRoot(folder) && request.headers["x-kennel-retry-linked"] === "1")
|
|
38531
38807
|
linkedDiscovery.retry(folder);
|
|
38532
38808
|
const discovery = linkedState(folder);
|
|
38809
|
+
const managed = await managedNotesFolder(folder);
|
|
38810
|
+
if (managed)
|
|
38811
|
+
watch(managed);
|
|
38533
38812
|
const result = await plugin.handle({
|
|
38534
38813
|
folder,
|
|
38535
|
-
linkedFolders: discovery.folders
|
|
38814
|
+
linkedFolders: discovery.folders,
|
|
38815
|
+
...managed ? { managedFolders: [managed] } : {}
|
|
38536
38816
|
}, subpath);
|
|
38537
38817
|
if (result.kind === "unavailable") {
|
|
38538
38818
|
response.writeHead(200, {
|
|
@@ -38635,7 +38915,7 @@ function createFolderApi({
|
|
|
38635
38915
|
workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
|
|
38636
38916
|
const files = [
|
|
38637
38917
|
workspace.configPath,
|
|
38638
|
-
|
|
38918
|
+
resolve12(workspace.root, "raggle.json")
|
|
38639
38919
|
].filter((path) => Boolean(path));
|
|
38640
38920
|
for (const path of files)
|
|
38641
38921
|
contexts.add(path);
|
|
@@ -38659,7 +38939,14 @@ function createFolderApi({
|
|
|
38659
38939
|
const machine = isMachineRoot(folder);
|
|
38660
38940
|
if (part === "notes") {
|
|
38661
38941
|
const scope = url.searchParams.get("notesScope");
|
|
38942
|
+
const managed = await managedNotesFolder(folder);
|
|
38662
38943
|
const localFiles = scope === "linked" ? Promise.resolve({ items: [] }) : (machine ? shallowMetadata(folder).then((data) => data.files.items) : listLocalMarkdownFiles({ folder, limit: 200 })).then((files) => notesFiles(folder, files));
|
|
38944
|
+
const managedFiles = async () => {
|
|
38945
|
+
if (scope === "linked" || !managed)
|
|
38946
|
+
return [];
|
|
38947
|
+
watch(managed);
|
|
38948
|
+
return managedNotesFiles(managed);
|
|
38949
|
+
};
|
|
38663
38950
|
const linkedFiles = async () => {
|
|
38664
38951
|
if (scope === "local" || machine || lockedRoot)
|
|
38665
38952
|
return [];
|
|
@@ -38670,20 +38957,25 @@ function createFolderApi({
|
|
|
38670
38957
|
throw new Error("Linked notes discovery failed. Retry to try again.");
|
|
38671
38958
|
return Promise.all(linkedFolders.map(async (linkedFolder) => {
|
|
38672
38959
|
watch(linkedFolder);
|
|
38673
|
-
|
|
38960
|
+
const group = await notesFiles(linkedFolder, await listLocalMarkdownFiles({
|
|
38674
38961
|
folder: linkedFolder,
|
|
38675
38962
|
limit: 200
|
|
38676
38963
|
}), linkedFolder);
|
|
38964
|
+
return {
|
|
38965
|
+
items: await withoutManagedFiles(group.items, managed)
|
|
38966
|
+
};
|
|
38677
38967
|
}));
|
|
38678
38968
|
};
|
|
38679
|
-
const [files, linked] = await Promise.all([
|
|
38969
|
+
const [files, managedItems, linked] = await Promise.all([
|
|
38680
38970
|
localFiles,
|
|
38971
|
+
managedFiles(),
|
|
38681
38972
|
linkedFiles()
|
|
38682
38973
|
]);
|
|
38683
38974
|
sendContract(response, url, {
|
|
38684
38975
|
files: {
|
|
38685
38976
|
items: [
|
|
38686
38977
|
...files.items,
|
|
38978
|
+
...managedItems,
|
|
38687
38979
|
...linked.flatMap((group) => group.items)
|
|
38688
38980
|
]
|
|
38689
38981
|
},
|
|
@@ -38782,26 +39074,26 @@ var contentTypes = {
|
|
|
38782
39074
|
".ttf": "font/ttf"
|
|
38783
39075
|
};
|
|
38784
39076
|
async function sendFile(request, response, directory, pathname, fallback) {
|
|
38785
|
-
const root = await
|
|
39077
|
+
const root = await realpath8(directory);
|
|
38786
39078
|
const inside = (path) => {
|
|
38787
|
-
const local =
|
|
38788
|
-
return local !== ".." && !local.startsWith("../") && !
|
|
39079
|
+
const local = relative12(root, path);
|
|
39080
|
+
return local !== ".." && !local.startsWith("../") && !isAbsolute8(local);
|
|
38789
39081
|
};
|
|
38790
|
-
let target =
|
|
39082
|
+
let target = resolve13(root, `.${decodeURIComponent(pathname)}`);
|
|
38791
39083
|
if (!inside(target)) {
|
|
38792
39084
|
response.writeHead(403);
|
|
38793
39085
|
response.end();
|
|
38794
39086
|
return;
|
|
38795
39087
|
}
|
|
38796
|
-
if (!(await
|
|
39088
|
+
if (!(await stat10(target).catch(() => null))?.isFile()) {
|
|
38797
39089
|
if (!fallback) {
|
|
38798
39090
|
response.writeHead(404);
|
|
38799
39091
|
response.end();
|
|
38800
39092
|
return;
|
|
38801
39093
|
}
|
|
38802
|
-
target =
|
|
39094
|
+
target = resolve13(root, fallback);
|
|
38803
39095
|
}
|
|
38804
|
-
target = await
|
|
39096
|
+
target = await realpath8(target);
|
|
38805
39097
|
if (!inside(target)) {
|
|
38806
39098
|
response.writeHead(403);
|
|
38807
39099
|
response.end();
|
|
@@ -38811,7 +39103,7 @@ async function sendFile(request, response, directory, pathname, fallback) {
|
|
|
38811
39103
|
"Content-Type": contentTypes[extname7(target)] ?? "application/octet-stream",
|
|
38812
39104
|
"X-Content-Type-Options": "nosniff"
|
|
38813
39105
|
});
|
|
38814
|
-
response.end(request.method === "HEAD" ? undefined : await
|
|
39106
|
+
response.end(request.method === "HEAD" ? undefined : await readFile11(target));
|
|
38815
39107
|
}
|
|
38816
39108
|
function createKennelApi(options) {
|
|
38817
39109
|
const remote = options.remoteAccess || !isLoopbackServerHost(options.host);
|