webpeel 0.18.7 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3996,6 +3996,88 @@ program
3996
3996
  }
3997
3997
  });
3998
3998
  // ============================================================
3999
+ // answer command — LLM-free web Q&A (search + fetch + BM25)
4000
+ // ============================================================
4001
+ program
4002
+ .command('webask <question>')
4003
+ .alias('ask-web')
4004
+ .description('Search the web and get a direct answer (no LLM key required)')
4005
+ .option('-n, --sources <n>', 'Number of sources to check (1-5, default 3)', '3')
4006
+ .option('--json', 'Output as JSON')
4007
+ .option('-s, --silent', 'Silent mode')
4008
+ .action(async (question, options) => {
4009
+ const isJson = !!options.json;
4010
+ const isSilent = !!options.silent;
4011
+ const numSources = Math.min(Math.max(parseInt(options.sources) || 3, 1), 5);
4012
+ const askCfg = loadConfig();
4013
+ const askApiKey = askCfg.apiKey || process.env.WEBPEEL_API_KEY;
4014
+ const askApiUrl = process.env.WEBPEEL_API_URL || 'https://api.webpeel.dev';
4015
+ if (!askApiKey) {
4016
+ console.error('No API key configured. Run: webpeel auth <your-key>');
4017
+ console.error('Get a free key at: https://app.webpeel.dev/keys');
4018
+ process.exit(2);
4019
+ }
4020
+ let spinner = null;
4021
+ if (!isSilent && !isJson) {
4022
+ const { default: ora } = await import('ora');
4023
+ spinner = ora(`Searching for: ${question}`).start();
4024
+ }
4025
+ try {
4026
+ const params = new URLSearchParams({ q: question, sources: String(numSources) });
4027
+ const res = await fetch(`${askApiUrl}/v1/ask?${params}`, {
4028
+ headers: { Authorization: `Bearer ${askApiKey}` },
4029
+ signal: AbortSignal.timeout(60000),
4030
+ });
4031
+ if (res.status === 401) {
4032
+ if (spinner)
4033
+ spinner.fail('API key invalid or expired. Run: webpeel auth <new-key>');
4034
+ process.exit(2);
4035
+ }
4036
+ if (res.status === 404) {
4037
+ if (spinner)
4038
+ spinner.fail('Ask endpoint not available on this server version');
4039
+ process.exit(1);
4040
+ }
4041
+ if (!res.ok) {
4042
+ const body = await res.text().catch(() => '');
4043
+ if (spinner)
4044
+ spinner.fail(`API error ${res.status}: ${body.slice(0, 100)}`);
4045
+ process.exit(1);
4046
+ }
4047
+ const data = await res.json();
4048
+ if (spinner) {
4049
+ if (data.answer) {
4050
+ spinner.succeed(`Found answer (confidence: ${Math.round((data.confidence || 0) * 100)}%)`);
4051
+ }
4052
+ else {
4053
+ spinner.warn('No confident answer found');
4054
+ }
4055
+ }
4056
+ if (isJson) {
4057
+ console.log(JSON.stringify(data, null, 2));
4058
+ }
4059
+ else {
4060
+ if (data.answer) {
4061
+ console.log('\n' + data.answer);
4062
+ if (data.sources?.length && !isSilent) {
4063
+ console.log('\nSources:');
4064
+ data.sources.slice(0, 3).forEach((s) => console.log(` • ${s.title || s.url} — ${s.url}`));
4065
+ }
4066
+ }
4067
+ else {
4068
+ console.log('\nNo confident answer found for:', question);
4069
+ }
4070
+ if (data.elapsed && !isSilent)
4071
+ console.log(`\n⚡ ${data.elapsed}ms`);
4072
+ }
4073
+ }
4074
+ catch (err) {
4075
+ if (spinner)
4076
+ spinner.fail(err.message);
4077
+ process.exit(1);
4078
+ }
4079
+ });
4080
+ // ============================================================
3999
4081
  // research command — autonomous multi-step web research
4000
4082
  // ============================================================
4001
4083
  program