sxng-cli 1.0.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/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # SearXNG CLI
2
+
3
+ A command-line interface for searching with SearXNG, a privacy-respecting meta search engine.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g sxng-cli
9
+ # or
10
+ pnpm add -g sxng-cli
11
+ # or
12
+ yarn global add sxng-cli
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ 1. Set up a SearXNG server:
18
+ ```bash
19
+ docker run -d --name searxng -p 8080:8080 searxng/searxng
20
+ ```
21
+
22
+ 2. Create a configuration file:
23
+ ```bash
24
+ cp searxng.config.example.json searxng.config.json
25
+ # Edit the file with your settings
26
+ ```
27
+
28
+ 3. Run a search:
29
+ ```bash
30
+ sxng "your search query"
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ Configuration can be provided via:
36
+ 1. Environment variables (highest priority)
37
+ 2. Config file: `./searxng.config.json`
38
+ 3. Default values (lowest priority)
39
+
40
+ See `searxng.config.example.json` for an example configuration.
41
+
42
+ ## Usage
43
+
44
+ ```bash
45
+ # Simple search
46
+ sxng "TypeScript tutorial"
47
+
48
+ # Limit results
49
+ sxng "TypeScript tutorial" --limit 5
50
+
51
+ # Use specific engines
52
+ sxng --engines google,github "react hooks"
53
+
54
+ # Search in specific categories
55
+ sxng --categories it,science "machine learning"
56
+
57
+ # Recent results only
58
+ sxng --time week "latest news"
59
+
60
+ # Output as CSV
61
+ sxng "docker tutorial" --format csv > results.csv
62
+
63
+ # Check server health
64
+ sxng --health
65
+
66
+ # List available engines
67
+ sxng --engines-list
68
+
69
+ # List available categories
70
+ sxng --categories-list
71
+ ```
72
+
73
+ ## Environment Variables
74
+
75
+ | Variable | Description | Default |
76
+ |----------|-------------|---------|
77
+ | `SEARXNG_BASE_URL` | SearXNG server URL | `http://localhost:8080` |
78
+ | `SEARXNG_DEFAULT_LIMIT` | Default result limit | `10` |
79
+ | `SEARXNG_TIMEOUT` | Request timeout in ms | `10000` |
80
+ | `SEARXNG_USE_PROXY` | Use proxy (`true`/`false`) | `false` |
81
+ | `SEARXNG_PROXY_URL` | Proxy URL | (none) |
82
+
83
+ ## License
84
+
85
+ MIT
@@ -0,0 +1,19 @@
1
+ /**
2
+ * SearXNG CLI - Configuration
3
+ *
4
+ * Configuration priority (highest to lowest):
5
+ * 1. Environment variables
6
+ * 2. Config file (./sxng.config.json)
7
+ * 3. Default values (localhost only)
8
+ */
9
+ export interface SearXNGConfig {
10
+ baseUrl: string;
11
+ defaultEngine: string;
12
+ allowedEngines: string[];
13
+ defaultLimit: number;
14
+ useProxy: boolean;
15
+ proxyUrl: string;
16
+ timeout: number;
17
+ }
18
+ export declare const config: SearXNGConfig;
19
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACnB;AAiGD,eAAO,MAAM,MAAM,EAAE,aAA6B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * SearXNG CLI - Configuration
3
+ *
4
+ * Configuration priority (highest to lowest):
5
+ * 1. Environment variables
6
+ * 2. Config file (./sxng.config.json)
7
+ * 3. Default values (localhost only)
8
+ */
9
+ import { readFileSync, existsSync } from 'fs';
10
+ const DEFAULT_CONFIG = {
11
+ baseUrl: '',
12
+ defaultEngine: '',
13
+ allowedEngines: [],
14
+ defaultLimit: 10,
15
+ useProxy: false,
16
+ proxyUrl: '',
17
+ timeout: 10000
18
+ };
19
+ function readOptionalEnv(name, defaultValue) {
20
+ const value = process.env[name]?.trim();
21
+ return value ? value : defaultValue;
22
+ }
23
+ function readIntEnv(name, defaultValue) {
24
+ const value = process.env[name];
25
+ if (!value)
26
+ return defaultValue;
27
+ const parsed = parseInt(value, 10);
28
+ return Number.isFinite(parsed) ? parsed : defaultValue;
29
+ }
30
+ function readBoolEnv(name, defaultValue) {
31
+ const value = process.env[name];
32
+ if (value === undefined)
33
+ return defaultValue;
34
+ return value === 'true' || value === '1';
35
+ }
36
+ function findConfigFile() {
37
+ const localConfig = './sxng.config.json';
38
+ if (existsSync(localConfig)) {
39
+ return localConfig;
40
+ }
41
+ return null;
42
+ }
43
+ function loadConfigFile() {
44
+ const configPath = findConfigFile();
45
+ if (!configPath) {
46
+ return {};
47
+ }
48
+ try {
49
+ const content = readFileSync(configPath, 'utf-8');
50
+ const parsed = JSON.parse(content);
51
+ return parsed;
52
+ }
53
+ catch {
54
+ return {};
55
+ }
56
+ }
57
+ function mergeConfig() {
58
+ const config = { ...DEFAULT_CONFIG };
59
+ const fileConfig = loadConfigFile();
60
+ if (fileConfig.baseUrl !== undefined)
61
+ config.baseUrl = fileConfig.baseUrl;
62
+ if (fileConfig.defaultEngine !== undefined)
63
+ config.defaultEngine = fileConfig.defaultEngine;
64
+ if (fileConfig.allowedEngines !== undefined)
65
+ config.allowedEngines = fileConfig.allowedEngines;
66
+ if (fileConfig.defaultLimit !== undefined)
67
+ config.defaultLimit = fileConfig.defaultLimit;
68
+ if (fileConfig.useProxy !== undefined)
69
+ config.useProxy = fileConfig.useProxy;
70
+ if (fileConfig.proxyUrl !== undefined)
71
+ config.proxyUrl = fileConfig.proxyUrl;
72
+ if (fileConfig.timeout !== undefined)
73
+ config.timeout = fileConfig.timeout;
74
+ const envBaseUrl = readOptionalEnv('SEARXNG_BASE_URL');
75
+ if (envBaseUrl)
76
+ config.baseUrl = envBaseUrl;
77
+ const envDefaultEngine = readOptionalEnv('SEARXNG_DEFAULT_ENGINE');
78
+ if (envDefaultEngine !== undefined)
79
+ config.defaultEngine = envDefaultEngine;
80
+ const envAllowedEngines = process.env.SEARXNG_ALLOWED_ENGINES;
81
+ if (envAllowedEngines !== undefined) {
82
+ config.allowedEngines = envAllowedEngines.split(',').map(e => e.trim()).filter(Boolean);
83
+ }
84
+ const envDefaultLimit = process.env.SEARXNG_DEFAULT_LIMIT;
85
+ if (envDefaultLimit !== undefined) {
86
+ config.defaultLimit = readIntEnv('SEARXNG_DEFAULT_LIMIT', config.defaultLimit);
87
+ }
88
+ const envUseProxy = process.env.SEARXNG_USE_PROXY;
89
+ if (envUseProxy !== undefined) {
90
+ config.useProxy = readBoolEnv('SEARXNG_USE_PROXY', config.useProxy);
91
+ }
92
+ const envProxyUrl = readOptionalEnv('SEARXNG_PROXY_URL');
93
+ if (envProxyUrl)
94
+ config.proxyUrl = envProxyUrl;
95
+ const envTimeout = process.env.SEARXNG_TIMEOUT;
96
+ if (envTimeout !== undefined) {
97
+ config.timeout = readIntEnv('SEARXNG_TIMEOUT', config.timeout);
98
+ }
99
+ return config;
100
+ }
101
+ export const config = mergeConfig();
102
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SearXNG CLI - Main Entry Point
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;GAEG"}
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * SearXNG CLI - Main Entry Point
4
+ */
5
+ import { runCli } from './runCli.js';
6
+ import { SearXNGService } from './service.js';
7
+ async function main() {
8
+ const args = process.argv.slice(2);
9
+ const service = new SearXNGService();
10
+ try {
11
+ const exitCode = await runCli(args, service);
12
+ if (exitCode !== null) {
13
+ process.exit(exitCode);
14
+ }
15
+ }
16
+ catch (error) {
17
+ console.error(`Fatal error: ${error instanceof Error ? error.message : String(error)}`);
18
+ process.exit(1);
19
+ }
20
+ }
21
+ main();
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,KAAK,UAAU,IAAI;IACf,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;IAErC,IAAI,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,gBAAgB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC"}
package/dist/init.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * SearXNG CLI - Interactive Configuration Setup
3
+ */
4
+ export interface SearXNGConfig {
5
+ baseUrl: string;
6
+ defaultEngine: string;
7
+ allowedEngines: string[];
8
+ defaultLimit: number;
9
+ useProxy: boolean;
10
+ proxyUrl: string;
11
+ timeout: number;
12
+ }
13
+ export declare function initConfig(): Promise<number>;
14
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACnB;AAsDD,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAiGlD"}
package/dist/init.js ADDED
@@ -0,0 +1,121 @@
1
+ /**
2
+ * SearXNG CLI - Interactive Configuration Setup
3
+ */
4
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
5
+ import { resolve } from 'path';
6
+ import readline from 'readline';
7
+ const DEFAULT_CONFIG = {
8
+ baseUrl: 'http://localhost:8080',
9
+ defaultEngine: '',
10
+ allowedEngines: [],
11
+ defaultLimit: 10,
12
+ useProxy: false,
13
+ proxyUrl: '',
14
+ timeout: 10000
15
+ };
16
+ class ConfiguratorInterface {
17
+ rl;
18
+ constructor() {
19
+ this.rl = readline.createInterface({
20
+ input: process.stdin,
21
+ output: process.stdout
22
+ });
23
+ }
24
+ async question(prompt, defaultValue) {
25
+ return new Promise((resolve) => {
26
+ const displayPrompt = defaultValue ? `${prompt} [${defaultValue}]: ` : `${prompt}: `;
27
+ this.rl.question(displayPrompt, (answer) => {
28
+ resolve(answer.trim() || defaultValue || '');
29
+ });
30
+ });
31
+ }
32
+ async confirm(prompt, defaultValue = true) {
33
+ const answer = await this.question(`${prompt} (${defaultValue ? 'Y/n' : 'y/N'})`, defaultValue ? 'y' : 'n');
34
+ return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
35
+ }
36
+ async multilineInput(prompt) {
37
+ console.log(prompt);
38
+ console.log('(Enter items separated by commas, or press Enter to skip)');
39
+ const input = await this.question('> ');
40
+ return input
41
+ .split(',')
42
+ .map(item => item.trim())
43
+ .filter(item => item.length > 0);
44
+ }
45
+ close() {
46
+ this.rl.close();
47
+ }
48
+ }
49
+ export async function initConfig() {
50
+ const configPath = resolve('./sxng.config.json');
51
+ const configurator = new ConfiguratorInterface();
52
+ console.log('\n📝 SearXNG CLI Configuration Setup\n');
53
+ console.log('This will guide you through setting up your SearXNG configuration.\n');
54
+ let config = { ...DEFAULT_CONFIG };
55
+ // Load existing config if available
56
+ if (existsSync(configPath)) {
57
+ const useExisting = await configurator.confirm('Found existing config. Use it as base');
58
+ if (useExisting) {
59
+ try {
60
+ const existing = JSON.parse(readFileSync(configPath, 'utf-8'));
61
+ config = { ...DEFAULT_CONFIG, ...existing };
62
+ console.log('✓ Loaded existing configuration\n');
63
+ }
64
+ catch (error) {
65
+ console.log('⚠ Failed to parse existing config, starting fresh\n');
66
+ }
67
+ }
68
+ }
69
+ // Configure baseUrl
70
+ console.log('--- SearXNG Server Configuration ---');
71
+ config.baseUrl = await configurator.question('SearXNG server URL', config.baseUrl);
72
+ // Configure default engine
73
+ const engineChoice = await configurator.question('Default search engine (leave blank for none)', config.defaultEngine);
74
+ config.defaultEngine = engineChoice;
75
+ // Configure allowed engines
76
+ const allowedEnginesInput = await configurator.multilineInput('Allowed engines (leave blank to allow all)');
77
+ if (allowedEnginesInput.length > 0) {
78
+ config.allowedEngines = allowedEnginesInput;
79
+ }
80
+ // Configure default limit
81
+ console.log('\n--- Result Settings ---');
82
+ const limitStr = await configurator.question('Default result limit', String(config.defaultLimit));
83
+ config.defaultLimit = Math.max(1, parseInt(limitStr, 10) || 10);
84
+ // Configure timeout
85
+ const timeoutStr = await configurator.question('Request timeout (milliseconds)', String(config.timeout));
86
+ config.timeout = Math.max(1000, parseInt(timeoutStr, 10) || 10000);
87
+ // Configure proxy
88
+ console.log('\n--- Proxy Configuration ---');
89
+ const useProxy = await configurator.confirm('Use HTTP proxy', config.useProxy);
90
+ config.useProxy = useProxy;
91
+ if (useProxy) {
92
+ config.proxyUrl = await configurator.question('Proxy URL', config.proxyUrl);
93
+ }
94
+ else {
95
+ config.proxyUrl = '';
96
+ }
97
+ // Review and confirm
98
+ console.log('\n--- Configuration Summary ---');
99
+ console.log(JSON.stringify(config, null, 2));
100
+ const confirm = await configurator.confirm('\nSave this configuration');
101
+ configurator.close();
102
+ if (confirm) {
103
+ try {
104
+ writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
105
+ console.log(`\n✓ Configuration saved to ${configPath}`);
106
+ console.log('\n✨ Setup complete! You can now use sxng to search.\n');
107
+ console.log('Try it out:');
108
+ console.log(' sxng "TypeScript tutorial"\n');
109
+ return 0;
110
+ }
111
+ catch (error) {
112
+ console.error(`\n✗ Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`);
113
+ return 1;
114
+ }
115
+ }
116
+ else {
117
+ console.log('\n❌ Configuration not saved.');
118
+ return 1;
119
+ }
120
+ }
121
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * SearXNG CLI - Protocol Definitions
3
+ * Standardized envelope format for CLI communication
4
+ */
5
+ export type CliEnvelopeError = {
6
+ code: string;
7
+ message: string;
8
+ retryable: boolean;
9
+ details?: Record<string, unknown>;
10
+ };
11
+ export type CliEnvelope<T> = {
12
+ status: 'ok' | 'error';
13
+ data: T | null;
14
+ error: CliEnvelopeError | null;
15
+ hint: string | null;
16
+ };
17
+ export declare function createSuccessEnvelope<T>(data: T): CliEnvelope<T>;
18
+ export declare function createErrorEnvelope(code: string, message: string, options?: {
19
+ retryable?: boolean;
20
+ details?: Record<string, unknown>;
21
+ hint?: string | null;
22
+ }): CliEnvelope<null>;
23
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IACzB,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAOhE;AAED,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACL,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB,GACP,WAAW,CAAC,IAAI,CAAC,CAYnB"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * SearXNG CLI - Protocol Definitions
3
+ * Standardized envelope format for CLI communication
4
+ */
5
+ export function createSuccessEnvelope(data) {
6
+ return {
7
+ status: 'ok',
8
+ data,
9
+ error: null,
10
+ hint: null
11
+ };
12
+ }
13
+ export function createErrorEnvelope(code, message, options = {}) {
14
+ return {
15
+ status: 'error',
16
+ data: null,
17
+ error: {
18
+ code,
19
+ message,
20
+ retryable: options.retryable ?? false,
21
+ details: options.details
22
+ },
23
+ hint: options.hint ?? null
24
+ };
25
+ }
26
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,MAAM,UAAU,qBAAqB,CAAI,IAAO;IAC5C,OAAO;QACH,MAAM,EAAE,IAAI;QACZ,IAAI;QACJ,KAAK,EAAE,IAAI;QACX,IAAI,EAAE,IAAI;KACb,CAAC;AACN,CAAC;AAED,MAAM,UAAU,mBAAmB,CAC/B,IAAY,EACZ,OAAe,EACf,UAII,EAAE;IAEN,OAAO;QACH,MAAM,EAAE,OAAO;QACf,IAAI,EAAE,IAAI;QACV,KAAK,EAAE;YACH,IAAI;YACJ,OAAO;YACP,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK;YACrC,OAAO,EAAE,OAAO,CAAC,OAAO;SAC3B;QACD,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;KAC7B,CAAC;AACN,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * SearXNG CLI - CLI Runner
3
+ */
4
+ import { SearXNGService } from './service.js';
5
+ export declare function runCli(args: string[], service: SearXNGService): Promise<number | null>;
6
+ //# sourceMappingURL=runCli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCli.d.ts","sourceRoot":"","sources":["../src/runCli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAiB,MAAM,cAAc,CAAC;AAgM7D,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsJ5F"}
package/dist/runCli.js ADDED
@@ -0,0 +1,276 @@
1
+ /**
2
+ * SearXNG CLI - CLI Runner
3
+ */
4
+ import { createSuccessEnvelope, createErrorEnvelope } from './protocol.js';
5
+ import { config } from './config.js';
6
+ import { initConfig } from './init.js';
7
+ const HELP_TEXT = `SearXNG CLI - Web Search Tool
8
+
9
+ Usage:
10
+ sxng <query> [options]
11
+ sxng init
12
+ sxng --engines <engine1,engine2> <query>
13
+ sxng --categories <category> <query>
14
+ sxng --health
15
+
16
+ Commands:
17
+ init Interactive configuration setup
18
+ <query> Perform a web search with the given query
19
+
20
+ Options:
21
+ -e, --engines <engines> Comma-separated list of search engines
22
+ -c, --categories <cats> Comma-separated list of categories
23
+ -l, --limit <n> Maximum number of results (default: ${config.defaultLimit})
24
+ -p, --page <n> Page number for pagination
25
+ --lang <code> Language code (e.g., en, zh, ja)
26
+ --time <range> Time range: day, week, month, year, all
27
+ -f, --format <fmt> Output format: json, csv, html (default: json)
28
+ --engines-list List available search engines
29
+ --categories-list List available categories
30
+ --health Check SearXNG server health
31
+ -h, --help Show this help message
32
+
33
+ Examples:
34
+ sxng init
35
+ sxng "TypeScript tutorial"
36
+ sxng --engines google,github "react hooks"
37
+ sxng --categories it,science "machine learning"
38
+ sxng --limit 5 --time week "latest news"
39
+
40
+ Environment Variables:
41
+ SEARXNG_BASE_URL SearXNG server URL
42
+ SEARXNG_DEFAULT_ENGINE Default search engine
43
+ SEARXNG_ALLOWED_ENGINES Comma-separated allowed engines
44
+ SEARXNG_DEFAULT_LIMIT Default result limit
45
+ SEARXNG_USE_PROXY Use proxy (true/false)
46
+ SEARXNG_PROXY_URL Proxy URL
47
+ SEARXNG_TIMEOUT Request timeout in ms
48
+ `;
49
+ function parseArgs(args) {
50
+ const options = {
51
+ init: false,
52
+ enginesList: false,
53
+ categoriesList: false,
54
+ health: false,
55
+ help: false
56
+ };
57
+ const positional = [];
58
+ for (let i = 0; i < args.length; i++) {
59
+ const arg = args[i];
60
+ switch (arg) {
61
+ case '-h':
62
+ case '--help':
63
+ options.help = true;
64
+ break;
65
+ case 'init':
66
+ options.init = true;
67
+ break;
68
+ case '-e':
69
+ case '--engines':
70
+ options.engines = args[++i]?.split(',').map(e => e.trim()).filter(Boolean);
71
+ break;
72
+ case '-c':
73
+ case '--categories':
74
+ options.categories = args[++i]?.split(',').map(c => c.trim()).filter(Boolean);
75
+ break;
76
+ case '-l':
77
+ case '--limit':
78
+ options.limit = parseInt(args[++i], 10);
79
+ break;
80
+ case '-p':
81
+ case '--page':
82
+ options.page = parseInt(args[++i], 10);
83
+ break;
84
+ case '--lang':
85
+ options.language = args[++i];
86
+ break;
87
+ case '--time':
88
+ const timeVal = args[++i];
89
+ if (['day', 'week', 'month', 'year', 'all'].includes(timeVal)) {
90
+ options.timeRange = timeVal;
91
+ }
92
+ break;
93
+ case '-f':
94
+ case '--format':
95
+ const fmt = args[++i];
96
+ if (['json', 'csv', 'html'].includes(fmt)) {
97
+ options.format = fmt;
98
+ }
99
+ break;
100
+ case '--engines-list':
101
+ options.enginesList = true;
102
+ break;
103
+ case '--categories-list':
104
+ options.categoriesList = true;
105
+ break;
106
+ case '--health':
107
+ options.health = true;
108
+ break;
109
+ default:
110
+ if (!arg.startsWith('-')) {
111
+ positional.push(arg);
112
+ }
113
+ break;
114
+ }
115
+ }
116
+ if (positional.length > 0) {
117
+ options.query = positional.join(' ');
118
+ }
119
+ return options;
120
+ }
121
+ function formatAsCsv(results) {
122
+ if (results.length === 0)
123
+ return '';
124
+ const headers = ['title', 'url', 'content', 'engine', 'category'];
125
+ const lines = [headers.join(',')];
126
+ for (const r of results) {
127
+ const row = headers.map(h => {
128
+ const val = String(r[h] || '').replace(/"/g, '""');
129
+ return `"${val}"`;
130
+ });
131
+ lines.push(row.join(','));
132
+ }
133
+ return lines.join('\n');
134
+ }
135
+ function formatAsHtml(results) {
136
+ const rows = results.map(r => `
137
+ <tr>
138
+ <td><a href="${r.url}">${r.title}</a></td>
139
+ <td>${r.content}</td>
140
+ <td>${r.engine}</td>
141
+ </tr>
142
+ `).join('');
143
+ return `<!DOCTYPE html>
144
+ <html>
145
+ <head><title>Search Results</title></head>
146
+ <body>
147
+ <table border="1">
148
+ <tr><th>Title</th><th>Content</th><th>Engine</th></tr>
149
+ ${rows}
150
+ </table>
151
+ </body>
152
+ </html>`;
153
+ }
154
+ function formatOutput(data, format) {
155
+ switch (format) {
156
+ case 'csv':
157
+ return formatAsCsv(data.results || []);
158
+ case 'html':
159
+ return formatAsHtml(data.results || []);
160
+ case 'json':
161
+ default:
162
+ return JSON.stringify(data, null, 2);
163
+ }
164
+ }
165
+ export async function runCli(args, service) {
166
+ const options = parseArgs(args);
167
+ if (options.help) {
168
+ console.log(HELP_TEXT);
169
+ return 0;
170
+ }
171
+ if (options.init) {
172
+ return await initConfig();
173
+ }
174
+ if (options.health) {
175
+ const health = await service.healthCheck();
176
+ const envelope = health.status === 'healthy'
177
+ ? createSuccessEnvelope(health)
178
+ : createErrorEnvelope('HEALTH_CHECK_FAILED', health.error || 'SearXNG server is not responding', { hint: `Check if SearXNG is running at ${config.baseUrl}` });
179
+ console.log(JSON.stringify(envelope, null, 2));
180
+ return health.status === 'healthy' ? 0 : 1;
181
+ }
182
+ if (options.enginesList) {
183
+ try {
184
+ const engines = await service.getEngines();
185
+ if (engines.length === 0) {
186
+ const envelope = createErrorEnvelope('ENGINES_FETCH_EMPTY', 'No engines returned from SearXNG server', { hint: 'Check if SearXNG server is properly configured' });
187
+ console.log(JSON.stringify(envelope, null, 2));
188
+ return 1;
189
+ }
190
+ const envelope = createSuccessEnvelope({
191
+ engines,
192
+ source: 'server'
193
+ });
194
+ console.log(JSON.stringify(envelope, null, 2));
195
+ return 0;
196
+ }
197
+ catch (error) {
198
+ const envelope = createErrorEnvelope('ENGINES_FETCH_FAILED', error instanceof Error ? error.message : 'Failed to fetch engines', { hint: 'Check your network connection and SearXNG server status' });
199
+ console.log(JSON.stringify(envelope, null, 2));
200
+ return 1;
201
+ }
202
+ }
203
+ if (options.categoriesList) {
204
+ try {
205
+ const categories = await service.getCategories();
206
+ if (categories.length === 0) {
207
+ const envelope = createErrorEnvelope('CATEGORIES_FETCH_EMPTY', 'No categories returned from SearXNG server', { hint: 'Check if SearXNG server is properly configured' });
208
+ console.log(JSON.stringify(envelope, null, 2));
209
+ return 1;
210
+ }
211
+ const envelope = createSuccessEnvelope({
212
+ categories,
213
+ source: 'server'
214
+ });
215
+ console.log(JSON.stringify(envelope, null, 2));
216
+ return 0;
217
+ }
218
+ catch (error) {
219
+ const envelope = createErrorEnvelope('CATEGORIES_FETCH_FAILED', error instanceof Error ? error.message : 'Failed to fetch categories', { hint: 'Check your network connection and SearXNG server status' });
220
+ console.log(JSON.stringify(envelope, null, 2));
221
+ return 1;
222
+ }
223
+ }
224
+ if (!options.query) {
225
+ const envelope = createErrorEnvelope('MISSING_QUERY', 'No search query provided', { hint: 'Use: sxng "your search query"' });
226
+ console.log(JSON.stringify(envelope, null, 2));
227
+ return 1;
228
+ }
229
+ if (options.engines && config.allowedEngines.length > 0) {
230
+ const invalid = options.engines.filter(e => !config.allowedEngines.includes(e));
231
+ if (invalid.length > 0) {
232
+ const envelope = createErrorEnvelope('INVALID_ENGINES', `Engines not allowed: ${invalid.join(', ')}`, { hint: `Allowed engines: ${config.allowedEngines.join(', ')}` });
233
+ console.log(JSON.stringify(envelope, null, 2));
234
+ return 1;
235
+ }
236
+ }
237
+ const searchOptions = {
238
+ query: options.query,
239
+ engines: options.engines,
240
+ categories: options.categories,
241
+ limit: options.limit ?? config.defaultLimit,
242
+ page: options.page,
243
+ language: options.language,
244
+ timeRange: options.timeRange,
245
+ format: options.format || 'json'
246
+ };
247
+ try {
248
+ const results = await service.search(searchOptions);
249
+ const envelope = createSuccessEnvelope({
250
+ query: results.query,
251
+ totalResults: results.numberOfResults,
252
+ returnedResults: results.results.length,
253
+ results: results.results,
254
+ suggestions: results.suggestions,
255
+ answers: results.answers,
256
+ unresponsiveEngines: results.unresponsiveEngines
257
+ });
258
+ if (options.format && options.format !== 'json') {
259
+ console.error(JSON.stringify(envelope, null, 2));
260
+ console.log(formatOutput(results, options.format));
261
+ }
262
+ else {
263
+ console.log(JSON.stringify(envelope, null, 2));
264
+ }
265
+ return 0;
266
+ }
267
+ catch (error) {
268
+ const envelope = createErrorEnvelope('SEARCH_FAILED', error instanceof Error ? error.message : 'Search request failed', {
269
+ retryable: true,
270
+ hint: 'Check your network connection and SearXNG server status'
271
+ });
272
+ console.log(JSON.stringify(envelope, null, 2));
273
+ return 1;
274
+ }
275
+ }
276
+ //# sourceMappingURL=runCli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runCli.js","sourceRoot":"","sources":["../src/runCli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * SearXNG CLI - Search Service
3
+ */
4
+ export interface SearchOptions {
5
+ query: string;
6
+ engines?: string[];
7
+ categories?: string[];
8
+ limit?: number;
9
+ page?: number;
10
+ language?: string;
11
+ timeRange?: 'day' | 'week' | 'month' | 'year' | 'all';
12
+ format?: 'json' | 'csv' | 'html';
13
+ }
14
+ export interface SearchResult {
15
+ title: string;
16
+ url: string;
17
+ content: string;
18
+ engine: string;
19
+ category: string;
20
+ publishedDate?: string;
21
+ thumbnail?: string;
22
+ score?: number;
23
+ }
24
+ export interface SearchResponse {
25
+ query: string;
26
+ numberOfResults: number;
27
+ results: SearchResult[];
28
+ suggestions: string[];
29
+ answers: string[];
30
+ corrections: string[];
31
+ infoboxes: Array<{
32
+ infobox: string;
33
+ content: string;
34
+ engine: string;
35
+ }>;
36
+ unresponsiveEngines: string[];
37
+ }
38
+ export declare class SearXNGService {
39
+ private client;
40
+ constructor();
41
+ getEngines(): Promise<string[]>;
42
+ getCategories(): Promise<string[]>;
43
+ search(options: SearchOptions): Promise<SearchResponse>;
44
+ healthCheck(): Promise<{
45
+ status: 'healthy' | 'unhealthy';
46
+ baseUrl: string;
47
+ engines?: string[];
48
+ error?: string;
49
+ }>;
50
+ }
51
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACtD,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,qBAAa,cAAc;IACvB,OAAO,CAAC,MAAM,CAAgB;;IAyBxB,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAa/B,aAAa,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAWlC,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IA+DvD,WAAW,IAAI,OAAO,CAAC;QACzB,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;QAChC,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CAiBL"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * SearXNG CLI - Search Service
3
+ */
4
+ import axios from 'axios';
5
+ import { HttpsProxyAgent } from 'https-proxy-agent';
6
+ import { config } from './config.js';
7
+ export class SearXNGService {
8
+ client;
9
+ constructor() {
10
+ const axiosConfig = {
11
+ baseURL: config.baseUrl,
12
+ timeout: config.timeout,
13
+ headers: {
14
+ 'Accept': 'application/json',
15
+ 'User-Agent': 'searxng-cli/1.0.0'
16
+ }
17
+ };
18
+ if (config.useProxy && config.proxyUrl) {
19
+ try {
20
+ const proxyAgent = new HttpsProxyAgent(config.proxyUrl);
21
+ axiosConfig.httpsAgent = proxyAgent;
22
+ axiosConfig.httpAgent = proxyAgent;
23
+ }
24
+ catch (error) {
25
+ console.error(`Failed to configure proxy: ${error instanceof Error ? error.message : String(error)}`);
26
+ }
27
+ }
28
+ this.client = axios.create(axiosConfig);
29
+ }
30
+ async getEngines() {
31
+ try {
32
+ const response = await this.client.get('/config');
33
+ const engines = response.data.engines || [];
34
+ return engines
35
+ .filter((e) => e.enabled)
36
+ .map((e) => e.name);
37
+ }
38
+ catch (error) {
39
+ console.error(`Failed to fetch engines: ${error instanceof Error ? error.message : String(error)}`);
40
+ return [];
41
+ }
42
+ }
43
+ async getCategories() {
44
+ try {
45
+ const response = await this.client.get('/config');
46
+ const categories = response.data.categories || [];
47
+ return categories.map((c) => typeof c === 'string' ? c : c?.name).filter(Boolean);
48
+ }
49
+ catch (error) {
50
+ console.error(`Failed to fetch categories: ${error instanceof Error ? error.message : String(error)}`);
51
+ return [];
52
+ }
53
+ }
54
+ async search(options) {
55
+ const params = {
56
+ q: options.query,
57
+ format: 'json'
58
+ };
59
+ if (options.engines && options.engines.length > 0) {
60
+ params.engines = options.engines.join(',');
61
+ }
62
+ if (options.categories && options.categories.length > 0) {
63
+ params.categories = options.categories.join(',');
64
+ }
65
+ if (options.language) {
66
+ params.language = options.language;
67
+ }
68
+ if (options.timeRange) {
69
+ params.time_range = options.timeRange;
70
+ }
71
+ if (options.page && options.page > 1) {
72
+ params.pageno = String(options.page);
73
+ }
74
+ try {
75
+ const response = await this.client.get('/search', { params });
76
+ const data = response.data;
77
+ const results = (data.results || []).map((item) => ({
78
+ title: item.title || '',
79
+ url: item.url || '',
80
+ content: item.content || item.snippet || '',
81
+ engine: item.engine || 'unknown',
82
+ category: item.category || 'general',
83
+ publishedDate: item.publishedDate,
84
+ thumbnail: item.thumbnail,
85
+ score: item.score
86
+ }));
87
+ const limitedResults = options.limit && options.limit > 0
88
+ ? results.slice(0, options.limit)
89
+ : results;
90
+ return {
91
+ query: data.query || options.query,
92
+ numberOfResults: data.numberOfResults || limitedResults.length,
93
+ results: limitedResults,
94
+ suggestions: data.suggestions || [],
95
+ answers: data.answers || [],
96
+ corrections: data.corrections || [],
97
+ infoboxes: data.infoboxes || [],
98
+ unresponsiveEngines: data.unresponsive_engines || []
99
+ };
100
+ }
101
+ catch (error) {
102
+ if (axios.isAxiosError(error)) {
103
+ throw new Error(`Search failed: ${error.response?.statusText || error.message}`);
104
+ }
105
+ throw error;
106
+ }
107
+ }
108
+ async healthCheck() {
109
+ try {
110
+ const response = await this.client.get('/healthz', { timeout: 5000 });
111
+ const engines = await this.getEngines();
112
+ return {
113
+ status: 'healthy',
114
+ baseUrl: config.baseUrl,
115
+ engines
116
+ };
117
+ }
118
+ catch (error) {
119
+ return {
120
+ status: 'unhealthy',
121
+ baseUrl: config.baseUrl,
122
+ error: error instanceof Error ? error.message : 'Unknown error'
123
+ };
124
+ }
125
+ }
126
+ }
127
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAwB,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAuCrC,MAAM,OAAO,cAAc;IACf,MAAM,CAAgB;IAE9B;QACI,MAAM,WAAW,GAAQ;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO,EAAE;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,YAAY,EAAE,mBAAmB;aACpC;SACJ,CAAC;QAEF,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrC,IAAI,CAAC;gBACD,MAAM,UAAU,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACxD,WAAW,CAAC,UAAU,GAAG,UAAU,CAAC;gBACpC,WAAW,CAAC,SAAS,GAAG,UAAU,CAAC;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC1G,CAAC;QACL,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,UAAU;QACZ,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;YAC5C,OAAO,OAAO;iBACT,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;iBAC7B,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACpG,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa;QACf,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;YAClD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACvG,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAsB;QAC/B,MAAM,MAAM,GAA2B;YACnC,CAAC,EAAE,OAAO,CAAC,KAAK;YAChB,MAAM,EAAE,MAAM;SACjB,CAAC;QAEF,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACvC,CAAC;QAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QAC1C,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAC9D,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;YAE3B,MAAM,OAAO,GAAmB,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;gBACrE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;gBACvB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;gBACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE;gBAC3C,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;gBAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS;gBACpC,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,KAAK,EAAE,IAAI,CAAC,KAAK;aACpB,CAAC,CAAC,CAAC;YAEJ,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC;gBACrD,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;gBACjC,CAAC,CAAC,OAAO,CAAC;YAEd,OAAO;gBACH,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK;gBAClC,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,cAAc,CAAC,MAAM;gBAC9D,OAAO,EAAE,cAAc;gBACvB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;gBACnC,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;gBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;gBACnC,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;gBAC/B,mBAAmB,EAAE,IAAI,CAAC,oBAAoB,IAAI,EAAE;aACvD,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,QAAQ,EAAE,UAAU,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACrF,CAAC;YACD,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QAMb,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;YACxC,OAAO;gBACH,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,OAAO;aACV,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO;gBACH,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAClE,CAAC;QACN,CAAC;IACL,CAAC;CACJ"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "sxng-cli",
3
+ "version": "1.0.1",
4
+ "description": "SearXNG CLI - Web Search Tool",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "sxng": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist/",
13
+ "sxng.config.example.json",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc && chmod +x dist/index.js",
18
+ "dev": "tsc --watch",
19
+ "start": "node dist/index.js",
20
+ "prepublishOnly": "npm run build",
21
+ "test": "echo \"Error: no test specified\" && exit 1"
22
+ },
23
+ "keywords": [
24
+ "searxng",
25
+ "search",
26
+ "cli",
27
+ "web-search"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "axios": "^1.6.0",
33
+ "https-proxy-agent": "^7.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0",
37
+ "typescript": "^5.3.0"
38
+ },
39
+ "engines": {
40
+ "node": ">=18.0.0"
41
+ }
42
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "baseUrl": "http://localhost:8080",
3
+ "defaultEngine": "",
4
+ "allowedEngines": [],
5
+ "defaultLimit": 10,
6
+ "useProxy": false,
7
+ "proxyUrl": "",
8
+ "timeout": 10000
9
+ }