@tavily/cli 0.0.0-stage → 1.0.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.
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ emit,
4
+ emitIfRequested,
5
+ footer,
6
+ invoke,
7
+ list,
8
+ readQueryArg,
9
+ resolveOutput
10
+ } from "./chunk-KQFC5TMG.js";
11
+ import "./chunk-U3B7BNFI.js";
12
+ import {
13
+ spinner,
14
+ withSpinner
15
+ } from "./chunk-K76OUMXM.js";
16
+ import {
17
+ isTerminal,
18
+ plain,
19
+ renderMarkdown,
20
+ sanitizeControl,
21
+ table
22
+ } from "./chunk-QHWWSCVM.js";
23
+ import "./chunk-AERAIFZU.js";
24
+ import {
25
+ resolveJson
26
+ } from "./chunk-IRCF6F3S.js";
27
+ import {
28
+ colors,
29
+ err,
30
+ out
31
+ } from "./chunk-E4N27QWM.js";
32
+ import {
33
+ messageOf
34
+ } from "./chunk-5JXZZSLS.js";
35
+
36
+ // src/commands/research.ts
37
+ import { readFileSync } from "fs";
38
+
39
+ // src/ui/render/research.ts
40
+ async function printResearchResult(data, opts) {
41
+ if (emitIfRequested(data, opts)) return;
42
+ const terminal = isTerminal(process.stdout);
43
+ const style = (paint, s) => terminal ? paint(s) : s;
44
+ const status2 = data.status ?? "unknown";
45
+ const content = data.content;
46
+ const sources = list(data.sources);
47
+ if (status2 !== "completed") {
48
+ out(`${style(colors.bold, "Status: ")}${sanitizeControl(status2)}`);
49
+ if (data.error) out(`${style(colors.pink, "Error: ")}${sanitizeControl(data.error)}`);
50
+ return;
51
+ }
52
+ if (!terminal) {
53
+ if (content) out(`${plain(content)}
54
+ `);
55
+ if (sources.length > 0) {
56
+ out("Sources:");
57
+ sources.forEach((s, i) => {
58
+ out(`${i + 1}. ${plain(s.title ?? "")} ${plain(s.url ?? "")}`);
59
+ });
60
+ }
61
+ return;
62
+ }
63
+ if (content) {
64
+ out();
65
+ out(` ${colors.bold(colors.aqua("Research Report"))}`);
66
+ out();
67
+ out(await renderMarkdown(String(content)));
68
+ }
69
+ if (sources.length > 0) {
70
+ out();
71
+ out(colors.bold(`Sources (${sources.length})`));
72
+ const rows = sources.map((s, i) => [
73
+ String(i + 1),
74
+ sanitizeControl(s.title ?? ""),
75
+ sanitizeControl(s.url ?? "")
76
+ ]);
77
+ out(table(["#", "Title", "URL"], rows));
78
+ }
79
+ footer(sources.length, "sources", data.response_time);
80
+ }
81
+
82
+ // src/commands/research.ts
83
+ var SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "status", "poll"]);
84
+ var NEED_ID = /* @__PURE__ */ new Set(["status", "poll"]);
85
+ function normalizeResearchArgs(args) {
86
+ const [first, next] = args;
87
+ if (first === void 0) return ["--help"];
88
+ if (first.startsWith("-")) return args;
89
+ if (!SUBCOMMANDS.has(first)) return ["run", ...args];
90
+ if (NEED_ID.has(first)) {
91
+ const hasPositional = next !== void 0 && !next.startsWith("-");
92
+ const hasHelp = args.includes("--help") || args.includes("-h");
93
+ if (!hasPositional && !hasHelp) return ["run", ...args];
94
+ }
95
+ return args;
96
+ }
97
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
98
+ var isDone = (r) => r.status === "completed" || r.status === "failed";
99
+ async function waitForResult(client, requestId, opts) {
100
+ const start = Date.now();
101
+ const deadline = start + opts.timeout * 1e3;
102
+ const message = () => colors.aqua(`${opts.label}... ${Math.floor((Date.now() - start) / 1e3)}s elapsed`);
103
+ const live = spinner(message(), !opts.json);
104
+ try {
105
+ let nextPoll = start;
106
+ while (Date.now() < deadline) {
107
+ live.update(message());
108
+ if (Date.now() >= nextPoll) {
109
+ const response = await client.getResearch(requestId);
110
+ if (isDone(response)) return response;
111
+ nextPoll = Date.now() + Math.max(1, opts.pollInterval) * 1e3;
112
+ }
113
+ await sleep(1e3);
114
+ }
115
+ return void 0;
116
+ } finally {
117
+ live.stop();
118
+ }
119
+ }
120
+ async function finishWait(response, requestId, opts, hint = "") {
121
+ const { json, outputFile } = opts;
122
+ if (response) return printResearchResult(response, { json, outputFile });
123
+ if (json) emit({ request_id: requestId, status: "timeout" }, { outputFile, pretty: false });
124
+ else err(colors.yellow(`Timed out after ${opts.timeout}s.${hint}`));
125
+ }
126
+ async function* streamEvents(payloads) {
127
+ for await (const payload of payloads) {
128
+ if (!payload.trim()) continue;
129
+ try {
130
+ yield JSON.parse(payload);
131
+ } catch {
132
+ }
133
+ }
134
+ }
135
+ async function renderStream(events, outputFile) {
136
+ const parts = [];
137
+ let sources = [];
138
+ let currentStep;
139
+ for await (const event of events) {
140
+ const delta = event?.choices?.[0]?.delta ?? {};
141
+ const toolCalls = delta.tool_calls ?? {};
142
+ if (toolCalls.type === "tool_call") {
143
+ for (const tc of toolCalls.tool_call ?? []) {
144
+ const name = tc.name ?? "";
145
+ const step = tc.arguments ? `${name}: ${tc.arguments}` : name;
146
+ if (step !== currentStep) {
147
+ currentStep = step;
148
+ err(` ${colors.dim(sanitizeControl(step))}`);
149
+ }
150
+ }
151
+ }
152
+ if (toolCalls.type === "tool_response") {
153
+ for (const tr of toolCalls.tool_response ?? []) {
154
+ const count = tr.sources?.length ?? 0;
155
+ if (tr.name === "WebSearch" && count) err(` ${colors.dim(`Found ${count} sources`)}`);
156
+ }
157
+ }
158
+ if (delta.content) parts.push(delta.content);
159
+ if (delta.sources?.length) sources = delta.sources;
160
+ }
161
+ await printResearchResult(
162
+ { status: "completed", content: parts.join(""), sources },
163
+ { json: false, outputFile }
164
+ );
165
+ }
166
+ async function run(query, opts, cmd, host) {
167
+ const json = resolveJson(cmd);
168
+ const input = readQueryArg(query, cmd);
169
+ const outputFile = resolveOutput(opts.output, "research", input);
170
+ const body = { input };
171
+ if (opts.model !== void 0) body.model = opts.model;
172
+ if (opts.outputSchema !== void 0) {
173
+ try {
174
+ body.output_schema = JSON.parse(readFileSync(opts.outputSchema, "utf8"));
175
+ } catch (e) {
176
+ cmd.error(`error: --output-schema must be a path to a JSON file: ${messageOf(e)}`);
177
+ }
178
+ }
179
+ if (opts.citationFormat !== void 0) body.citation_format = opts.citationFormat;
180
+ const call = { keyed: "research", clientName: opts.clientName };
181
+ if (opts.stream) {
182
+ await invoke(cmd, host, call, async (client) => {
183
+ const events = streamEvents(await client.researchStream(body));
184
+ if (!json) return renderStream(events, outputFile);
185
+ for await (const event of events) out(JSON.stringify(event));
186
+ });
187
+ return;
188
+ }
189
+ const render = await invoke(cmd, host, call, async (client) => {
190
+ const result = await withSpinner("Starting research...", !json, () => client.research(body));
191
+ if (isDone(result) || result.content) {
192
+ return () => printResearchResult(result, { json, outputFile });
193
+ }
194
+ const requestId = result.request_id;
195
+ if (typeof requestId !== "string" || !requestId) {
196
+ throw new Error(`Unexpected API response: ${JSON.stringify(result)}`);
197
+ }
198
+ if (opts.wait === false) {
199
+ const pending = { request_id: requestId, status: result.status ?? "pending" };
200
+ return () => emit(pending, { outputFile, pretty: false });
201
+ }
202
+ const response = await waitForResult(client, requestId, {
203
+ json,
204
+ pollInterval: opts.pollInterval,
205
+ timeout: opts.timeout,
206
+ label: "Researching"
207
+ });
208
+ const hint = ` Resume with: tvly research poll ${sanitizeControl(requestId)}`;
209
+ return () => finishWait(response, requestId, { json, outputFile, timeout: opts.timeout }, hint);
210
+ });
211
+ await render();
212
+ }
213
+ var STATUS_COLORS = {
214
+ completed: colors.green,
215
+ failed: colors.pink
216
+ };
217
+ async function status(requestId, opts, cmd, host) {
218
+ const json = resolveJson(cmd);
219
+ const response = await invoke(
220
+ cmd,
221
+ host,
222
+ { keyed: "research status", clientName: opts.clientName },
223
+ (client) => client.getResearch(requestId)
224
+ );
225
+ if (json) {
226
+ emit(response, { pretty: false });
227
+ return;
228
+ }
229
+ const s = String(response.status ?? "unknown");
230
+ const paint = STATUS_COLORS[s] ?? colors.yellow;
231
+ const safeId = sanitizeControl(requestId);
232
+ out(` ${colors.bold("Request:")} ${safeId}`);
233
+ out(` ${colors.bold("Status:")} ${paint(sanitizeControl(s))}`);
234
+ if (s === "completed")
235
+ out(` ${colors.dim(`Run 'tvly research poll ${safeId}' to view results.`)}`);
236
+ else if (s === "failed") {
237
+ out(` ${colors.pink("Error:")} ${sanitizeControl(response.error ?? "Unknown error")}`);
238
+ }
239
+ }
240
+ async function poll(requestId, opts, cmd, host) {
241
+ const json = resolveJson(cmd);
242
+ const outputFile = resolveOutput(opts.output, "research", requestId);
243
+ const label = `Polling research ${sanitizeControl(requestId.slice(0, 8))}`;
244
+ const response = await invoke(
245
+ cmd,
246
+ host,
247
+ { keyed: "research poll", clientName: opts.clientName },
248
+ (client) => waitForResult(client, requestId, {
249
+ json,
250
+ pollInterval: opts.pollInterval,
251
+ timeout: opts.timeout,
252
+ label
253
+ })
254
+ );
255
+ await finishWait(response, requestId, { json, outputFile, timeout: opts.timeout });
256
+ }
257
+ export {
258
+ normalizeResearchArgs,
259
+ poll,
260
+ run,
261
+ status
262
+ };
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ LIMIT_STATUSES,
4
+ QUERY_REQUIRED,
5
+ applySearchFlags,
6
+ emit,
7
+ footer,
8
+ invoke,
9
+ keylessCapError,
10
+ list,
11
+ printKeylessCapHit,
12
+ readQueryArg,
13
+ resolveOutput,
14
+ searchBodyFromOptions
15
+ } from "./chunk-KQFC5TMG.js";
16
+ import "./chunk-U3B7BNFI.js";
17
+ import "./chunk-K76OUMXM.js";
18
+ import {
19
+ isTerminal,
20
+ plain,
21
+ renderMarkdown,
22
+ safeLink,
23
+ sanitizeControl,
24
+ snippet
25
+ } from "./chunk-QHWWSCVM.js";
26
+ import "./chunk-AERAIFZU.js";
27
+ import {
28
+ addCommonOptions,
29
+ parseNumberArg,
30
+ resolveJson
31
+ } from "./chunk-IRCF6F3S.js";
32
+ import {
33
+ colors,
34
+ out
35
+ } from "./chunk-E4N27QWM.js";
36
+ import {
37
+ ExitError,
38
+ messageOf
39
+ } from "./chunk-5JXZZSLS.js";
40
+
41
+ // src/commands/search.ts
42
+ import { TavilyKeylessLimitError, TavilyRequestError } from "@tavily/core";
43
+ import { Option } from "commander";
44
+
45
+ // src/ui/render/search.ts
46
+ var imageUrl = (img) => typeof img === "object" && img !== null ? img.url ?? JSON.stringify(img) : img;
47
+ async function printSearchResults(data, opts) {
48
+ const maxChars = opts.maxChars ?? 600;
49
+ if (opts.json || opts.outputFile) {
50
+ emit(data, { outputFile: opts.outputFile, pretty: true });
51
+ if (opts.json || isTerminal(process.stdout)) return;
52
+ }
53
+ const results = list(data.results);
54
+ const images = list(data.images);
55
+ const answer = data.answer;
56
+ if (!isTerminal(process.stdout)) {
57
+ if (answer) out(`Answer: ${plain(answer)}
58
+ `);
59
+ results.forEach((r, i) => {
60
+ out(`${i + 1}. ${plain(r.title || "Untitled")}`);
61
+ out(plain(r.url ?? ""));
62
+ if (r.content) out(snippet(r.content, maxChars));
63
+ out();
64
+ });
65
+ if (results.length === 0) out("No results found.");
66
+ for (const img of images) out(`Image: ${plain(imageUrl(img))}`);
67
+ return;
68
+ }
69
+ if (answer) {
70
+ out();
71
+ out(` ${colors.bold(colors.aqua("Answer"))}`);
72
+ out();
73
+ out(await renderMarkdown(String(answer)));
74
+ out();
75
+ }
76
+ if (results.length === 0) {
77
+ out(colors.dim("No results found."));
78
+ return;
79
+ }
80
+ results.forEach((r, i) => {
81
+ const score = typeof r.score === "number" ? colors.dim(` score: ${r.score.toFixed(2)}`) : "";
82
+ out(
83
+ `${colors.bold(colors.purple(`${i + 1}. `))}${colors.bold(sanitizeControl(r.title ?? "Untitled"))} ${score}`
84
+ );
85
+ out(` ${colors.pink(safeLink(r.url ?? "", void 0, { stream: process.stdout }))}`);
86
+ if (r.content) out(` ${colors.dim(snippet(r.content, maxChars))}`);
87
+ out();
88
+ });
89
+ footer(results.length, "results", data.response_time);
90
+ if (images.length > 0) {
91
+ out();
92
+ out(colors.bold(`Images (${images.length}):`));
93
+ for (const img of images) out(` ${sanitizeControl(imageUrl(img))}`);
94
+ }
95
+ }
96
+ async function printMultiSearchResults(responses, opts) {
97
+ if (opts.json || opts.outputFile) {
98
+ emit(responses, { outputFile: opts.outputFile, pretty: true });
99
+ if (opts.json || isTerminal(process.stdout)) return;
100
+ }
101
+ const terminal = isTerminal(process.stdout);
102
+ for (const [i, r] of responses.entries()) {
103
+ const header = `=== [${i + 1}/${responses.length}] ${plain(r.query ?? "")}`;
104
+ out(terminal ? colors.bold(header) : header);
105
+ if ("error" in r) {
106
+ const error = r.error;
107
+ const text = typeof error === "object" && error !== null ? error.message : error;
108
+ out(`error: ${plain(text)}
109
+ `);
110
+ } else {
111
+ await printSearchResults(r, {
112
+ json: false,
113
+ ...opts.maxChars !== void 0 ? { maxChars: opts.maxChars } : {}
114
+ });
115
+ }
116
+ }
117
+ }
118
+
119
+ // src/commands/search.ts
120
+ var MAX_QUERIES = 5;
121
+ var DEFAULT_CHARS = 600;
122
+ function configure(cmd) {
123
+ applySearchFlags(cmd);
124
+ cmd.addOption(
125
+ new Option(
126
+ "--max-chars <n>",
127
+ "Characters of each result's content to print (default 600, shared across queries: 300 each for 2, 200 for 3); 0 prints it whole. --json and -o files keep it whole."
128
+ ).argParser((raw) => parseNumberArg(raw, { integer: true, minimum: 0 }))
129
+ );
130
+ addCommonOptions(cmd);
131
+ }
132
+ function readQueries(args, cmd) {
133
+ const queries = args.length === 1 && args[0] === "-" ? [readQueryArg("-", cmd)] : args.filter((q) => q.trim());
134
+ if (queries.length === 0) cmd.error(QUERY_REQUIRED);
135
+ if (queries.length > MAX_QUERIES) {
136
+ cmd.error(`error: At most ${MAX_QUERIES} queries per command, got ${queries.length}.`);
137
+ }
138
+ return queries;
139
+ }
140
+ async function run(args, opts, cmd, host) {
141
+ const json = resolveJson(cmd);
142
+ const queries = readQueries(args, cmd);
143
+ const outputFile = resolveOutput(opts.output, "search", queries[0]);
144
+ const maxChars = opts.maxChars ?? Math.floor(DEFAULT_CHARS / queries.length);
145
+ const body = searchBodyFromOptions(opts);
146
+ const call = { label: "Searching...", keyed: false, clientName: opts.clientName };
147
+ if (queries.length === 1) {
148
+ const response = await invoke(
149
+ cmd,
150
+ host,
151
+ call,
152
+ (client) => client.search({ query: queries[0], ...body })
153
+ );
154
+ await printSearchResults(response, { json, outputFile, maxChars });
155
+ return;
156
+ }
157
+ const capHits = [];
158
+ let limitHit = false;
159
+ const responses = await invoke(
160
+ cmd,
161
+ host,
162
+ call,
163
+ (client) => Promise.all(
164
+ queries.map(async (query) => {
165
+ try {
166
+ return await client.search({ query, ...body });
167
+ } catch (e) {
168
+ if (e instanceof TavilyKeylessLimitError) {
169
+ capHits.push(e);
170
+ return { query, error: keylessCapError(e) };
171
+ }
172
+ if (e instanceof TavilyRequestError && LIMIT_STATUSES.has(e.status)) limitHit = true;
173
+ return { query, error: messageOf(e) };
174
+ }
175
+ })
176
+ )
177
+ );
178
+ const [firstCap] = capHits;
179
+ if (firstCap && !json) {
180
+ printKeylessCapHit(firstCap);
181
+ throw new ExitError(3);
182
+ }
183
+ await printMultiSearchResults(responses, { json, outputFile, maxChars });
184
+ if (responses.every((r) => "error" in r)) {
185
+ throw new ExitError(capHits.length > 0 || limitHit ? 3 : 4);
186
+ }
187
+ }
188
+ export {
189
+ configure,
190
+ run
191
+ };
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AGENT_NAMES
4
+ } from "./chunk-HQ7QBZ5H.js";
5
+ import {
6
+ MCP_AUTH_NOTE,
7
+ detectAgents,
8
+ installMcp
9
+ } from "./chunk-ULNXDG4Q.js";
10
+ import {
11
+ sanitizeControl
12
+ } from "./chunk-QHWWSCVM.js";
13
+ import {
14
+ resolveJson
15
+ } from "./chunk-IRCF6F3S.js";
16
+ import {
17
+ colors,
18
+ out
19
+ } from "./chunk-E4N27QWM.js";
20
+ import {
21
+ ExitError
22
+ } from "./chunk-5JXZZSLS.js";
23
+
24
+ // src/commands/setup.ts
25
+ async function runMcp(opts, cmd, host) {
26
+ const json = resolveJson(cmd);
27
+ const requested = opts.agent ?? [];
28
+ const agents = requested.includes("all") ? [...AGENT_NAMES] : requested.length ? [...new Set(requested)] : detectAgents(host);
29
+ const result = await installMcp(agents, opts.keyless === true, host);
30
+ const ok = agents.length === 0 || result.configured.length > 0;
31
+ printMcp(result, ok, json);
32
+ if (!ok) throw new ExitError(1);
33
+ }
34
+ function printMcp(result, ok, json) {
35
+ if (json) {
36
+ out(JSON.stringify({ ok, ...result }));
37
+ return;
38
+ }
39
+ for (const agent of result.configured) {
40
+ out(` ${colors.green("\u2713")} ${agent}: Tavily MCP server added`);
41
+ }
42
+ for (const { agent, reason } of result.skipped) {
43
+ out(` ${colors.dim("\u2022")} ${agent}: skipped (${sanitizeControl(reason)})`);
44
+ }
45
+ if (result.configured.length) out(` ${colors.dim(MCP_AUTH_NOTE[result.auth])}`);
46
+ else if (!result.skipped.length) {
47
+ out(` ${colors.dim("No supported agents detected. Use --agent <name> or --agent all.")}`);
48
+ }
49
+ }
50
+ export {
51
+ runMcp
52
+ };
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ definedSnake,
4
+ echoPages,
5
+ emitIfRequested,
6
+ footer,
7
+ invoke,
8
+ list,
9
+ resolveOutput,
10
+ splitCsv
11
+ } from "./chunk-KQFC5TMG.js";
12
+ import "./chunk-U3B7BNFI.js";
13
+ import "./chunk-K76OUMXM.js";
14
+ import {
15
+ hostOf,
16
+ isTerminal,
17
+ plain,
18
+ safeLink,
19
+ sanitizeControl,
20
+ tree
21
+ } from "./chunk-QHWWSCVM.js";
22
+ import "./chunk-AERAIFZU.js";
23
+ import {
24
+ resolveJson
25
+ } from "./chunk-IRCF6F3S.js";
26
+ import {
27
+ colors,
28
+ err,
29
+ out
30
+ } from "./chunk-E4N27QWM.js";
31
+ import "./chunk-5JXZZSLS.js";
32
+
33
+ // src/ui/render/crawl.ts
34
+ import { mkdirSync, writeFileSync } from "fs";
35
+ import path from "path";
36
+ function slugOf(url) {
37
+ let location = url;
38
+ try {
39
+ const parsed = new URL(url);
40
+ location = parsed.host + parsed.pathname.replace(/\/+$/, "") + parsed.search;
41
+ } catch {
42
+ }
43
+ return location.replace(/[^\p{L}\p{N}_\-.]/gu, "_").replace(/^_+|_+$/g, "") || "index";
44
+ }
45
+ function saveCrawlToDir(data, dir) {
46
+ mkdirSync(dir, { recursive: true });
47
+ const used = /* @__PURE__ */ new Set();
48
+ for (const page of list(data.results)) {
49
+ if (!page.raw_content) continue;
50
+ const url = page.url ?? "";
51
+ const slug = slugOf(url);
52
+ let name = slug;
53
+ for (let n = 2; used.has(name.toLowerCase()); n++) name = `${slug}-${n}`;
54
+ used.add(name.toLowerCase());
55
+ writeFileSync(path.join(dir, `${name}.md`), `# ${url}
56
+
57
+ ${page.raw_content}
58
+ `);
59
+ }
60
+ err(`Saved ${used.size} pages to ${dir}/`);
61
+ }
62
+ function printCrawlResults(data, opts) {
63
+ if (opts.outputDir) {
64
+ saveCrawlToDir(data, opts.outputDir);
65
+ emitIfRequested(data, opts);
66
+ return;
67
+ }
68
+ if (emitIfRequested(data, opts)) return;
69
+ const pages = list(data.results);
70
+ if (!isTerminal(process.stdout)) {
71
+ echoPages(pages);
72
+ return;
73
+ }
74
+ const nodes = pages.map((page) => {
75
+ const url = page.url ?? "";
76
+ const raw = page.raw_content ?? "";
77
+ let pathname = url;
78
+ try {
79
+ pathname = new URL(url).pathname || "/";
80
+ } catch {
81
+ }
82
+ const label = `${colors.aqua(sanitizeControl(pathname))}${colors.dim(` (${raw.length.toLocaleString("en-US")} chars)`)}`;
83
+ const preview = raw.trim().split("\n")[0]?.slice(0, 120);
84
+ return { label, children: preview ? [colors.dim(sanitizeControl(preview))] : [] };
85
+ });
86
+ out(tree(colors.bold(sanitizeControl(data.base_url ?? "")), nodes));
87
+ footer(pages.length, "pages", data.response_time);
88
+ }
89
+ function printMapResults(data, opts) {
90
+ if (emitIfRequested(data, opts)) return;
91
+ const urls = list(data.results);
92
+ if (!isTerminal(process.stdout)) {
93
+ for (const url of urls) out(plain(url));
94
+ return;
95
+ }
96
+ out(
97
+ tree(
98
+ colors.bold(sanitizeControl(data.base_url ?? "")),
99
+ urls.map((url) => ({ label: safeLink(url, void 0, { stream: process.stdout }) }))
100
+ )
101
+ );
102
+ footer(urls.length, "URLs", data.response_time);
103
+ }
104
+
105
+ // src/commands/site.ts
106
+ function siteBody(url, opts) {
107
+ const body = {
108
+ url,
109
+ ...definedSnake(opts, ["maxDepth", "maxBreadth", "limit", "instructions"])
110
+ };
111
+ const lists = {
112
+ select_paths: splitCsv(opts.selectPaths),
113
+ exclude_paths: splitCsv(opts.excludePaths),
114
+ select_domains: splitCsv(opts.selectDomains),
115
+ exclude_domains: splitCsv(opts.excludeDomains)
116
+ };
117
+ for (const [key, value] of Object.entries(lists)) if (value) body[key] = value;
118
+ const allowExternal = opts.external === false ? false : opts.allowExternal;
119
+ if (allowExternal !== void 0) body.allow_external = allowExternal;
120
+ return { ...body, ...definedSnake(opts, ["timeout"]) };
121
+ }
122
+ async function runCrawl(url, opts, cmd, host) {
123
+ const json = resolveJson(cmd);
124
+ const outputFile = resolveOutput(opts.output, "crawl", hostOf(url));
125
+ const body = {
126
+ ...siteBody(url, opts),
127
+ ...definedSnake(opts, ["chunksPerSource", "extractDepth", "format", "includeImages"])
128
+ };
129
+ const response = await invoke(
130
+ cmd,
131
+ host,
132
+ { label: `Crawling ${url}...`, keyed: "crawl", clientName: opts.clientName },
133
+ (client) => client.crawl(body)
134
+ );
135
+ await printCrawlResults(response, { json, outputFile, outputDir: opts.outputDir });
136
+ }
137
+ async function runMap(url, opts, cmd, host) {
138
+ const json = resolveJson(cmd);
139
+ const outputFile = resolveOutput(opts.output, "map", hostOf(url));
140
+ const response = await invoke(
141
+ cmd,
142
+ host,
143
+ { label: `Mapping ${url}...`, keyed: "map", clientName: opts.clientName },
144
+ (client) => client.map(siteBody(url, opts))
145
+ );
146
+ await printMapResults(response, { json, outputFile });
147
+ }
148
+ export {
149
+ runCrawl,
150
+ runMap
151
+ };
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ VERSION
4
+ } from "./chunk-SPBOEASO.js";
5
+ import {
6
+ getApiKeyOrExit
7
+ } from "./chunk-U3B7BNFI.js";
8
+ import "./chunk-QHWWSCVM.js";
9
+ import {
10
+ authSource
11
+ } from "./chunk-AERAIFZU.js";
12
+ import {
13
+ colors,
14
+ out
15
+ } from "./chunk-E4N27QWM.js";
16
+ import "./chunk-5JXZZSLS.js";
17
+
18
+ // src/commands/status.ts
19
+ async function printStatus(json, host) {
20
+ const key = await getApiKeyOrExit(json, host);
21
+ if (json) {
22
+ out(JSON.stringify({ version: VERSION, authenticated: key !== null }));
23
+ return;
24
+ }
25
+ out(` ${colors.bold(colors.green("tavily"))} v${VERSION}`);
26
+ out();
27
+ if (key !== null) {
28
+ out(` ${colors.green(">")} Authenticated via ${authSource(key, host.env)}`);
29
+ } else {
30
+ out(` ${colors.pink(">")} Not authenticated`);
31
+ out(" Run: tvly login");
32
+ }
33
+ }
34
+ export {
35
+ printStatus
36
+ };