nusus 0.3.0 → 0.4.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.
- package/README.md +24 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/models.d.ts +9 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/turath/catalog.d.ts +2 -1
- package/dist/turath/catalog.d.ts.map +1 -1
- package/dist/turath/catalog.js +12 -7
- package/dist/turath/catalog.js.map +1 -1
- package/dist/turath/client.d.ts +1 -1
- package/dist/turath/normalize.d.ts.map +1 -1
- package/dist/turath/normalize.js +27 -0
- package/dist/turath/normalize.js.map +1 -1
- package/package.json +1 -1
- package/scripts/search.mjs +630 -143
package/README.md
CHANGED
|
@@ -49,15 +49,31 @@ All requests support `AbortSignal`; failures use the exported `NususError` codes
|
|
|
49
49
|
## Agent CLI
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
npx nusus
|
|
53
|
-
npx nusus --
|
|
54
|
-
npx nusus --
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
npx nusus --help
|
|
53
|
+
npx nusus --version
|
|
54
|
+
npx nusus find-books "الأربعون النووية" --limit 5
|
|
55
|
+
npx nusus find-books --author-id 44 --limit 10
|
|
56
|
+
npx nusus find-authors "النووي" --limit 5
|
|
57
|
+
npx nusus search "إنما الأعمال بالنيات" --book-id 147927
|
|
58
|
+
npx nusus retrieve "النية" --max-passages 3 --max-chars 2000
|
|
59
|
+
npx nusus get-page --book-id 147927 --page-id 5
|
|
60
|
+
npx nusus get-context --book-id 147927 --page-id 5
|
|
61
|
+
npx nusus get-book 147927
|
|
62
|
+
npx nusus get-author 44
|
|
63
|
+
npx nusus list-categories
|
|
64
|
+
npx nusus catalog
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
Default stdout is **JSONL** (one object per line, camelCase, every line has `type`). Use `--format text` for compact human lines. Diagnostics are JSON on **stderr** only. Unknown or command-inapplicable flags are rejected.
|
|
68
|
+
|
|
69
|
+
| Exit | Meaning |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| 0 | Success (including zero hits; find/search/retrieve emit a `meta` line; offline finders report `returned`, live search/retrieve report `totalMatches`) |
|
|
72
|
+
| 1 | Usage / invalid argument |
|
|
73
|
+
| 2 | Not found |
|
|
74
|
+
| 3 | Rate limit / HTTP / invalid response / timeout (`ABORTED`) / internal |
|
|
75
|
+
|
|
76
|
+
`page-id` is Turath’s internal page (`location.internalPage`), not the printed page. `search`/`retrieve` accept at most one `--book-id`, one `--author-id`, and one `--category-id` (filters may be combined). Offline `find-books` accepts repeated `--author-id`/`--category-id` and may omit the title query when those filters are set. `--timeout 0` disables the request timeout (max `600000`). There is no madhhab ranking or multi-book fanout in the CLI.
|
|
61
77
|
|
|
62
78
|
## Known limitations
|
|
63
79
|
|
|
@@ -71,9 +87,9 @@ Browser support is not claimed because the checked API responses do not advertis
|
|
|
71
87
|
|
|
72
88
|
```bash
|
|
73
89
|
bun install
|
|
90
|
+
bun run build # CLI bin loads dist/; also auto-built by CLI tests if missing
|
|
74
91
|
bun test
|
|
75
92
|
bun run typecheck
|
|
76
|
-
bun run build
|
|
77
93
|
|
|
78
94
|
# optional live contract (also scheduled via GitHub Actions)
|
|
79
95
|
bun run test:live
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { NususError } from "./errors.js";
|
|
2
2
|
export type { NususErrorCode } from "./errors.js";
|
|
3
|
-
export type { Author, AuthorSummary, Book, BookSummary, CatalogMetadata, Category, CategorySummary, Passage, PassageProvenance, RetrievedContext, RetrieveScope, SearchPage, SourceLocation, SourceLocator, TurathId, } from "./models.js";
|
|
3
|
+
export type { Author, AuthorSummary, Book, BookSummary, BookTocEntry, CatalogMetadata, Category, CategorySummary, Passage, PassageProvenance, RetrievedContext, RetrieveScope, SearchPage, SourceLocation, SourceLocator, TurathId, } from "./models.js";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,MAAM,EACN,aAAa,EACb,IAAI,EACJ,WAAW,EACX,eAAe,EACf,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,cAAc,EACd,aAAa,EACb,QAAQ,GACT,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,MAAM,EACN,aAAa,EACb,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,eAAe,EACf,OAAO,EACP,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,cAAc,EACd,aAAa,EACb,QAAQ,GACT,MAAM,aAAa,CAAC"}
|
package/dist/models.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ export type BookSummary = {
|
|
|
16
16
|
id: string;
|
|
17
17
|
title: string;
|
|
18
18
|
};
|
|
19
|
+
export type BookTocEntry = {
|
|
20
|
+
title: string;
|
|
21
|
+
level?: number;
|
|
22
|
+
page?: number;
|
|
23
|
+
};
|
|
19
24
|
export type Author = AuthorSummary & {
|
|
20
25
|
provider: "turath";
|
|
21
26
|
id: string;
|
|
@@ -29,6 +34,10 @@ export type Book = BookSummary & {
|
|
|
29
34
|
category?: CategorySummary;
|
|
30
35
|
description?: string;
|
|
31
36
|
hasPdf?: boolean;
|
|
37
|
+
/** Normalized TOC from Turath book indexes when present. */
|
|
38
|
+
toc?: BookTocEntry[];
|
|
39
|
+
/** Volume labels from Turath book indexes when present. */
|
|
40
|
+
volumes?: string[];
|
|
32
41
|
raw?: unknown;
|
|
33
42
|
};
|
|
34
43
|
export type SourceLocation = {
|
package/dist/models.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
|
package/dist/turath/catalog.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Author, Book, CatalogMetadata, Category, TurathId } from "../models.js";
|
|
2
2
|
export type FindBooksOptions = {
|
|
3
|
+
authorIds?: TurathId[];
|
|
3
4
|
categoryIds?: TurathId[];
|
|
4
5
|
limit?: number;
|
|
5
6
|
};
|
|
@@ -7,7 +8,7 @@ export type FindAuthorsOptions = {
|
|
|
7
8
|
limit?: number;
|
|
8
9
|
};
|
|
9
10
|
export declare const getCatalogMetadata: () => CatalogMetadata;
|
|
10
|
-
export declare const findCatalogBooks: (query
|
|
11
|
+
export declare const findCatalogBooks: (query?: string, options?: FindBooksOptions) => Book[];
|
|
11
12
|
export declare const findCatalogAuthors: (query: string, options?: FindAuthorsOptions) => Author[];
|
|
12
13
|
export declare const listCatalogCategories: () => Category[];
|
|
13
14
|
export declare const listCatalogAuthorIds: () => string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/turath/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAItF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyCF,eAAO,MAAM,kBAAkB,QAAO,eAarC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/turath/catalog.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAItF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyCF,eAAO,MAAM,kBAAkB,QAAO,eAarC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,cAAU,EAAE,UAAS,gBAAqB,KAAG,IAAI,EAkCjF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,EAAE,UAAS,kBAAuB,KAAG,MAAM,EAqB1F,CAAC;AAEF,eAAO,MAAM,qBAAqB,QAAO,QAAQ,EAShD,CAAC;AAEF,eAAO,MAAM,oBAAoB,QAAO,MAAM,EACU,CAAC"}
|
package/dist/turath/catalog.js
CHANGED
|
@@ -54,22 +54,27 @@ export const getCatalogMetadata = () => {
|
|
|
54
54
|
unresolvedAuthorIdCount: uniqueAuthorIds.size - authorNameCount,
|
|
55
55
|
};
|
|
56
56
|
};
|
|
57
|
-
export const findCatalogBooks = (query, options = {}) => {
|
|
57
|
+
export const findCatalogBooks = (query = "", options = {}) => {
|
|
58
58
|
const needle = normalize(query);
|
|
59
|
-
|
|
60
|
-
throw new NususError("INVALID_ARGUMENT", "query must not be empty");
|
|
61
|
-
const limit = positiveLimit(options.limit, 20);
|
|
59
|
+
const authors = options.authorIds?.map((value) => positiveId(value, "author id"));
|
|
62
60
|
const categories = options.categoryIds?.map((value) => positiveId(value, "category id"));
|
|
61
|
+
const authorSet = authors?.length ? new Set(authors) : undefined;
|
|
63
62
|
const categorySet = categories?.length ? new Set(categories) : undefined;
|
|
64
|
-
|
|
63
|
+
if (!needle && !authorSet && !categorySet) {
|
|
64
|
+
throw new NususError("INVALID_ARGUMENT", "query must not be empty unless authorIds or categoryIds are set");
|
|
65
|
+
}
|
|
66
|
+
const limit = positiveLimit(options.limit, 20);
|
|
67
|
+
const words = needle ? needle.split(/\s+/) : [];
|
|
65
68
|
return CATALOG_BOOKS
|
|
66
69
|
.flatMap(([bookId, title, authorId, categoryId]) => {
|
|
70
|
+
const authorKey = String(authorId);
|
|
71
|
+
if (authorSet && !authorSet.has(authorKey))
|
|
72
|
+
return [];
|
|
67
73
|
if (categorySet && !categorySet.has(String(categoryId)))
|
|
68
74
|
return [];
|
|
69
|
-
const score = scoreMatch(normalize(title), needle, words);
|
|
75
|
+
const score = needle ? scoreMatch(normalize(title), needle, words) : 0;
|
|
70
76
|
if (score < 0)
|
|
71
77
|
return [];
|
|
72
|
-
const authorKey = String(authorId);
|
|
73
78
|
const authorName = authorNames.get(authorKey);
|
|
74
79
|
return [{
|
|
75
80
|
score,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/turath/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/turath/catalog.ts"],"names":[],"mappings":"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"}
|
package/dist/turath/client.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export type RetrieveOptions = RequestOptions & {
|
|
|
24
24
|
};
|
|
25
25
|
export type TurathClientOptions = TransportOptions;
|
|
26
26
|
export declare const createTurathClient: (options?: TurathClientOptions) => {
|
|
27
|
-
findBooks: (query
|
|
27
|
+
findBooks: (query?: string, options?: import("./catalog.js").FindBooksOptions) => Book[];
|
|
28
28
|
findAuthors: (query: string, options?: import("./catalog.js").FindAuthorsOptions) => import("../models.js").Author[];
|
|
29
29
|
listCategories: () => import("../models.js").Category[];
|
|
30
30
|
getCatalogMetadata: () => import("../models.js").CatalogMetadata;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/turath/normalize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/turath/normalize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAgB,OAAO,EAAE,MAAM,cAAc,CAAC;AA6CxE,eAAO,MAAM,eAAe,GAAI,KAAK,OAAO,KAAG,MAa9C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,KAAG,IAiB5C,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,EAAE,QAAQ,MAAM,KAAG,OAoB5D,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,KAAK,OAAO,KAAG,OAsBjD,CAAC"}
|
package/dist/turath/normalize.js
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
import { NususError } from "../errors.js";
|
|
2
2
|
import { decoratePassage } from "./citations.js";
|
|
3
3
|
const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
const extractIndexes = (raw) => {
|
|
5
|
+
const indexes = raw.indexes;
|
|
6
|
+
if (!record(indexes))
|
|
7
|
+
return {};
|
|
8
|
+
const out = {};
|
|
9
|
+
if (Array.isArray(indexes.headings)) {
|
|
10
|
+
const toc = [];
|
|
11
|
+
for (const item of indexes.headings) {
|
|
12
|
+
if (!record(item) || typeof item.title !== "string")
|
|
13
|
+
continue;
|
|
14
|
+
const entry = { title: item.title };
|
|
15
|
+
if (typeof item.level === "number")
|
|
16
|
+
entry.level = item.level;
|
|
17
|
+
if (typeof item.page === "number")
|
|
18
|
+
entry.page = item.page;
|
|
19
|
+
toc.push(entry);
|
|
20
|
+
}
|
|
21
|
+
if (toc.length)
|
|
22
|
+
out.toc = toc;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(indexes.volumes) && indexes.volumes.every((value) => typeof value === "string")) {
|
|
25
|
+
out.volumes = indexes.volumes;
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
};
|
|
4
29
|
const invalid = (message, cause) => {
|
|
5
30
|
throw new NususError("INVALID_RESPONSE", message, { cause });
|
|
6
31
|
};
|
|
@@ -39,6 +64,7 @@ export const normalizeBook = (raw) => {
|
|
|
39
64
|
return invalid("Turath returned an invalid book");
|
|
40
65
|
}
|
|
41
66
|
const book = raw;
|
|
67
|
+
const indexes = extractIndexes(raw);
|
|
42
68
|
return {
|
|
43
69
|
provider: "turath",
|
|
44
70
|
id: String(book.meta.id),
|
|
@@ -47,6 +73,7 @@ export const normalizeBook = (raw) => {
|
|
|
47
73
|
...(typeof book.meta.cat_id === "number" && { category: { id: String(book.meta.cat_id) } }),
|
|
48
74
|
...(typeof book.meta.info === "string" && { description: book.meta.info }),
|
|
49
75
|
...(Boolean(book.meta.pdf_links) && { hasPdf: true }),
|
|
76
|
+
...indexes,
|
|
50
77
|
raw,
|
|
51
78
|
};
|
|
52
79
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/turath/normalize.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/turath/normalize.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/scripts/search.mjs
CHANGED
|
@@ -1,155 +1,642 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Agent-facing Turath
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// Usage:
|
|
6
|
-
// node search.mjs "<Arabic query>" [max_results]
|
|
7
|
-
// node search.mjs --madhhab hanafi|maliki|shafii|hanbali "<topic>" [max_results]
|
|
8
|
-
// node search.mjs --books "بدائع الصنائع,الهداية" "<query>" [max_results]
|
|
9
|
-
// node search.mjs --book-id 147927 "<query>" [max_results]
|
|
10
|
-
// node search.mjs --page <book_id> <page_id> # fetch one page + context
|
|
11
|
-
//
|
|
12
|
-
// Output: JSON per line — book_id, title, author, volume, page_id, printed_page,
|
|
13
|
-
// headings, citation, url, text (bounded), prev/next context.
|
|
2
|
+
// Agent-facing Turath CLI — thin subcommand surface over the nusus SDK.
|
|
3
|
+
// Default stdout: JSONL. Diagnostics: stderr JSON. No ranking heuristics.
|
|
14
4
|
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
15
8
|
import { createTurathClient } from "../dist/turath/index.js";
|
|
9
|
+
import { NususError } from "../dist/errors.js";
|
|
16
10
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let maxResults = 5;
|
|
39
|
-
|
|
40
|
-
for (let i = 0; i < args.length; i++) {
|
|
41
|
-
const a = args[i];
|
|
42
|
-
if (a === "--madhhab") {
|
|
43
|
-
madhhab = args[++i]?.toLowerCase();
|
|
44
|
-
if (!MADHHAB_BOOKS[madhhab]) fail(`Invalid madhhab. Use: ${Object.keys(MADHHAB_BOOKS).join(", ")}`);
|
|
45
|
-
} else if (a === "--books") books = args[++i]?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
46
|
-
else if (a === "--book-id") bookId = args[++i];
|
|
47
|
-
else if (a === "--page") pageMode = true;
|
|
48
|
-
else if (a === "--topic") query = args[++i];
|
|
49
|
-
else if (!a.startsWith("--") && query === null && isNaN(Number(a))) query = a;
|
|
50
|
-
}
|
|
51
|
-
// last numeric non-flag arg = max results (or page args in --page mode)
|
|
52
|
-
const numbers = args.filter((a) => !a.startsWith("--") && /^\d+$/.test(a));
|
|
53
|
-
if (numbers.length && !pageMode) maxResults = parseInt(numbers[numbers.length - 1], 10);
|
|
54
|
-
|
|
55
|
-
function fail(message) {
|
|
56
|
-
console.error(message);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const turath = createTurathClient({ timeout: 15_000 });
|
|
61
|
-
|
|
62
|
-
const emit = (item) => console.log(JSON.stringify(item));
|
|
63
|
-
|
|
64
|
-
const passageToJson = (p, extra = {}) => ({
|
|
65
|
-
book_id: Number(p.book.id),
|
|
66
|
-
title: p.book.title,
|
|
67
|
-
author: p.author?.name,
|
|
68
|
-
volume: p.location.volume,
|
|
69
|
-
page_id: p.location.internalPage,
|
|
70
|
-
printed_page: p.location.printedPage,
|
|
71
|
-
headings: p.headings.join(" > "),
|
|
72
|
-
citation: p.citation,
|
|
73
|
-
url: p.url,
|
|
74
|
-
...extra,
|
|
11
|
+
const DEFAULT_TIMEOUT = 15_000;
|
|
12
|
+
const MAX_TIMEOUT = 600_000;
|
|
13
|
+
const MAX_FILTER_IDS = 50;
|
|
14
|
+
const VERSION = createRequire(import.meta.url)(join(dirname(fileURLToPath(import.meta.url)), "../package.json")).version;
|
|
15
|
+
|
|
16
|
+
const EXIT = {
|
|
17
|
+
USAGE: 1,
|
|
18
|
+
INVALID_ARGUMENT: 1,
|
|
19
|
+
NOT_FOUND: 2,
|
|
20
|
+
RATE_LIMITED: 3,
|
|
21
|
+
HTTP_ERROR: 3,
|
|
22
|
+
INVALID_RESPONSE: 3,
|
|
23
|
+
ABORTED: 3,
|
|
24
|
+
INTERNAL_ERROR: 3,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// --- I/O ---
|
|
28
|
+
|
|
29
|
+
process.stdout.on("error", (error) => {
|
|
30
|
+
if (error?.code === "EPIPE") process.exit(0);
|
|
31
|
+
throw error;
|
|
75
32
|
});
|
|
76
33
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
34
|
+
const writeJsonl = (record) => {
|
|
35
|
+
process.stdout.write(`${JSON.stringify(record)}\n`);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const fail = (code, error) => {
|
|
39
|
+
const payload = {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: {
|
|
42
|
+
code,
|
|
43
|
+
message: error?.message ?? String(error),
|
|
44
|
+
...(error?.status !== undefined && { status: error.status }),
|
|
45
|
+
...(error?.url !== undefined && { url: error.url }),
|
|
46
|
+
...(error?.retryAfter !== undefined && { retryAfter: error.retryAfter }),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
process.stderr.write(`${JSON.stringify(payload)}\n`);
|
|
50
|
+
process.exit(EXIT[code] ?? 3);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const usage = (message) => fail("USAGE", { message });
|
|
54
|
+
|
|
55
|
+
// --- text format ---
|
|
56
|
+
|
|
57
|
+
const preview = (text, n = 160) => {
|
|
58
|
+
const one = String(text ?? "").replace(/\s+/g, " ").trim();
|
|
59
|
+
return one.length <= n ? one : `${one.slice(0, n)}…`;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const writeText = (record) => {
|
|
63
|
+
switch (record.type) {
|
|
64
|
+
case "meta": {
|
|
65
|
+
const parts = [`meta\t${record.command}`];
|
|
66
|
+
if (record.query !== undefined) parts.push(`q=${record.query}`);
|
|
67
|
+
if (record.returned !== undefined) parts.push(`returned=${record.returned}`);
|
|
68
|
+
if (record.totalMatches !== undefined) parts.push(`totalMatches=${record.totalMatches}`);
|
|
69
|
+
if (record.page !== undefined) parts.push(`page=${record.page}`);
|
|
70
|
+
process.stdout.write(`${parts.join("\t")}\n`);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "book":
|
|
74
|
+
process.stdout.write(
|
|
75
|
+
`book\t${record.id}\t${record.title}` +
|
|
76
|
+
(record.author?.name ? `\t${record.author.name}` : record.author?.id ? `\tauthor=${record.author.id}` : "") +
|
|
77
|
+
(record.toc ? `\ttoc=${record.toc.length}` : "") +
|
|
78
|
+
"\n",
|
|
79
|
+
);
|
|
80
|
+
break;
|
|
81
|
+
case "author":
|
|
82
|
+
process.stdout.write(`author\t${record.id}\t${record.name}\n`);
|
|
83
|
+
break;
|
|
84
|
+
case "category":
|
|
85
|
+
process.stdout.write(`category\t${record.id}\t${record.title}\tbooks=${record.bookCount}\n`);
|
|
86
|
+
break;
|
|
87
|
+
case "catalog":
|
|
88
|
+
process.stdout.write(
|
|
89
|
+
`catalog\tbooks=${record.bookCount}\tcategories=${record.categoryCount}\tauthors=${record.authorIdCount}\n`,
|
|
90
|
+
);
|
|
91
|
+
break;
|
|
92
|
+
case "passage":
|
|
93
|
+
process.stdout.write(
|
|
94
|
+
`passage\t${record.book?.id ?? "?"}\t${record.citation ?? ""}\t${record.url ?? ""}\t${preview(record.text)}\n`,
|
|
95
|
+
);
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
process.stdout.write(`${record.type}\t${JSON.stringify(record)}\n`);
|
|
98
99
|
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const emit = (format, record) => {
|
|
103
|
+
if (format === "text") writeText(record);
|
|
104
|
+
else writeJsonl(record);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// --- records ---
|
|
108
|
+
|
|
109
|
+
const toPassageRecord = (passage) => {
|
|
110
|
+
const record = {
|
|
111
|
+
type: "passage",
|
|
112
|
+
book: passage.book,
|
|
113
|
+
location: passage.location,
|
|
114
|
+
text: passage.text,
|
|
115
|
+
headings: passage.headings ?? [],
|
|
116
|
+
url: passage.url,
|
|
117
|
+
citation: passage.citation,
|
|
118
|
+
};
|
|
119
|
+
if (passage.author) record.author = passage.author;
|
|
120
|
+
if (passage.category) record.category = passage.category;
|
|
121
|
+
if (passage.snippet !== undefined) record.snippet = passage.snippet;
|
|
122
|
+
if (passage.locator) record.locator = passage.locator;
|
|
123
|
+
if (passage.provenance) record.provenance = passage.provenance;
|
|
124
|
+
return record;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const toBookRecord = (book) => {
|
|
128
|
+
const record = {
|
|
129
|
+
type: "book",
|
|
130
|
+
id: book.id,
|
|
131
|
+
title: book.title,
|
|
132
|
+
provider: book.provider ?? "turath",
|
|
133
|
+
};
|
|
134
|
+
if (book.author) record.author = book.author;
|
|
135
|
+
if (book.category) record.category = book.category;
|
|
136
|
+
if (book.description !== undefined) record.description = book.description;
|
|
137
|
+
if (book.hasPdf !== undefined) record.hasPdf = book.hasPdf;
|
|
138
|
+
if (book.toc) record.toc = book.toc;
|
|
139
|
+
if (book.volumes) record.volumes = book.volumes;
|
|
140
|
+
return record;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const toAuthorRecord = (author) => {
|
|
144
|
+
const record = {
|
|
145
|
+
type: "author",
|
|
146
|
+
id: author.id,
|
|
147
|
+
name: author.name,
|
|
148
|
+
provider: author.provider ?? "turath",
|
|
149
|
+
};
|
|
150
|
+
if (author.biography !== undefined) record.biography = author.biography;
|
|
151
|
+
if (author.deathYear !== undefined) record.deathYear = author.deathYear;
|
|
152
|
+
return record;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const toCategoryRecord = (category) => ({
|
|
156
|
+
type: "category",
|
|
157
|
+
id: category.id,
|
|
158
|
+
title: category.title,
|
|
159
|
+
provider: category.provider ?? "turath",
|
|
160
|
+
bookCount: category.bookCount,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const toCatalogRecord = (meta) => ({ type: "catalog", ...meta });
|
|
164
|
+
|
|
165
|
+
// --- parse helpers ---
|
|
166
|
+
|
|
167
|
+
const parseIntFlag = (raw, name, { def, min, max } = {}) => {
|
|
168
|
+
if (raw === undefined) return def;
|
|
169
|
+
if (raw === true) usage(`--${name} requires a value`);
|
|
170
|
+
if (!/^\d+$/.test(String(raw))) usage(`--${name} must be an integer`);
|
|
171
|
+
const n = Number(raw);
|
|
172
|
+
if (min !== undefined && n < min) usage(`--${name} must be >= ${min}`);
|
|
173
|
+
if (max !== undefined && n > max) usage(`--${name} must be <= ${max}`);
|
|
174
|
+
return n;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const parsePositiveId = (raw, label) => {
|
|
178
|
+
const value = String(raw);
|
|
179
|
+
if (!/^[1-9]\d*$/.test(value)) usage(`${label} must be a positive integer`);
|
|
180
|
+
return value;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const requireFlag = (flags, name) => {
|
|
184
|
+
const value = flags[name];
|
|
185
|
+
if (value === undefined || value === true) usage(`--${name} is required`);
|
|
186
|
+
return value;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const optionalFlag = (flags, name) => {
|
|
190
|
+
const value = flags[name];
|
|
191
|
+
if (value === undefined) return undefined;
|
|
192
|
+
if (value === true) usage(`--${name} requires a value`);
|
|
193
|
+
return value;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const parseIdList = (values, label, { max = 1 } = {}) => {
|
|
197
|
+
const list = values ?? [];
|
|
198
|
+
if (list.length > max) {
|
|
199
|
+
usage(max === 1 ? `at most one --${label}` : `at most ${max} --${label} values`);
|
|
103
200
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
201
|
+
return list.map((value) => parsePositiveId(value, label));
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const takeQuery = (positionals, { required = true } = {}) => {
|
|
205
|
+
if (positionals.length > 1) usage("accepts a single <query> argument");
|
|
206
|
+
if (!positionals.length) {
|
|
207
|
+
if (required) usage("requires a <query>");
|
|
208
|
+
return "";
|
|
209
|
+
}
|
|
210
|
+
const query = positionals[0];
|
|
211
|
+
if (!String(query).trim()) {
|
|
212
|
+
if (required) usage("query must not be empty");
|
|
213
|
+
return "";
|
|
214
|
+
}
|
|
215
|
+
return query;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const takeIdArg = (positionals, label) => {
|
|
219
|
+
if (positionals.length === 0) usage(`${label} requires an id argument`);
|
|
220
|
+
if (positionals.length > 1) usage(`${label} accepts a single id argument`);
|
|
221
|
+
return parsePositiveId(positionals[0], `${label} id`);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const rejectExtras = (positionals, flags, multi) => {
|
|
225
|
+
if (positionals.length) usage("this command accepts no positional arguments");
|
|
226
|
+
if (Object.keys(flags).length) usage(`unknown option --${Object.keys(flags)[0]}`);
|
|
227
|
+
if (Object.keys(multi).length) usage(`unknown option --${Object.keys(multi)[0]}`);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const parseArgs = (argv) => {
|
|
231
|
+
const positionals = [];
|
|
232
|
+
const flags = Object.create(null);
|
|
233
|
+
const multi = Object.create(null);
|
|
234
|
+
const multiKeys = new Set(["author-id", "category-id"]);
|
|
235
|
+
|
|
236
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
237
|
+
const arg = argv[i];
|
|
238
|
+
if (arg === "--") {
|
|
239
|
+
positionals.push(...argv.slice(i + 1));
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
if (arg === "-h" || arg === "--help") {
|
|
243
|
+
flags.help = true;
|
|
244
|
+
continue;
|
|
120
245
|
}
|
|
121
|
-
|
|
122
|
-
|
|
246
|
+
if (arg === "-v" || arg === "--version") {
|
|
247
|
+
flags.version = true;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (arg.startsWith("-") && !arg.startsWith("--")) {
|
|
251
|
+
usage(`unknown option ${arg}`);
|
|
252
|
+
}
|
|
253
|
+
if (!arg.startsWith("--")) {
|
|
254
|
+
positionals.push(arg);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const eq = arg.indexOf("=");
|
|
258
|
+
let key;
|
|
259
|
+
let value;
|
|
260
|
+
if (eq !== -1) {
|
|
261
|
+
key = arg.slice(2, eq);
|
|
262
|
+
value = arg.slice(eq + 1);
|
|
263
|
+
} else {
|
|
264
|
+
key = arg.slice(2);
|
|
265
|
+
const next = argv[i + 1];
|
|
266
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
267
|
+
value = next;
|
|
268
|
+
i += 1;
|
|
269
|
+
} else {
|
|
270
|
+
value = true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (!key) usage("invalid option");
|
|
274
|
+
if (multiKeys.has(key)) {
|
|
275
|
+
if (value === true) usage(`--${key} requires a value`);
|
|
276
|
+
(multi[key] ??= []).push(value);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (flags[key] !== undefined) usage(`duplicate flag --${key}`);
|
|
280
|
+
flags[key] = value;
|
|
123
281
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
282
|
+
|
|
283
|
+
return { positionals, flags, multi };
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const takeAllowed = (flags, multi, allowed) => {
|
|
287
|
+
const allowedSet = new Set(allowed);
|
|
288
|
+
const outFlags = Object.create(null);
|
|
289
|
+
const outMulti = Object.create(null);
|
|
290
|
+
for (const [key, value] of Object.entries(flags)) {
|
|
291
|
+
if (!allowedSet.has(key)) usage(`unknown option --${key}`);
|
|
292
|
+
outFlags[key] = value;
|
|
293
|
+
}
|
|
294
|
+
for (const [key, value] of Object.entries(multi)) {
|
|
295
|
+
if (!allowedSet.has(key)) usage(`unknown option --${key}`);
|
|
296
|
+
outMulti[key] = value;
|
|
297
|
+
}
|
|
298
|
+
return { flags: outFlags, multi: outMulti };
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const parseLiveScope = (flags, multi) => {
|
|
302
|
+
const bookId = optionalFlag(flags, "book-id");
|
|
303
|
+
const authorIds = parseIdList(multi["author-id"], "author-id", { max: 1 });
|
|
304
|
+
const categoryIds = parseIdList(multi["category-id"], "category-id", { max: 1 });
|
|
305
|
+
const scope = {};
|
|
306
|
+
if (bookId !== undefined) scope.bookIds = [parsePositiveId(bookId, "book-id")];
|
|
307
|
+
if (authorIds.length) scope.authorIds = authorIds;
|
|
308
|
+
if (categoryIds.length) scope.categoryIds = categoryIds;
|
|
309
|
+
return scope;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// --- command table ---
|
|
313
|
+
|
|
314
|
+
const GLOBAL_FLAGS = ["format", "timeout"];
|
|
315
|
+
|
|
316
|
+
const COMMANDS = {
|
|
317
|
+
"find-books": {
|
|
318
|
+
help: `nusus find-books [query] [--limit N] [--author-id <id>]... [--category-id <id>]...
|
|
319
|
+
Offline catalog book search.
|
|
320
|
+
Query optional only when --author-id and/or --category-id is set.
|
|
321
|
+
--limit default 20, max 100
|
|
322
|
+
--author-id repeatable offline author filter (max ${MAX_FILTER_IDS})
|
|
323
|
+
--category-id repeatable offline category filter (max ${MAX_FILTER_IDS})`,
|
|
324
|
+
flags: [...GLOBAL_FLAGS, "limit", "author-id", "category-id"],
|
|
325
|
+
run: async ({ turath, format, positionals, flags, multi }) => {
|
|
326
|
+
const query = takeQuery(positionals, { required: false });
|
|
327
|
+
const limit = parseIntFlag(flags.limit, "limit", { def: 20, min: 1, max: 100 });
|
|
328
|
+
const authorIds = parseIdList(multi["author-id"], "author-id", { max: MAX_FILTER_IDS });
|
|
329
|
+
const categoryIds = parseIdList(multi["category-id"], "category-id", { max: MAX_FILTER_IDS });
|
|
330
|
+
if (!query.trim() && !authorIds.length && !categoryIds.length) {
|
|
331
|
+
usage("find-books requires a <query> or --author-id/--category-id");
|
|
140
332
|
}
|
|
141
|
-
|
|
142
|
-
|
|
333
|
+
const books = turath.findBooks(query, {
|
|
334
|
+
limit,
|
|
335
|
+
...(authorIds.length && { authorIds }),
|
|
336
|
+
...(categoryIds.length && { categoryIds }),
|
|
337
|
+
});
|
|
338
|
+
emit(format, {
|
|
339
|
+
type: "meta",
|
|
340
|
+
command: "find-books",
|
|
341
|
+
query,
|
|
342
|
+
...(authorIds.length && { authorIds }),
|
|
343
|
+
...(categoryIds.length && { categoryIds }),
|
|
344
|
+
limit,
|
|
345
|
+
returned: books.length,
|
|
346
|
+
});
|
|
347
|
+
for (const book of books) emit(format, toBookRecord(book));
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
"find-authors": {
|
|
351
|
+
help: `nusus find-authors <query> [--limit N]
|
|
352
|
+
Offline catalog author search.
|
|
353
|
+
--limit default 20, max 100`,
|
|
354
|
+
flags: [...GLOBAL_FLAGS, "limit"],
|
|
355
|
+
run: async ({ turath, format, positionals, flags }) => {
|
|
356
|
+
const query = takeQuery(positionals);
|
|
357
|
+
const limit = parseIntFlag(flags.limit, "limit", { def: 20, min: 1, max: 100 });
|
|
358
|
+
const authors = turath.findAuthors(query, { limit });
|
|
359
|
+
emit(format, {
|
|
360
|
+
type: "meta",
|
|
361
|
+
command: "find-authors",
|
|
362
|
+
query,
|
|
363
|
+
limit,
|
|
364
|
+
returned: authors.length,
|
|
365
|
+
});
|
|
366
|
+
for (const author of authors) emit(format, toAuthorRecord(author));
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
"list-categories": {
|
|
370
|
+
help: `nusus list-categories
|
|
371
|
+
Offline category list.`,
|
|
372
|
+
flags: [...GLOBAL_FLAGS],
|
|
373
|
+
run: async ({ turath, format, positionals, flags, multi }) => {
|
|
374
|
+
rejectExtras(positionals, flags, multi);
|
|
375
|
+
for (const category of turath.listCategories()) emit(format, toCategoryRecord(category));
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
catalog: {
|
|
379
|
+
help: `nusus catalog
|
|
380
|
+
Offline catalog metadata.`,
|
|
381
|
+
flags: [...GLOBAL_FLAGS],
|
|
382
|
+
run: async ({ turath, format, positionals, flags, multi }) => {
|
|
383
|
+
rejectExtras(positionals, flags, multi);
|
|
384
|
+
emit(format, toCatalogRecord(turath.getCatalogMetadata()));
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
search: {
|
|
388
|
+
help: `nusus search <query> [--book-id ID] [--author-id ID] [--category-id ID]
|
|
389
|
+
[--page N] [--sort relevance|page]
|
|
390
|
+
Live Turath search (JSONL meta + passages).
|
|
391
|
+
--page default 1
|
|
392
|
+
--sort relevance (default) | page
|
|
393
|
+
At most one ID per filter; filters may be combined.`,
|
|
394
|
+
flags: [...GLOBAL_FLAGS, "book-id", "author-id", "category-id", "page", "sort"],
|
|
395
|
+
run: async ({ turath, format, positionals, flags, multi }) => {
|
|
396
|
+
const query = takeQuery(positionals);
|
|
397
|
+
const scope = parseLiveScope(flags, multi);
|
|
398
|
+
const page = parseIntFlag(flags.page, "page", { def: 1, min: 1 });
|
|
399
|
+
const sortRaw = optionalFlag(flags, "sort") ?? "relevance";
|
|
400
|
+
if (sortRaw !== "relevance" && sortRaw !== "page") usage("--sort must be relevance or page");
|
|
401
|
+
const result = await turath.search(query, {
|
|
402
|
+
...scope,
|
|
403
|
+
page,
|
|
404
|
+
...(sortRaw === "page" && { sort: "page" }),
|
|
405
|
+
});
|
|
406
|
+
emit(format, {
|
|
407
|
+
type: "meta",
|
|
408
|
+
command: "search",
|
|
409
|
+
query,
|
|
410
|
+
totalMatches: result.totalMatches,
|
|
411
|
+
page: result.page,
|
|
412
|
+
sort: sortRaw,
|
|
413
|
+
...(scope.bookIds && { bookIds: scope.bookIds }),
|
|
414
|
+
...(scope.authorIds && { authorIds: scope.authorIds }),
|
|
415
|
+
...(scope.categoryIds && { categoryIds: scope.categoryIds }),
|
|
416
|
+
});
|
|
417
|
+
for (const item of result.items) emit(format, toPassageRecord(item));
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
retrieve: {
|
|
421
|
+
help: `nusus retrieve <query> [--book-id ID] [--author-id ID] [--category-id ID]
|
|
422
|
+
[--max-passages N] [--max-chars N]
|
|
423
|
+
[--pages-before N] [--pages-after N]
|
|
424
|
+
Live retrieve with citations/locators.
|
|
425
|
+
--max-passages default 5, max 20
|
|
426
|
+
--max-chars default 2000, max 20000
|
|
427
|
+
--pages-before default 0, max 2
|
|
428
|
+
--pages-after default 0, max 2
|
|
429
|
+
At most one ID per filter; filters may be combined.`,
|
|
430
|
+
flags: [
|
|
431
|
+
...GLOBAL_FLAGS,
|
|
432
|
+
"book-id",
|
|
433
|
+
"author-id",
|
|
434
|
+
"category-id",
|
|
435
|
+
"max-passages",
|
|
436
|
+
"max-chars",
|
|
437
|
+
"pages-before",
|
|
438
|
+
"pages-after",
|
|
439
|
+
],
|
|
440
|
+
run: async ({ turath, format, positionals, flags, multi }) => {
|
|
441
|
+
const query = takeQuery(positionals);
|
|
442
|
+
const scope = parseLiveScope(flags, multi);
|
|
443
|
+
const maxPassages = parseIntFlag(flags["max-passages"], "max-passages", { def: 5, min: 1, max: 20 });
|
|
444
|
+
const maxChars = parseIntFlag(flags["max-chars"], "max-chars", { def: 2000, min: 1, max: 20_000 });
|
|
445
|
+
const pagesBefore = parseIntFlag(flags["pages-before"], "pages-before", { def: 0, min: 0, max: 2 });
|
|
446
|
+
const pagesAfter = parseIntFlag(flags["pages-after"], "pages-after", { def: 0, min: 0, max: 2 });
|
|
447
|
+
const result = await turath.retrieve(query, {
|
|
448
|
+
maxPassages,
|
|
449
|
+
maxCharsPerPassage: maxChars,
|
|
450
|
+
pagesBefore,
|
|
451
|
+
pagesAfter,
|
|
452
|
+
...(Object.keys(scope).length && { scope }),
|
|
453
|
+
});
|
|
454
|
+
emit(format, {
|
|
455
|
+
type: "meta",
|
|
456
|
+
command: "retrieve",
|
|
457
|
+
query,
|
|
458
|
+
totalMatches: result.totalMatches,
|
|
459
|
+
maxPassages,
|
|
460
|
+
maxChars,
|
|
461
|
+
pagesBefore,
|
|
462
|
+
pagesAfter,
|
|
463
|
+
...(scope.bookIds && { bookIds: scope.bookIds }),
|
|
464
|
+
...(scope.authorIds && { authorIds: scope.authorIds }),
|
|
465
|
+
...(scope.categoryIds && { categoryIds: scope.categoryIds }),
|
|
466
|
+
});
|
|
467
|
+
for (const passage of result.passages) emit(format, toPassageRecord(passage));
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
"get-page": {
|
|
471
|
+
help: `nusus get-page --book-id <id> --page-id <id>
|
|
472
|
+
Fetch one internal page.
|
|
473
|
+
page-id is Turath internal page, not printed page.`,
|
|
474
|
+
flags: [...GLOBAL_FLAGS, "book-id", "page-id"],
|
|
475
|
+
run: async ({ turath, format, positionals, flags }) => {
|
|
476
|
+
if (positionals.length) usage("get-page takes --book-id and --page-id flags only");
|
|
477
|
+
const bookId = parsePositiveId(requireFlag(flags, "book-id"), "book-id");
|
|
478
|
+
const pageId = parsePositiveId(requireFlag(flags, "page-id"), "page-id");
|
|
479
|
+
const page = await turath.getPage(bookId, pageId);
|
|
480
|
+
emit(format, toPassageRecord(page));
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
"get-context": {
|
|
484
|
+
help: `nusus get-context --book-id <id> --page-id <id>
|
|
485
|
+
[--pages-before N] [--pages-after N]
|
|
486
|
+
Fetch page plus surrounding pages.
|
|
487
|
+
--pages-before default 1, max 3
|
|
488
|
+
--pages-after default 1, max 3
|
|
489
|
+
page-id is Turath internal page, not printed page.`,
|
|
490
|
+
flags: [...GLOBAL_FLAGS, "book-id", "page-id", "pages-before", "pages-after"],
|
|
491
|
+
run: async ({ turath, format, positionals, flags }) => {
|
|
492
|
+
if (positionals.length) usage("get-context takes --book-id and --page-id flags only");
|
|
493
|
+
const bookId = parsePositiveId(requireFlag(flags, "book-id"), "book-id");
|
|
494
|
+
const pageId = parsePositiveId(requireFlag(flags, "page-id"), "page-id");
|
|
495
|
+
const pagesBefore = parseIntFlag(flags["pages-before"], "pages-before", { def: 1, min: 0, max: 3 });
|
|
496
|
+
const pagesAfter = parseIntFlag(flags["pages-after"], "pages-after", { def: 1, min: 0, max: 3 });
|
|
497
|
+
const center = await turath.getPage(bookId, pageId);
|
|
498
|
+
const ctx = await turath.getContext(center, { pagesBefore, pagesAfter });
|
|
499
|
+
emit(format, toPassageRecord(ctx));
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
"get-book": {
|
|
503
|
+
help: `nusus get-book <book-id>
|
|
504
|
+
Book metadata + TOC (normalized from indexes).`,
|
|
505
|
+
flags: [...GLOBAL_FLAGS],
|
|
506
|
+
run: async ({ turath, format, positionals }) => {
|
|
507
|
+
const bookId = takeIdArg(positionals, "get-book");
|
|
508
|
+
const book = await turath.getBook(bookId);
|
|
509
|
+
emit(format, toBookRecord(book));
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
"get-author": {
|
|
513
|
+
help: `nusus get-author <author-id>
|
|
514
|
+
Author metadata.`,
|
|
515
|
+
flags: [...GLOBAL_FLAGS],
|
|
516
|
+
run: async ({ turath, format, positionals }) => {
|
|
517
|
+
const authorId = takeIdArg(positionals, "get-author");
|
|
518
|
+
const author = await turath.getAuthor(authorId);
|
|
519
|
+
emit(format, toAuthorRecord(author));
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const GLOBAL_HELP = `nusus <command> [args] [options]
|
|
525
|
+
|
|
526
|
+
Commands:
|
|
527
|
+
find-books [query] Offline catalog book search (query optional with filters)
|
|
528
|
+
find-authors <query> Offline catalog author search
|
|
529
|
+
list-categories Offline category list
|
|
530
|
+
catalog Offline catalog metadata
|
|
531
|
+
search <query> Live Turath search (JSONL meta + passages)
|
|
532
|
+
retrieve <query> Live retrieve with citations/locators
|
|
533
|
+
get-page Fetch one internal page
|
|
534
|
+
get-context Fetch page plus surrounding pages
|
|
535
|
+
get-book <book-id> Book metadata + TOC
|
|
536
|
+
get-author <author-id> Author metadata
|
|
537
|
+
|
|
538
|
+
Global options:
|
|
539
|
+
--format jsonl|text Output format (default: jsonl)
|
|
540
|
+
--timeout <ms> Request timeout (default: ${DEFAULT_TIMEOUT}, 0 = no timeout, max ${MAX_TIMEOUT})
|
|
541
|
+
-h, --help Show help
|
|
542
|
+
-v, --version Print package version
|
|
543
|
+
|
|
544
|
+
Notes:
|
|
545
|
+
• page-id is Turath internal page (location.internalPage), not printed page.
|
|
546
|
+
• search/retrieve accept at most one --book-id, one --author-id, and one --category-id
|
|
547
|
+
(filters may be combined; multiple values of the same filter are rejected).
|
|
548
|
+
• find-books allows repeated --author-id/--category-id (offline catalog only).
|
|
549
|
+
• Errors are JSON on stderr; data only on stdout.
|
|
550
|
+
• Exit: 0 ok (incl. zero hits), 1 usage/invalid, 2 not found, 3 http/rate/abort/invalid-response/internal.
|
|
551
|
+
|
|
552
|
+
nusus help | nusus --help | nusus <command> --help
|
|
553
|
+
`;
|
|
554
|
+
|
|
555
|
+
const printHelp = (command) => {
|
|
556
|
+
if (command && COMMANDS[command]) {
|
|
557
|
+
process.stdout.write(`${COMMANDS[command].help}\n\nGlobal: --format jsonl|text --timeout <ms> --version\n`);
|
|
558
|
+
} else {
|
|
559
|
+
process.stdout.write(GLOBAL_HELP);
|
|
143
560
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const createClient = (timeout) => {
|
|
564
|
+
const options = { timeout };
|
|
565
|
+
// Test-only override used by CLI subprocess tests. Not a supported public config surface.
|
|
566
|
+
const baseUrl = process.env.NUSUS_TURATH_BASE_URL;
|
|
567
|
+
if (baseUrl) options.baseUrl = baseUrl;
|
|
568
|
+
return createTurathClient(options);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
const run = async () => {
|
|
572
|
+
const argv = process.argv.slice(2);
|
|
573
|
+
if (argv.length === 0) {
|
|
574
|
+
printHelp();
|
|
575
|
+
process.exit(0);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const { positionals, flags, multi } = parseArgs(argv);
|
|
579
|
+
|
|
580
|
+
if (flags.version) {
|
|
581
|
+
const extraFlags = Object.keys(flags).filter((k) => k !== "version" && k !== "help");
|
|
582
|
+
if (positionals.length || Object.keys(multi).length || extraFlags.length) {
|
|
583
|
+
usage("--version does not accept other arguments");
|
|
584
|
+
}
|
|
585
|
+
process.stdout.write(`${VERSION}\n`);
|
|
586
|
+
process.exit(0);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
let command = positionals[0];
|
|
590
|
+
let rest = positionals.slice(1);
|
|
591
|
+
|
|
592
|
+
if (flags.help && !command) {
|
|
593
|
+
printHelp();
|
|
594
|
+
process.exit(0);
|
|
595
|
+
}
|
|
596
|
+
if (command === "help") {
|
|
597
|
+
printHelp(rest[0]);
|
|
598
|
+
process.exit(0);
|
|
599
|
+
}
|
|
600
|
+
if (!command || !COMMANDS[command]) {
|
|
601
|
+
if (flags.help) {
|
|
602
|
+
printHelp();
|
|
603
|
+
process.exit(0);
|
|
604
|
+
}
|
|
605
|
+
usage(command ? `unknown command: ${command}` : "missing command");
|
|
606
|
+
}
|
|
607
|
+
if (flags.help) {
|
|
608
|
+
printHelp(command);
|
|
609
|
+
process.exit(0);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
const spec = COMMANDS[command];
|
|
613
|
+
const { flags: cmdFlags, multi: cmdMulti } = takeAllowed(flags, multi, spec.flags);
|
|
614
|
+
|
|
615
|
+
const formatRaw = optionalFlag(cmdFlags, "format") ?? "jsonl";
|
|
616
|
+
if (formatRaw !== "jsonl" && formatRaw !== "text") usage("--format must be jsonl or text");
|
|
617
|
+
delete cmdFlags.format;
|
|
618
|
+
|
|
619
|
+
const timeout = parseIntFlag(cmdFlags.timeout, "timeout", {
|
|
620
|
+
def: DEFAULT_TIMEOUT,
|
|
621
|
+
min: 0,
|
|
622
|
+
max: MAX_TIMEOUT,
|
|
623
|
+
});
|
|
624
|
+
delete cmdFlags.timeout;
|
|
625
|
+
|
|
626
|
+
const turath = createClient(timeout);
|
|
627
|
+
|
|
628
|
+
try {
|
|
629
|
+
await spec.run({
|
|
630
|
+
turath,
|
|
631
|
+
format: formatRaw,
|
|
632
|
+
positionals: rest,
|
|
633
|
+
flags: cmdFlags,
|
|
634
|
+
multi: cmdMulti,
|
|
635
|
+
});
|
|
636
|
+
} catch (error) {
|
|
637
|
+
if (error instanceof NususError) fail(error.code, error);
|
|
638
|
+
fail("INTERNAL_ERROR", { message: error?.message ?? String(error) });
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
await run();
|