negotium 0.2.17 → 0.2.19
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/agent-helpers.js +41 -32
- package/dist/agent-helpers.js.map +10 -10
- package/dist/{chunk-5cpdbbeh.js → chunk-fc58cqry.js} +3 -1
- package/dist/{chunk-5cpdbbeh.js.map → chunk-fc58cqry.js.map} +3 -3
- package/dist/hosted-agent.js +4 -2
- package/dist/hosted-agent.js.map +4 -4
- package/dist/main.js +1377 -285
- package/dist/main.js.map +14 -13
- package/dist/mcp-factories.js +1497 -403
- package/dist/mcp-factories.js.map +14 -13
- package/dist/prompts.js +8 -8
- package/dist/prompts.js.map +3 -3
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/archiver.ts +1 -1
- package/dist/runtime/src/agents/idle-archiver.ts +3 -2
- package/dist/runtime/src/mcp/canonical-proxy-server.ts +31 -15
- package/dist/runtime/src/mcp/wiki-search-index.ts +359 -0
- package/dist/runtime/src/mcp/wiki-server.ts +1358 -155
- package/dist/runtime/src/platform/mcp-config.ts +2 -1
- package/dist/runtime/src/prompts/agents/wiki-archiver.md +73 -48
- package/dist/runtime/src/prompts/builders.ts +17 -34
- package/dist/runtime/src/runtime/turn-runner.ts +14 -17
- package/dist/runtime/src/storage/api-topics.ts +13 -4
- package/dist/runtime/src/topics/lifecycle.ts +3 -2
- package/dist/runtime/src/types/api.ts +6 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/storage.js +12 -6
- package/dist/storage.js.map +3 -3
- package/dist/types/packages/core/src/agents/archiver.d.ts +1 -1
- package/dist/types/packages/core/src/mcp/wiki-search-index.d.ts +59 -0
- package/dist/types/packages/core/src/mcp/wiki-server.d.ts +4 -0
- package/dist/types/packages/core/src/prompts/builders.d.ts +2 -4
- package/dist/types/packages/core/src/storage/api-topics.d.ts +1 -0
- package/dist/types/packages/core/src/types/api.d.ts +6 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -177,11 +177,12 @@ export function archiveActiveTopicForMemory(
|
|
|
177
177
|
);
|
|
178
178
|
return "below-threshold";
|
|
179
179
|
}
|
|
180
|
-
const memoryTopic = getTopicMemoryOrigin(topicId) ?? topic;
|
|
180
|
+
const memoryTopic = topic.memoryKey ? topic : (getTopicMemoryOrigin(topicId) ?? topic);
|
|
181
|
+
const memoryTitle = memoryTopic.memoryKey?.trim() || memoryTopic.title;
|
|
181
182
|
const launched = (options.launchArchiver ?? runArchiverTurn)({
|
|
182
183
|
userId,
|
|
183
184
|
topicId: memoryTopic.id,
|
|
184
|
-
topicTitle:
|
|
185
|
+
topicTitle: memoryTitle,
|
|
185
186
|
archivePath: job.archivePath,
|
|
186
187
|
...(options.rawArchivePaths ? { rawArchivePaths: options.rawArchivePaths } : {}),
|
|
187
188
|
messageCount: job.messageCount,
|
|
@@ -91,10 +91,27 @@ const wikiTools: Tool[] = [
|
|
|
91
91
|
description: "Search the canonical workspace wiki.",
|
|
92
92
|
inputSchema: {
|
|
93
93
|
type: "object",
|
|
94
|
-
properties: {
|
|
94
|
+
properties: {
|
|
95
|
+
question: { type: "string" },
|
|
96
|
+
kind: { type: "string", enum: ["all", "topic", "article", "summary"] },
|
|
97
|
+
limit: { type: "number", minimum: 1, maximum: 20 },
|
|
98
|
+
},
|
|
95
99
|
required: ["question"],
|
|
96
100
|
},
|
|
97
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: "wiki_read",
|
|
104
|
+
description: "Read a canonical wiki result and optionally adopt a topic memory.",
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
kind: { type: "string", enum: ["topic", "article", "summary"] },
|
|
109
|
+
key: { type: "string" },
|
|
110
|
+
adopt: { type: "boolean" },
|
|
111
|
+
},
|
|
112
|
+
required: ["kind", "key"],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
98
115
|
{
|
|
99
116
|
name: "wiki_topic_brief",
|
|
100
117
|
description: "Read this canonical topic's brief.",
|
|
@@ -109,27 +126,26 @@ const wikiTools: Tool[] = [
|
|
|
109
126
|
},
|
|
110
127
|
},
|
|
111
128
|
{
|
|
112
|
-
name: "
|
|
113
|
-
description: "Save a summary to the canonical workspace wiki.",
|
|
114
|
-
inputSchema: {
|
|
115
|
-
type: "object",
|
|
116
|
-
properties: { content: { type: "string" } },
|
|
117
|
-
required: ["content"],
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
name: "save_topic_brief",
|
|
129
|
+
name: "wiki_write",
|
|
122
130
|
description:
|
|
123
|
-
"Write
|
|
131
|
+
"Write a canonical wiki document and its catalog row together (summary, article, or topic brief).",
|
|
124
132
|
inputSchema: {
|
|
125
133
|
type: "object",
|
|
126
|
-
properties: {
|
|
127
|
-
|
|
134
|
+
properties: {
|
|
135
|
+
kind: { type: "string", enum: ["summary", "article", "topic"] },
|
|
136
|
+
content: { type: "string" },
|
|
137
|
+
description: { type: "string" },
|
|
138
|
+
topic: { type: "string" },
|
|
139
|
+
slug: { type: "string" },
|
|
140
|
+
section: { type: "string" },
|
|
141
|
+
date: { type: "string" },
|
|
142
|
+
},
|
|
143
|
+
required: ["kind", "content", "description"],
|
|
128
144
|
},
|
|
129
145
|
},
|
|
130
146
|
{
|
|
131
147
|
name: "index_upsert",
|
|
132
|
-
description: "
|
|
148
|
+
description: "Correct the description or section of an existing canonical wiki entry.",
|
|
133
149
|
inputSchema: {
|
|
134
150
|
type: "object",
|
|
135
151
|
properties: {
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { chmodSync, existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { Database } from "#storage/sqlite";
|
|
3
|
+
|
|
4
|
+
const SEARCH_INDEX_SCHEMA_VERSION = 3;
|
|
5
|
+
|
|
6
|
+
export type IndexedWikiKind = "article" | "summary";
|
|
7
|
+
|
|
8
|
+
export interface WikiSearchIndexSource {
|
|
9
|
+
kind: IndexedWikiKind;
|
|
10
|
+
key: string;
|
|
11
|
+
path: string;
|
|
12
|
+
mtimeMs: number;
|
|
13
|
+
size: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PreparedWikiSearchDocument {
|
|
17
|
+
title: string;
|
|
18
|
+
text: string;
|
|
19
|
+
tokens: string[];
|
|
20
|
+
date?: string;
|
|
21
|
+
family?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface WikiSearchIndexDocument extends PreparedWikiSearchDocument {
|
|
25
|
+
id: string;
|
|
26
|
+
kind: IndexedWikiKind;
|
|
27
|
+
key: string;
|
|
28
|
+
path: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WikiSearchIndexMetadata {
|
|
32
|
+
id: string;
|
|
33
|
+
kind: IndexedWikiKind;
|
|
34
|
+
key: string;
|
|
35
|
+
title: string;
|
|
36
|
+
date?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface WikiSummaryTemporalQuery {
|
|
40
|
+
year?: string;
|
|
41
|
+
month?: string;
|
|
42
|
+
direction: "latest" | "oldest";
|
|
43
|
+
limit: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface WikiSearchIndexSyncResult {
|
|
47
|
+
added: number;
|
|
48
|
+
updated: number;
|
|
49
|
+
removed: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface StoredSourceRow {
|
|
53
|
+
id: string;
|
|
54
|
+
path: string;
|
|
55
|
+
mtime_ms: number;
|
|
56
|
+
size: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface StoredDocumentRow {
|
|
60
|
+
id: string;
|
|
61
|
+
kind: IndexedWikiKind;
|
|
62
|
+
key: string;
|
|
63
|
+
path: string;
|
|
64
|
+
title: string;
|
|
65
|
+
text: string;
|
|
66
|
+
date: string | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface StoredMetadataRow {
|
|
70
|
+
id: string;
|
|
71
|
+
kind: IndexedWikiKind;
|
|
72
|
+
key: string;
|
|
73
|
+
title: string;
|
|
74
|
+
date: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function documentId(source: Pick<WikiSearchIndexSource, "kind" | "key">): string {
|
|
78
|
+
return `${source.kind}:${source.key}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function ensureSchema(database: InstanceType<typeof Database>): void {
|
|
82
|
+
const version = database
|
|
83
|
+
.query<{ user_version: number }, []>("PRAGMA user_version")
|
|
84
|
+
.get()?.user_version;
|
|
85
|
+
if (version !== 0 && version !== SEARCH_INDEX_SCHEMA_VERSION) {
|
|
86
|
+
throw new Error(`Unsupported wiki search index schema: ${version}`);
|
|
87
|
+
}
|
|
88
|
+
database.exec(`
|
|
89
|
+
CREATE TABLE IF NOT EXISTS wiki_search_documents (
|
|
90
|
+
id TEXT PRIMARY KEY,
|
|
91
|
+
kind TEXT NOT NULL,
|
|
92
|
+
key TEXT NOT NULL,
|
|
93
|
+
path TEXT NOT NULL,
|
|
94
|
+
mtime_ms REAL NOT NULL,
|
|
95
|
+
size INTEGER NOT NULL,
|
|
96
|
+
title TEXT NOT NULL,
|
|
97
|
+
text TEXT NOT NULL,
|
|
98
|
+
date TEXT,
|
|
99
|
+
family TEXT
|
|
100
|
+
);
|
|
101
|
+
CREATE UNIQUE INDEX IF NOT EXISTS wiki_search_documents_kind_key
|
|
102
|
+
ON wiki_search_documents(kind, key);
|
|
103
|
+
CREATE INDEX IF NOT EXISTS wiki_search_documents_kind_date
|
|
104
|
+
ON wiki_search_documents(kind, date);
|
|
105
|
+
CREATE INDEX IF NOT EXISTS wiki_search_documents_kind_family_date
|
|
106
|
+
ON wiki_search_documents(kind, family, date);
|
|
107
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS wiki_search_fts USING fts5(
|
|
108
|
+
document_id UNINDEXED,
|
|
109
|
+
kind UNINDEXED,
|
|
110
|
+
tokens,
|
|
111
|
+
tokenize = 'unicode61'
|
|
112
|
+
);
|
|
113
|
+
PRAGMA user_version = ${SEARCH_INDEX_SCHEMA_VERSION};
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export class WikiSearchIndex {
|
|
118
|
+
readonly #database: InstanceType<typeof Database>;
|
|
119
|
+
|
|
120
|
+
constructor(readonly path: string) {
|
|
121
|
+
this.#database = new Database(path, { create: true });
|
|
122
|
+
try {
|
|
123
|
+
this.#database.exec("PRAGMA foreign_keys = ON; PRAGMA journal_mode = DELETE;");
|
|
124
|
+
ensureSchema(this.#database);
|
|
125
|
+
chmodSync(path, 0o600);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
this.#database.close();
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
close(): void {
|
|
133
|
+
this.#database.close();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Record one document that was just written.
|
|
138
|
+
*
|
|
139
|
+
* The write path already holds the document text, so the index is maintained
|
|
140
|
+
* incrementally and never needs to scan the content directories. `sync` stays
|
|
141
|
+
* for the explicit backfill of documents that predate the write tool.
|
|
142
|
+
*/
|
|
143
|
+
upsert(source: WikiSearchIndexSource, document: PreparedWikiSearchDocument): void {
|
|
144
|
+
const id = documentId(source);
|
|
145
|
+
const apply = this.#database.transaction(() => {
|
|
146
|
+
this.#database.query("DELETE FROM wiki_search_fts WHERE document_id = ?").run(id);
|
|
147
|
+
this.#database.query("DELETE FROM wiki_search_documents WHERE id = ?").run(id);
|
|
148
|
+
this.#database
|
|
149
|
+
.query(`
|
|
150
|
+
INSERT INTO wiki_search_documents
|
|
151
|
+
(id, kind, key, path, mtime_ms, size, title, text, date, family)
|
|
152
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
153
|
+
`)
|
|
154
|
+
.run(
|
|
155
|
+
id,
|
|
156
|
+
source.kind,
|
|
157
|
+
source.key,
|
|
158
|
+
source.path,
|
|
159
|
+
source.mtimeMs,
|
|
160
|
+
source.size,
|
|
161
|
+
document.title,
|
|
162
|
+
document.text,
|
|
163
|
+
document.date ?? null,
|
|
164
|
+
document.family ?? null,
|
|
165
|
+
);
|
|
166
|
+
this.#database
|
|
167
|
+
.query("INSERT INTO wiki_search_fts(document_id, kind, tokens) VALUES (?, ?, ?)")
|
|
168
|
+
.run(id, source.kind, document.tokens.join(" "));
|
|
169
|
+
});
|
|
170
|
+
apply.immediate();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
sync(
|
|
174
|
+
sources: WikiSearchIndexSource[],
|
|
175
|
+
prepare: (source: WikiSearchIndexSource, text: string) => PreparedWikiSearchDocument,
|
|
176
|
+
): WikiSearchIndexSyncResult {
|
|
177
|
+
const result: WikiSearchIndexSyncResult = { added: 0, updated: 0, removed: 0 };
|
|
178
|
+
const existingRows = this.#database
|
|
179
|
+
.query<StoredSourceRow, []>(
|
|
180
|
+
"SELECT id, path, mtime_ms, size FROM wiki_search_documents ORDER BY id",
|
|
181
|
+
)
|
|
182
|
+
.all();
|
|
183
|
+
const existing = new Map(existingRows.map((row) => [row.id, row]));
|
|
184
|
+
const present = new Set<string>();
|
|
185
|
+
const changed: Array<{
|
|
186
|
+
source: WikiSearchIndexSource;
|
|
187
|
+
document: PreparedWikiSearchDocument;
|
|
188
|
+
added: boolean;
|
|
189
|
+
}> = [];
|
|
190
|
+
|
|
191
|
+
for (const source of sources) {
|
|
192
|
+
const id = documentId(source);
|
|
193
|
+
present.add(id);
|
|
194
|
+
const stored = existing.get(id);
|
|
195
|
+
if (
|
|
196
|
+
stored &&
|
|
197
|
+
stored.path === source.path &&
|
|
198
|
+
stored.mtime_ms === source.mtimeMs &&
|
|
199
|
+
stored.size === source.size
|
|
200
|
+
) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const text = readFileSync(source.path, "utf-8");
|
|
205
|
+
changed.push({ source, document: prepare(source, text), added: !stored });
|
|
206
|
+
} catch {
|
|
207
|
+
// Keep the previous indexed copy, if any. A later sync can retry after
|
|
208
|
+
// a concurrent writer finishes or permissions are restored.
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const removed = existingRows.filter((row) => !present.has(row.id));
|
|
212
|
+
|
|
213
|
+
const apply = this.#database.transaction(() => {
|
|
214
|
+
const deleteTerms = this.#database.query("DELETE FROM wiki_search_fts WHERE document_id = ?");
|
|
215
|
+
const deleteDocument = this.#database.query("DELETE FROM wiki_search_documents WHERE id = ?");
|
|
216
|
+
const insertDocument = this.#database.query(`
|
|
217
|
+
INSERT INTO wiki_search_documents
|
|
218
|
+
(id, kind, key, path, mtime_ms, size, title, text, date, family)
|
|
219
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
220
|
+
`);
|
|
221
|
+
const insertTerms = this.#database.query(`
|
|
222
|
+
INSERT INTO wiki_search_fts(document_id, kind, tokens)
|
|
223
|
+
VALUES (?, ?, ?)
|
|
224
|
+
`);
|
|
225
|
+
|
|
226
|
+
for (const row of removed) {
|
|
227
|
+
deleteTerms.run(row.id);
|
|
228
|
+
deleteDocument.run(row.id);
|
|
229
|
+
result.removed += 1;
|
|
230
|
+
}
|
|
231
|
+
for (const { source, document, added } of changed) {
|
|
232
|
+
const id = documentId(source);
|
|
233
|
+
deleteTerms.run(id);
|
|
234
|
+
deleteDocument.run(id);
|
|
235
|
+
insertDocument.run(
|
|
236
|
+
id,
|
|
237
|
+
source.kind,
|
|
238
|
+
source.key,
|
|
239
|
+
source.path,
|
|
240
|
+
source.mtimeMs,
|
|
241
|
+
source.size,
|
|
242
|
+
document.title,
|
|
243
|
+
document.text,
|
|
244
|
+
document.date ?? null,
|
|
245
|
+
document.family ?? null,
|
|
246
|
+
);
|
|
247
|
+
insertTerms.run(id, source.kind, document.tokens.join(" "));
|
|
248
|
+
if (added) result.added += 1;
|
|
249
|
+
else result.updated += 1;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
apply.immediate();
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
metadata(kinds: IndexedWikiKind[]): WikiSearchIndexMetadata[] {
|
|
257
|
+
if (kinds.length === 0) return [];
|
|
258
|
+
const placeholders = kinds.map(() => "?").join(", ");
|
|
259
|
+
const rows = this.#database
|
|
260
|
+
.query<StoredMetadataRow, string[]>(
|
|
261
|
+
`SELECT id, kind, key, title, date
|
|
262
|
+
FROM wiki_search_documents
|
|
263
|
+
WHERE kind IN (${placeholders})`,
|
|
264
|
+
)
|
|
265
|
+
.all(...kinds);
|
|
266
|
+
return rows.map((row) => ({
|
|
267
|
+
id: row.id,
|
|
268
|
+
kind: row.kind,
|
|
269
|
+
key: row.key,
|
|
270
|
+
title: row.title,
|
|
271
|
+
...(row.date ? { date: row.date } : {}),
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
summaryMetadataByDate(query: WikiSummaryTemporalQuery): WikiSearchIndexMetadata[] {
|
|
276
|
+
const conditions = ["kind = 'summary'", "date IS NOT NULL"];
|
|
277
|
+
const parameters: Array<string | number> = [];
|
|
278
|
+
if (query.year && query.month) {
|
|
279
|
+
const month = Number(query.month);
|
|
280
|
+
const nextYear = month === 12 ? String(Number(query.year) + 1) : query.year;
|
|
281
|
+
const nextMonth = String(month === 12 ? 1 : month + 1).padStart(2, "0");
|
|
282
|
+
conditions.push("date >= ?", "date < ?");
|
|
283
|
+
parameters.push(`${query.year}-${query.month}-01`, `${nextYear}-${nextMonth}-01`);
|
|
284
|
+
} else if (query.year) {
|
|
285
|
+
conditions.push("date >= ?", "date < ?");
|
|
286
|
+
parameters.push(`${query.year}-01-01`, `${Number(query.year) + 1}-01-01`);
|
|
287
|
+
} else if (query.month) {
|
|
288
|
+
conditions.push("substr(date, 6, 2) = ?");
|
|
289
|
+
parameters.push(query.month);
|
|
290
|
+
}
|
|
291
|
+
parameters.push(query.limit);
|
|
292
|
+
const rows = this.#database
|
|
293
|
+
.query<StoredMetadataRow, Array<string | number>>(
|
|
294
|
+
`SELECT id, kind, key, title, date
|
|
295
|
+
FROM wiki_search_documents
|
|
296
|
+
WHERE ${conditions.join(" AND ")}
|
|
297
|
+
ORDER BY date ${query.direction === "oldest" ? "ASC" : "DESC"}, key ASC
|
|
298
|
+
LIMIT ?`,
|
|
299
|
+
)
|
|
300
|
+
.all(...parameters);
|
|
301
|
+
return rows.map((row) => ({
|
|
302
|
+
id: row.id,
|
|
303
|
+
kind: row.kind,
|
|
304
|
+
key: row.key,
|
|
305
|
+
title: row.title,
|
|
306
|
+
...(row.date ? { date: row.date } : {}),
|
|
307
|
+
}));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
matchingDocumentIds(kinds: IndexedWikiKind[], terms: string[]): Set<string> {
|
|
311
|
+
if (kinds.length === 0 || terms.length === 0) return new Set();
|
|
312
|
+
const kindPlaceholders = kinds.map(() => "?").join(", ");
|
|
313
|
+
const match = terms.map((term) => `"${term.replaceAll('"', '""')}"`).join(" OR ");
|
|
314
|
+
const rows = this.#database
|
|
315
|
+
.query<{ document_id: string }, string[]>(
|
|
316
|
+
`SELECT DISTINCT document_id
|
|
317
|
+
FROM wiki_search_fts
|
|
318
|
+
WHERE wiki_search_fts MATCH ? AND kind IN (${kindPlaceholders})`,
|
|
319
|
+
)
|
|
320
|
+
.all(match, ...kinds);
|
|
321
|
+
return new Set(rows.map((row) => row.document_id));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
documents(ids: Set<string>): WikiSearchIndexDocument[] {
|
|
325
|
+
if (ids.size === 0) return [];
|
|
326
|
+
const values = [...ids];
|
|
327
|
+
const rows: StoredDocumentRow[] = [];
|
|
328
|
+
for (let offset = 0; offset < values.length; offset += 500) {
|
|
329
|
+
const chunk = values.slice(offset, offset + 500);
|
|
330
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
331
|
+
rows.push(
|
|
332
|
+
...this.#database
|
|
333
|
+
.query<StoredDocumentRow, string[]>(
|
|
334
|
+
`SELECT id, kind, key, path, title, text, date
|
|
335
|
+
FROM wiki_search_documents
|
|
336
|
+
WHERE id IN (${placeholders})`,
|
|
337
|
+
)
|
|
338
|
+
.all(...chunk),
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return rows.map((row) => ({
|
|
342
|
+
id: row.id,
|
|
343
|
+
kind: row.kind,
|
|
344
|
+
key: row.key,
|
|
345
|
+
path: row.path,
|
|
346
|
+
title: row.title,
|
|
347
|
+
text: row.text,
|
|
348
|
+
tokens: [],
|
|
349
|
+
...(row.date ? { date: row.date } : {}),
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function resetCorruptWikiSearchIndex(path: string): void {
|
|
355
|
+
for (const candidate of [path, `${path}-journal`, `${path}-wal`, `${path}-shm`]) {
|
|
356
|
+
if (!existsSync(candidate)) continue;
|
|
357
|
+
unlinkSync(candidate);
|
|
358
|
+
}
|
|
359
|
+
}
|