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/package/preview.js
CHANGED
|
@@ -38305,17 +38305,215 @@ var init_server2 = __esm(() => {
|
|
|
38305
38305
|
init_node3();
|
|
38306
38306
|
});
|
|
38307
38307
|
|
|
38308
|
-
// packages/
|
|
38309
|
-
import {
|
|
38308
|
+
// packages/notes/src/store.ts
|
|
38309
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
38310
|
+
import {
|
|
38311
|
+
mkdir as mkdir3,
|
|
38312
|
+
readdir as readdir3,
|
|
38313
|
+
readFile as readFile5,
|
|
38314
|
+
realpath as realpath3,
|
|
38315
|
+
rename as rename2,
|
|
38316
|
+
rm as rm2,
|
|
38317
|
+
stat as stat3,
|
|
38318
|
+
writeFile as writeFile3
|
|
38319
|
+
} from "node:fs/promises";
|
|
38310
38320
|
import { homedir as homedir2 } from "node:os";
|
|
38321
|
+
import { basename as basename5, dirname as dirname6, join as join2, relative as relative7, resolve as resolve7, sep as sep4 } from "node:path";
|
|
38322
|
+
import { promisify } from "node:util";
|
|
38323
|
+
function kennelConfigHome() {
|
|
38324
|
+
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
38325
|
+
return xdg ? xdg : join2(homedir2(), ".config");
|
|
38326
|
+
}
|
|
38327
|
+
function kennelConfigFile() {
|
|
38328
|
+
return join2(kennelConfigHome(), "kennel", "config.json");
|
|
38329
|
+
}
|
|
38330
|
+
function repositoryNotesRoot() {
|
|
38331
|
+
return join2(kennelConfigHome(), "kennel", "repos");
|
|
38332
|
+
}
|
|
38333
|
+
function isRepositoryId(value) {
|
|
38334
|
+
return typeof value === "string" && UUID.test(value);
|
|
38335
|
+
}
|
|
38336
|
+
function managedNotesDirectory(id) {
|
|
38337
|
+
if (!isRepositoryId(id)) {
|
|
38338
|
+
throw new RepositoryNotesError("invalid-id", `Invalid repository id: ${id}`);
|
|
38339
|
+
}
|
|
38340
|
+
return join2(repositoryNotesRoot(), id);
|
|
38341
|
+
}
|
|
38342
|
+
async function readRepositoryNotesSettings() {
|
|
38343
|
+
const file = kennelConfigFile();
|
|
38344
|
+
let text;
|
|
38345
|
+
try {
|
|
38346
|
+
text = await readFile5(file, "utf8");
|
|
38347
|
+
} catch (error) {
|
|
38348
|
+
if (error.code === "ENOENT") {
|
|
38349
|
+
return { enabled: false };
|
|
38350
|
+
}
|
|
38351
|
+
throw new RepositoryNotesError("config-invalid", `Cannot read ${file}: ${errorMessage(error)}`);
|
|
38352
|
+
}
|
|
38353
|
+
let value;
|
|
38354
|
+
try {
|
|
38355
|
+
value = JSON.parse(text);
|
|
38356
|
+
} catch (error) {
|
|
38357
|
+
throw new RepositoryNotesError("config-invalid", `Invalid JSON in ${file}: ${errorMessage(error)}`);
|
|
38358
|
+
}
|
|
38359
|
+
if (!isRecord(value)) {
|
|
38360
|
+
throw new RepositoryNotesError("config-invalid", `${file} must hold an object`);
|
|
38361
|
+
}
|
|
38362
|
+
const section = value.repositoryNotes;
|
|
38363
|
+
if (section === undefined)
|
|
38364
|
+
return { enabled: false };
|
|
38365
|
+
if (!isRecord(section) || section.enabled !== undefined && typeof section.enabled !== "boolean") {
|
|
38366
|
+
throw new RepositoryNotesError("config-invalid", `${file}: repositoryNotes must be an object with a boolean "enabled" field`);
|
|
38367
|
+
}
|
|
38368
|
+
return { enabled: section.enabled === true };
|
|
38369
|
+
}
|
|
38370
|
+
async function git(args, cwd) {
|
|
38371
|
+
const { stdout } = await run("git", args, {
|
|
38372
|
+
cwd,
|
|
38373
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" },
|
|
38374
|
+
maxBuffer: 1024 * 1024
|
|
38375
|
+
});
|
|
38376
|
+
return stdout.replace(/\r?\n$/u, "");
|
|
38377
|
+
}
|
|
38378
|
+
function gitExitCode(error) {
|
|
38379
|
+
const code = error.code;
|
|
38380
|
+
return typeof code === "number" ? code : undefined;
|
|
38381
|
+
}
|
|
38382
|
+
async function resolveCheckout(folder) {
|
|
38383
|
+
let start;
|
|
38384
|
+
try {
|
|
38385
|
+
start = await realpath3(resolve7(folder));
|
|
38386
|
+
if (!(await stat3(start)).isDirectory())
|
|
38387
|
+
start = dirname6(start);
|
|
38388
|
+
} catch {
|
|
38389
|
+
throw new RepositoryNotesError("not-git", `Folder does not exist: ${folder}`);
|
|
38390
|
+
}
|
|
38391
|
+
let output;
|
|
38392
|
+
try {
|
|
38393
|
+
output = await git(["rev-parse", "--path-format=absolute", "--show-toplevel", "--git-common-dir"], start);
|
|
38394
|
+
} catch (error) {
|
|
38395
|
+
if (gitExitCode(error) === 128) {
|
|
38396
|
+
throw new RepositoryNotesError("not-git", `Not a Git repository: ${start}. Repository notes need a Git checkout.`);
|
|
38397
|
+
}
|
|
38398
|
+
throw error;
|
|
38399
|
+
}
|
|
38400
|
+
const [top, common] = output.split(/\r?\n/u);
|
|
38401
|
+
if (!top || !common) {
|
|
38402
|
+
throw new RepositoryNotesError("not-git", `Not a Git work tree: ${start}`);
|
|
38403
|
+
}
|
|
38404
|
+
const root = await realpath3(top);
|
|
38405
|
+
const commonDir = await realpath3(common);
|
|
38406
|
+
return { root, commonDir, configFile: join2(commonDir, "config") };
|
|
38407
|
+
}
|
|
38408
|
+
async function readBinding(checkout) {
|
|
38409
|
+
let value;
|
|
38410
|
+
try {
|
|
38411
|
+
value = await git(["config", "--file", checkout.configFile, "--get", REPOSITORY_ID_KEY], checkout.root);
|
|
38412
|
+
} catch (error) {
|
|
38413
|
+
if (gitExitCode(error) === 1)
|
|
38414
|
+
return;
|
|
38415
|
+
throw error;
|
|
38416
|
+
}
|
|
38417
|
+
const id = value.trim();
|
|
38418
|
+
if (!id)
|
|
38419
|
+
return;
|
|
38420
|
+
if (!isRepositoryId(id)) {
|
|
38421
|
+
throw new RepositoryNotesError("invalid-binding", `${checkout.configFile} has an invalid ${REPOSITORY_ID_KEY} value. Remove it or run "kennel notes new-identity".`);
|
|
38422
|
+
}
|
|
38423
|
+
return id;
|
|
38424
|
+
}
|
|
38425
|
+
function isRecord(value) {
|
|
38426
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
38427
|
+
}
|
|
38428
|
+
function errorMessage(error) {
|
|
38429
|
+
return error instanceof Error ? error.message : String(error);
|
|
38430
|
+
}
|
|
38431
|
+
async function lookupRepositoryNotes(folder) {
|
|
38432
|
+
let checkout;
|
|
38433
|
+
try {
|
|
38434
|
+
checkout = await resolveCheckout(folder);
|
|
38435
|
+
} catch (error) {
|
|
38436
|
+
if (error instanceof RepositoryNotesError && error.code === "not-git") {
|
|
38437
|
+
return { status: "not-git", folder };
|
|
38438
|
+
}
|
|
38439
|
+
throw error;
|
|
38440
|
+
}
|
|
38441
|
+
const id = await readBinding(checkout);
|
|
38442
|
+
if (!id)
|
|
38443
|
+
return { status: "unregistered", checkout, root: checkout.root };
|
|
38444
|
+
return {
|
|
38445
|
+
status: "registered",
|
|
38446
|
+
checkout,
|
|
38447
|
+
id,
|
|
38448
|
+
notesDirectory: managedNotesDirectory(id),
|
|
38449
|
+
root: checkout.root
|
|
38450
|
+
};
|
|
38451
|
+
}
|
|
38452
|
+
async function listManagedNotes(directory) {
|
|
38453
|
+
const notes = [];
|
|
38454
|
+
const visit = async (current) => {
|
|
38455
|
+
let entries;
|
|
38456
|
+
try {
|
|
38457
|
+
entries = await readdir3(current, { withFileTypes: true });
|
|
38458
|
+
} catch (error) {
|
|
38459
|
+
if (error.code === "ENOENT")
|
|
38460
|
+
return;
|
|
38461
|
+
throw error;
|
|
38462
|
+
}
|
|
38463
|
+
for (const entry of entries) {
|
|
38464
|
+
if (entry.name.startsWith(".") || entry.isSymbolicLink())
|
|
38465
|
+
continue;
|
|
38466
|
+
const path = join2(current, entry.name);
|
|
38467
|
+
if (entry.isDirectory()) {
|
|
38468
|
+
await visit(path);
|
|
38469
|
+
continue;
|
|
38470
|
+
}
|
|
38471
|
+
if (!entry.isFile())
|
|
38472
|
+
continue;
|
|
38473
|
+
const dot = entry.name.lastIndexOf(".");
|
|
38474
|
+
const extension = dot === -1 ? "" : entry.name.slice(dot).toLowerCase();
|
|
38475
|
+
if (!markdownExtensions.has(extension))
|
|
38476
|
+
continue;
|
|
38477
|
+
const details = await stat3(path);
|
|
38478
|
+
notes.push({
|
|
38479
|
+
activityAt: details.mtimeMs,
|
|
38480
|
+
name: dot === -1 ? entry.name : entry.name.slice(0, dot),
|
|
38481
|
+
path,
|
|
38482
|
+
relativePath: relative7(directory, path).split(sep4).join("/"),
|
|
38483
|
+
size: details.size
|
|
38484
|
+
});
|
|
38485
|
+
}
|
|
38486
|
+
};
|
|
38487
|
+
await visit(directory);
|
|
38488
|
+
return notes.sort((left, right) => right.activityAt - left.activityAt);
|
|
38489
|
+
}
|
|
38490
|
+
var run, REPOSITORY_ID_KEY = "kennel.repoId", UUID, markdownExtensions, caseInsensitiveHosts, RepositoryNotesError;
|
|
38491
|
+
var init_store = __esm(() => {
|
|
38492
|
+
run = promisify(execFile2);
|
|
38493
|
+
UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u;
|
|
38494
|
+
markdownExtensions = new Set([".md", ".markdown"]);
|
|
38495
|
+
caseInsensitiveHosts = new Set(["github.com", "gitlab.com", "bitbucket.org"]);
|
|
38496
|
+
RepositoryNotesError = class RepositoryNotesError extends Error {
|
|
38497
|
+
code;
|
|
38498
|
+
constructor(code, message) {
|
|
38499
|
+
super(message);
|
|
38500
|
+
this.name = "RepositoryNotesError";
|
|
38501
|
+
this.code = code;
|
|
38502
|
+
}
|
|
38503
|
+
};
|
|
38504
|
+
});
|
|
38505
|
+
|
|
38506
|
+
// packages/api/src/server/entity-folder.ts
|
|
38507
|
+
import { readdir as readdir4, readFile as readFile6, realpath as realpath4, stat as stat4 } from "node:fs/promises";
|
|
38508
|
+
import { homedir as homedir3 } from "node:os";
|
|
38311
38509
|
import {
|
|
38312
|
-
basename as
|
|
38313
|
-
dirname as
|
|
38510
|
+
basename as basename6,
|
|
38511
|
+
dirname as dirname7,
|
|
38314
38512
|
extname as extname2,
|
|
38315
38513
|
isAbsolute as isAbsolute4,
|
|
38316
|
-
join as
|
|
38317
|
-
relative as
|
|
38318
|
-
resolve as
|
|
38514
|
+
join as join3,
|
|
38515
|
+
relative as relative8,
|
|
38516
|
+
resolve as resolve8
|
|
38319
38517
|
} from "node:path";
|
|
38320
38518
|
function parseMarkdown(source) {
|
|
38321
38519
|
if (!source.startsWith(`---
|
|
@@ -38350,11 +38548,11 @@ function paragraph(body) {
|
|
|
38350
38548
|
}
|
|
38351
38549
|
function configPath() {
|
|
38352
38550
|
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
38353
|
-
return xdg ?
|
|
38551
|
+
return xdg ? join3(xdg, CONFIG_FILE) : join3(homedir3(), ".config", CONFIG_FILE);
|
|
38354
38552
|
}
|
|
38355
38553
|
async function readConfig(path) {
|
|
38356
38554
|
try {
|
|
38357
|
-
return parseConfig(await
|
|
38555
|
+
return parseConfig(await readFile6(path, "utf8"));
|
|
38358
38556
|
} catch (error) {
|
|
38359
38557
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
|
|
38360
38558
|
return null;
|
|
@@ -38371,6 +38569,8 @@ function parseConfig(source) {
|
|
|
38371
38569
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
38372
38570
|
throw new Error("Invalid Kennel entity root config");
|
|
38373
38571
|
const roots = value.allowedRoots;
|
|
38572
|
+
if (roots === undefined)
|
|
38573
|
+
return {};
|
|
38374
38574
|
if (!Array.isArray(roots))
|
|
38375
38575
|
throw new Error("Invalid Kennel entity root config");
|
|
38376
38576
|
const allowedRoots = roots.map((root) => {
|
|
@@ -38382,12 +38582,12 @@ function parseConfig(source) {
|
|
|
38382
38582
|
}
|
|
38383
38583
|
async function allowedRoots(currentWorkspaceRoot) {
|
|
38384
38584
|
const config = await readConfig(configPath());
|
|
38385
|
-
if (config)
|
|
38585
|
+
if (config?.allowedRoots)
|
|
38386
38586
|
return config.allowedRoots;
|
|
38387
38587
|
return currentWorkspaceRoot?.trim() ? [currentWorkspaceRoot] : [];
|
|
38388
38588
|
}
|
|
38389
38589
|
function inside4(root, path) {
|
|
38390
|
-
const local =
|
|
38590
|
+
const local = relative8(root, path);
|
|
38391
38591
|
return local === "" || !local.startsWith("..") && !isAbsolute4(local);
|
|
38392
38592
|
}
|
|
38393
38593
|
async function normalizeRoot(rootPath, currentWorkspaceRoot) {
|
|
@@ -38395,13 +38595,13 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
|
|
|
38395
38595
|
const fallback = currentWorkspaceRoot?.trim();
|
|
38396
38596
|
if (!trimmed && !fallback)
|
|
38397
38597
|
return null;
|
|
38398
|
-
const requested =
|
|
38399
|
-
const resolved = await
|
|
38400
|
-
const info = await
|
|
38598
|
+
const requested = resolve8(trimmed || fallback);
|
|
38599
|
+
const resolved = await realpath4(requested);
|
|
38600
|
+
const info = await stat4(resolved);
|
|
38401
38601
|
if (!info.isDirectory())
|
|
38402
38602
|
throw new Error(`${resolved} is not a folder`);
|
|
38403
38603
|
const roots = await allowedRoots(currentWorkspaceRoot);
|
|
38404
|
-
const realAllowedRoots = await Promise.all(roots.map((root) =>
|
|
38604
|
+
const realAllowedRoots = await Promise.all(roots.map((root) => realpath4(root)));
|
|
38405
38605
|
const allowed = realAllowedRoots.some((root) => inside4(root, resolved));
|
|
38406
38606
|
if (!allowed)
|
|
38407
38607
|
throw new Error("Entity root is outside the configured roots");
|
|
@@ -38410,11 +38610,11 @@ async function normalizeRoot(rootPath, currentWorkspaceRoot) {
|
|
|
38410
38610
|
async function markdownFiles(folderPath) {
|
|
38411
38611
|
const files = [];
|
|
38412
38612
|
async function walk(currentPath) {
|
|
38413
|
-
const entries = await
|
|
38613
|
+
const entries = await readdir4(currentPath, { withFileTypes: true });
|
|
38414
38614
|
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
38415
38615
|
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
|
|
38416
38616
|
continue;
|
|
38417
|
-
const entryPath =
|
|
38617
|
+
const entryPath = join3(currentPath, entry.name);
|
|
38418
38618
|
if (entry.isDirectory()) {
|
|
38419
38619
|
if (entryPath !== folderPath && await hasIndex(entryPath))
|
|
38420
38620
|
continue;
|
|
@@ -38429,36 +38629,36 @@ async function markdownFiles(folderPath) {
|
|
|
38429
38629
|
return files;
|
|
38430
38630
|
}
|
|
38431
38631
|
async function hasIndex(folderPath) {
|
|
38432
|
-
return
|
|
38632
|
+
return stat4(join3(folderPath, "index.md")).then((value) => value.isFile()).catch(() => false);
|
|
38433
38633
|
}
|
|
38434
38634
|
async function schemaPath(folderPath, rootPath) {
|
|
38435
38635
|
let current = folderPath;
|
|
38436
38636
|
while (current === rootPath || current.startsWith(`${rootPath}/`)) {
|
|
38437
38637
|
for (const name of ["schema.json", "_schema.json"]) {
|
|
38438
|
-
const candidate =
|
|
38439
|
-
if (await
|
|
38638
|
+
const candidate = join3(current, name);
|
|
38639
|
+
if (await stat4(candidate).then((value) => value.isFile()).catch(() => false))
|
|
38440
38640
|
return candidate;
|
|
38441
38641
|
}
|
|
38442
38642
|
if (current === rootPath)
|
|
38443
38643
|
return null;
|
|
38444
|
-
current =
|
|
38644
|
+
current = dirname7(current);
|
|
38445
38645
|
}
|
|
38446
38646
|
return null;
|
|
38447
38647
|
}
|
|
38448
38648
|
async function readEntity(folderPath, rootPath) {
|
|
38449
|
-
const indexPath =
|
|
38450
|
-
const index = parseMarkdown(await
|
|
38649
|
+
const indexPath = join3(folderPath, "index.md");
|
|
38650
|
+
const index = parseMarkdown(await readFile6(indexPath, "utf8"));
|
|
38451
38651
|
const files = await markdownFiles(folderPath);
|
|
38452
38652
|
const documents = await Promise.all(files.map(async (filePath) => {
|
|
38453
|
-
const document2 = parseMarkdown(await
|
|
38653
|
+
const document2 = parseMarkdown(await readFile6(filePath, "utf8"));
|
|
38454
38654
|
return {
|
|
38455
38655
|
content: document2.body,
|
|
38456
|
-
path:
|
|
38457
|
-
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ??
|
|
38656
|
+
path: relative8(folderPath, filePath),
|
|
38657
|
+
title: stringValue(document2.frontmatter.title) ?? heading(document2.body) ?? basename6(filePath, extname2(filePath))
|
|
38458
38658
|
};
|
|
38459
38659
|
}));
|
|
38460
|
-
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ??
|
|
38461
|
-
const relativePath =
|
|
38660
|
+
const title = stringValue(index.frontmatter.title) ?? heading(index.body) ?? basename6(folderPath);
|
|
38661
|
+
const relativePath = relative8(rootPath, folderPath) || ".";
|
|
38462
38662
|
return {
|
|
38463
38663
|
aliases: stringValues(index.frontmatter.aliases),
|
|
38464
38664
|
content: [
|
|
@@ -38486,11 +38686,11 @@ async function discover(rootPath) {
|
|
|
38486
38686
|
async function walk(folderPath) {
|
|
38487
38687
|
if (await hasIndex(folderPath))
|
|
38488
38688
|
folders.push(folderPath);
|
|
38489
|
-
const entries = await
|
|
38689
|
+
const entries = await readdir4(folderPath, { withFileTypes: true });
|
|
38490
38690
|
for (const entry of entries) {
|
|
38491
38691
|
if (!entry.isDirectory() || entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name))
|
|
38492
38692
|
continue;
|
|
38493
|
-
await walk(
|
|
38693
|
+
await walk(join3(folderPath, entry.name));
|
|
38494
38694
|
}
|
|
38495
38695
|
}
|
|
38496
38696
|
await walk(rootPath);
|
|
@@ -38529,7 +38729,7 @@ async function loadEntityWorkspace(root, query, selectedPath, currentWorkspaceRo
|
|
|
38529
38729
|
var CONFIG_FILE, IGNORED_DIRECTORIES;
|
|
38530
38730
|
var init_entity_folder = __esm(() => {
|
|
38531
38731
|
init_dist3();
|
|
38532
|
-
CONFIG_FILE =
|
|
38732
|
+
CONFIG_FILE = join3("kennel", "config.json");
|
|
38533
38733
|
IGNORED_DIRECTORIES = new Set([
|
|
38534
38734
|
".git",
|
|
38535
38735
|
".gtl",
|
|
@@ -38549,20 +38749,20 @@ function replaceSelection(content, start, end, replacement) {
|
|
|
38549
38749
|
// packages/api/src/server/api/document-edit.ts
|
|
38550
38750
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
38551
38751
|
import {
|
|
38552
|
-
readFile as
|
|
38553
|
-
realpath as
|
|
38554
|
-
rename as
|
|
38555
|
-
stat as
|
|
38752
|
+
readFile as readFile7,
|
|
38753
|
+
realpath as realpath5,
|
|
38754
|
+
rename as rename3,
|
|
38755
|
+
stat as stat5,
|
|
38556
38756
|
unlink as unlink2,
|
|
38557
|
-
writeFile as
|
|
38757
|
+
writeFile as writeFile4
|
|
38558
38758
|
} from "node:fs/promises";
|
|
38559
38759
|
import {
|
|
38560
|
-
dirname as
|
|
38760
|
+
dirname as dirname8,
|
|
38561
38761
|
extname as extname3,
|
|
38562
38762
|
isAbsolute as isAbsolute5,
|
|
38563
|
-
relative as
|
|
38564
|
-
resolve as
|
|
38565
|
-
sep as
|
|
38763
|
+
relative as relative9,
|
|
38764
|
+
resolve as resolve9,
|
|
38765
|
+
sep as sep5
|
|
38566
38766
|
} from "node:path";
|
|
38567
38767
|
async function editDocument(folder, input) {
|
|
38568
38768
|
const { path, original, start, end, replacement } = input;
|
|
@@ -38570,10 +38770,10 @@ async function editDocument(folder, input) {
|
|
|
38570
38770
|
throw apiError("bad-request", "Invalid document edit.");
|
|
38571
38771
|
if (![".md", ".markdown"].includes(extname3(path).toLowerCase()))
|
|
38572
38772
|
throw apiError("bad-request", "Only Markdown notes can be edited.");
|
|
38573
|
-
const root = await
|
|
38574
|
-
const target = await
|
|
38575
|
-
const local =
|
|
38576
|
-
if (!local || local === ".." || local.startsWith(`..${
|
|
38773
|
+
const root = await realpath5(folder);
|
|
38774
|
+
const target = await realpath5(resolve9(root, path));
|
|
38775
|
+
const local = relative9(root, target);
|
|
38776
|
+
if (!local || local === ".." || local.startsWith(`..${sep5}`) || isAbsolute5(local))
|
|
38577
38777
|
throw apiError("forbidden", "The document must be inside its folder.");
|
|
38578
38778
|
let content;
|
|
38579
38779
|
try {
|
|
@@ -38583,20 +38783,20 @@ async function editDocument(folder, input) {
|
|
|
38583
38783
|
}
|
|
38584
38784
|
const previous = pending.get(target) ?? Promise.resolve();
|
|
38585
38785
|
const operation = previous.catch(() => {}).then(async () => {
|
|
38586
|
-
const details = await
|
|
38786
|
+
const details = await stat5(target);
|
|
38587
38787
|
if (!details.isFile() || details.nlink !== 1)
|
|
38588
38788
|
throw apiError("forbidden", "Only regular, unlinked files can be edited.");
|
|
38589
|
-
if (await
|
|
38789
|
+
if (await readFile7(target, "utf8") !== original)
|
|
38590
38790
|
throw apiError("conflict", "This note changed on disk. Close this preview, reload the note, and try again.");
|
|
38591
|
-
const temporary =
|
|
38791
|
+
const temporary = resolve9(dirname8(target), `.kennel-edit-${randomUUID3()}.tmp`);
|
|
38592
38792
|
try {
|
|
38593
|
-
await
|
|
38793
|
+
await writeFile4(temporary, content, {
|
|
38594
38794
|
flag: "wx",
|
|
38595
38795
|
mode: details.mode & 511
|
|
38596
38796
|
});
|
|
38597
|
-
if (await
|
|
38797
|
+
if (await readFile7(target, "utf8") !== original)
|
|
38598
38798
|
throw apiError("conflict", "This note changed on disk. Reload before applying an edit.");
|
|
38599
|
-
await
|
|
38799
|
+
await rename3(temporary, target);
|
|
38600
38800
|
} finally {
|
|
38601
38801
|
await unlink2(temporary).catch(() => {});
|
|
38602
38802
|
}
|
|
@@ -38624,7 +38824,7 @@ var init_document_edit = __esm(() => {
|
|
|
38624
38824
|
throw apiError("bad-request", "Invalid document edit.");
|
|
38625
38825
|
const input = parsed.data;
|
|
38626
38826
|
const folder = context.authorize(input.folder);
|
|
38627
|
-
if (context.lockedFile && (typeof input.path !== "string" || await
|
|
38827
|
+
if (context.lockedFile && (typeof input.path !== "string" || await realpath5(resolve9(folder, input.path)) !== context.lockedFile))
|
|
38628
38828
|
throw apiError("forbidden", "Only the selected document can be edited.");
|
|
38629
38829
|
await editDocument(folder, input);
|
|
38630
38830
|
jsonResponse(response, 200, routes.documentEdit.response.parse({ saved: true }));
|
|
@@ -38633,8 +38833,8 @@ var init_document_edit = __esm(() => {
|
|
|
38633
38833
|
});
|
|
38634
38834
|
|
|
38635
38835
|
// packages/api/src/server/api/create-agent-file.ts
|
|
38636
|
-
import { writeFile as
|
|
38637
|
-
import { resolve as
|
|
38836
|
+
import { writeFile as writeFile5 } from "node:fs/promises";
|
|
38837
|
+
import { resolve as resolve10 } from "node:path";
|
|
38638
38838
|
var createAgentFileRoute;
|
|
38639
38839
|
var init_create_agent_file = __esm(() => {
|
|
38640
38840
|
init_endpoint();
|
|
@@ -38648,9 +38848,9 @@ var init_create_agent_file = __esm(() => {
|
|
|
38648
38848
|
} catch (error) {
|
|
38649
38849
|
throw apiError("not-found", error instanceof Error ? error.message : "Folder not found");
|
|
38650
38850
|
}
|
|
38651
|
-
const target =
|
|
38851
|
+
const target = resolve10(folder, "AGENTS.md");
|
|
38652
38852
|
try {
|
|
38653
|
-
await
|
|
38853
|
+
await writeFile5(target, "", { flag: "wx" });
|
|
38654
38854
|
} catch (error) {
|
|
38655
38855
|
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
38656
38856
|
throw apiError("conflict", "AGENTS.md already exists in this folder");
|
|
@@ -38663,7 +38863,7 @@ var init_create_agent_file = __esm(() => {
|
|
|
38663
38863
|
});
|
|
38664
38864
|
|
|
38665
38865
|
// packages/api/src/server/api/open-path.ts
|
|
38666
|
-
import { execFile as
|
|
38866
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
38667
38867
|
var openPathRoute;
|
|
38668
38868
|
var init_open_path = __esm(() => {
|
|
38669
38869
|
init_endpoint();
|
|
@@ -38677,7 +38877,7 @@ var init_open_path = __esm(() => {
|
|
|
38677
38877
|
throw apiError("not-found", error instanceof Error ? error.message : "Path not found");
|
|
38678
38878
|
}
|
|
38679
38879
|
await new Promise((done, fail) => {
|
|
38680
|
-
|
|
38880
|
+
execFile3("/usr/bin/open", [target], (error) => {
|
|
38681
38881
|
if (error)
|
|
38682
38882
|
fail(apiError("failed", "Could not open path"));
|
|
38683
38883
|
else
|
|
@@ -38696,11 +38896,11 @@ var init_favicon2 = __esm(() => {
|
|
|
38696
38896
|
});
|
|
38697
38897
|
|
|
38698
38898
|
// packages/api/src/server/folder-favicon.ts
|
|
38699
|
-
import { readFile as
|
|
38700
|
-
import { join as
|
|
38899
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
38900
|
+
import { join as join4 } from "node:path";
|
|
38701
38901
|
async function folderWebsite(folder) {
|
|
38702
38902
|
for (const name of ["AGENTS.md", "README.md"]) {
|
|
38703
|
-
const content = await
|
|
38903
|
+
const content = await readFile8(join4(folder, name), "utf8").catch(() => "");
|
|
38704
38904
|
const entry = markdownMetadata(content).entries.find(([key]) => key === "website");
|
|
38705
38905
|
if (entry)
|
|
38706
38906
|
return websiteHost(entry[1]);
|
|
@@ -38714,7 +38914,7 @@ var init_folder_favicon = __esm(() => {
|
|
|
38714
38914
|
|
|
38715
38915
|
// packages/api/src/server/folder-icons.ts
|
|
38716
38916
|
import { createHash as createHash4 } from "node:crypto";
|
|
38717
|
-
import { readFile as
|
|
38917
|
+
import { readFile as readFile9, stat as stat6 } from "node:fs/promises";
|
|
38718
38918
|
import { extname as extname4 } from "node:path";
|
|
38719
38919
|
import { discoverFolderIcon } from "@raggle-ai/local";
|
|
38720
38920
|
function assetKey(value) {
|
|
@@ -38733,13 +38933,13 @@ function iconUrl(folder, hash, mediaType) {
|
|
|
38733
38933
|
async function folderIcon(folder, assets, assetFiles, fallback) {
|
|
38734
38934
|
const icon = discoverFolderIcon(folder);
|
|
38735
38935
|
const mediaType = icon ? iconTypes.get(extname4(icon).toLowerCase()) : null;
|
|
38736
|
-
const details = icon && mediaType ? await
|
|
38936
|
+
const details = icon && mediaType ? await stat6(icon).catch(() => {
|
|
38737
38937
|
return;
|
|
38738
38938
|
}) : undefined;
|
|
38739
38939
|
if (!icon || !mediaType || !details?.size || details.size > maxIconBytes2) {
|
|
38740
38940
|
return fallback ? await fallback(folder, assets) : undefined;
|
|
38741
38941
|
}
|
|
38742
|
-
const data = await
|
|
38942
|
+
const data = await readFile9(icon);
|
|
38743
38943
|
const hash = assetKey(data);
|
|
38744
38944
|
const key = `local:${hash}`;
|
|
38745
38945
|
assetFiles.set(hash, { mediaType, path: icon });
|
|
@@ -38758,12 +38958,12 @@ async function searchIconDataUrl(icon) {
|
|
|
38758
38958
|
const mediaType = searchIconTypes.get(extname4(icon).toLowerCase());
|
|
38759
38959
|
if (!mediaType)
|
|
38760
38960
|
return;
|
|
38761
|
-
const details = await
|
|
38961
|
+
const details = await stat6(icon).catch(() => {
|
|
38762
38962
|
return;
|
|
38763
38963
|
});
|
|
38764
38964
|
if (!details?.size || details.size > maxSearchIconBytes)
|
|
38765
38965
|
return;
|
|
38766
|
-
return `data:${mediaType};base64,${(await
|
|
38966
|
+
return `data:${mediaType};base64,${(await readFile9(icon)).toString("base64")}`;
|
|
38767
38967
|
}
|
|
38768
38968
|
async function faviconAsset(folderName, assets) {
|
|
38769
38969
|
if (process.env.KENNEL_EXTERNAL_ICONS !== "1")
|
|
@@ -38847,47 +39047,77 @@ var init_manifest = __esm(() => {
|
|
|
38847
39047
|
});
|
|
38848
39048
|
|
|
38849
39049
|
// packages/notes/src/server.ts
|
|
38850
|
-
import { readdir as
|
|
38851
|
-
import {
|
|
39050
|
+
import { readdir as readdir5, realpath as realpath6, stat as stat7 } from "node:fs/promises";
|
|
39051
|
+
import {
|
|
39052
|
+
basename as basename7,
|
|
39053
|
+
extname as extname5,
|
|
39054
|
+
isAbsolute as isAbsolute6,
|
|
39055
|
+
relative as relative10,
|
|
39056
|
+
resolve as resolve11,
|
|
39057
|
+
sep as sep6
|
|
39058
|
+
} from "node:path";
|
|
38852
39059
|
async function listMarkdownFiles(folder) {
|
|
38853
39060
|
const files = [];
|
|
38854
39061
|
const visit = async (current) => {
|
|
38855
|
-
const entries = await
|
|
39062
|
+
const entries = await readdir5(current, { withFileTypes: true }).catch(() => []);
|
|
38856
39063
|
for (const entry of entries) {
|
|
38857
39064
|
if (entry.name === "node_modules" || entry.name.startsWith("."))
|
|
38858
39065
|
continue;
|
|
38859
|
-
const path =
|
|
39066
|
+
const path = resolve11(current, entry.name);
|
|
38860
39067
|
if (entry.isDirectory()) {
|
|
38861
39068
|
await visit(path);
|
|
38862
39069
|
continue;
|
|
38863
39070
|
}
|
|
38864
|
-
if (!entry.isFile() || !
|
|
39071
|
+
if (!entry.isFile() || !markdownExtensions2.has(extname5(entry.name))) {
|
|
38865
39072
|
continue;
|
|
38866
39073
|
}
|
|
38867
|
-
const details = await
|
|
39074
|
+
const details = await stat7(path).catch(() => {
|
|
38868
39075
|
return;
|
|
38869
39076
|
});
|
|
38870
39077
|
files.push({
|
|
38871
39078
|
activityAt: details?.mtimeMs ?? 0,
|
|
38872
|
-
name:
|
|
38873
|
-
relativePath:
|
|
39079
|
+
name: basename7(entry.name, extname5(entry.name)),
|
|
39080
|
+
relativePath: relative10(folder, path).split(sep6).join("/")
|
|
38874
39081
|
});
|
|
38875
39082
|
}
|
|
38876
39083
|
};
|
|
38877
39084
|
await visit(folder);
|
|
38878
39085
|
return files.sort((left, right) => right.activityAt - left.activityAt);
|
|
38879
39086
|
}
|
|
38880
|
-
|
|
39087
|
+
function inside5(path, root) {
|
|
39088
|
+
const local = relative10(root, path);
|
|
39089
|
+
return local === "" || !local.startsWith("..") && !isAbsolute6(local);
|
|
39090
|
+
}
|
|
39091
|
+
async function withoutManaged(groups, managedRoots) {
|
|
39092
|
+
if (!managedRoots.length)
|
|
39093
|
+
return groups;
|
|
39094
|
+
const roots = await Promise.all(managedRoots.map(canonical));
|
|
39095
|
+
return Promise.all(groups.map(async (group) => {
|
|
39096
|
+
if (roots.includes(await canonical(group.folder))) {
|
|
39097
|
+
return { ...group, files: [] };
|
|
39098
|
+
}
|
|
39099
|
+
const files = await Promise.all(group.files.map(async (file) => {
|
|
39100
|
+
const real = await canonical(resolve11(group.folder, file.relativePath));
|
|
39101
|
+
return roots.some((root) => inside5(real, root)) ? undefined : file;
|
|
39102
|
+
}));
|
|
39103
|
+
return { ...group, files: files.filter((file) => file !== undefined) };
|
|
39104
|
+
}));
|
|
39105
|
+
}
|
|
39106
|
+
async function loadNotes(folder, linkedFolders = [], managedFolders = []) {
|
|
38881
39107
|
const localFiles = await listMarkdownFiles(folder);
|
|
38882
|
-
const
|
|
38883
|
-
files: await listMarkdownFiles(
|
|
38884
|
-
folder:
|
|
39108
|
+
const listGroups = (folders) => Promise.all(folders.map(async (source) => ({
|
|
39109
|
+
files: await listMarkdownFiles(source),
|
|
39110
|
+
folder: source
|
|
38885
39111
|
})));
|
|
39112
|
+
const [managed, linked] = await Promise.all([
|
|
39113
|
+
listGroups(managedFolders),
|
|
39114
|
+
listGroups(linkedFolders).then((groups) => withoutManaged(groups, managedFolders))
|
|
39115
|
+
]);
|
|
38886
39116
|
return {
|
|
38887
39117
|
files: {
|
|
38888
39118
|
items: [
|
|
38889
39119
|
...localFiles,
|
|
38890
|
-
...linked.flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
|
|
39120
|
+
...[...managed, ...linked].flatMap((group) => group.files.map((file) => ({ ...file, folder: group.folder })))
|
|
38891
39121
|
]
|
|
38892
39122
|
},
|
|
38893
39123
|
folder,
|
|
@@ -38904,14 +39134,20 @@ function codeSpan(text) {
|
|
|
38904
39134
|
async function notesLlmSections({
|
|
38905
39135
|
folder,
|
|
38906
39136
|
linkedFolders = [],
|
|
39137
|
+
managedFolders = [],
|
|
38907
39138
|
signal
|
|
38908
39139
|
}) {
|
|
38909
39140
|
const groups = [];
|
|
38910
39141
|
const notes = [];
|
|
38911
|
-
|
|
39142
|
+
const seen = new Set;
|
|
39143
|
+
for (const source of [folder, ...managedFolders, ...linkedFolders]) {
|
|
38912
39144
|
signal.throwIfAborted();
|
|
39145
|
+
const identity = await canonical(source);
|
|
39146
|
+
if (seen.has(identity))
|
|
39147
|
+
continue;
|
|
39148
|
+
seen.add(identity);
|
|
38913
39149
|
const files = await listMarkdownFiles(source);
|
|
38914
|
-
const paths = files.map((file) =>
|
|
39150
|
+
const paths = files.map((file) => resolve11(source, file.relativePath));
|
|
38915
39151
|
groups.push({ folder: source, paths });
|
|
38916
39152
|
files.forEach((file, index) => notes.push({ activityAt: file.activityAt, path: paths[index] }));
|
|
38917
39153
|
}
|
|
@@ -38944,24 +39180,28 @@ function createNotesPlugin() {
|
|
|
38944
39180
|
},
|
|
38945
39181
|
async handle({
|
|
38946
39182
|
folder,
|
|
38947
|
-
linkedFolders
|
|
39183
|
+
linkedFolders,
|
|
39184
|
+
managedFolders
|
|
38948
39185
|
}) {
|
|
38949
|
-
return {
|
|
39186
|
+
return {
|
|
39187
|
+
data: await loadNotes(folder, linkedFolders, managedFolders),
|
|
39188
|
+
kind: "available"
|
|
39189
|
+
};
|
|
38950
39190
|
},
|
|
38951
39191
|
llm: notesLlmSections
|
|
38952
39192
|
};
|
|
38953
39193
|
}
|
|
38954
|
-
var
|
|
39194
|
+
var markdownExtensions2, llmNoteLimit = 20, canonical = (path) => realpath6(path).catch(() => path);
|
|
38955
39195
|
var init_server3 = __esm(() => {
|
|
38956
39196
|
init_manifest();
|
|
38957
|
-
|
|
39197
|
+
markdownExtensions2 = new Set([".md", ".markdown"]);
|
|
38958
39198
|
});
|
|
38959
39199
|
|
|
38960
39200
|
// packages/api/src/server/plugins.ts
|
|
38961
39201
|
import { pathToFileURL } from "node:url";
|
|
38962
39202
|
import { existsSync as existsSync4 } from "node:fs";
|
|
38963
|
-
import { mkdir as
|
|
38964
|
-
import { dirname as
|
|
39203
|
+
import { mkdir as mkdir4, readFile as readFile10, stat as stat8, writeFile as writeFile6 } from "node:fs/promises";
|
|
39204
|
+
import { dirname as dirname9, resolve as resolve12, sep as sep7 } from "node:path";
|
|
38965
39205
|
function configuredFolderServerRegistry(registry, resolvedConfig) {
|
|
38966
39206
|
const disabled = new Set(resolvedConfig.plugins.disable);
|
|
38967
39207
|
const orderedIds = resolvedConfig.plugins.order.filter((id) => registry.has(id) && !disabled.has(id));
|
|
@@ -38985,14 +39225,14 @@ function parsePluginRegistryConfig(content) {
|
|
|
38985
39225
|
};
|
|
38986
39226
|
}
|
|
38987
39227
|
async function nearestKennelConfig(folder) {
|
|
38988
|
-
for (let current = folder;; current =
|
|
38989
|
-
const candidate =
|
|
38990
|
-
const details = await
|
|
39228
|
+
for (let current = folder;; current = dirname9(current)) {
|
|
39229
|
+
const candidate = resolve12(current, "kennel.json");
|
|
39230
|
+
const details = await stat8(candidate).catch(() => {
|
|
38991
39231
|
return;
|
|
38992
39232
|
});
|
|
38993
39233
|
if (details?.isFile())
|
|
38994
39234
|
return candidate;
|
|
38995
|
-
if (
|
|
39235
|
+
if (dirname9(current) === current)
|
|
38996
39236
|
return;
|
|
38997
39237
|
}
|
|
38998
39238
|
}
|
|
@@ -39001,7 +39241,7 @@ async function folderPluginRegistryConfig(folder) {
|
|
|
39001
39241
|
if (!path)
|
|
39002
39242
|
return defaultPluginRegistryConfig;
|
|
39003
39243
|
try {
|
|
39004
|
-
return parsePluginRegistryConfig(await
|
|
39244
|
+
return parsePluginRegistryConfig(await readFile10(path, "utf8"));
|
|
39005
39245
|
} catch {
|
|
39006
39246
|
return defaultPluginRegistryConfig;
|
|
39007
39247
|
}
|
|
@@ -39049,7 +39289,7 @@ async function folderServerRegistryAsync() {
|
|
|
39049
39289
|
...server,
|
|
39050
39290
|
async detect(context) {
|
|
39051
39291
|
const detection = await server.detect(context);
|
|
39052
|
-
if (detection.status === "unavailable" && existsSync4(
|
|
39292
|
+
if (detection.status === "unavailable" && existsSync4(resolve12(context.folder, entry.trigger))) {
|
|
39053
39293
|
return { status: "available" };
|
|
39054
39294
|
}
|
|
39055
39295
|
return detection;
|
|
@@ -39095,10 +39335,10 @@ var init_plugins = __esm(() => {
|
|
|
39095
39335
|
});
|
|
39096
39336
|
|
|
39097
39337
|
// packages/api/src/server/linked-folder-discovery.ts
|
|
39098
|
-
import { execFile as
|
|
39338
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
39099
39339
|
import { existsSync as existsSync5 } from "node:fs";
|
|
39100
39340
|
import { createRequire as createRequire2 } from "node:module";
|
|
39101
|
-
import { dirname as
|
|
39341
|
+
import { dirname as dirname10, join as join5 } from "node:path";
|
|
39102
39342
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
39103
39343
|
function resolveLocalModule(from = import.meta.url) {
|
|
39104
39344
|
const require2 = createRequire2(from);
|
|
@@ -39106,19 +39346,19 @@ function resolveLocalModule(from = import.meta.url) {
|
|
|
39106
39346
|
try {
|
|
39107
39347
|
candidates.push(require2.resolve("@raggle-ai/local"));
|
|
39108
39348
|
} catch {}
|
|
39109
|
-
let directory =
|
|
39110
|
-
while (
|
|
39111
|
-
if (existsSync5(
|
|
39349
|
+
let directory = dirname10(fileURLToPath2(from));
|
|
39350
|
+
while (dirname10(directory) !== directory) {
|
|
39351
|
+
if (existsSync5(join5(directory, "raggle.json"))) {
|
|
39112
39352
|
for (const candidate of [
|
|
39113
|
-
|
|
39114
|
-
|
|
39353
|
+
join5(dirname10(directory), "raggle-ai-scout", "packages", "local"),
|
|
39354
|
+
join5(directory, "..", "..", "raggle-ai-scout", "packages", "local")
|
|
39115
39355
|
]) {
|
|
39116
39356
|
if (!candidates.includes(candidate))
|
|
39117
39357
|
candidates.push(candidate);
|
|
39118
39358
|
}
|
|
39119
39359
|
break;
|
|
39120
39360
|
}
|
|
39121
|
-
directory =
|
|
39361
|
+
directory = dirname10(directory);
|
|
39122
39362
|
}
|
|
39123
39363
|
for (const candidate of candidates) {
|
|
39124
39364
|
try {
|
|
@@ -39127,11 +39367,11 @@ function resolveLocalModule(from = import.meta.url) {
|
|
|
39127
39367
|
return candidate;
|
|
39128
39368
|
} catch {}
|
|
39129
39369
|
}
|
|
39130
|
-
return candidates[0] ??
|
|
39370
|
+
return candidates[0] ?? join5(dirname10(fileURLToPath2(from)), "..", "..", "..", "raggle-ai-scout", "packages", "local");
|
|
39131
39371
|
}
|
|
39132
39372
|
function discoverLinkedFolders(folder, signal, modulePath = resolveLocalModule()) {
|
|
39133
39373
|
return new Promise((resolve, reject) => {
|
|
39134
|
-
|
|
39374
|
+
execFile4(process.execPath, ["-e", script, modulePath, folder], {
|
|
39135
39375
|
signal,
|
|
39136
39376
|
timeout: 60000,
|
|
39137
39377
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -39227,10 +39467,10 @@ const { stat } = require('node:fs/promises');
|
|
|
39227
39467
|
var init_linked_folder_discovery = () => {};
|
|
39228
39468
|
|
|
39229
39469
|
// packages/api/src/server/project-discovery.ts
|
|
39230
|
-
import { execFile as
|
|
39470
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
39231
39471
|
function discoverProjects(folder, signal) {
|
|
39232
39472
|
return new Promise((resolve, reject) => {
|
|
39233
|
-
|
|
39473
|
+
execFile5(process.execPath, ["-e", script2, resolveLocalModule(), folder], {
|
|
39234
39474
|
signal,
|
|
39235
39475
|
timeout: 60000,
|
|
39236
39476
|
maxBuffer: 8 * 1024 * 1024,
|
|
@@ -39317,24 +39557,24 @@ var init_project_discovery = __esm(() => {
|
|
|
39317
39557
|
// packages/api/src/server/folder-metadata-plugin.ts
|
|
39318
39558
|
import { createHash as createHash5 } from "node:crypto";
|
|
39319
39559
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
39320
|
-
import { readdir as
|
|
39321
|
-
import { homedir as
|
|
39560
|
+
import { readdir as readdir6, readFile as readFile11, realpath as realpath7, stat as stat9 } from "node:fs/promises";
|
|
39561
|
+
import { homedir as homedir4 } from "node:os";
|
|
39322
39562
|
import {
|
|
39323
|
-
basename as
|
|
39324
|
-
dirname as
|
|
39563
|
+
basename as basename8,
|
|
39564
|
+
dirname as dirname11,
|
|
39325
39565
|
extname as extname6,
|
|
39326
|
-
isAbsolute as
|
|
39327
|
-
relative as
|
|
39328
|
-
resolve as
|
|
39329
|
-
sep as
|
|
39566
|
+
isAbsolute as isAbsolute7,
|
|
39567
|
+
relative as relative12,
|
|
39568
|
+
resolve as resolve13,
|
|
39569
|
+
sep as sep8
|
|
39330
39570
|
} from "node:path";
|
|
39331
|
-
import { execFile as
|
|
39571
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
39332
39572
|
import local from "@raggle-ai/local";
|
|
39333
39573
|
function metadataFile(path, root) {
|
|
39334
|
-
const candidate =
|
|
39335
|
-
if (!candidate || candidate.startsWith("..") ||
|
|
39574
|
+
const candidate = relative12(root, path);
|
|
39575
|
+
if (!candidate || candidate.startsWith("..") || isAbsolute7(candidate))
|
|
39336
39576
|
return false;
|
|
39337
|
-
const parts = candidate.split(
|
|
39577
|
+
const parts = candidate.split(sep8);
|
|
39338
39578
|
if (parts.some((part) => part === "node_modules"))
|
|
39339
39579
|
return false;
|
|
39340
39580
|
return metadataExtensions.has(extname6(candidate).toLowerCase());
|
|
@@ -39345,11 +39585,11 @@ async function configuredAiInputModule(folder) {
|
|
|
39345
39585
|
return typeof module === "string" && module.trim() ? module.trim() : null;
|
|
39346
39586
|
}
|
|
39347
39587
|
function folderPath(value, root, lockedRoot) {
|
|
39348
|
-
const folder = realpathSync2(
|
|
39588
|
+
const folder = realpathSync2(resolve13(value ?? root));
|
|
39349
39589
|
if (!lockedRoot)
|
|
39350
39590
|
return folder;
|
|
39351
|
-
const path =
|
|
39352
|
-
if (!path || !path.startsWith("..") && !
|
|
39591
|
+
const path = relative12(lockedRoot, folder);
|
|
39592
|
+
if (!path || !path.startsWith("..") && !isAbsolute7(path))
|
|
39353
39593
|
return folder;
|
|
39354
39594
|
throw new Error(`Folder is outside the viewer root: ${folder}`);
|
|
39355
39595
|
}
|
|
@@ -39413,15 +39653,15 @@ function unique(values) {
|
|
|
39413
39653
|
return [...new Set(values)];
|
|
39414
39654
|
}
|
|
39415
39655
|
function isMachineRoot(folder) {
|
|
39416
|
-
return folder ===
|
|
39656
|
+
return folder === homedir4() || dirname11(folder) === folder;
|
|
39417
39657
|
}
|
|
39418
39658
|
async function shallowMetadata(folder) {
|
|
39419
|
-
const entries = await
|
|
39659
|
+
const entries = await readdir6(folder, { withFileTypes: true });
|
|
39420
39660
|
const visible = entries.filter((entry) => !entry.name.startsWith(".") && entry.name !== "node_modules");
|
|
39421
39661
|
const files = await Promise.all(visible.filter((entry) => entry.isFile() && [".markdown", ".md"].includes(extname6(entry.name).toLowerCase())).map(async (entry) => {
|
|
39422
|
-
const path =
|
|
39423
|
-
const details = await
|
|
39424
|
-
const content = await
|
|
39662
|
+
const path = resolve13(folder, entry.name);
|
|
39663
|
+
const details = await stat9(path);
|
|
39664
|
+
const content = await readFile11(path, "utf8").catch(() => "");
|
|
39425
39665
|
const title = markdownMetadata(content).title;
|
|
39426
39666
|
return {
|
|
39427
39667
|
activityAt: details.mtimeMs,
|
|
@@ -39434,10 +39674,10 @@ async function shallowMetadata(folder) {
|
|
|
39434
39674
|
};
|
|
39435
39675
|
}));
|
|
39436
39676
|
const projects = visible.filter((entry) => entry.isDirectory()).map((entry) => ({
|
|
39437
|
-
id: createHash5("sha1").update(
|
|
39677
|
+
id: createHash5("sha1").update(resolve13(folder, entry.name)).digest("hex"),
|
|
39438
39678
|
name: entry.name,
|
|
39439
39679
|
relativePath: entry.name,
|
|
39440
|
-
worktree:
|
|
39680
|
+
worktree: resolve13(folder, entry.name)
|
|
39441
39681
|
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
39442
39682
|
return {
|
|
39443
39683
|
changes: { items: [] },
|
|
@@ -39463,8 +39703,8 @@ async function loadFolderData(folder) {
|
|
|
39463
39703
|
items: projects.map((project) => ({
|
|
39464
39704
|
id: project.id,
|
|
39465
39705
|
worktree: project.worktree,
|
|
39466
|
-
name: project.name ??
|
|
39467
|
-
relativePath:
|
|
39706
|
+
name: project.name ?? basename8(project.worktree),
|
|
39707
|
+
relativePath: relative12(folder, project.worktree)
|
|
39468
39708
|
}))
|
|
39469
39709
|
}
|
|
39470
39710
|
};
|
|
@@ -39472,10 +39712,10 @@ async function loadFolderData(folder) {
|
|
|
39472
39712
|
async function notesFiles(folder, files, origin) {
|
|
39473
39713
|
return {
|
|
39474
39714
|
items: await Promise.all(files.map(async (file) => {
|
|
39475
|
-
const path = file.path ?? (file.relativePath ?
|
|
39715
|
+
const path = file.path ?? (file.relativePath ? resolve13(folder, file.relativePath) : undefined);
|
|
39476
39716
|
if (!path)
|
|
39477
39717
|
return origin ? { ...file, folder: origin } : file;
|
|
39478
|
-
const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await
|
|
39718
|
+
const content = [".markdown", ".md"].includes(extname6(path).toLowerCase()) ? await readFile11(path, "utf8").catch(() => "") : "";
|
|
39479
39719
|
const title = content ? markdownMetadata(content).title : undefined;
|
|
39480
39720
|
return {
|
|
39481
39721
|
...file,
|
|
@@ -39486,6 +39726,30 @@ async function notesFiles(folder, files, origin) {
|
|
|
39486
39726
|
}))
|
|
39487
39727
|
};
|
|
39488
39728
|
}
|
|
39729
|
+
async function managedNotesFiles(directory) {
|
|
39730
|
+
const notes = await listManagedNotes(directory);
|
|
39731
|
+
return Promise.all(notes.map(async (note) => {
|
|
39732
|
+
const content = await readFile11(note.path, "utf8").catch(() => "");
|
|
39733
|
+
const title = content ? markdownMetadata(content).title : undefined;
|
|
39734
|
+
return {
|
|
39735
|
+
...note,
|
|
39736
|
+
folder: directory,
|
|
39737
|
+
name: title ?? note.name,
|
|
39738
|
+
openHref: openPathHref(note.path)
|
|
39739
|
+
};
|
|
39740
|
+
}));
|
|
39741
|
+
}
|
|
39742
|
+
function insideFolder(path, root) {
|
|
39743
|
+
const local = relative12(root, path);
|
|
39744
|
+
return local === "" || !local.startsWith("..") && !isAbsolute7(local);
|
|
39745
|
+
}
|
|
39746
|
+
async function withoutManagedFiles(items, managedRoot) {
|
|
39747
|
+
if (!managedRoot)
|
|
39748
|
+
return items;
|
|
39749
|
+
const root = await canonicalPath(managedRoot);
|
|
39750
|
+
const outside = await Promise.all(items.map(async (item) => !item.path || !insideFolder(await canonicalPath(item.path), root)));
|
|
39751
|
+
return items.filter((_, index) => outside[index]);
|
|
39752
|
+
}
|
|
39489
39753
|
async function localProjectSearchCache(cache) {
|
|
39490
39754
|
if (cache && Date.now() - cache.loadedAt < projectSearchCacheMs)
|
|
39491
39755
|
return cache;
|
|
@@ -39508,16 +39772,16 @@ async function localProjectSearchCache(cache) {
|
|
|
39508
39772
|
function mediaTarget(folder, file, lockedTarget) {
|
|
39509
39773
|
if (!file)
|
|
39510
39774
|
return;
|
|
39511
|
-
const rootReal = realpathSync2(
|
|
39512
|
-
const path =
|
|
39775
|
+
const rootReal = realpathSync2(resolve13(folder));
|
|
39776
|
+
const path = resolve13(folder, file);
|
|
39513
39777
|
let pathReal;
|
|
39514
39778
|
try {
|
|
39515
39779
|
pathReal = realpathSync2(path);
|
|
39516
39780
|
} catch {
|
|
39517
39781
|
return;
|
|
39518
39782
|
}
|
|
39519
|
-
const rel =
|
|
39520
|
-
if (rel.startsWith("..") ||
|
|
39783
|
+
const rel = relative12(rootReal, pathReal);
|
|
39784
|
+
if (rel.startsWith("..") || isAbsolute7(rel))
|
|
39521
39785
|
return;
|
|
39522
39786
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
39523
39787
|
return;
|
|
@@ -39556,16 +39820,16 @@ function documentError(response) {
|
|
|
39556
39820
|
function documentTarget(folder, file, lockedTarget) {
|
|
39557
39821
|
if (!file)
|
|
39558
39822
|
return;
|
|
39559
|
-
const rootReal = realpathSync2(
|
|
39560
|
-
const path =
|
|
39823
|
+
const rootReal = realpathSync2(resolve13(folder));
|
|
39824
|
+
const path = resolve13(folder, file);
|
|
39561
39825
|
let pathReal;
|
|
39562
39826
|
try {
|
|
39563
39827
|
pathReal = realpathSync2(path);
|
|
39564
39828
|
} catch {
|
|
39565
39829
|
return;
|
|
39566
39830
|
}
|
|
39567
|
-
const rel =
|
|
39568
|
-
if (rel.startsWith("..") ||
|
|
39831
|
+
const rel = relative12(rootReal, pathReal);
|
|
39832
|
+
if (rel.startsWith("..") || isAbsolute7(rel))
|
|
39569
39833
|
return;
|
|
39570
39834
|
if (lockedTarget && pathReal !== lockedTarget)
|
|
39571
39835
|
return;
|
|
@@ -39643,6 +39907,18 @@ function createFolderApi({
|
|
|
39643
39907
|
const linkedDiscovery = createLinkedFolderDiscovery(linkedFolderLoader, (folder) => discoveryChanged(folder));
|
|
39644
39908
|
const linkedState = (folder) => lockedRoot || isMachineRoot(folder) ? { folders: [], status: "ready" } : linkedDiscovery.read(folder);
|
|
39645
39909
|
const resolveLinkedFolders = (folder) => lockedRoot || isMachineRoot(folder) ? Promise.resolve([]) : linkedDiscovery.folders(folder);
|
|
39910
|
+
const managedNotesFolder = async (folder) => {
|
|
39911
|
+
if (lockedRoot || lockedTarget || isMachineRoot(folder))
|
|
39912
|
+
return;
|
|
39913
|
+
try {
|
|
39914
|
+
if (!(await readRepositoryNotesSettings()).enabled)
|
|
39915
|
+
return;
|
|
39916
|
+
const lookup = await lookupRepositoryNotes(folder);
|
|
39917
|
+
return lookup.status === "registered" ? lookup.notesDirectory : undefined;
|
|
39918
|
+
} catch {
|
|
39919
|
+
return;
|
|
39920
|
+
}
|
|
39921
|
+
};
|
|
39646
39922
|
const favicon = faviconService();
|
|
39647
39923
|
return {
|
|
39648
39924
|
name: "kennel-folder-metadata",
|
|
@@ -39685,7 +39961,7 @@ function createFolderApi({
|
|
|
39685
39961
|
if (!folder)
|
|
39686
39962
|
return;
|
|
39687
39963
|
server.emit({
|
|
39688
|
-
data: { path:
|
|
39964
|
+
data: { path: relative12(folder, path) },
|
|
39689
39965
|
event: "kennel:metadata-changed",
|
|
39690
39966
|
type: "custom"
|
|
39691
39967
|
});
|
|
@@ -39768,7 +40044,7 @@ function createFolderApi({
|
|
|
39768
40044
|
"Content-Type": asset.mediaType,
|
|
39769
40045
|
ETag: `"${hash}"`
|
|
39770
40046
|
});
|
|
39771
|
-
response.end(await
|
|
40047
|
+
response.end(await readFile11(asset.path));
|
|
39772
40048
|
} catch (error) {
|
|
39773
40049
|
response.writeHead(404, {
|
|
39774
40050
|
"Content-Type": "text/plain; charset=utf-8"
|
|
@@ -39833,21 +40109,21 @@ function createFolderApi({
|
|
|
39833
40109
|
}
|
|
39834
40110
|
const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
|
|
39835
40111
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer.exe" : "xdg-open";
|
|
39836
|
-
await new Promise((done, fail) =>
|
|
40112
|
+
await new Promise((done, fail) => execFile6(command, [folder], (error) => error ? fail(error) : done()));
|
|
39837
40113
|
response.writeHead(204);
|
|
39838
40114
|
response.end();
|
|
39839
40115
|
return;
|
|
39840
40116
|
}
|
|
39841
40117
|
if (url.pathname === "/api/documents") {
|
|
39842
40118
|
const folder = folderPath(url.searchParams.get("path"), root, lockedRoot);
|
|
39843
|
-
const target = lockedTarget ? documentTarget(folder,
|
|
40119
|
+
const target = lockedTarget ? documentTarget(folder, relative12(folder, lockedTarget), lockedTarget) : undefined;
|
|
39844
40120
|
const dataset = lockedTarget ? {
|
|
39845
40121
|
root: folder,
|
|
39846
40122
|
documents: target ? [
|
|
39847
40123
|
{
|
|
39848
|
-
path:
|
|
40124
|
+
path: relative12(folder, target.path),
|
|
39849
40125
|
kind: target.kind,
|
|
39850
|
-
content: previewTextKind(target.kind) ? await
|
|
40126
|
+
content: previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : ""
|
|
39851
40127
|
}
|
|
39852
40128
|
] : []
|
|
39853
40129
|
} : await readFolder(folder);
|
|
@@ -39863,7 +40139,7 @@ function createFolderApi({
|
|
|
39863
40139
|
documentError(response);
|
|
39864
40140
|
return;
|
|
39865
40141
|
}
|
|
39866
|
-
const details = await
|
|
40142
|
+
const details = await stat9(target.path);
|
|
39867
40143
|
if (!details.isFile()) {
|
|
39868
40144
|
documentError(response);
|
|
39869
40145
|
return;
|
|
@@ -39875,16 +40151,16 @@ function createFolderApi({
|
|
|
39875
40151
|
return;
|
|
39876
40152
|
}
|
|
39877
40153
|
watch(folder);
|
|
39878
|
-
const content = previewTextKind(target.kind) ? await
|
|
40154
|
+
const content = previewTextKind(target.kind) ? await readFile11(target.path, "utf8") : "";
|
|
39879
40155
|
sendContract(response, url, {
|
|
39880
40156
|
content,
|
|
39881
40157
|
kind: target.kind,
|
|
39882
|
-
path:
|
|
40158
|
+
path: relative12(target.root, target.path),
|
|
39883
40159
|
root: folder,
|
|
39884
40160
|
...previewTextKind(target.kind) ? {} : {
|
|
39885
40161
|
rawUrl: `/api/raw?${new URLSearchParams({
|
|
39886
40162
|
path: folder,
|
|
39887
|
-
file:
|
|
40163
|
+
file: relative12(target.root, target.path)
|
|
39888
40164
|
})}`
|
|
39889
40165
|
}
|
|
39890
40166
|
}, {
|
|
@@ -39904,7 +40180,7 @@ function createFolderApi({
|
|
|
39904
40180
|
documentError(response);
|
|
39905
40181
|
return;
|
|
39906
40182
|
}
|
|
39907
|
-
const details = await
|
|
40183
|
+
const details = await stat9(target.path);
|
|
39908
40184
|
if (!details.isFile()) {
|
|
39909
40185
|
documentError(response);
|
|
39910
40186
|
return;
|
|
@@ -39925,10 +40201,10 @@ function createFolderApi({
|
|
|
39925
40201
|
ETag: etag,
|
|
39926
40202
|
"X-Content-Type-Options": "nosniff",
|
|
39927
40203
|
...download ? {
|
|
39928
|
-
"Content-Disposition": `attachment; filename="${
|
|
40204
|
+
"Content-Disposition": `attachment; filename="${basename8(target.path)}"`
|
|
39929
40205
|
} : {}
|
|
39930
40206
|
});
|
|
39931
|
-
response.end(await
|
|
40207
|
+
response.end(await readFile11(target.path));
|
|
39932
40208
|
} catch (error) {
|
|
39933
40209
|
documentError(response);
|
|
39934
40210
|
}
|
|
@@ -39942,7 +40218,7 @@ function createFolderApi({
|
|
|
39942
40218
|
documentError(response);
|
|
39943
40219
|
return;
|
|
39944
40220
|
}
|
|
39945
|
-
const details = await
|
|
40221
|
+
const details = await stat9(target.path);
|
|
39946
40222
|
if (!details.isFile()) {
|
|
39947
40223
|
documentError(response);
|
|
39948
40224
|
return;
|
|
@@ -39954,7 +40230,7 @@ function createFolderApi({
|
|
|
39954
40230
|
return;
|
|
39955
40231
|
}
|
|
39956
40232
|
watch(folder);
|
|
39957
|
-
const data = await
|
|
40233
|
+
const data = await readFile11(target.path);
|
|
39958
40234
|
const baseHeaders = {
|
|
39959
40235
|
"Accept-Ranges": "bytes",
|
|
39960
40236
|
"Cache-Control": "private, max-age=0, must-revalidate",
|
|
@@ -40008,9 +40284,13 @@ function createFolderApi({
|
|
|
40008
40284
|
if (!lockedRoot && !isMachineRoot(folder) && request.headers["x-kennel-retry-linked"] === "1")
|
|
40009
40285
|
linkedDiscovery.retry(folder);
|
|
40010
40286
|
const discovery = linkedState(folder);
|
|
40287
|
+
const managed = await managedNotesFolder(folder);
|
|
40288
|
+
if (managed)
|
|
40289
|
+
watch(managed);
|
|
40011
40290
|
const result = await plugin.handle({
|
|
40012
40291
|
folder,
|
|
40013
|
-
linkedFolders: discovery.folders
|
|
40292
|
+
linkedFolders: discovery.folders,
|
|
40293
|
+
...managed ? { managedFolders: [managed] } : {}
|
|
40014
40294
|
}, subpath);
|
|
40015
40295
|
if (result.kind === "unavailable") {
|
|
40016
40296
|
response.writeHead(200, {
|
|
@@ -40113,7 +40393,7 @@ function createFolderApi({
|
|
|
40113
40393
|
workspace.breadcrumbs = await workspaceBreadcrumbIcons(workspace.breadcrumbs ?? [], assetFiles);
|
|
40114
40394
|
const files = [
|
|
40115
40395
|
workspace.configPath,
|
|
40116
|
-
|
|
40396
|
+
resolve13(workspace.root, "raggle.json")
|
|
40117
40397
|
].filter((path) => Boolean(path));
|
|
40118
40398
|
for (const path of files)
|
|
40119
40399
|
contexts.add(path);
|
|
@@ -40137,7 +40417,14 @@ function createFolderApi({
|
|
|
40137
40417
|
const machine = isMachineRoot(folder);
|
|
40138
40418
|
if (part === "notes") {
|
|
40139
40419
|
const scope = url.searchParams.get("notesScope");
|
|
40420
|
+
const managed = await managedNotesFolder(folder);
|
|
40140
40421
|
const localFiles = scope === "linked" ? Promise.resolve({ items: [] }) : (machine ? shallowMetadata(folder).then((data) => data.files.items) : listLocalMarkdownFiles({ folder, limit: 200 })).then((files) => notesFiles(folder, files));
|
|
40422
|
+
const managedFiles = async () => {
|
|
40423
|
+
if (scope === "linked" || !managed)
|
|
40424
|
+
return [];
|
|
40425
|
+
watch(managed);
|
|
40426
|
+
return managedNotesFiles(managed);
|
|
40427
|
+
};
|
|
40141
40428
|
const linkedFiles = async () => {
|
|
40142
40429
|
if (scope === "local" || machine || lockedRoot)
|
|
40143
40430
|
return [];
|
|
@@ -40148,20 +40435,25 @@ function createFolderApi({
|
|
|
40148
40435
|
throw new Error("Linked notes discovery failed. Retry to try again.");
|
|
40149
40436
|
return Promise.all(linkedFolders.map(async (linkedFolder) => {
|
|
40150
40437
|
watch(linkedFolder);
|
|
40151
|
-
|
|
40438
|
+
const group = await notesFiles(linkedFolder, await listLocalMarkdownFiles({
|
|
40152
40439
|
folder: linkedFolder,
|
|
40153
40440
|
limit: 200
|
|
40154
40441
|
}), linkedFolder);
|
|
40442
|
+
return {
|
|
40443
|
+
items: await withoutManagedFiles(group.items, managed)
|
|
40444
|
+
};
|
|
40155
40445
|
}));
|
|
40156
40446
|
};
|
|
40157
|
-
const [files, linked] = await Promise.all([
|
|
40447
|
+
const [files, managedItems, linked] = await Promise.all([
|
|
40158
40448
|
localFiles,
|
|
40449
|
+
managedFiles(),
|
|
40159
40450
|
linkedFiles()
|
|
40160
40451
|
]);
|
|
40161
40452
|
sendContract(response, url, {
|
|
40162
40453
|
files: {
|
|
40163
40454
|
items: [
|
|
40164
40455
|
...files.items,
|
|
40456
|
+
...managedItems,
|
|
40165
40457
|
...linked.flatMap((group) => group.items)
|
|
40166
40458
|
]
|
|
40167
40459
|
},
|
|
@@ -40242,7 +40534,7 @@ function createFolderApi({
|
|
|
40242
40534
|
}
|
|
40243
40535
|
};
|
|
40244
40536
|
}
|
|
40245
|
-
var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts;
|
|
40537
|
+
var applyProjectFavorites, buildProjectCatalog, buildProjectSearchIndex, catalogProjectToLocalProject, discoverProjectIcon, discoverProjectsUnderFolder, listLocalMarkdownFiles, projectRepositoryAccessory, projectSubtitle, projectTitle, readRaggleUserSettings, gitRemoteUrl, githubRepositoryPath, repositoryDisplayName, searchProjectIndex, sortProjectList, projectSearchCacheMs = 5000, metadataExtensions, loopbackServerHosts, canonicalPath = (path) => realpath7(path).catch(() => path);
|
|
40246
40538
|
var init_folder_metadata_plugin = __esm(() => {
|
|
40247
40539
|
init_contracts();
|
|
40248
40540
|
init_markdown();
|
|
@@ -40252,6 +40544,7 @@ var init_folder_metadata_plugin = __esm(() => {
|
|
|
40252
40544
|
init_server();
|
|
40253
40545
|
init_workspace2();
|
|
40254
40546
|
init_server2();
|
|
40547
|
+
init_store();
|
|
40255
40548
|
init_entity_folder();
|
|
40256
40549
|
init_document_edit();
|
|
40257
40550
|
init_create_agent_file();
|
|
@@ -40305,29 +40598,29 @@ var init_folder_metadata_plugin = __esm(() => {
|
|
|
40305
40598
|
import {
|
|
40306
40599
|
createServer
|
|
40307
40600
|
} from "node:http";
|
|
40308
|
-
import { readFile as
|
|
40309
|
-
import { extname as extname7, isAbsolute as
|
|
40601
|
+
import { readFile as readFile12, realpath as realpath8, stat as stat10 } from "node:fs/promises";
|
|
40602
|
+
import { extname as extname7, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14 } from "node:path";
|
|
40310
40603
|
async function sendFile(request, response, directory, pathname, fallback) {
|
|
40311
|
-
const root = await
|
|
40604
|
+
const root = await realpath8(directory);
|
|
40312
40605
|
const inside = (path) => {
|
|
40313
|
-
const local =
|
|
40314
|
-
return local !== ".." && !local.startsWith("../") && !
|
|
40606
|
+
const local = relative13(root, path);
|
|
40607
|
+
return local !== ".." && !local.startsWith("../") && !isAbsolute8(local);
|
|
40315
40608
|
};
|
|
40316
|
-
let target =
|
|
40609
|
+
let target = resolve14(root, `.${decodeURIComponent(pathname)}`);
|
|
40317
40610
|
if (!inside(target)) {
|
|
40318
40611
|
response.writeHead(403);
|
|
40319
40612
|
response.end();
|
|
40320
40613
|
return;
|
|
40321
40614
|
}
|
|
40322
|
-
if (!(await
|
|
40615
|
+
if (!(await stat10(target).catch(() => null))?.isFile()) {
|
|
40323
40616
|
if (!fallback) {
|
|
40324
40617
|
response.writeHead(404);
|
|
40325
40618
|
response.end();
|
|
40326
40619
|
return;
|
|
40327
40620
|
}
|
|
40328
|
-
target =
|
|
40621
|
+
target = resolve14(root, fallback);
|
|
40329
40622
|
}
|
|
40330
|
-
target = await
|
|
40623
|
+
target = await realpath8(target);
|
|
40331
40624
|
if (!inside(target)) {
|
|
40332
40625
|
response.writeHead(403);
|
|
40333
40626
|
response.end();
|
|
@@ -40337,7 +40630,7 @@ async function sendFile(request, response, directory, pathname, fallback) {
|
|
|
40337
40630
|
"Content-Type": contentTypes[extname7(target)] ?? "application/octet-stream",
|
|
40338
40631
|
"X-Content-Type-Options": "nosniff"
|
|
40339
40632
|
});
|
|
40340
|
-
response.end(request.method === "HEAD" ? undefined : await
|
|
40633
|
+
response.end(request.method === "HEAD" ? undefined : await readFile12(target));
|
|
40341
40634
|
}
|
|
40342
40635
|
function createKennelApi(options) {
|
|
40343
40636
|
const remote = options.remoteAccess || !isLoopbackServerHost(options.host);
|
|
@@ -40513,7 +40806,7 @@ var init_server4 = __esm(() => {
|
|
|
40513
40806
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
40514
40807
|
import { existsSync as existsSync6, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
40515
40808
|
import { createServer as createServer2 } from "node:net";
|
|
40516
|
-
import { dirname as
|
|
40809
|
+
import { dirname as dirname12, isAbsolute as isAbsolute9, relative as relative14, resolve as resolve15 } from "node:path";
|
|
40517
40810
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
40518
40811
|
function isLoopbackHost(host) {
|
|
40519
40812
|
return loopbackHosts.has(host) || host.startsWith("127.");
|
|
@@ -40592,7 +40885,7 @@ function splitViewerArgs(input) {
|
|
|
40592
40885
|
}
|
|
40593
40886
|
if (arg.startsWith("-"))
|
|
40594
40887
|
throw new Error(`Unknown viewer option: ${arg}`);
|
|
40595
|
-
const candidate =
|
|
40888
|
+
const candidate = resolve15(arg);
|
|
40596
40889
|
const isDirectory = existsSync6(candidate) && statSync3(candidate).isDirectory();
|
|
40597
40890
|
if (isDirectory) {
|
|
40598
40891
|
if (hasRoot)
|
|
@@ -40603,7 +40896,7 @@ function splitViewerArgs(input) {
|
|
|
40603
40896
|
}
|
|
40604
40897
|
if (hasRoot)
|
|
40605
40898
|
throw new Error("kennel accepts one folder");
|
|
40606
|
-
options.root =
|
|
40899
|
+
options.root = dirname12(candidate);
|
|
40607
40900
|
options.target = candidate;
|
|
40608
40901
|
hasRoot = true;
|
|
40609
40902
|
}
|
|
@@ -40644,7 +40937,7 @@ function popUrl(host, port, options) {
|
|
|
40644
40937
|
url.pathname = `/folder/${encodeBase64Url2(options.root)}/notes`;
|
|
40645
40938
|
return url.href;
|
|
40646
40939
|
}
|
|
40647
|
-
url.pathname = `/folder/${encodeBase64Url2(options.root)}/document/${encodeBase64Url2(
|
|
40940
|
+
url.pathname = `/folder/${encodeBase64Url2(options.root)}/document/${encodeBase64Url2(relative14(options.root, options.target))}`;
|
|
40648
40941
|
if (options.watch)
|
|
40649
40942
|
url.searchParams.set("watch", "1");
|
|
40650
40943
|
return url.href;
|
|
@@ -40655,8 +40948,8 @@ function viewerPortWasExplicit(input) {
|
|
|
40655
40948
|
function isSameOrDescendant(parent, child) {
|
|
40656
40949
|
const parentReal = realpathSync3(parent);
|
|
40657
40950
|
const childReal = realpathSync3(child);
|
|
40658
|
-
const childRelative =
|
|
40659
|
-
return childRelative === "" || !childRelative.startsWith("..") && !
|
|
40951
|
+
const childRelative = relative14(parentReal, childReal);
|
|
40952
|
+
return childRelative === "" || !childRelative.startsWith("..") && !isAbsolute9(childRelative);
|
|
40660
40953
|
}
|
|
40661
40954
|
async function runningViewer(url) {
|
|
40662
40955
|
try {
|
|
@@ -40717,8 +41010,8 @@ async function viewer(input) {
|
|
|
40717
41010
|
await hotViewer(options);
|
|
40718
41011
|
return;
|
|
40719
41012
|
}
|
|
40720
|
-
const web =
|
|
40721
|
-
if (!existsSync6(
|
|
41013
|
+
const web = resolve15(dirname12(fileURLToPath3(import.meta.url)), "../../web");
|
|
41014
|
+
if (!existsSync6(resolve15(web, "index.html"))) {
|
|
40722
41015
|
throw new Error(`Kennel web app not found. Run the CLI build first.`);
|
|
40723
41016
|
}
|
|
40724
41017
|
const remote = options.tailscale || !isLoopbackHost(options.host);
|
|
@@ -40857,14 +41150,14 @@ async function startProtectedShare(handle) {
|
|
|
40857
41150
|
var init_protected_share = () => {};
|
|
40858
41151
|
|
|
40859
41152
|
// packages/preview/src/tailscale-share.ts
|
|
40860
|
-
import { execFile as
|
|
40861
|
-
import { promisify } from "node:util";
|
|
41153
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
41154
|
+
import { promisify as promisify2 } from "node:util";
|
|
40862
41155
|
async function serveConfig() {
|
|
40863
|
-
const { stdout } = await
|
|
41156
|
+
const { stdout } = await run2("tailscale", ["serve", "status", "--json"]);
|
|
40864
41157
|
return JSON.parse(stdout);
|
|
40865
41158
|
}
|
|
40866
41159
|
async function tailscaleHostname() {
|
|
40867
|
-
const { stdout } = await
|
|
41160
|
+
const { stdout } = await run2("tailscale", ["status", "--json"]);
|
|
40868
41161
|
const status = JSON.parse(stdout);
|
|
40869
41162
|
const host = status.Self?.DNSName?.replace(/\.$/, "");
|
|
40870
41163
|
if (status.BackendState !== "Running" || !host || !host.endsWith(".ts.net")) {
|
|
@@ -40883,11 +41176,11 @@ async function startTailscaleProxy(host, port) {
|
|
|
40883
41176
|
const current = await serveConfig();
|
|
40884
41177
|
const handlers = current.Web?.[authority]?.Handlers;
|
|
40885
41178
|
if (handlers?.["/"]?.Proxy === target && Object.keys(handlers).length === 1) {
|
|
40886
|
-
await
|
|
41179
|
+
await run2("tailscale", ["serve", `--https=${port}`, "off"]);
|
|
40887
41180
|
}
|
|
40888
41181
|
};
|
|
40889
41182
|
try {
|
|
40890
|
-
await
|
|
41183
|
+
await run2("tailscale", ["serve", "--bg", "--yes", `--https=${port}`, target], { timeout: 30000 });
|
|
40891
41184
|
const current = await serveConfig();
|
|
40892
41185
|
if (current.AllowFunnel?.[authority] || current.Web?.[authority]?.Handlers?.["/"]?.Proxy !== target) {
|
|
40893
41186
|
throw new Error("Tailscale did not create the expected private HTTPS proxy");
|
|
@@ -40898,13 +41191,13 @@ async function startTailscaleProxy(host, port) {
|
|
|
40898
41191
|
throw error;
|
|
40899
41192
|
}
|
|
40900
41193
|
}
|
|
40901
|
-
var
|
|
41194
|
+
var run2;
|
|
40902
41195
|
var init_tailscale_share = __esm(() => {
|
|
40903
|
-
|
|
41196
|
+
run2 = promisify2(execFile7);
|
|
40904
41197
|
});
|
|
40905
41198
|
|
|
40906
41199
|
// packages/preview/src/share.ts
|
|
40907
|
-
import { dirname as
|
|
41200
|
+
import { dirname as dirname13, resolve as resolve16 } from "node:path";
|
|
40908
41201
|
import { existsSync as existsSync7, realpathSync as realpathSync4 } from "node:fs";
|
|
40909
41202
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
40910
41203
|
function splitShareArgs(input) {
|
|
@@ -40956,7 +41249,7 @@ function splitShareArgs(input) {
|
|
|
40956
41249
|
if (folder && !existsSync7(folder)) {
|
|
40957
41250
|
throw new Error(`Folder not found: ${folder}`);
|
|
40958
41251
|
}
|
|
40959
|
-
options.folder = folder ?
|
|
41252
|
+
options.folder = folder ? resolve16(folder) : undefined;
|
|
40960
41253
|
return options;
|
|
40961
41254
|
}
|
|
40962
41255
|
async function runningViewerRoot(port = plainViewerPort) {
|
|
@@ -40977,8 +41270,8 @@ async function runningViewerRoot(port = plainViewerPort) {
|
|
|
40977
41270
|
}
|
|
40978
41271
|
async function startPreviewShare(target) {
|
|
40979
41272
|
const root = realpathSync4(target.root);
|
|
40980
|
-
const web =
|
|
40981
|
-
if (!existsSync7(
|
|
41273
|
+
const web = resolve16(dirname13(fileURLToPath4(import.meta.url)), "../../web");
|
|
41274
|
+
if (!existsSync7(resolve16(web, "index.html"))) {
|
|
40982
41275
|
throw new Error("Kennel web app not found. Run the CLI build first.");
|
|
40983
41276
|
}
|
|
40984
41277
|
const host = await tailscaleHostname();
|