@qverisai/cli 0.1.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.
package/README.md ADDED
@@ -0,0 +1,323 @@
1
+ # QVeris CLI
2
+
3
+ Discover, inspect, and call 10,000+ API capabilities from your terminal.
4
+
5
+ ```
6
+ $ qveris discover "weather forecast API"
7
+ Found 5 capabilities matching your query
8
+
9
+ 1. gridpoint_forecast by Weather.gov
10
+ weather_gov.gridpoints.forecast.retrieve.v1
11
+ Returns a textual forecast for a 2.5km grid area
12
+ relevance: 95% · success: 99.8% · latency: ~180ms · cost: 3 cr
13
+
14
+ 2. current_weather by OpenWeather
15
+ openweathermap.weather.current.v1
16
+ Get current weather data for any location
17
+ relevance: 87% · success: 99.5% · latency: ~200ms · cost: 2 cr
18
+
19
+ $ qveris inspect 1
20
+ gridpoint_forecast
21
+ Returns a textual forecast for a 2.5km grid area
22
+
23
+ Provider: Weather.gov
24
+ Latency: ~180ms
25
+ Success: 99.8%
26
+ Cost: 3 credits
27
+
28
+ Parameters:
29
+ wfo string required
30
+ Forecast office ID
31
+ values: "LWX", "OKX", "LAX", ...
32
+ x integer required
33
+ Forecast grid X coordinate
34
+ y integer required
35
+ Forecast grid Y coordinate
36
+
37
+ Example:
38
+ {"wfo": "LWX", "x": 90, "y": 90}
39
+
40
+ $ qveris call 1 --params '{"wfo":"LWX","x":90,"y":90}'
41
+ ✓ success · 523ms · 10 credits · (1368087.93 remaining)
42
+ tool: weather_gov... · id: 7ebcaf9d-...
43
+
44
+ {
45
+ "type": "Feature",
46
+ "properties": {
47
+ "periods": [
48
+ { "name": "Today", "temperature": 79, "shortForecast": "Partly Sunny" },
49
+ ...
50
+ ]
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## Install
56
+
57
+ **One-liner (recommended):**
58
+
59
+ ```bash
60
+ curl -fsSL https://qveris.ai/install | bash
61
+ ```
62
+
63
+ **Or via npm:**
64
+
65
+ ```bash
66
+ npm install -g @qverisai/cli
67
+ ```
68
+
69
+ **Or run without installing:**
70
+
71
+ ```bash
72
+ npx @qverisai/cli discover "stock price API"
73
+ ```
74
+
75
+ Requires Node.js 18+.
76
+
77
+ ## Quick Start
78
+
79
+ ```bash
80
+ # 1. Authenticate
81
+ qveris login
82
+
83
+ # 2. Discover capabilities
84
+ qveris discover "weather forecast"
85
+
86
+ # 3. Inspect a tool (by index from discover results)
87
+ qveris inspect 1
88
+
89
+ # 4. Call it
90
+ qveris call 1 --params '{"wfo": "LWX", "x": 90, "y": 90}'
91
+ ```
92
+
93
+ ## Commands
94
+
95
+ ### Core
96
+
97
+ | Command | Description |
98
+ |---------|-------------|
99
+ | `qveris discover <query>` | Find capabilities by natural language. Shows tool ID, provider, description, relevance, success rate, latency, and cost for each result. |
100
+ | `qveris inspect <id\|index>` | View full tool details: parameters (type, required, description, enum values), example, provider info, execution history. |
101
+ | `qveris call <id\|index>` | Execute a capability. Shows result data, execution time, cost, and remaining credits. |
102
+
103
+ ### Account
104
+
105
+ | Command | Description |
106
+ |---------|-------------|
107
+ | `qveris login` | Authenticate with API key (opens browser or `--token` for direct input) |
108
+ | `qveris logout` | Remove stored key |
109
+ | `qveris whoami` | Show current auth status and key source |
110
+ | `qveris credits` | Check credit balance |
111
+
112
+ ### Utilities
113
+
114
+ | Command | Description |
115
+ |---------|-------------|
116
+ | `qveris interactive` | Launch REPL mode (discover/inspect/call/codegen in one session) |
117
+ | `qveris doctor` | Self-check: Node.js version, API key, connectivity |
118
+ | `qveris config <subcommand>` | Manage CLI settings (set, get, list, reset, path) |
119
+ | `qveris completions <shell>` | Generate shell completions (bash/zsh/fish) |
120
+
121
+ ## Usage
122
+
123
+ ### Discover
124
+
125
+ Search for capabilities using natural language. Each result shows the tool name, provider, tool ID, description, and quality metrics to help you choose.
126
+
127
+ ```bash
128
+ qveris discover "stock price API"
129
+ qveris discover "translate text" --limit 10
130
+ ```
131
+
132
+ ### Inspect
133
+
134
+ View full details of a tool before calling it. Shows parameters with types, required/optional, descriptions, allowed values (enum), and example parameters.
135
+
136
+ ```bash
137
+ # By index (from last discover)
138
+ qveris inspect 1
139
+
140
+ # By tool ID
141
+ qveris inspect alphavantage.quote.execute.v1
142
+
143
+ # Inspect multiple tools
144
+ qveris inspect 1 2 3
145
+ ```
146
+
147
+ ### Call
148
+
149
+ Execute a tool. Results are automatically truncated for terminal display (4KB). Large results get an OSS download link.
150
+
151
+ ```bash
152
+ # Inline params
153
+ qveris call 1 --params '{"symbol": "AAPL"}'
154
+
155
+ # From file
156
+ qveris call 1 --params @params.json
157
+
158
+ # From stdin
159
+ echo '{"symbol": "AAPL"}' | qveris call 1 --params -
160
+
161
+ # Dry run (validate without executing, no credits consumed)
162
+ qveris call 1 --params '{"symbol": "AAPL"}' --dry-run
163
+
164
+ # Full result (no truncation)
165
+ qveris call 1 --params '{"symbol": "AAPL"}' --max-size -1
166
+
167
+ # Generate code snippet after call
168
+ qveris call 1 --params '{"symbol": "AAPL"}' --codegen curl
169
+ qveris call 1 --params '{"symbol": "AAPL"}' --codegen python
170
+ qveris call 1 --params '{"symbol": "AAPL"}' --codegen js
171
+ ```
172
+
173
+ #### Response Truncation
174
+
175
+ For terminal use, results larger than 4KB are automatically truncated. The CLI shows:
176
+ - A preview of the truncated content
177
+ - A download link (valid 120 minutes) for the full result
178
+ - The response schema so you know the data structure
179
+
180
+ ```
181
+ ✓ success · 1200ms · 5 credits
182
+
183
+ Response truncated (32KB → 4KB preview)
184
+
185
+ Full content (valid 120 min):
186
+ https://qveris-tool-results-cache-bj.oss-cn-beijing...
187
+ Download: curl -o result.json '<url>'
188
+
189
+ Schema:
190
+ query: string
191
+ total_results: number
192
+ articles: array of
193
+ pmid: string
194
+ title: string
195
+
196
+ Preview:
197
+ {"query": "evolution", "total_results": 890994, ...
198
+ ```
199
+
200
+ For agent/script use (`--json` or piped output), the default increases to 20KB (matching the MCP server). Use `--max-size -1` for unlimited.
201
+
202
+ ### Interactive Mode
203
+
204
+ ```bash
205
+ $ qveris interactive
206
+
207
+ qveris> discover "weather API"
208
+ Found 3 capabilities matching your query
209
+ 1. openweathermap.weather.current.v1 by OpenWeather
210
+ ...
211
+
212
+ qveris> inspect 1
213
+ openweathermap.weather.current.v1
214
+ Parameters:
215
+ city string required
216
+ ...
217
+
218
+ qveris> call 1 '{"city": "London"}'
219
+ ✓ success · 200ms · 2 credits
220
+ { "temp": 18.5, ... }
221
+
222
+ qveris> codegen curl
223
+ curl -sS -X POST "https://qveris.ai/api/v1/tools/execute?tool_id=..." ...
224
+
225
+ qveris> exit
226
+ ```
227
+
228
+ ## Configuration
229
+
230
+ ### API Key
231
+
232
+ ```bash
233
+ # Option 1: Login (saves to ~/.config/qveris/config.json)
234
+ qveris login
235
+
236
+ # Option 2: Environment variable
237
+ export QVERIS_API_KEY="sk-1_..."
238
+
239
+ # Option 3: Per-command flag
240
+ qveris discover "weather" --api-key "sk-1_..."
241
+ ```
242
+
243
+ **Resolution order:** `--api-key` flag > `QVERIS_API_KEY` env > config file
244
+
245
+ ### Config File
246
+
247
+ Located at `~/.config/qveris/config.json` (respects `XDG_CONFIG_HOME`).
248
+
249
+ ```bash
250
+ qveris config list # View all settings with sources
251
+ qveris config set key value # Set a value
252
+ qveris config get key # Get a value
253
+ qveris config path # Print config file location
254
+ qveris config reset # Reset to defaults
255
+ ```
256
+
257
+ ### Shell Completions
258
+
259
+ ```bash
260
+ # Bash
261
+ eval "$(qveris completions bash)"
262
+
263
+ # Zsh
264
+ eval "$(qveris completions zsh)"
265
+
266
+ # Fish
267
+ qveris completions fish | source
268
+ ```
269
+
270
+ ## Global Flags
271
+
272
+ | Flag | Short | Description |
273
+ |------|-------|-------------|
274
+ | `--json` | `-j` | Output raw JSON (for piping/agent use) |
275
+ | `--api-key <key>` | | Override API key |
276
+ | `--timeout <seconds>` | | Request timeout |
277
+ | `--max-size <bytes>` | | Response size limit (-1 = unlimited) |
278
+ | `--no-color` | | Disable colors |
279
+ | `--version` | `-V` | Print version |
280
+ | `--help` | `-h` | Show help |
281
+
282
+ ## Agent / LLM Integration
283
+
284
+ When used by agents or in scripts, the CLI auto-detects non-TTY environments:
285
+
286
+ | Context | `max_response_size` | Rationale |
287
+ |---------|---------------------|-----------|
288
+ | Terminal (TTY) | 4KB | Human-friendly, auto-truncate |
289
+ | Piped / scripted | 20KB | Agent-friendly, matches MCP server |
290
+ | `--json` flag | 20KB | Explicit agent mode |
291
+ | `--max-size N` | N | User override |
292
+
293
+ ```bash
294
+ # Agent workflow: discover → select → call → parse
295
+ TOOL=$(qveris discover "weather" --json | jq -r '.results[0].tool_id')
296
+ qveris call "$TOOL" --params '{"city":"London"}' --json | jq '.result.data'
297
+ ```
298
+
299
+ ## Exit Codes
300
+
301
+ | Code | Meaning |
302
+ |------|---------|
303
+ | 0 | Success |
304
+ | 2 | Usage error (bad arguments) |
305
+ | 69 | Service unavailable |
306
+ | 75 | Temporary failure (timeout, rate limit) |
307
+ | 77 | Auth error (invalid key, insufficient credits) |
308
+ | 78 | Config error (missing key) |
309
+
310
+ ## Zero Dependencies
311
+
312
+ QVeris CLI uses only Node.js built-in APIs. No `chalk`, no `commander`, no `yargs`. Installs instantly via `npx`.
313
+
314
+ ## Links
315
+
316
+ - Website: https://qveris.ai
317
+ - API Docs: https://qveris.ai/docs
318
+ - Get API Key: https://qveris.ai/account?page=api-keys
319
+ - GitHub: https://github.com/QVerisAI/QVerisAI
320
+
321
+ ## License
322
+
323
+ MIT
package/bin/qveris.mjs ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "../src/main.mjs";
3
+
4
+ main(process.argv).catch((err) => {
5
+ console.error(err.message || err);
6
+ process.exitCode = process.exitCode || 1;
7
+ });
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@qverisai/cli",
3
+ "version": "0.1.0",
4
+ "description": "QVeris CLI -- discover, inspect, and call 10,000+ capabilities",
5
+ "type": "module",
6
+ "bin": {
7
+ "qveris": "./bin/qveris.mjs"
8
+ },
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "src/",
15
+ "scripts/"
16
+ ],
17
+ "keywords": [
18
+ "qveris",
19
+ "cli",
20
+ "api",
21
+ "agent",
22
+ "capabilities",
23
+ "tool-calling"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/QVerisAI/QVerisAI",
28
+ "directory": "packages/cli"
29
+ },
30
+ "homepage": "https://qveris.ai",
31
+ "license": "MIT",
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env bash
2
+ # QVeris CLI Installer
3
+ # Usage: curl -fsSL https://qveris.ai/install | bash
4
+ set -euo pipefail
5
+
6
+ BOLD='\033[1m'
7
+ GREEN='\033[32m'
8
+ YELLOW='\033[33m'
9
+ CYAN='\033[36m'
10
+ RED='\033[31m'
11
+ DIM='\033[2m'
12
+ RESET='\033[0m'
13
+
14
+ info() { echo -e " ${CYAN}▸${RESET} $1"; }
15
+ ok() { echo -e " ${GREEN}✓${RESET} $1"; }
16
+ warn() { echo -e " ${YELLOW}!${RESET} $1"; }
17
+ fail() { echo -e " ${RED}✘${RESET} $1"; exit 1; }
18
+
19
+ echo ""
20
+ echo -e " ${BOLD}QVeris CLI Installer${RESET}"
21
+ echo ""
22
+
23
+ # ── 1. Check Node.js ──────────────────────────────────────────────────
24
+ if ! command -v node &>/dev/null; then
25
+ fail "Node.js not found. Install Node.js 18+ first: ${CYAN}https://nodejs.org${RESET}"
26
+ fi
27
+
28
+ NODE_VERSION=$(node -v | sed 's/v//')
29
+ NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1)
30
+
31
+ if [ "$NODE_MAJOR" -lt 18 ]; then
32
+ fail "Node.js $NODE_VERSION detected. QVeris CLI requires Node.js 18+."
33
+ fi
34
+
35
+ ok "Node.js v$NODE_VERSION"
36
+
37
+ # ── 2. Check npm ──────────────────────────────────────────────────────
38
+ if ! command -v npm &>/dev/null; then
39
+ fail "npm not found. Install npm first."
40
+ fi
41
+
42
+ ok "npm $(npm -v)"
43
+
44
+ # ── 3. Install @qverisai/cli globally ────────────────────────────────
45
+ info "Installing @qverisai/cli..."
46
+
47
+ if ! npm install -g @qverisai/cli 2>&1 | while IFS= read -r line; do
48
+ # suppress verbose npm output, show only errors
49
+ case "$line" in
50
+ *ERR*|*error*|*Error*) echo " $line" ;;
51
+ esac
52
+ done; then
53
+ # If global install fails (permissions), try with --prefix
54
+ warn "Global install failed, trying user-local install..."
55
+ NPM_DIR="${HOME}/.npm-global"
56
+ mkdir -p "$NPM_DIR"
57
+ if ! npm_output=$(npm install -g @qverisai/cli --prefix "$NPM_DIR" 2>&1); then
58
+ echo "$npm_output" | grep -i err
59
+ fi
60
+ fi
61
+
62
+ # ── 4. Verify installation ───────────────────────────────────────────
63
+ # Find where qveris was installed
64
+ QVERIS_BIN=""
65
+ if command -v qveris &>/dev/null; then
66
+ QVERIS_BIN=$(command -v qveris)
67
+ else
68
+ # Check common locations
69
+ for dir in \
70
+ "$(npm prefix -g 2>/dev/null)/bin" \
71
+ "${HOME}/.npm-global/bin" \
72
+ "/usr/local/bin" \
73
+ "/opt/homebrew/bin" \
74
+ "${HOME}/.local/bin"; do
75
+ if [ -f "$dir/qveris" ]; then
76
+ QVERIS_BIN="$dir/qveris"
77
+ break
78
+ fi
79
+ done
80
+ fi
81
+
82
+ if [ -z "$QVERIS_BIN" ]; then
83
+ fail "Installation succeeded but 'qveris' not found in PATH."
84
+ fi
85
+
86
+ # ── 5. Check if qveris is in PATH ────────────────────────────────────
87
+ if command -v qveris &>/dev/null; then
88
+ ok "qveris installed at $QVERIS_BIN"
89
+ else
90
+ # Not in PATH — add it
91
+ BIN_DIR=$(dirname "$QVERIS_BIN")
92
+ SHELL_RC=""
93
+ case "${SHELL:-/bin/bash}" in
94
+ */zsh) SHELL_RC="$HOME/.zshrc" ;;
95
+ */bash) SHELL_RC="$HOME/.bashrc" ;;
96
+ */fish) SHELL_RC="$HOME/.config/fish/config.fish" ;;
97
+ *) SHELL_RC="$HOME/.profile" ;;
98
+ esac
99
+
100
+ # Add to PATH in shell rc if not already there
101
+ if [ -n "$SHELL_RC" ] && ! grep -Fq "$BIN_DIR" "$SHELL_RC" 2>/dev/null; then
102
+ echo "" >> "$SHELL_RC"
103
+ echo "# QVeris CLI" >> "$SHELL_RC"
104
+ if [[ "$SHELL_RC" == *"fish"* ]]; then
105
+ echo "set -gx PATH $BIN_DIR \$PATH" >> "$SHELL_RC"
106
+ else
107
+ echo "export PATH=\"$BIN_DIR:\$PATH\"" >> "$SHELL_RC"
108
+ fi
109
+ ok "Added $BIN_DIR to PATH in $SHELL_RC"
110
+ warn "Run ${CYAN}source $SHELL_RC${RESET} or open a new terminal to use 'qveris'"
111
+ fi
112
+ export PATH="$BIN_DIR:$PATH"
113
+ ok "qveris installed at $QVERIS_BIN"
114
+ fi
115
+
116
+ # ── 6. Print version and next steps ──────────────────────────────────
117
+ VERSION=$(qveris --version 2>/dev/null || echo "unknown")
118
+
119
+ echo ""
120
+ echo -e " ${GREEN}${BOLD}QVeris CLI installed successfully!${RESET} ${DIM}($VERSION)${RESET}"
121
+ echo ""
122
+ echo -e " ${BOLD}Get started:${RESET}"
123
+ echo -e " ${CYAN}qveris login${RESET} Authenticate"
124
+ echo -e " ${CYAN}qveris discover${RESET} \"weather API\" Find capabilities"
125
+ echo -e " ${CYAN}qveris inspect${RESET} 1 View tool details"
126
+ echo -e " ${CYAN}qveris call${RESET} 1 --params '{...}' Execute a tool"
127
+ echo ""
128
+ echo -e " ${DIM}Docs: https://qveris.ai/docs${RESET}"
129
+ echo ""
@@ -0,0 +1,96 @@
1
+ import { resolve } from "../config/resolve.mjs";
2
+ import { CliError } from "../errors/handler.mjs";
3
+
4
+ function getBaseUrl(flagValue) {
5
+ return resolve("base_url", flagValue).value;
6
+ }
7
+
8
+ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs = 30000, apiKey, baseUrl }) {
9
+ const controller = new AbortController();
10
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
11
+
12
+ try {
13
+ const url = new URL(baseUrl.replace(/\/$/, "") + path);
14
+ for (const [key, value] of Object.entries(query)) {
15
+ if (value !== undefined && value !== null) {
16
+ url.searchParams.set(key, String(value));
17
+ }
18
+ }
19
+
20
+ const response = await fetch(url.toString(), {
21
+ method,
22
+ headers: {
23
+ Authorization: `Bearer ${apiKey}`,
24
+ "Content-Type": "application/json",
25
+ },
26
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
27
+ signal: controller.signal,
28
+ });
29
+
30
+ if (!response.ok) {
31
+ const status = response.status;
32
+ const rawText = (await response.text()).slice(0, 8192);
33
+ let errorDetail, jsonBody;
34
+ try {
35
+ jsonBody = JSON.parse(rawText);
36
+ errorDetail = jsonBody.error_message || jsonBody.message || null;
37
+ } catch { /* not JSON */ }
38
+ // For known error codes, pass errorDetail only if it's a meaningful message
39
+ // (not raw JSON); otherwise let the CliError template message apply
40
+ if (status === 401 || status === 403) throw new CliError("AUTH_INVALID_KEY", errorDetail);
41
+ if (status === 402) throw new CliError("CREDITS_INSUFFICIENT", errorDetail);
42
+ if (status === 429) throw new CliError("RATE_LIMITED", errorDetail);
43
+ const err = new CliError("API_ERROR", `HTTP ${status}: ${errorDetail || rawText}`);
44
+ if (jsonBody) err.responseData = jsonBody;
45
+ throw err;
46
+ }
47
+
48
+ try {
49
+ return await response.json();
50
+ } catch {
51
+ throw new CliError("API_ERROR", "Invalid JSON response from API");
52
+ }
53
+ } catch (err) {
54
+ if (err instanceof CliError) throw err;
55
+ if (err.name === "AbortError") throw new CliError("NET_TIMEOUT");
56
+ throw err;
57
+ } finally {
58
+ clearTimeout(timeout);
59
+ }
60
+ }
61
+
62
+ export async function discoverTools({ apiKey, baseUrl: baseUrlFlag, query, limit = 5, timeoutMs = 30000 }) {
63
+ const baseUrl = getBaseUrl(baseUrlFlag);
64
+ return requestJson("/search", { apiKey, baseUrl, body: { query, limit }, timeoutMs });
65
+ }
66
+
67
+ export async function inspectToolsByIds({ apiKey, baseUrl: baseUrlFlag, toolIds, discoveryId, timeoutMs = 30000 }) {
68
+ const baseUrl = getBaseUrl(baseUrlFlag);
69
+ const body = { tool_ids: toolIds };
70
+ if (discoveryId) body.search_id = discoveryId;
71
+ return requestJson("/tools/by-ids", { apiKey, baseUrl, body, timeoutMs });
72
+ }
73
+
74
+ export async function callTool({
75
+ apiKey,
76
+ baseUrl: baseUrlFlag,
77
+ toolId,
78
+ discoveryId,
79
+ parameters,
80
+ maxResponseSize = 102400,
81
+ timeoutMs = 120000,
82
+ }) {
83
+ const baseUrl = getBaseUrl(baseUrlFlag);
84
+ return requestJson("/tools/execute", {
85
+ apiKey,
86
+ baseUrl,
87
+ query: { tool_id: toolId },
88
+ body: {
89
+ search_id: discoveryId,
90
+ parameters,
91
+ max_response_size: maxResponseSize,
92
+ },
93
+ timeoutMs,
94
+ });
95
+ }
96
+
@@ -0,0 +1,26 @@
1
+ import { resolve } from "../config/resolve.mjs";
2
+ import { CliError } from "../errors/handler.mjs";
3
+
4
+ const PLACEHOLDER_PATTERNS = [
5
+ /^your[_-]?(qveris)?[_-]?api[_-]?key/i,
6
+ /^sk-1_xxx/,
7
+ /^sk-1_$/,
8
+ /^YOUR_/,
9
+ ];
10
+
11
+ export function resolveApiKey(flagValue) {
12
+ const { value } = resolve("api_key", flagValue);
13
+
14
+ if (!value || !value.trim()) {
15
+ throw new CliError("AUTH_MISSING_KEY");
16
+ }
17
+
18
+ const trimmed = value.trim();
19
+ for (const pattern of PLACEHOLDER_PATTERNS) {
20
+ if (pattern.test(trimmed)) {
21
+ throw new CliError("AUTH_MISSING_KEY", "API key appears to be a placeholder. Set a real key.");
22
+ }
23
+ }
24
+
25
+ return trimmed;
26
+ }