@decodo/cli
Advanced tools
| export declare class ConfigParseError extends Error { | ||
| constructor(configPath: string); | ||
| } |
| export class ConfigParseError extends Error { | ||
| constructor(configPath) { | ||
| super(`Configuration file is invalid (${configPath}). Run \`decodo setup\` to reconfigure.`); | ||
| this.name = "ConfigParseError"; | ||
| } | ||
| } |
| export declare function promptHidden(message: string): Promise<string>; |
| import { stdin, stdout } from "node:process"; |
| import { createInterface } from "node:readline/promises"; |
| function handleHiddenPromptChar(char, state) { |
| if (char === "\u0003") { |
| state.cleanup(); |
| stdout.write("\n"); |
| state.reject(new Error("Cancelled.")); |
| return; |
| } |
| if (char === "\r" || char === "\n") { |
| state.cleanup(); |
| stdout.write("\n"); |
| state.resolve(state.input.trim()); |
| return; |
| } |
| if (char === "\u007f" || char === "\b") { |
| if (state.input.length > 0) { |
| state.input = state.input.slice(0, -1); |
| stdout.write("\b \b"); |
| } |
| return; |
| } |
| state.input += char; |
| } |
| export async function promptHidden(message) { |
| if (!stdin.isTTY) { |
| const rl = createInterface({ input: stdin, output: stdout }); |
| try { |
| return (await rl.question(message)).trim(); |
| } |
| finally { |
| rl.close(); |
| } |
| } |
| stdout.write(message); |
| return new Promise((resolve, reject) => { |
| stdin.setRawMode(true); |
| stdin.resume(); |
| stdin.setEncoding("utf8"); |
| const state = { |
| input: "", |
| cleanup: () => undefined, |
| resolve, |
| reject, |
| }; |
| const onData = (chunk) => { |
| for (const char of chunk) { |
| handleHiddenPromptChar(char, state); |
| } |
| }; |
| state.cleanup = () => { |
| stdin.setRawMode(false); |
| stdin.pause(); |
| stdin.removeListener("data", onData); |
| }; |
| stdin.on("data", onData); |
| }); |
| } |
@@ -1,6 +0,5 @@ | ||
| import { stdin as input, stdout as output } from "node:process"; | ||
| import { createInterface } from "node:readline/promises"; | ||
| import { Command } from "commander"; | ||
| import { getRootOpts } from "../../cli/services/global-opts.js"; | ||
| import { CliUsageError, handleCliError, } from "../../platform/services/handle-cli-error.js"; | ||
| import { promptHidden } from "../../platform/services/prompt-hidden.js"; | ||
| import { validateAuthToken } from "../../scrape/services/auth-validation.js"; | ||
@@ -10,12 +9,2 @@ import { PLAYGROUND_URL } from "../constants.js"; | ||
| const TOKEN_PROMPT = `Paste your Web Scraping API basic auth token (${PLAYGROUND_URL}): `; | ||
| async function promptForToken() { | ||
| const rl = createInterface({ input, output }); | ||
| try { | ||
| const token = await rl.question(TOKEN_PROMPT); | ||
| return token.trim(); | ||
| } | ||
| finally { | ||
| rl.close(); | ||
| } | ||
| } | ||
| export const setupCommand = new Command("setup") | ||
@@ -26,5 +15,5 @@ .description("Configure the Decodo CLI with your auth token") | ||
| const rootOpts = getRootOpts(command); | ||
| const token = options.token?.trim() || | ||
| const token = (options.token?.trim() || | ||
| rootOpts.token?.trim() || | ||
| (await promptForToken()); | ||
| (await promptHidden(TOKEN_PROMPT))).trim(); | ||
| if (!token) { | ||
@@ -31,0 +20,0 @@ handleCliError(new CliUsageError("auth token is required.")); |
| export declare const PLAYGROUND_URL = "https://dashboard.decodo.com/playground"; | ||
| export declare const AUTH_MISSING_MESSAGE = "No auth token found. Run `decodo setup` or set DECODO_AUTH_TOKEN."; | ||
| export declare const AUTH_MISSING_MESSAGE = "No auth token found."; |
| export const PLAYGROUND_URL = "https://dashboard.decodo.com/playground"; | ||
| export const AUTH_MISSING_MESSAGE = "No auth token found. Run `decodo setup` or set DECODO_AUTH_TOKEN."; | ||
| export const AUTH_MISSING_MESSAGE = "No auth token found."; |
| import { mkdir, readFile, unlink, writeFile } from "node:fs/promises"; | ||
| import { dirname, join } from "node:path"; | ||
| import { getConfigDir } from "../../platform/services/paths.js"; | ||
| import { ConfigParseError } from "../errors/config-parse-error.js"; | ||
| const CONFIG_FILE = "config.json"; | ||
@@ -8,4 +9,10 @@ export function getConfigPath() { | ||
| } | ||
| function parseConfig(raw) { | ||
| const parsed = JSON.parse(raw); | ||
| function parseConfig(raw, configPath) { | ||
| let parsed; | ||
| try { | ||
| parsed = JSON.parse(raw); | ||
| } | ||
| catch { | ||
| throw new ConfigParseError(configPath); | ||
| } | ||
| if (typeof parsed.authToken === "string" && parsed.authToken.length > 0) { | ||
@@ -22,3 +29,3 @@ return { | ||
| const raw = await readFile(configPath, "utf8"); | ||
| return parseConfig(raw); | ||
| return parseConfig(raw, configPath); | ||
| } | ||
@@ -25,0 +32,0 @@ catch (err) { |
@@ -10,5 +10,4 @@ #!/usr/bin/env node | ||
| function readVersion() { | ||
| const __filename = fileURLToPath(import.meta.url); | ||
| const packageJsonPath = join(dirname(__filename), "..", "..", "package.json"); | ||
| const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8")); | ||
| const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json"); | ||
| const pkg = JSON.parse(readFileSync(pkgPath, "utf8")); | ||
| return pkg.version; | ||
@@ -15,0 +14,0 @@ } |
| import { AuthenticationError, DecodoError, RateLimitError, TimeoutError, ValidationError, } from "@decodo/sdk-ts"; | ||
| import { PLAYGROUND_URL } from "../../auth/constants.js"; | ||
| import { AuthRequiredError } from "../../auth/errors/auth-required-error.js"; | ||
@@ -82,3 +83,9 @@ import { EXIT } from "../constants.js"; | ||
| } | ||
| if (err instanceof AuthRequiredError || err instanceof AuthenticationError) { | ||
| if (err instanceof AuthRequiredError) { | ||
| console.error("\nThe Decodo CLI is installed and working - it just needs an auth token:\n" + | ||
| ` 1. Get your Web Scraping API token at ${PLAYGROUND_URL}\n` + | ||
| " 2. Run `decodo setup` to save it (or set DECODO_AUTH_TOKEN)\n" + | ||
| " 3. Re-run your command"); | ||
| } | ||
| else if (err instanceof AuthenticationError) { | ||
| console.error("Hint: Run `decodo setup` to configure your auth token."); | ||
@@ -85,0 +92,0 @@ } |
@@ -7,4 +7,4 @@ import { Target as ScrapeTarget } from "@decodo/sdk-ts"; | ||
| target: ScrapeTarget.Universal, | ||
| url: "https://ip.decodo.com", | ||
| url: "https://does-not-exist.decodo.com", | ||
| }); | ||
| } |
+1
-1
| { | ||
| "name": "@decodo/cli", | ||
| "version": "0.1.3", | ||
| "version": "0.1.4", | ||
| "description": "Official CLI for the Decodo APIs", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+3
-3
@@ -147,3 +147,3 @@ # Decodo CLI | ||
| decodo scrape https://ip.decodo.com | ||
| decodo search "top articles hacker news" --limit 5 --parse | ||
| decodo google-search "top articles hacker news" --limit 5 --parse | ||
| ``` | ||
@@ -211,3 +211,3 @@ | ||
| # Search and extract titles | ||
| decodo search "rust web scraping" --limit 3 --parse | jq '.[].title' | ||
| decodo google-search "rust web scraping" --limit 3 --parse | jq '.[].title' | ||
@@ -226,3 +226,3 @@ # Scrape JSON API endpoint | ||
| decodo scrape https://example.com --country us | ||
| decodo search "shoes" --geo de --parse | ||
| decodo search "shoes" --geo de | ||
| decodo google-search "shoes" --geo de --parse | ||
@@ -229,0 +229,0 @@ ``` |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
61567
3.7%113
3.67%1256
5.9%