webpeel 0.18.3 → 0.18.4

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
@@ -1971,6 +1971,163 @@ program
1971
1971
  process.exit(1);
1972
1972
  }
1973
1973
  });
1974
+ // auth command — set and verify API key in one step
1975
+ program
1976
+ .command('auth [key]')
1977
+ .description('Set and verify your WebPeel API key')
1978
+ .option('--json', 'Output as JSON')
1979
+ .action(async (key, opts) => {
1980
+ const config = loadConfig();
1981
+ // If no key provided, show current auth status (or error if not set)
1982
+ if (!key) {
1983
+ const currentKey = config.apiKey;
1984
+ if (!currentKey) {
1985
+ if (opts.json) {
1986
+ console.log(JSON.stringify({ authenticated: false, error: 'No API key set. Run: webpeel auth <key>' }));
1987
+ }
1988
+ else {
1989
+ console.error('No API key set. Run: webpeel auth <your-key>');
1990
+ console.error('Get a free key at: https://app.webpeel.dev/keys');
1991
+ }
1992
+ process.exit(2);
1993
+ }
1994
+ // Fall through to verify current key
1995
+ key = currentKey;
1996
+ }
1997
+ // Save the key first
1998
+ config.apiKey = key;
1999
+ saveConfig(config);
2000
+ // Verify by calling the API
2001
+ const apiUrl = (process.env.WEBPEEL_API_URL || 'https://api.webpeel.dev');
2002
+ try {
2003
+ const res = await fetch(`${apiUrl}/v1/usage`, {
2004
+ headers: { Authorization: `Bearer ${key}` },
2005
+ signal: AbortSignal.timeout(8000),
2006
+ });
2007
+ if (res.status === 401) {
2008
+ if (opts.json) {
2009
+ console.log(JSON.stringify({ authenticated: false, error: 'Invalid API key' }));
2010
+ }
2011
+ else {
2012
+ console.error('❌ Invalid API key. Get a valid key at: https://app.webpeel.dev/keys');
2013
+ }
2014
+ // Revert the key save
2015
+ config.apiKey = undefined;
2016
+ saveConfig(config);
2017
+ process.exit(2);
2018
+ }
2019
+ if (res.ok) {
2020
+ const data = await res.json();
2021
+ const plan = data.tier || (typeof data.plan === 'string' ? data.plan : data.plan?.tier) || 'free';
2022
+ const used = data.used ?? data.totalRequests ?? data.weekly?.used ?? 0;
2023
+ const limit = data.limit ?? data.weeklyLimit ?? data.weekly?.limit ?? 500;
2024
+ const remaining = limit - used;
2025
+ if (opts.json) {
2026
+ console.log(JSON.stringify({
2027
+ authenticated: true,
2028
+ plan,
2029
+ used,
2030
+ limit,
2031
+ remaining,
2032
+ keyPrefix: key.slice(0, 12) + '...',
2033
+ }));
2034
+ }
2035
+ else {
2036
+ console.log(`✅ API key verified`);
2037
+ console.log(` Plan: ${plan}`);
2038
+ console.log(` Usage: ${used} / ${limit} this week (${remaining} remaining)`);
2039
+ console.log(` Key: ${key.slice(0, 12)}...`);
2040
+ }
2041
+ process.exit(0);
2042
+ }
2043
+ // Non-200 non-401 — still save key but warn
2044
+ if (opts.json) {
2045
+ console.log(JSON.stringify({ authenticated: 'unknown', warning: `API returned ${res.status}` }));
2046
+ }
2047
+ else {
2048
+ console.log(`⚠️ Key saved but couldn't verify (API returned ${res.status})`);
2049
+ }
2050
+ }
2051
+ catch (e) {
2052
+ if (opts.json) {
2053
+ console.log(JSON.stringify({ authenticated: 'unknown', warning: 'Network error', error: e.message }));
2054
+ }
2055
+ else {
2056
+ console.log(`⚠️ Key saved but couldn't verify (network error: ${e.message})`);
2057
+ }
2058
+ }
2059
+ });
2060
+ // status command — check auth status and API health
2061
+ program
2062
+ .command('status')
2063
+ .description('Check authentication status and API usage')
2064
+ .option('--json', 'Output as JSON')
2065
+ .action(async (opts) => {
2066
+ const config = loadConfig();
2067
+ const key = config.apiKey;
2068
+ if (!key) {
2069
+ if (opts.json) {
2070
+ console.log(JSON.stringify({ authenticated: false, error: 'No API key configured' }));
2071
+ }
2072
+ else {
2073
+ console.error('Not authenticated. Run: webpeel auth <your-key>');
2074
+ console.error('Get a free key at: https://app.webpeel.dev/keys');
2075
+ }
2076
+ process.exit(2);
2077
+ }
2078
+ const apiUrl = (process.env.WEBPEEL_API_URL || 'https://api.webpeel.dev');
2079
+ try {
2080
+ const [healthRes, usageRes] = await Promise.all([
2081
+ fetch(`${apiUrl}/health`, { signal: AbortSignal.timeout(5000) }).catch(() => null),
2082
+ fetch(`${apiUrl}/v1/usage`, {
2083
+ headers: { Authorization: `Bearer ${key}` },
2084
+ signal: AbortSignal.timeout(8000),
2085
+ }),
2086
+ ]);
2087
+ const apiOnline = healthRes?.ok ?? false;
2088
+ if (usageRes.status === 401) {
2089
+ if (opts.json) {
2090
+ console.log(JSON.stringify({ authenticated: false, apiOnline, error: 'API key is invalid or expired' }));
2091
+ }
2092
+ else {
2093
+ console.error('❌ API key is invalid. Run: webpeel auth <new-key>');
2094
+ }
2095
+ process.exit(2);
2096
+ }
2097
+ const usage = usageRes.ok ? await usageRes.json() : null;
2098
+ const plan = usage?.tier || (typeof usage?.plan === 'string' ? usage?.plan : usage?.plan?.tier) || 'free';
2099
+ const used = usage?.used ?? usage?.totalRequests ?? usage?.weekly?.used ?? 0;
2100
+ const limit = usage?.limit ?? usage?.weeklyLimit ?? usage?.weekly?.limit ?? 500;
2101
+ const remaining = limit - used;
2102
+ if (opts.json) {
2103
+ console.log(JSON.stringify({
2104
+ authenticated: true,
2105
+ apiOnline,
2106
+ plan,
2107
+ used,
2108
+ limit,
2109
+ remaining,
2110
+ keyPrefix: key.slice(0, 12) + '...',
2111
+ }));
2112
+ }
2113
+ else {
2114
+ console.log(`✅ Authenticated`);
2115
+ console.log(` API: ${apiOnline ? '🟢 online' : '🔴 offline'}`);
2116
+ console.log(` Plan: ${plan}`);
2117
+ console.log(` Usage: ${used} / ${limit} this week (${remaining} remaining)`);
2118
+ console.log(` Key: ${key.slice(0, 12)}...`);
2119
+ }
2120
+ }
2121
+ catch (e) {
2122
+ if (opts.json) {
2123
+ console.log(JSON.stringify({ authenticated: 'unknown', error: e.message }));
2124
+ }
2125
+ else {
2126
+ console.error(`❌ Could not reach API: ${e.message}`);
2127
+ }
2128
+ process.exit(1);
2129
+ }
2130
+ });
1974
2131
  program
1975
2132
  .command('login')
1976
2133
  .description('Authenticate the CLI with your API key')
@@ -2080,6 +2237,7 @@ program
2080
2237
  // Settable config keys (safe for user modification)
2081
2238
  // Supports dot-notation for nested keys (e.g., llm.apiKey)
2082
2239
  const SETTABLE_KEYS = {
2240
+ apiKey: 'WebPeel API key (tip: use `webpeel auth <key>` to set and verify in one step)',
2083
2241
  braveApiKey: 'Brave Search API key',
2084
2242
  'llm.apiKey': 'LLM API key for AI-powered extraction (OpenAI-compatible)',
2085
2243
  'llm.model': 'LLM model name (default: gpt-4o-mini)',
@@ -2139,6 +2297,11 @@ program
2139
2297
  console.log('');
2140
2298
  console.log(' Settable keys: ' + Object.keys(SETTABLE_KEYS).join(', '));
2141
2299
  console.log(' Usage: webpeel config set <key> <value>');
2300
+ if (!config.apiKey) {
2301
+ console.log('');
2302
+ console.log(' Tip: Run `webpeel auth <your-key>` to set and verify your API key.');
2303
+ console.log(' Get a free key at: https://app.webpeel.dev/keys');
2304
+ }
2142
2305
  process.exit(0);
2143
2306
  }
2144
2307
  if (action === 'set') {