@askalf/deepdive 0.1.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 (64) hide show
  1. package/DISCLAIMER.md +158 -0
  2. package/LICENSE +21 -0
  3. package/README.md +140 -0
  4. package/dist/agent.d.ts +55 -0
  5. package/dist/agent.d.ts.map +1 -0
  6. package/dist/agent.js +100 -0
  7. package/dist/agent.js.map +1 -0
  8. package/dist/browser.d.ts +26 -0
  9. package/dist/browser.d.ts.map +1 -0
  10. package/dist/browser.js +75 -0
  11. package/dist/browser.js.map +1 -0
  12. package/dist/citations.d.ts +10 -0
  13. package/dist/citations.d.ts.map +1 -0
  14. package/dist/citations.js +25 -0
  15. package/dist/citations.js.map +1 -0
  16. package/dist/cli.d.ts +11 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +206 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/config.d.ts +26 -0
  21. package/dist/config.d.ts.map +1 -0
  22. package/dist/config.js +63 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/extract.d.ts +10 -0
  25. package/dist/extract.d.ts.map +1 -0
  26. package/dist/extract.js +52 -0
  27. package/dist/extract.js.map +1 -0
  28. package/dist/index.d.ts +10 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +11 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/llm.d.ts +19 -0
  33. package/dist/llm.d.ts.map +1 -0
  34. package/dist/llm.js +33 -0
  35. package/dist/llm.js.map +1 -0
  36. package/dist/plan.d.ts +8 -0
  37. package/dist/plan.d.ts.map +1 -0
  38. package/dist/plan.js +72 -0
  39. package/dist/plan.js.map +1 -0
  40. package/dist/search/brave.d.ts +8 -0
  41. package/dist/search/brave.d.ts.map +1 -0
  42. package/dist/search/brave.js +31 -0
  43. package/dist/search/brave.js.map +1 -0
  44. package/dist/search/duckduckgo.d.ts +7 -0
  45. package/dist/search/duckduckgo.d.ts.map +1 -0
  46. package/dist/search/duckduckgo.js +94 -0
  47. package/dist/search/duckduckgo.js.map +1 -0
  48. package/dist/search/searxng.d.ts +8 -0
  49. package/dist/search/searxng.d.ts.map +1 -0
  50. package/dist/search/searxng.js +29 -0
  51. package/dist/search/searxng.js.map +1 -0
  52. package/dist/search/tavily.d.ts +8 -0
  53. package/dist/search/tavily.d.ts.map +1 -0
  54. package/dist/search/tavily.js +38 -0
  55. package/dist/search/tavily.js.map +1 -0
  56. package/dist/search.d.ts +13 -0
  57. package/dist/search.d.ts.map +1 -0
  58. package/dist/search.js +47 -0
  59. package/dist/search.js.map +1 -0
  60. package/dist/synthesize.d.ts +8 -0
  61. package/dist/synthesize.d.ts.map +1 -0
  62. package/dist/synthesize.js +40 -0
  63. package/dist/synthesize.js.map +1 -0
  64. package/package.json +67 -0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { type CLIFlags } from "./config.js";
3
+ interface ParsedArgs {
4
+ question?: string;
5
+ outPath?: string;
6
+ flags: CLIFlags;
7
+ help: boolean;
8
+ }
9
+ export declare function parseArgs(argv: string[]): ParsedArgs;
10
+ export {};
11
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAaA,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AA+B3D,UAAU,UAAU;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAGD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAsEpD"}
package/dist/cli.js ADDED
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env node
2
+ // deepdive CLI entry.
3
+ //
4
+ // deepdive "how does claude's rate limiter work"
5
+ // deepdive "..." --model=claude-opus-4-7 --search=brave --out=report.md
6
+ // deepdive --help
7
+ //
8
+ // Prints the cited markdown report to stdout. Progress events go to stderr
9
+ // when --verbose is set or DEEPDIVE_VERBOSE=1.
10
+ import { writeFileSync } from "node:fs";
11
+ import { resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { resolveConfig } from "./config.js";
14
+ import { resolveSearchAdapter } from "./search.js";
15
+ import { runAgent } from "./agent.js";
16
+ const USAGE = `deepdive — local research agent
17
+
18
+ Usage:
19
+ deepdive "<question>" [flags]
20
+
21
+ Flags:
22
+ --base-url=<url> LLM endpoint. Default: http://localhost:3456 (dario)
23
+ --api-key=<key> LLM API key. Default: dario
24
+ --model=<name> Model to use. Default: claude-sonnet-4-6
25
+ --max-tokens=<n> Output max tokens per LLM call. Default: 4096
26
+ --search=<adapter> Search adapter: duckduckgo | searxng | brave | tavily
27
+ Default: duckduckgo (no key required)
28
+ --results-per-query=<n> Results per sub-query. Default: 5
29
+ --max-sources=<n> Total sources to fetch. Default: 12
30
+ --max-words-per-source=<n> Per-source content cap before synthesis. Default: 2000
31
+ --timeout-ms=<ms> Per-fetch timeout. Default: 30000
32
+ --out=<path> Write markdown to a file (also prints to stdout)
33
+ --verbose, -v Stream progress events to stderr
34
+ --help, -h Show this help
35
+
36
+ Environment:
37
+ DEEPDIVE_BASE_URL, DEEPDIVE_API_KEY, DEEPDIVE_MODEL, DEEPDIVE_SEARCH,
38
+ DEEPDIVE_SEARXNG_URL, DEEPDIVE_BRAVE_KEY, DEEPDIVE_TAVILY_KEY,
39
+ DEEPDIVE_MAX_SOURCES, DEEPDIVE_FETCH_TIMEOUT_MS, DEEPDIVE_HEADED,
40
+ DEEPDIVE_VERBOSE
41
+ `;
42
+ // Exported for unit tests.
43
+ export function parseArgs(argv) {
44
+ const flags = {};
45
+ let question;
46
+ let outPath;
47
+ let help = false;
48
+ for (const a of argv) {
49
+ if (a === "--help" || a === "-h") {
50
+ help = true;
51
+ continue;
52
+ }
53
+ if (a === "--verbose" || a === "-v") {
54
+ flags.verbose = true;
55
+ continue;
56
+ }
57
+ const m = /^--([a-z0-9-]+)=(.*)$/.exec(a);
58
+ if (m) {
59
+ const [, key, rawValue] = m;
60
+ const value = rawValue.trim();
61
+ switch (key) {
62
+ case "base-url":
63
+ flags.baseUrl = value;
64
+ break;
65
+ case "api-key":
66
+ flags.apiKey = value;
67
+ break;
68
+ case "model":
69
+ flags.model = value;
70
+ break;
71
+ case "max-tokens":
72
+ flags.maxTokens = parsePositiveInt(value);
73
+ break;
74
+ case "search":
75
+ flags.search = value.toLowerCase();
76
+ break;
77
+ case "results-per-query":
78
+ flags.resultsPerQuery = parsePositiveInt(value);
79
+ break;
80
+ case "max-sources":
81
+ flags.maxSources = parsePositiveInt(value);
82
+ break;
83
+ case "max-words-per-source":
84
+ flags.maxWordsPerSource = parsePositiveInt(value);
85
+ break;
86
+ case "timeout-ms":
87
+ flags.timeoutMs = parsePositiveInt(value);
88
+ break;
89
+ case "out":
90
+ outPath = value;
91
+ break;
92
+ default:
93
+ throw new Error(`unknown flag: --${key}`);
94
+ }
95
+ continue;
96
+ }
97
+ if (a.startsWith("--")) {
98
+ throw new Error(`flags must be in --key=value form (got: ${a}). See --help.`);
99
+ }
100
+ if (question === undefined) {
101
+ question = a;
102
+ continue;
103
+ }
104
+ throw new Error(`unexpected positional argument: ${JSON.stringify(a)}. Wrap the question in quotes.`);
105
+ }
106
+ return { question, outPath, flags, help };
107
+ }
108
+ function parsePositiveInt(s) {
109
+ if (!/^\d+$/.test(s))
110
+ return undefined;
111
+ const n = Number(s);
112
+ return Number.isFinite(n) && n > 0 ? n : undefined;
113
+ }
114
+ function renderEvent(e) {
115
+ switch (e.type) {
116
+ case "plan.start":
117
+ return ` plan planning sub-queries for: ${ellipsize(e.question, 60)}`;
118
+ case "plan.done":
119
+ return ` plan ${e.plan.queries.length} sub-queries`;
120
+ case "search.start":
121
+ return ` search ${e.query}`;
122
+ case "search.done":
123
+ return ` ${e.count} result${e.count === 1 ? "" : "s"}`;
124
+ case "fetch.start":
125
+ return ` fetch ${e.url}`;
126
+ case "fetch.done":
127
+ return ` ${e.ok ? "OK " : "!! "}${e.status} · ${e.words} words`;
128
+ case "synthesize.start":
129
+ return ` synth synthesizing from ${e.sourceCount} source${e.sourceCount === 1 ? "" : "s"}`;
130
+ case "synthesize.done":
131
+ return ` synth done`;
132
+ }
133
+ }
134
+ function ellipsize(s, max) {
135
+ return s.length <= max ? s : s.slice(0, max - 1) + "…";
136
+ }
137
+ async function main(argv) {
138
+ let parsed;
139
+ try {
140
+ parsed = parseArgs(argv);
141
+ }
142
+ catch (err) {
143
+ process.stderr.write(`deepdive: ${err.message}\n\n${USAGE}`);
144
+ return 2;
145
+ }
146
+ if (parsed.help) {
147
+ process.stdout.write(USAGE);
148
+ return 0;
149
+ }
150
+ if (!parsed.question) {
151
+ process.stderr.write(`deepdive: missing question.\n\n${USAGE}`);
152
+ return 2;
153
+ }
154
+ const config = resolveConfig(parsed.flags, process.env);
155
+ const search = await resolveSearchAdapter(config.searchAdapter, process.env);
156
+ const ac = new AbortController();
157
+ const sigint = () => ac.abort();
158
+ process.on("SIGINT", sigint);
159
+ process.on("SIGTERM", sigint);
160
+ try {
161
+ const result = await runAgent(parsed.question, {
162
+ llm: config.llm,
163
+ search,
164
+ browser: config.browser,
165
+ resultsPerQuery: config.resultsPerQuery,
166
+ maxSources: config.maxSources,
167
+ maxWordsPerSource: config.maxWordsPerSource,
168
+ onEvent: (e) => {
169
+ if (config.verbose)
170
+ process.stderr.write(renderEvent(e) + "\n");
171
+ },
172
+ }, ac.signal);
173
+ process.stdout.write(result.markdown);
174
+ if (!result.markdown.endsWith("\n"))
175
+ process.stdout.write("\n");
176
+ if (parsed.outPath) {
177
+ const path = resolve(parsed.outPath);
178
+ writeFileSync(path, result.markdown, "utf-8");
179
+ process.stderr.write(`\nwrote ${path}\n`);
180
+ }
181
+ return 0;
182
+ }
183
+ catch (err) {
184
+ process.stderr.write(`deepdive: ${err.message}\n`);
185
+ return 1;
186
+ }
187
+ finally {
188
+ process.off("SIGINT", sigint);
189
+ process.off("SIGTERM", sigint);
190
+ }
191
+ }
192
+ // Only run main() when invoked as a script, not when imported (e.g. by tests
193
+ // that exercise `parseArgs` in isolation).
194
+ const isEntryPoint = process.argv[1] !== undefined &&
195
+ (() => {
196
+ try {
197
+ return process.argv[1] === fileURLToPath(import.meta.url);
198
+ }
199
+ catch {
200
+ return false;
201
+ }
202
+ })();
203
+ if (isEntryPoint) {
204
+ main(process.argv.slice(2)).then((code) => process.exit(code));
205
+ }
206
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { LLMConfig } from "./llm.js";
2
+ import type { BrowserOptions } from "./browser.js";
3
+ export interface RuntimeConfig {
4
+ llm: LLMConfig;
5
+ browser: BrowserOptions;
6
+ searchAdapter: string;
7
+ resultsPerQuery: number;
8
+ maxSources: number;
9
+ maxWordsPerSource: number;
10
+ verbose: boolean;
11
+ }
12
+ export interface CLIFlags {
13
+ baseUrl?: string;
14
+ apiKey?: string;
15
+ model?: string;
16
+ maxTokens?: number;
17
+ search?: string;
18
+ resultsPerQuery?: number;
19
+ maxSources?: number;
20
+ maxWordsPerSource?: number;
21
+ timeoutMs?: number;
22
+ verbose?: boolean;
23
+ }
24
+ export declare function resolveConfig(flags: CLIFlags, env: Record<string, string | undefined>): RuntimeConfig;
25
+ export declare function parsePositiveInt(s: string | undefined): number | undefined;
26
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,SAAS,CAAC;IACf,OAAO,EAAE,cAAc,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAeD,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,aAAa,CA+Cf;AAGD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO1E"}
package/dist/config.js ADDED
@@ -0,0 +1,63 @@
1
+ // Config resolution — merges CLI flags, env vars, and defaults.
2
+ // No config file in v0.1.0: keep the surface small. Env vars are prefixed
3
+ // DEEPDIVE_* to avoid collisions with other tools in the user's shell.
4
+ const DEFAULTS = {
5
+ baseUrl: "http://localhost:3456",
6
+ apiKey: "dario",
7
+ model: "claude-sonnet-4-6",
8
+ maxTokens: 4096,
9
+ searchAdapter: "duckduckgo",
10
+ resultsPerQuery: 5,
11
+ maxSources: 12,
12
+ maxWordsPerSource: 2000,
13
+ timeoutMs: 30000,
14
+ maxBytesPerFetch: 2_000_000,
15
+ };
16
+ export function resolveConfig(flags, env) {
17
+ const baseUrl = flags.baseUrl ?? env.DEEPDIVE_BASE_URL ?? DEFAULTS.baseUrl;
18
+ const apiKey = flags.apiKey ?? env.DEEPDIVE_API_KEY ?? DEFAULTS.apiKey;
19
+ const model = flags.model ?? env.DEEPDIVE_MODEL ?? DEFAULTS.model;
20
+ const maxTokens = flags.maxTokens ??
21
+ parsePositiveInt(env.DEEPDIVE_MAX_TOKENS) ??
22
+ DEFAULTS.maxTokens;
23
+ const searchAdapter = flags.search ?? env.DEEPDIVE_SEARCH ?? DEFAULTS.searchAdapter;
24
+ const resultsPerQuery = flags.resultsPerQuery ??
25
+ parsePositiveInt(env.DEEPDIVE_RESULTS_PER_QUERY) ??
26
+ DEFAULTS.resultsPerQuery;
27
+ const maxSources = flags.maxSources ??
28
+ parsePositiveInt(env.DEEPDIVE_MAX_SOURCES) ??
29
+ DEFAULTS.maxSources;
30
+ const maxWordsPerSource = flags.maxWordsPerSource ??
31
+ parsePositiveInt(env.DEEPDIVE_MAX_WORDS_PER_SOURCE) ??
32
+ DEFAULTS.maxWordsPerSource;
33
+ const timeoutMs = flags.timeoutMs ??
34
+ parsePositiveInt(env.DEEPDIVE_FETCH_TIMEOUT_MS) ??
35
+ DEFAULTS.timeoutMs;
36
+ const verbose = flags.verbose ?? env.DEEPDIVE_VERBOSE === "1";
37
+ return {
38
+ llm: { baseUrl, apiKey, model, maxTokens },
39
+ browser: {
40
+ headless: env.DEEPDIVE_HEADED === "1" ? false : true,
41
+ timeoutMs,
42
+ maxBytes: DEFAULTS.maxBytesPerFetch,
43
+ },
44
+ searchAdapter,
45
+ resultsPerQuery,
46
+ maxSources,
47
+ maxWordsPerSource,
48
+ verbose,
49
+ };
50
+ }
51
+ // Exported for unit tests.
52
+ export function parsePositiveInt(s) {
53
+ if (!s)
54
+ return undefined;
55
+ const trimmed = s.trim();
56
+ if (!/^\d+$/.test(trimmed))
57
+ return undefined;
58
+ const n = Number(trimmed);
59
+ if (!Number.isFinite(n) || n <= 0)
60
+ return undefined;
61
+ return n;
62
+ }
63
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,0EAA0E;AAC1E,uEAAuE;AA4BvE,MAAM,QAAQ,GAAG;IACf,OAAO,EAAE,uBAAuB;IAChC,MAAM,EAAE,OAAO;IACf,KAAK,EAAE,mBAAmB;IAC1B,SAAS,EAAE,IAAI;IACf,aAAa,EAAE,YAAY;IAC3B,eAAe,EAAE,CAAC;IAClB,UAAU,EAAE,EAAE;IACd,iBAAiB,EAAE,IAAI;IACvB,SAAS,EAAE,KAAK;IAChB,gBAAgB,EAAE,SAAS;CAC5B,CAAC;AAEF,MAAM,UAAU,aAAa,CAC3B,KAAe,EACf,GAAuC;IAEvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,iBAAiB,IAAI,QAAQ,CAAC,OAAO,CAAC;IAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,gBAAgB,IAAI,QAAQ,CAAC,MAAM,CAAC;IACvE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,cAAc,IAAI,QAAQ,CAAC,KAAK,CAAC;IAClE,MAAM,SAAS,GACb,KAAK,CAAC,SAAS;QACf,gBAAgB,CAAC,GAAG,CAAC,mBAAmB,CAAC;QACzC,QAAQ,CAAC,SAAS,CAAC;IAErB,MAAM,aAAa,GACjB,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,eAAe,IAAI,QAAQ,CAAC,aAAa,CAAC;IAEhE,MAAM,eAAe,GACnB,KAAK,CAAC,eAAe;QACrB,gBAAgB,CAAC,GAAG,CAAC,0BAA0B,CAAC;QAChD,QAAQ,CAAC,eAAe,CAAC;IAE3B,MAAM,UAAU,GACd,KAAK,CAAC,UAAU;QAChB,gBAAgB,CAAC,GAAG,CAAC,oBAAoB,CAAC;QAC1C,QAAQ,CAAC,UAAU,CAAC;IAEtB,MAAM,iBAAiB,GACrB,KAAK,CAAC,iBAAiB;QACvB,gBAAgB,CAAC,GAAG,CAAC,6BAA6B,CAAC;QACnD,QAAQ,CAAC,iBAAiB,CAAC;IAE7B,MAAM,SAAS,GACb,KAAK,CAAC,SAAS;QACf,gBAAgB,CAAC,GAAG,CAAC,yBAAyB,CAAC;QAC/C,QAAQ,CAAC,SAAS,CAAC;IAErB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,GAAG,CAAC,gBAAgB,KAAK,GAAG,CAAC;IAE9D,OAAO;QACL,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE;QAC1C,OAAO,EAAE;YACP,QAAQ,EAAE,GAAG,CAAC,eAAe,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YACpD,SAAS;YACT,QAAQ,EAAE,QAAQ,CAAC,gBAAgB;SACpC;QACD,aAAa;QACb,eAAe;QACf,UAAU;QACV,iBAAiB;QACjB,OAAO;KACR,CAAC;AACJ,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,gBAAgB,CAAC,CAAqB;IACpD,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACpD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface ExtractedContent {
2
+ title: string;
3
+ text: string;
4
+ wordCount: number;
5
+ truncated: boolean;
6
+ }
7
+ export declare function extractContent(rawText: string, rawTitle: string, maxWords: number): ExtractedContent;
8
+ export declare function normalizeWhitespace(s: string): string;
9
+ export declare function isMeaningfulParagraph(p: string): boolean;
10
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAoBlB;AAGD,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CASrD;AAGD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAcxD"}
@@ -0,0 +1,52 @@
1
+ // Lightweight content extraction — strips boilerplate (nav, footer, scripts),
2
+ // keeps main content. Intentionally hand-rolled so deepdive has zero
3
+ // extraction dependencies. For quality-sensitive use cases, swap in
4
+ // @mozilla/readability via a custom extractor.
5
+ export function extractContent(rawText, rawTitle, maxWords) {
6
+ const normalized = normalizeWhitespace(rawText);
7
+ const paragraphs = normalized
8
+ .split(/\n{2,}|\r\n{2,}|(?:\r?\n\s*){2,}/g)
9
+ .map((p) => p.trim())
10
+ .filter((p) => p.length > 0);
11
+ const meaningful = paragraphs.filter(isMeaningfulParagraph);
12
+ const joined = meaningful.join("\n\n");
13
+ const words = joined.split(/\s+/).filter(Boolean);
14
+ const truncated = words.length > maxWords;
15
+ const clamped = truncated ? words.slice(0, maxWords).join(" ") + " …" : joined;
16
+ return {
17
+ title: rawTitle.trim(),
18
+ text: clamped,
19
+ wordCount: truncated ? maxWords : words.length,
20
+ truncated,
21
+ };
22
+ }
23
+ // Exported for unit tests.
24
+ export function normalizeWhitespace(s) {
25
+ return s
26
+ .replace(/\r\n/g, "\n")
27
+ .replace(/\t/g, " ")
28
+ .replace(/\u00a0/g, " ")
29
+ .replace(/[ \f\v]+/g, " ")
30
+ .replace(/ *\n */g, "\n")
31
+ .replace(/\n{3,}/g, "\n\n")
32
+ .trim();
33
+ }
34
+ // Exported for unit tests.
35
+ export function isMeaningfulParagraph(p) {
36
+ if (p.length < 40)
37
+ return false;
38
+ const words = p.split(/\s+/).filter(Boolean);
39
+ if (words.length < 8)
40
+ return false;
41
+ // Drop lines that are mostly links or UI chrome.
42
+ const linkyRatio = (p.match(/https?:\/\/|www\./g)?.length ?? 0) /
43
+ Math.max(1, words.length / 10);
44
+ if (linkyRatio > 1)
45
+ return false;
46
+ // Drop cookie banners and nav lists.
47
+ const chromeRe = /\b(cookie|cookies|privacy|sign in|sign up|log in|subscribe now|menu|navigation|skip to content)\b/i;
48
+ if (chromeRe.test(p) && words.length < 25)
49
+ return false;
50
+ return true;
51
+ }
52
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,qEAAqE;AACrE,oEAAoE;AACpE,+CAA+C;AAS/C,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,QAAgB,EAChB,QAAgB;IAEhB,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,UAAU;SAC1B,KAAK,CAAC,mCAAmC,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE/B,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;IAC1C,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;IAE/E,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE;QACtB,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM;QAC9C,SAAS;KACV,CAAC;AACJ,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,OAAO,CAAC;SACL,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;SACtB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC;SACzB,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC;SACxB,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,qBAAqB,CAAC,CAAS;IAC7C,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IAChC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,iDAAiD;IACjD,MAAM,UAAU,GACd,CAAC,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACjC,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,qCAAqC;IACrC,MAAM,QAAQ,GACZ,oGAAoG,CAAC;IACvG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { runAgent, type AgentConfig, type AgentEvent, type AgentResult } from "./agent.js";
2
+ export { planQueries, parsePlan, type Plan } from "./plan.js";
3
+ export { resolveSearchAdapter, dedupeByUrl, type SearchAdapter, type SearchResult, } from "./search.js";
4
+ export { BrowserSession, type BrowserOptions, type FetchedPage } from "./browser.js";
5
+ export { extractContent, type ExtractedContent } from "./extract.js";
6
+ export { synthesize, type SourceWithContent } from "./synthesize.js";
7
+ export { buildSourceTable, renderAnswerMarkdown, renderSourcesMarkdown, type Source, } from "./citations.js";
8
+ export { callLLM, type LLMConfig, type LLMMessage, type LLMResult } from "./llm.js";
9
+ export { resolveConfig, type RuntimeConfig, type CLIFlags } from "./config.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,MAAM,GACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // Library exports for programmatic use.
2
+ export { runAgent } from "./agent.js";
3
+ export { planQueries, parsePlan } from "./plan.js";
4
+ export { resolveSearchAdapter, dedupeByUrl, } from "./search.js";
5
+ export { BrowserSession } from "./browser.js";
6
+ export { extractContent } from "./extract.js";
7
+ export { synthesize } from "./synthesize.js";
8
+ export { buildSourceTable, renderAnswerMarkdown, renderSourcesMarkdown, } from "./citations.js";
9
+ export { callLLM } from "./llm.js";
10
+ export { resolveConfig } from "./config.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,OAAO,EAAE,QAAQ,EAAuD,MAAM,YAAY,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAE,SAAS,EAAa,MAAM,WAAW,CAAC;AAC9D,OAAO,EACL,oBAAoB,EACpB,WAAW,GAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAyC,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,cAAc,EAAyB,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,OAAO,EAAmD,MAAM,UAAU,CAAC;AACpF,OAAO,EAAE,aAAa,EAAqC,MAAM,aAAa,CAAC"}
package/dist/llm.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ export interface LLMConfig {
2
+ baseUrl: string;
3
+ apiKey: string;
4
+ model: string;
5
+ maxTokens: number;
6
+ }
7
+ export interface LLMMessage {
8
+ role: "user" | "assistant";
9
+ content: string;
10
+ }
11
+ export interface LLMResult {
12
+ text: string;
13
+ usage?: {
14
+ input_tokens: number;
15
+ output_tokens: number;
16
+ };
17
+ }
18
+ export declare function callLLM(messages: LLMMessage[], system: string, config: LLMConfig, signal?: AbortSignal): Promise<LLMResult>;
19
+ //# sourceMappingURL=llm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACzD;AAED,wBAAsB,OAAO,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,SAAS,CAAC,CAoCpB"}
package/dist/llm.js ADDED
@@ -0,0 +1,33 @@
1
+ // Minimal Anthropic Messages-compatible client.
2
+ // Default target is dario at http://localhost:3456, but any Anthropic-compat
3
+ // endpoint works. The only header dario cares about is Authorization / x-api-key.
4
+ export async function callLLM(messages, system, config, signal) {
5
+ const url = `${config.baseUrl.replace(/\/+$/, "")}/v1/messages`;
6
+ const body = {
7
+ model: config.model,
8
+ max_tokens: config.maxTokens,
9
+ system,
10
+ messages,
11
+ };
12
+ const res = await fetch(url, {
13
+ method: "POST",
14
+ headers: {
15
+ "content-type": "application/json",
16
+ "anthropic-version": "2023-06-01",
17
+ "x-api-key": config.apiKey,
18
+ },
19
+ body: JSON.stringify(body),
20
+ signal,
21
+ });
22
+ if (!res.ok) {
23
+ const detail = await res.text().catch(() => "");
24
+ throw new Error(`LLM ${res.status} ${res.statusText}: ${detail.slice(0, 500)}`);
25
+ }
26
+ const json = (await res.json());
27
+ const text = json.content
28
+ .filter((b) => b.type === "text" && typeof b.text === "string")
29
+ .map((b) => b.text)
30
+ .join("");
31
+ return { text, usage: json.usage };
32
+ }
33
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,6EAA6E;AAC7E,kFAAkF;AAmBlF,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,QAAsB,EACtB,MAAc,EACd,MAAiB,EACjB,MAAoB;IAEpB,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC;IAChE,MAAM,IAAI,GAAG;QACX,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,MAAM,CAAC,SAAS;QAC5B,MAAM;QACN,QAAQ;KACT,CAAC;IAEF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,mBAAmB,EAAE,YAAY;YACjC,WAAW,EAAE,MAAM,CAAC,MAAM;SAC3B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QAC1B,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAG7B,CAAC;IAEF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO;SACtB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;SAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAK,CAAC;SACnB,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;AACrC,CAAC"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { type LLMConfig } from "./llm.js";
2
+ export interface Plan {
3
+ queries: string[];
4
+ reasoning: string;
5
+ }
6
+ export declare function planQueries(question: string, config: LLMConfig, signal?: AbortSignal): Promise<Plan>;
7
+ export declare function parsePlan(raw: string): Plan;
8
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAaD,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAQf;AAGD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAiB3C"}
package/dist/plan.js ADDED
@@ -0,0 +1,72 @@
1
+ // Query planning — decompose a user question into 3–5 sub-queries suitable
2
+ // for web search. Pure over its inputs; wraps one LLM call.
3
+ import { callLLM } from "./llm.js";
4
+ const PLANNER_SYSTEM = `You are a research planner. Given a user's question, produce 3-5 specific, searchable sub-queries that together will gather the evidence needed to answer it well.
5
+
6
+ Rules:
7
+ - Each sub-query must be a concrete phrase someone would type into a search engine.
8
+ - Cover distinct facets of the question (definition, how it works, pros/cons, recent changes, alternatives, etc.) — do not list near-duplicates.
9
+ - Prefer specific terminology over vague phrasing.
10
+ - Do not include the user's exact question verbatim unless it is already search-engine-shaped.
11
+
12
+ Output FORMAT (strict): one JSON object, no prose before or after, matching:
13
+ {"reasoning": "<1-2 sentences on your decomposition>", "queries": ["q1", "q2", ...]}`;
14
+ export async function planQueries(question, config, signal) {
15
+ const { text } = await callLLM([{ role: "user", content: question }], PLANNER_SYSTEM, config, signal);
16
+ return parsePlan(text);
17
+ }
18
+ // Exported for unit tests.
19
+ export function parsePlan(raw) {
20
+ const json = extractFirstJsonObject(raw);
21
+ if (!json)
22
+ throw new Error(`planner did not return JSON: ${raw.slice(0, 200)}`);
23
+ const parsed = JSON.parse(json);
24
+ if (!Array.isArray(parsed.queries) || parsed.queries.length === 0) {
25
+ throw new Error(`planner returned no queries: ${raw.slice(0, 200)}`);
26
+ }
27
+ const queries = parsed.queries
28
+ .filter((q) => typeof q === "string")
29
+ .map((q) => q.trim())
30
+ .filter((q) => q.length > 0)
31
+ .slice(0, 8);
32
+ if (queries.length === 0)
33
+ throw new Error("planner queries were all empty");
34
+ return {
35
+ queries,
36
+ reasoning: typeof parsed.reasoning === "string" ? parsed.reasoning : "",
37
+ };
38
+ }
39
+ function extractFirstJsonObject(s) {
40
+ const start = s.indexOf("{");
41
+ if (start === -1)
42
+ return null;
43
+ let depth = 0;
44
+ let inString = false;
45
+ let escape = false;
46
+ for (let i = start; i < s.length; i++) {
47
+ const c = s[i];
48
+ if (escape) {
49
+ escape = false;
50
+ continue;
51
+ }
52
+ if (c === "\\" && inString) {
53
+ escape = true;
54
+ continue;
55
+ }
56
+ if (c === '"') {
57
+ inString = !inString;
58
+ continue;
59
+ }
60
+ if (inString)
61
+ continue;
62
+ if (c === "{")
63
+ depth++;
64
+ else if (c === "}") {
65
+ depth--;
66
+ if (depth === 0)
67
+ return s.slice(start, i + 1);
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { SearchAdapter, SearchResult } from "../search.js";
2
+ export declare class BraveSearch implements SearchAdapter {
3
+ private readonly key;
4
+ readonly name = "brave";
5
+ constructor(key: string);
6
+ search(query: string, limit: number, signal?: AbortSignal): Promise<SearchResult[]>;
7
+ }
8
+ //# sourceMappingURL=brave.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brave.d.ts","sourceRoot":"","sources":["../../src/search/brave.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEhE,qBAAa,WAAY,YAAW,aAAa;IAEnC,OAAO,CAAC,QAAQ,CAAC,GAAG;IADhC,QAAQ,CAAC,IAAI,WAAW;gBACK,GAAG,EAAE,MAAM;IAElC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAuB1F"}
@@ -0,0 +1,31 @@
1
+ // Brave Search API adapter. Requires DEEPDIVE_BRAVE_KEY.
2
+ export class BraveSearch {
3
+ key;
4
+ name = "brave";
5
+ constructor(key) {
6
+ this.key = key;
7
+ }
8
+ async search(query, limit, signal) {
9
+ const url = new URL("https://api.search.brave.com/res/v1/web/search");
10
+ url.searchParams.set("q", query);
11
+ url.searchParams.set("count", String(Math.min(limit, 20)));
12
+ const res = await fetch(url, {
13
+ headers: {
14
+ accept: "application/json",
15
+ "x-subscription-token": this.key,
16
+ },
17
+ signal,
18
+ });
19
+ if (!res.ok)
20
+ throw new Error(`brave ${res.status} ${res.statusText}`);
21
+ const json = (await res.json());
22
+ const items = json.web?.results ?? [];
23
+ return items.map((r, i) => ({
24
+ url: r.url,
25
+ title: r.title ?? "",
26
+ snippet: r.description ?? "",
27
+ rank: i + 1,
28
+ }));
29
+ }
30
+ }
31
+ //# sourceMappingURL=brave.js.map