botdocs 0.4.0 → 0.5.0

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.
Files changed (36) hide show
  1. package/README.md +20 -2
  2. package/dist/src/builder/chunker.d.ts +18 -0
  3. package/dist/src/builder/chunker.js +62 -1
  4. package/dist/src/builder/embedder.js +4 -1
  5. package/dist/src/builder/index-size.d.ts +3 -0
  6. package/dist/src/builder/index-size.js +16 -0
  7. package/dist/src/builder/index.js +16 -3
  8. package/dist/src/builder/paths.d.ts +6 -0
  9. package/dist/src/builder/paths.js +13 -0
  10. package/dist/src/builder/site-generator.d.ts +2 -0
  11. package/dist/src/builder/site-generator.js +39 -11
  12. package/dist/src/builder/template-engine.d.ts +4 -4
  13. package/dist/src/builder/template-engine.js +5 -1
  14. package/dist/src/builder/vector-db-builder.d.ts +1 -0
  15. package/dist/src/builder/vector-db-builder.js +1 -0
  16. package/dist/src/cli/index.js +60 -17
  17. package/dist/src/cli/options.d.ts +2 -0
  18. package/dist/src/cli/options.js +2 -0
  19. package/dist/src/cli/server.d.ts +6 -0
  20. package/dist/src/cli/server.js +79 -0
  21. package/dist/src/cli/watcher.d.ts +7 -0
  22. package/dist/src/cli/watcher.js +40 -0
  23. package/dist/src/shared/site-root.d.ts +2 -0
  24. package/dist/src/shared/site-root.js +9 -0
  25. package/dist/src/types/config.d.ts +3 -0
  26. package/dist/src/types/config.js +2 -0
  27. package/dist/src/types/document.d.ts +1 -1
  28. package/dist-client/assets/chatbox-Dw_HFrfR.js +8 -0
  29. package/dist-client/assets/rag-engine-B9wYRzqT.js +1 -0
  30. package/dist-client/bundle.js +1 -1
  31. package/man/botdocs.1 +74 -3
  32. package/package.json +10 -1
  33. package/src/styles/chat.css +13 -0
  34. package/src/templates/layout.html +14 -2
  35. package/dist-client/assets/chatbox-P1j6YP1y.js +0 -7
  36. package/dist-client/assets/rag-engine-CU9vjCAg.js +0 -1
@@ -0,0 +1,79 @@
1
+ import { createServer } from 'http';
2
+ import { promises as fs } from 'fs';
3
+ import { join, resolve, sep, extname } from 'path';
4
+ const MIME_TYPES = {
5
+ '.html': 'text/html; charset=utf-8',
6
+ '.css': 'text/css; charset=utf-8',
7
+ '.js': 'text/javascript; charset=utf-8',
8
+ '.mjs': 'text/javascript; charset=utf-8',
9
+ '.json': 'application/json; charset=utf-8',
10
+ '.map': 'application/json; charset=utf-8',
11
+ '.svg': 'image/svg+xml',
12
+ '.png': 'image/png',
13
+ '.jpg': 'image/jpeg',
14
+ '.jpeg': 'image/jpeg',
15
+ '.gif': 'image/gif',
16
+ '.webp': 'image/webp',
17
+ '.ico': 'image/x-icon',
18
+ '.xml': 'application/xml; charset=utf-8',
19
+ '.txt': 'text/plain; charset=utf-8',
20
+ '.woff': 'font/woff',
21
+ '.woff2': 'font/woff2',
22
+ };
23
+ export async function startServer(rootDir, port = 0) {
24
+ const root = resolve(rootDir);
25
+ const server = createServer((req, res) => {
26
+ handleRequest(req, res, root).catch(() => {
27
+ respond(res, 500, 'text/plain; charset=utf-8', 'Internal server error');
28
+ });
29
+ });
30
+ return new Promise((resolveStart, rejectStart) => {
31
+ server.once('error', rejectStart);
32
+ server.listen(port, '127.0.0.1', () => {
33
+ const address = server.address();
34
+ const actualPort = typeof address === 'object' && address ? address.port : port;
35
+ resolveStart({
36
+ url: `http://127.0.0.1:${actualPort}`,
37
+ port: actualPort,
38
+ close: () => new Promise((done) => server.close(() => done())),
39
+ });
40
+ });
41
+ });
42
+ }
43
+ async function handleRequest(req, res, root) {
44
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
45
+ respond(res, 405, 'text/plain; charset=utf-8', 'Method not allowed');
46
+ return;
47
+ }
48
+ let urlPath;
49
+ try {
50
+ urlPath = decodeURIComponent(new URL(req.url ?? '/', 'http://localhost').pathname);
51
+ }
52
+ catch {
53
+ respond(res, 400, 'text/plain; charset=utf-8', 'Bad request');
54
+ return;
55
+ }
56
+ // Complete mediation: every request resolves inside the site root or it
57
+ // never touches the filesystem.
58
+ const filePath = resolve(root, `.${sep}${urlPath}`);
59
+ if (filePath !== root && !filePath.startsWith(root + sep)) {
60
+ respond(res, 403, 'text/plain; charset=utf-8', 'Forbidden');
61
+ return;
62
+ }
63
+ let target = filePath;
64
+ try {
65
+ const stat = await fs.stat(target);
66
+ if (stat.isDirectory()) {
67
+ target = join(target, 'index.html');
68
+ }
69
+ const body = await fs.readFile(target);
70
+ respond(res, 200, MIME_TYPES[extname(target).toLowerCase()] ?? 'application/octet-stream', body);
71
+ }
72
+ catch {
73
+ respond(res, 404, 'text/plain; charset=utf-8', 'Not found');
74
+ }
75
+ }
76
+ function respond(res, status, contentType, body) {
77
+ res.writeHead(status, { 'Content-Type': contentType });
78
+ res.end(body);
79
+ }
@@ -0,0 +1,7 @@
1
+ export interface WatchedChange {
2
+ path: string;
3
+ }
4
+ export interface DocWatcher {
5
+ close(): void;
6
+ }
7
+ export declare function watchDocs(inputDir: string, onChange: (changes: WatchedChange[]) => void, debounceMs?: number): DocWatcher;
@@ -0,0 +1,40 @@
1
+ import { watch } from 'fs';
2
+ const WATCHED_EXTENSIONS = ['.md', '.markdown'];
3
+ const WATCHED_FILES = ['botdocs.config.json'];
4
+ function isWatchedPath(path) {
5
+ if (WATCHED_EXTENSIONS.some((ext) => path.endsWith(ext)))
6
+ return true;
7
+ return WATCHED_FILES.some((file) => path === file || path.endsWith(`/${file}`));
8
+ }
9
+ export function watchDocs(inputDir, onChange, debounceMs = 300) {
10
+ let pending = new Map();
11
+ let timer;
12
+ const flush = () => {
13
+ timer = undefined;
14
+ const changes = [...pending.values()];
15
+ pending = new Map();
16
+ if (changes.length > 0) {
17
+ onChange(changes);
18
+ }
19
+ };
20
+ const schedule = (path) => {
21
+ pending.set(path, { path });
22
+ if (!timer) {
23
+ timer = setTimeout(flush, debounceMs);
24
+ }
25
+ };
26
+ const watcher = watch(inputDir, { recursive: true }, (_eventType, filename) => {
27
+ const path = filename ?? '';
28
+ // Some platforms report no filename — treat as a full rebuild trigger.
29
+ if (!path || isWatchedPath(path)) {
30
+ schedule(path);
31
+ }
32
+ });
33
+ return {
34
+ close: () => {
35
+ if (timer)
36
+ clearTimeout(timer);
37
+ watcher.close();
38
+ },
39
+ };
40
+ }
@@ -0,0 +1,2 @@
1
+ export declare function rootPrefix(pageUrl: string): string;
2
+ export declare function relativeUrl(root: string, url: string): string;
@@ -0,0 +1,9 @@
1
+ export function rootPrefix(pageUrl) {
2
+ const depth = pageUrl.split('/').length - 2;
3
+ return depth <= 0 ? './' : '../'.repeat(depth);
4
+ }
5
+ export function relativeUrl(root, url) {
6
+ if (!url.startsWith('/') || url.startsWith('//'))
7
+ return url;
8
+ return root + (url === '/' ? 'index.html' : url.slice(1));
9
+ }
@@ -5,6 +5,7 @@ export interface BotdocsConfig {
5
5
  theme?: Theme;
6
6
  customCss?: string;
7
7
  attribution?: boolean;
8
+ baseUrl?: string;
8
9
  chat?: {
9
10
  enabled?: boolean;
10
11
  welcomeMessage?: string;
@@ -12,7 +13,9 @@ export interface BotdocsConfig {
12
13
  build?: {
13
14
  chunkSize?: number;
14
15
  chunkOverlap?: number;
16
+ minChunkSize?: number;
15
17
  topK?: number;
18
+ minScore?: number;
16
19
  };
17
20
  }
18
21
  export interface BuildOptions {
@@ -10,6 +10,8 @@ export const defaultConfig = {
10
10
  build: {
11
11
  chunkSize: 500,
12
12
  chunkOverlap: 50,
13
+ minChunkSize: 15,
13
14
  topK: 3,
15
+ minScore: 0.75,
14
16
  },
15
17
  };
@@ -3,7 +3,7 @@ export interface DocumentMetadata {
3
3
  description?: string;
4
4
  date?: string;
5
5
  author?: string;
6
- [key: string]: any;
6
+ [key: string]: unknown;
7
7
  }
8
8
  export interface ProcessedDocument {
9
9
  filePath: string;