forge-openclaw-plugin 0.2.21 → 0.2.23
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/assets/{index-B4A6TooJ.js → index-Ch_xeZ2u.js} +34 -34
- package/dist/assets/index-Ch_xeZ2u.js.map +1 -0
- package/dist/assets/{index-D6Xs_2mo.css → index-DvVM7K6j.css} +1 -1
- package/dist/index.html +2 -2
- package/dist/server/app.js +27 -0
- package/dist/server/db.js +16 -1
- package/dist/server/repositories/wiki-memory.js +108 -5
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/assets/index-B4A6TooJ.js.map +0 -1
package/dist/index.html
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
/>
|
|
14
14
|
<link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
15
15
|
<link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
|
|
16
|
-
<script type="module" crossorigin src="/forge/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/forge/assets/index-Ch_xeZ2u.js"></script>
|
|
17
17
|
<link rel="modulepreload" crossorigin href="/forge/assets/viz-C6hfyqzu.js">
|
|
18
18
|
<link rel="modulepreload" crossorigin href="/forge/assets/vendor-De38P6YR.js">
|
|
19
19
|
<link rel="modulepreload" crossorigin href="/forge/assets/ui-BzK4azQb.js">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<link rel="modulepreload" crossorigin href="/forge/assets/table-BWzTaky1.js">
|
|
22
22
|
<link rel="modulepreload" crossorigin href="/forge/assets/board-_C6oMy5w.js">
|
|
23
23
|
<link rel="stylesheet" crossorigin href="/forge/assets/vendor-DT3pnAKJ.css">
|
|
24
|
-
<link rel="stylesheet" crossorigin href="/forge/assets/index-
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/forge/assets/index-DvVM7K6j.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body class="bg-canvas text-ink antialiased">
|
|
27
27
|
<div id="root"></div>
|
package/dist/server/app.js
CHANGED
|
@@ -4688,6 +4688,33 @@ export async function buildServer(options = {}) {
|
|
|
4688
4688
|
}
|
|
4689
4689
|
return getWikiPageDetail(note.id);
|
|
4690
4690
|
});
|
|
4691
|
+
app.delete("/api/v1/wiki/pages/:id", async (request, reply) => {
|
|
4692
|
+
const { id } = request.params;
|
|
4693
|
+
const current = getNoteById(id);
|
|
4694
|
+
if (!current || (current.kind !== "wiki" && current.kind !== "evidence")) {
|
|
4695
|
+
reply.code(404);
|
|
4696
|
+
return { error: "Wiki page not found" };
|
|
4697
|
+
}
|
|
4698
|
+
if (current.slug === "index") {
|
|
4699
|
+
reply.code(400);
|
|
4700
|
+
return { error: "The wiki home page cannot be deleted." };
|
|
4701
|
+
}
|
|
4702
|
+
const linkedEntityType = current.links[0]?.entityType ?? null;
|
|
4703
|
+
const auth = requireNoteAccess(request.headers, linkedEntityType, {
|
|
4704
|
+
route: "/api/v1/wiki/pages/:id",
|
|
4705
|
+
entityType: linkedEntityType
|
|
4706
|
+
});
|
|
4707
|
+
const deleted = deleteEntity("note", id, entityDeleteQuerySchema.parse(request.query ?? {}), toActivityContext(auth));
|
|
4708
|
+
if (!deleted) {
|
|
4709
|
+
reply.code(404);
|
|
4710
|
+
return { error: "Wiki page not found" };
|
|
4711
|
+
}
|
|
4712
|
+
return {
|
|
4713
|
+
deleted: {
|
|
4714
|
+
id: deleted.id
|
|
4715
|
+
}
|
|
4716
|
+
};
|
|
4717
|
+
});
|
|
4691
4718
|
app.post("/api/v1/wiki/search", async (request) => {
|
|
4692
4719
|
requireScopedAccess(request.headers, ["read", "write"], { route: "/api/v1/wiki/search" });
|
|
4693
4720
|
return searchWikiPages(wikiSearchQuerySchema.parse(request.body ?? {}), managers.secrets);
|
package/dist/server/db.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import { mkdir, readdir, readFile } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -12,7 +13,21 @@ function dateOffsetIso(days) {
|
|
|
12
13
|
}
|
|
13
14
|
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
14
15
|
const migrationsDir = path.join(projectRoot, "server", "migrations");
|
|
15
|
-
|
|
16
|
+
const monorepoForgeDataRoot = path.resolve(projectRoot, "..", "..", "data", "forge");
|
|
17
|
+
export function resolveDefaultDataRoot(currentWorkingDir = process.cwd()) {
|
|
18
|
+
const configured = process.env.FORGE_DATA_ROOT?.trim();
|
|
19
|
+
if (configured) {
|
|
20
|
+
return path.resolve(configured);
|
|
21
|
+
}
|
|
22
|
+
// Inside the private monorepo, prefer the tracked shared Forge data root so
|
|
23
|
+
// the local app, Hermes, OpenClaw, and repo-managed data all point at the
|
|
24
|
+
// same state by default.
|
|
25
|
+
if (existsSync(path.join(monorepoForgeDataRoot, "data"))) {
|
|
26
|
+
return monorepoForgeDataRoot;
|
|
27
|
+
}
|
|
28
|
+
return path.resolve(currentWorkingDir);
|
|
29
|
+
}
|
|
30
|
+
let dataRoot = resolveDefaultDataRoot();
|
|
16
31
|
let seedDemoDataEnabled = false;
|
|
17
32
|
let db = null;
|
|
18
33
|
let transactionDepth = 0;
|
|
@@ -8,6 +8,7 @@ import { resolveDataDir, getDatabase } from "../db.js";
|
|
|
8
8
|
import { decorateOwnedEntity } from "./entity-ownership.js";
|
|
9
9
|
import { createNoteLinkSchema, crudEntityTypeSchema, noteKindSchema, noteSchema as persistedNoteSchema, wikiSearchModeSchema, wikiSpaceVisibilitySchema } from "../types.js";
|
|
10
10
|
import { deleteEncryptedSecret, readEncryptedSecret, storeEncryptedSecret } from "./calendar.js";
|
|
11
|
+
import { isEntityDeleted } from "./deleted-entities.js";
|
|
11
12
|
import { recordDiagnosticLog } from "./diagnostic-logs.js";
|
|
12
13
|
const wikiSpaceSchema = z.object({
|
|
13
14
|
id: z.string(),
|
|
@@ -528,6 +529,103 @@ function getNoteBySlugRaw(spaceId, slug, exceptNoteId) {
|
|
|
528
529
|
.get(...(exceptNoteId ? [spaceId, slug, exceptNoteId] : [spaceId, slug]));
|
|
529
530
|
return row;
|
|
530
531
|
}
|
|
532
|
+
function getNoteByTitleRaw(spaceId, title, exceptNoteId) {
|
|
533
|
+
return getDatabase()
|
|
534
|
+
.prepare(`SELECT id, kind, title, slug, space_id, aliases_json, summary, content_markdown, content_plain, author, source,
|
|
535
|
+
tags_json, destroy_at, source_path, frontmatter_json, revision_hash, last_synced_at, parent_slug, index_order, show_in_index, created_at, updated_at
|
|
536
|
+
FROM notes
|
|
537
|
+
WHERE space_id = ?
|
|
538
|
+
AND lower(title) = lower(?)
|
|
539
|
+
${exceptNoteId ? "AND id != ?" : ""}
|
|
540
|
+
LIMIT 1`)
|
|
541
|
+
.get(...(exceptNoteId ? [spaceId, title, exceptNoteId] : [spaceId, title]));
|
|
542
|
+
}
|
|
543
|
+
function listNotesByTitleRaw(spaceId, title, exceptNoteId) {
|
|
544
|
+
return getDatabase()
|
|
545
|
+
.prepare(`SELECT id, kind, title, slug, space_id, aliases_json, summary, content_markdown, content_plain, author, source,
|
|
546
|
+
tags_json, destroy_at, source_path, frontmatter_json, revision_hash, last_synced_at, parent_slug, index_order, show_in_index, created_at, updated_at
|
|
547
|
+
FROM notes
|
|
548
|
+
WHERE space_id = ?
|
|
549
|
+
AND lower(title) = lower(?)
|
|
550
|
+
${exceptNoteId ? "AND id != ?" : ""}
|
|
551
|
+
ORDER BY updated_at DESC`)
|
|
552
|
+
.all(...(exceptNoteId ? [spaceId, title, exceptNoteId] : [spaceId, title]));
|
|
553
|
+
}
|
|
554
|
+
function getActiveNoteByIdRaw(noteId) {
|
|
555
|
+
const row = getNoteByIdRaw(noteId);
|
|
556
|
+
if (!row || isEntityDeleted("note", row.id)) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
return row;
|
|
560
|
+
}
|
|
561
|
+
function getActiveNoteBySlugRaw(spaceId, slug, exceptNoteId) {
|
|
562
|
+
const row = getNoteBySlugRaw(spaceId, slug, exceptNoteId);
|
|
563
|
+
if (!row || isEntityDeleted("note", row.id)) {
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
return row;
|
|
567
|
+
}
|
|
568
|
+
function scoreReferenceMatch(reference, row) {
|
|
569
|
+
const referenceSlug = slugify(reference);
|
|
570
|
+
const titleSlug = slugify(row.title);
|
|
571
|
+
if (referenceSlug && row.slug === referenceSlug) {
|
|
572
|
+
return 0;
|
|
573
|
+
}
|
|
574
|
+
if (titleSlug && row.slug === titleSlug) {
|
|
575
|
+
return 1;
|
|
576
|
+
}
|
|
577
|
+
const parseSuffix = (base) => {
|
|
578
|
+
if (!base || !row.slug.startsWith(`${base}-`)) {
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
const suffix = Number(row.slug.slice(base.length + 1));
|
|
582
|
+
return Number.isFinite(suffix) ? suffix : null;
|
|
583
|
+
};
|
|
584
|
+
const referenceSuffix = parseSuffix(referenceSlug);
|
|
585
|
+
if (referenceSuffix !== null) {
|
|
586
|
+
return 100 + referenceSuffix;
|
|
587
|
+
}
|
|
588
|
+
const titleSuffix = parseSuffix(titleSlug);
|
|
589
|
+
if (titleSuffix !== null) {
|
|
590
|
+
return 200 + titleSuffix;
|
|
591
|
+
}
|
|
592
|
+
return 10_000;
|
|
593
|
+
}
|
|
594
|
+
function chooseBestActiveReferenceMatch(reference, rows) {
|
|
595
|
+
const activeRows = rows.filter((row) => !isEntityDeleted("note", row.id));
|
|
596
|
+
if (activeRows.length === 0) {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
return [...activeRows].sort((left, right) => {
|
|
600
|
+
const leftScore = scoreReferenceMatch(reference, left);
|
|
601
|
+
const rightScore = scoreReferenceMatch(reference, right);
|
|
602
|
+
if (leftScore !== rightScore) {
|
|
603
|
+
return leftScore - rightScore;
|
|
604
|
+
}
|
|
605
|
+
if (left.updated_at !== right.updated_at) {
|
|
606
|
+
return right.updated_at.localeCompare(left.updated_at);
|
|
607
|
+
}
|
|
608
|
+
return left.id.localeCompare(right.id);
|
|
609
|
+
})[0];
|
|
610
|
+
}
|
|
611
|
+
function getActiveNoteByReferenceRaw(spaceId, reference, exceptNoteId) {
|
|
612
|
+
const normalized = reference.trim();
|
|
613
|
+
if (!normalized) {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
const slugMatch = getActiveNoteBySlugRaw(spaceId, normalized, exceptNoteId);
|
|
617
|
+
if (slugMatch) {
|
|
618
|
+
return slugMatch;
|
|
619
|
+
}
|
|
620
|
+
const titleMatch = chooseBestActiveReferenceMatch(normalized, listNotesByTitleRaw(spaceId, normalized, exceptNoteId));
|
|
621
|
+
if (titleMatch) {
|
|
622
|
+
return titleMatch;
|
|
623
|
+
}
|
|
624
|
+
const lowered = normalized.toLowerCase();
|
|
625
|
+
const aliasMatch = chooseBestActiveReferenceMatch(normalized, getNoteRows("WHERE space_id = ?", [spaceId]).filter((row) => row.id !== exceptNoteId &&
|
|
626
|
+
parseJsonStringArray(row.aliases_json).some((alias) => alias.trim().toLowerCase() === lowered)));
|
|
627
|
+
return aliasMatch ?? null;
|
|
628
|
+
}
|
|
531
629
|
function buildContentPlain(markdown) {
|
|
532
630
|
return markdown
|
|
533
631
|
.replace(/^---[\s\S]*?---\s*/m, "")
|
|
@@ -1428,7 +1526,7 @@ function rebuildWikiLinkEdges(note) {
|
|
|
1428
1526
|
continue;
|
|
1429
1527
|
}
|
|
1430
1528
|
}
|
|
1431
|
-
const targetNote =
|
|
1529
|
+
const targetNote = getActiveNoteByReferenceRaw(note.spaceId, left.trim(), note.id);
|
|
1432
1530
|
if (targetNote) {
|
|
1433
1531
|
insert.run(note.id, "page", targetNote.id, null, null, label, left.trim(), isEmbed ? 1 : 0, now, now);
|
|
1434
1532
|
continue;
|
|
@@ -1465,7 +1563,9 @@ function listAllNotes() {
|
|
|
1465
1563
|
current.push(link);
|
|
1466
1564
|
linksByNoteId.set(link.note_id, current);
|
|
1467
1565
|
}
|
|
1468
|
-
return rows
|
|
1566
|
+
return rows
|
|
1567
|
+
.filter((row) => !isEntityDeleted("note", row.id))
|
|
1568
|
+
.map((row) => mapNoteRow(row, linksByNoteId.get(row.id) ?? []));
|
|
1469
1569
|
}
|
|
1470
1570
|
export function listWikiSpaces() {
|
|
1471
1571
|
ensureSharedWikiSpace();
|
|
@@ -1530,7 +1630,7 @@ export function listWikiPageTree(query) {
|
|
|
1530
1630
|
export function getWikiHomePageDetail(input = {}) {
|
|
1531
1631
|
const spaceId = resolveSpaceId(input.spaceId, null);
|
|
1532
1632
|
ensureWikiSpaceSeedPages(spaceId);
|
|
1533
|
-
const home =
|
|
1633
|
+
const home = getActiveNoteBySlugRaw(spaceId, "index");
|
|
1534
1634
|
if (!home) {
|
|
1535
1635
|
return null;
|
|
1536
1636
|
}
|
|
@@ -1539,14 +1639,14 @@ export function getWikiHomePageDetail(input = {}) {
|
|
|
1539
1639
|
export function getWikiPageDetailBySlug(input) {
|
|
1540
1640
|
const spaceId = resolveSpaceId(input.spaceId, null);
|
|
1541
1641
|
ensureWikiSpaceSeedPages(spaceId);
|
|
1542
|
-
const row =
|
|
1642
|
+
const row = getActiveNoteByReferenceRaw(spaceId, input.slug.trim());
|
|
1543
1643
|
if (!row) {
|
|
1544
1644
|
return null;
|
|
1545
1645
|
}
|
|
1546
1646
|
return getWikiPageDetail(row.id);
|
|
1547
1647
|
}
|
|
1548
1648
|
export function getWikiPageDetail(noteId) {
|
|
1549
|
-
const row =
|
|
1649
|
+
const row = getActiveNoteByIdRaw(noteId);
|
|
1550
1650
|
if (!row) {
|
|
1551
1651
|
return null;
|
|
1552
1652
|
}
|
|
@@ -3125,6 +3225,9 @@ export async function reviewWikiIngestJob(jobId, input, options) {
|
|
|
3125
3225
|
if (!candidate) {
|
|
3126
3226
|
continue;
|
|
3127
3227
|
}
|
|
3228
|
+
if (candidate.status === "applied") {
|
|
3229
|
+
continue;
|
|
3230
|
+
}
|
|
3128
3231
|
const action = decision.action ??
|
|
3129
3232
|
(decision.keep === false ? "discard" : "keep");
|
|
3130
3233
|
if (action === "discard") {
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED