@qverisai/cli 0.4.0 → 0.6.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 +210 -42
- package/package.json +6 -3
- package/src/client/api.mjs +63 -1
- package/src/client/auth.mjs +8 -4
- package/src/commands/completions.mjs +1 -1
- package/src/commands/credits.mjs +18 -4
- package/src/commands/discover.mjs +2 -2
- package/src/commands/doctor.mjs +1 -1
- package/src/commands/init.mjs +378 -0
- package/src/commands/interactive.mjs +4 -5
- package/src/commands/ledger.mjs +186 -0
- package/src/commands/mcp.mjs +623 -0
- package/src/commands/usage.mjs +190 -0
- package/src/compat/aliases.mjs +4 -0
- package/src/errors/codes.mjs +18 -3
- package/src/main.mjs +101 -0
- package/src/output/audit.mjs +536 -0
- package/src/output/formatter.mjs +59 -6
package/README.md
CHANGED
|
@@ -2,53 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
Discover, inspect, and call 10,000+ API capabilities from your terminal.
|
|
4
4
|
|
|
5
|
+
New users can run the guided first-call wizard:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
qveris init
|
|
9
|
+
```
|
|
10
|
+
|
|
5
11
|
```
|
|
6
12
|
$ qveris discover "weather forecast API"
|
|
7
13
|
Found 5 capabilities matching your query
|
|
8
14
|
|
|
9
|
-
1.
|
|
10
|
-
weather_gov.
|
|
11
|
-
Returns a
|
|
12
|
-
relevance:
|
|
15
|
+
1. icons
|
|
16
|
+
weather_gov.icons.retrieve.v3.ad0b4d80
|
|
17
|
+
Returns a forecast icon. Icon services in API are deprecated.
|
|
18
|
+
relevance: 99% · success: 100.0% · latency: ~325ms · billing: Charged 5.75 credits per call
|
|
13
19
|
|
|
14
|
-
2.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
relevance:
|
|
20
|
+
2. Weather Information Retrieve
|
|
21
|
+
amap_webservice.weather.weatherinfo.retrieve.v3
|
|
22
|
+
Query current (base) or future (all) weather information for a specified city using adcode.
|
|
23
|
+
relevance: 96% · success: 83.0% · latency: ~467ms · billing: Charged 5.75 credits per call
|
|
18
24
|
|
|
19
25
|
$ qveris inspect 1
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
icons
|
|
27
|
+
weather_gov.icons.retrieve.v3.ad0b4d80
|
|
28
|
+
Returns a forecast icon. Icon services in API are deprecated.
|
|
22
29
|
|
|
23
|
-
Provider:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
Provider: weather.gov
|
|
31
|
+
Region: global
|
|
32
|
+
Latency: ~325ms
|
|
33
|
+
Success: 100.0%
|
|
34
|
+
Billing: Charged 5.75 credits per call
|
|
27
35
|
|
|
28
36
|
Parameters:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
set string required
|
|
38
|
+
.
|
|
39
|
+
timeOfDay string required The time of day for which to retrieve data (e.g., "morning", "afternoon")
|
|
40
|
+
|
|
41
|
+
first string required The first record to retrieve (index or ID)
|
|
42
|
+
|
|
43
|
+
size string optional
|
|
44
|
+
Font size
|
|
45
|
+
fontsize integer optional
|
|
46
|
+
Font size
|
|
36
47
|
|
|
37
48
|
Example:
|
|
38
|
-
{"
|
|
49
|
+
{"set":"land","first":"sct","timeOfDay":"day"}
|
|
50
|
+
|
|
51
|
+
$ qveris call 1 --params '{"set":"land","first":"sct","timeOfDay":"day"}'
|
|
52
|
+
✓ success · 311ms · 5.75 credits pre-settlement · (1078.15 remaining)
|
|
53
|
+
tool: amap_webservice.weather.weatherinfo.retrieve.v3 · id: d79cd15b-2f36-4ce1-bb3c-6ea28b5ecfa2
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
Billing:
|
|
56
|
+
Total estimated pre-settlement charge: 5.75 credits
|
|
57
|
+
Pre-settlement: 5.75 credits
|
|
58
|
+
Final charge status: qveris usage --mode search --execution-id d79cd15b-2f36-4ce1-bb3c-6ea28b5ecfa2
|
|
43
59
|
|
|
44
60
|
{
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
"status": "1",
|
|
62
|
+
"count": "1",
|
|
63
|
+
"info": "OK",
|
|
64
|
+
"infocode": "10000",
|
|
65
|
+
"lives": [
|
|
66
|
+
{
|
|
67
|
+
"province": "北京",
|
|
68
|
+
"city": "东城区",
|
|
69
|
+
"adcode": "110101",
|
|
70
|
+
"weather": "多云",
|
|
71
|
+
"temperature": "31",
|
|
72
|
+
"winddirection": "西南",
|
|
73
|
+
"windpower": "≤3",
|
|
74
|
+
"humidity": "50",
|
|
75
|
+
"reporttime": "2026-05-13 13:33:58",
|
|
76
|
+
"temperature_float": "31.0",
|
|
77
|
+
"humidity_float": "50.0"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
52
80
|
}
|
|
53
81
|
```
|
|
54
82
|
|
|
@@ -57,12 +85,16 @@ tool: weather_gov... · id: 7ebcaf9d-...
|
|
|
57
85
|
**One-liner (recommended):**
|
|
58
86
|
|
|
59
87
|
```bash
|
|
88
|
+
# Linux / macOS
|
|
60
89
|
curl -fsSL https://qveris.ai/cli/install | bash
|
|
90
|
+
|
|
91
|
+
# Windows (PowerShell)
|
|
92
|
+
irm https://qveris.ai/cli/install.ps1 | iex
|
|
61
93
|
```
|
|
62
94
|
|
|
63
95
|
**Or via npm:**
|
|
64
96
|
|
|
65
|
-
```
|
|
97
|
+
```sh
|
|
66
98
|
npm install -g @qverisai/cli
|
|
67
99
|
```
|
|
68
100
|
|
|
@@ -77,6 +109,10 @@ Requires Node.js 18+.
|
|
|
77
109
|
## Quick Start
|
|
78
110
|
|
|
79
111
|
```bash
|
|
112
|
+
# Guided path: auth → discover → inspect → call → usage/ledger guidance
|
|
113
|
+
qveris init
|
|
114
|
+
|
|
115
|
+
# Manual path
|
|
80
116
|
# 1. Authenticate
|
|
81
117
|
qveris login
|
|
82
118
|
|
|
@@ -87,7 +123,7 @@ qveris discover "weather forecast"
|
|
|
87
123
|
qveris inspect 1
|
|
88
124
|
|
|
89
125
|
# 4. Call it
|
|
90
|
-
qveris call 1 --params '{"
|
|
126
|
+
qveris call 1 --params '{"set":"land","first":"sct","timeOfDay":"day"}'
|
|
91
127
|
```
|
|
92
128
|
|
|
93
129
|
## Commands
|
|
@@ -96,9 +132,10 @@ qveris call 1 --params '{"wfo": "LWX", "x": 90, "y": 90}'
|
|
|
96
132
|
|
|
97
133
|
| Command | Description |
|
|
98
134
|
|---------|-------------|
|
|
99
|
-
| `qveris
|
|
135
|
+
| `qveris init` | Guided first-call wizard: auth, discover, inspect, call, and usage/ledger reconciliation guidance. |
|
|
136
|
+
| `qveris discover <query>` | Find capabilities by natural language. Shows tool ID, description, relevance, success rate, latency, billing rule, provider, tags, and region when available. |
|
|
100
137
|
| `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,
|
|
138
|
+
| `qveris call <id\|index>` | Execute a capability. Shows result data, execution time, pre-settlement billing, and remaining credits. |
|
|
102
139
|
|
|
103
140
|
### Account
|
|
104
141
|
|
|
@@ -108,6 +145,8 @@ qveris call 1 --params '{"wfo": "LWX", "x": 90, "y": 90}'
|
|
|
108
145
|
| `qveris logout` | Remove stored key |
|
|
109
146
|
| `qveris whoami` | Show current auth status, key source, and region |
|
|
110
147
|
| `qveris credits` | Check credit balance |
|
|
148
|
+
| `qveris usage` | Context-safe usage audit summary/search/export |
|
|
149
|
+
| `qveris ledger` | Context-safe credits ledger summary/search/export |
|
|
111
150
|
|
|
112
151
|
### Utilities
|
|
113
152
|
|
|
@@ -116,10 +155,26 @@ qveris call 1 --params '{"wfo": "LWX", "x": 90, "y": 90}'
|
|
|
116
155
|
| `qveris interactive` | Launch REPL mode (discover/inspect/call/codegen in one session) |
|
|
117
156
|
| `qveris doctor` | Self-check: Node.js version, API key, region, connectivity |
|
|
118
157
|
| `qveris config <subcommand>` | Manage CLI settings (set, get, list, reset, path) |
|
|
158
|
+
| `qveris mcp configure` | Generate MCP client config for Cursor, Claude Desktop, OpenCode, OpenClaw, or generic stdio; generate a `claude mcp add` command for Claude Code |
|
|
159
|
+
| `qveris mcp validate` | Validate an MCP config file, with optional live stdio tool probing |
|
|
119
160
|
| `qveris completions <shell>` | Generate shell completions (bash/zsh/fish) |
|
|
120
161
|
|
|
121
162
|
## Usage
|
|
122
163
|
|
|
164
|
+
### Init
|
|
165
|
+
|
|
166
|
+
Run the guided first-call wizard:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
qveris init
|
|
170
|
+
qveris init --query "weather forecast API"
|
|
171
|
+
qveris init --dry-run
|
|
172
|
+
qveris init --resume --params '{"city": "London"}'
|
|
173
|
+
qveris init --json
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`init` discovers a capability, inspects the selected result, calls it with sample parameters when available, and ends with exact `usage` / `ledger` commands so you can reconcile final billing. Use `--resume` after recoverable parameter or provider failures to reuse the last discovery session.
|
|
177
|
+
|
|
123
178
|
### Discover
|
|
124
179
|
|
|
125
180
|
Search for capabilities using natural language. Each result shows the tool name, provider, tool ID, description, and quality metrics to help you choose.
|
|
@@ -146,7 +201,7 @@ qveris inspect 1 2 3
|
|
|
146
201
|
|
|
147
202
|
### Call
|
|
148
203
|
|
|
149
|
-
Execute a tool. Results are automatically truncated for terminal display (4KB). Large results get an OSS download link.
|
|
204
|
+
Execute a tool. Results are automatically truncated for terminal display (4KB). Large results get an OSS download link. Billing shown here is pre-settlement; use `qveris usage` or `qveris ledger` for final charge status.
|
|
150
205
|
|
|
151
206
|
```bash
|
|
152
207
|
# Inline params
|
|
@@ -178,13 +233,14 @@ For terminal use, results larger than 4KB are automatically truncated. The CLI s
|
|
|
178
233
|
- The response schema so you know the data structure
|
|
179
234
|
|
|
180
235
|
```
|
|
181
|
-
✓ success · 1200ms · 5 credits
|
|
236
|
+
✓ success · 1200ms · 5 credits pre-settlement
|
|
182
237
|
|
|
183
238
|
Response truncated (32KB → 4KB preview)
|
|
184
239
|
|
|
185
240
|
Full content (valid 120 min):
|
|
186
241
|
https://qveris-tool-results-cache-bj.oss-cn-beijing...
|
|
187
|
-
Download: curl -o result.json '<url>'
|
|
242
|
+
Download: curl -o result.json '<url>' (Linux/macOS)
|
|
243
|
+
Download: Invoke-WebRequest -Uri '<url>' -OutFile result.json (Windows PowerShell)
|
|
188
244
|
|
|
189
245
|
Schema:
|
|
190
246
|
query: string
|
|
@@ -199,6 +255,76 @@ Preview:
|
|
|
199
255
|
|
|
200
256
|
For agent/script use (`--json` or piped output), the default increases to 20KB (matching the MCP server). Use `--max-size -1` for unlimited.
|
|
201
257
|
|
|
258
|
+
### MCP Configuration
|
|
259
|
+
|
|
260
|
+
Generate MCP client config without hand-editing JSON. Print mode is the default and uses `YOUR_QVERIS_API_KEY` placeholders so the output is safe to share. Placeholder output intentionally fails API key validation until you replace it or use `--include-key`. For Claude Code, the command prints a `claude mcp add` command instead of writing a config file.
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Print safe config for Cursor
|
|
264
|
+
qveris mcp configure --target cursor
|
|
265
|
+
|
|
266
|
+
# Write a working Cursor config using your resolved API key
|
|
267
|
+
qveris mcp configure --target cursor --write --include-key
|
|
268
|
+
|
|
269
|
+
# Other supported targets
|
|
270
|
+
qveris mcp configure --target claude-desktop --write --include-key
|
|
271
|
+
qveris mcp configure --target opencode --write --include-key
|
|
272
|
+
qveris mcp configure --target openclaw --write --include-key
|
|
273
|
+
qveris mcp configure --target claude-code
|
|
274
|
+
qveris mcp configure --target generic --json
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Config file locations and destinations:
|
|
278
|
+
- **Cursor**: `~/.cursor/mcp.json` (Linux/macOS) or `%USERPROFILE%\.cursor\mcp.json` (Windows)
|
|
279
|
+
- **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows)
|
|
280
|
+
- **Claude Code**: no config file is written; `qveris mcp configure --target claude-code` outputs a `claude mcp add` command
|
|
281
|
+
|
|
282
|
+
Validate an existing config:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Static config validation
|
|
286
|
+
qveris mcp validate --target cursor
|
|
287
|
+
|
|
288
|
+
# Live stdio probe: starts the MCP server and confirms discover/inspect/call are visible
|
|
289
|
+
qveris mcp validate --target cursor --probe
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Supported targets: `cursor`, `claude-desktop`, `claude-code`, `opencode`, `openclaw`, `generic`.
|
|
293
|
+
|
|
294
|
+
### Usage Audit
|
|
295
|
+
|
|
296
|
+
Use `qveris usage` to answer whether recent calls succeeded, failed, or charged credits. It defaults to `summary` mode to protect Agent context.
|
|
297
|
+
Summary mode requests service-side `summary=true` aggregates when available and falls back to bounded client-side aggregation for older deployments.
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# Aggregate recent usage by hour/day/week
|
|
301
|
+
qveris usage --mode summary --bucket hour
|
|
302
|
+
|
|
303
|
+
# Check one execution precisely
|
|
304
|
+
qveris usage --mode search --execution-id <execution_id> --json
|
|
305
|
+
|
|
306
|
+
# Find high-cost or suspicious records
|
|
307
|
+
qveris usage --mode search --min-credits 30 --max-credits 100 --json
|
|
308
|
+
qveris usage --mode search --charge-outcome failed_charged_review --json
|
|
309
|
+
|
|
310
|
+
# Export raw matching rows to a local JSONL file instead of stdout
|
|
311
|
+
qveris usage --mode export-file --start-date 2026-05-01 --end-date 2026-05-04
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Credits Ledger
|
|
315
|
+
|
|
316
|
+
Use `qveris ledger` to explain final credit balance movements. It also defaults to `summary` mode.
|
|
317
|
+
Summary mode requests service-side `summary=true` aggregates when available and falls back to bounded client-side aggregation for older deployments.
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
qveris ledger --mode summary --bucket day
|
|
321
|
+
qveris ledger --mode search --direction consume --min-credits 50 --json
|
|
322
|
+
qveris ledger --mode search --entry-type consume_tool_execute --json
|
|
323
|
+
qveris ledger --mode export-file --start-date 2026-05-01 --end-date 2026-05-04
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`summary` and `search` never print full history. Large record sets are written to `.qveris/exports/*.jsonl` with `--mode export-file`.
|
|
327
|
+
|
|
202
328
|
### Interactive Mode
|
|
203
329
|
|
|
204
330
|
```bash
|
|
@@ -230,12 +356,19 @@ qveris> exit
|
|
|
230
356
|
### API Key
|
|
231
357
|
|
|
232
358
|
```bash
|
|
233
|
-
# Option 1: Login (saves to
|
|
359
|
+
# Option 1: Login (saves to the qveris config file)
|
|
234
360
|
qveris login
|
|
235
361
|
|
|
236
362
|
# Option 2: Environment variable
|
|
363
|
+
# Linux / macOS
|
|
237
364
|
export QVERIS_API_KEY="sk-1_..."
|
|
238
365
|
|
|
366
|
+
# Windows (PowerShell)
|
|
367
|
+
$env:QVERIS_API_KEY="sk-1_..."
|
|
368
|
+
|
|
369
|
+
# Windows (CMD)
|
|
370
|
+
set QVERIS_API_KEY=sk-1_...
|
|
371
|
+
|
|
239
372
|
# Option 3: Per-command flag
|
|
240
373
|
qveris discover "weather" --api-key "sk-1_..."
|
|
241
374
|
```
|
|
@@ -260,18 +393,34 @@ No extra configuration needed. `qveris login` prompts for region selection inter
|
|
|
260
393
|
qveris login --token "sk-cn-xxx"
|
|
261
394
|
|
|
262
395
|
# Or environment variable
|
|
396
|
+
# Linux / macOS
|
|
263
397
|
export QVERIS_REGION=cn
|
|
264
398
|
|
|
399
|
+
# Windows (PowerShell)
|
|
400
|
+
$env:QVERIS_REGION="cn"
|
|
401
|
+
|
|
402
|
+
# Windows (CMD)
|
|
403
|
+
set QVERIS_REGION=cn
|
|
404
|
+
|
|
265
405
|
# Or custom base URL
|
|
406
|
+
# Linux / macOS
|
|
266
407
|
export QVERIS_BASE_URL=https://custom.endpoint/api/v1
|
|
267
408
|
|
|
409
|
+
# Windows (PowerShell)
|
|
410
|
+
$env:QVERIS_BASE_URL="https://custom.endpoint/api/v1"
|
|
411
|
+
|
|
412
|
+
# Windows (CMD)
|
|
413
|
+
set QVERIS_BASE_URL=https://custom.endpoint/api/v1
|
|
414
|
+
|
|
268
415
|
# Or per-command
|
|
269
416
|
qveris discover "weather" --base-url https://qveris.cn/api/v1
|
|
270
417
|
```
|
|
271
418
|
|
|
272
419
|
### Config File
|
|
273
420
|
|
|
274
|
-
Located at
|
|
421
|
+
Located at:
|
|
422
|
+
- **All platforms**: `$XDG_CONFIG_HOME/qveris/config.json` when `XDG_CONFIG_HOME` is set
|
|
423
|
+
- **Default**: `~/.config/qveris/config.json` (for example, `C:\Users\<user>\.config\qveris\config.json` on Windows)
|
|
275
424
|
|
|
276
425
|
```bash
|
|
277
426
|
qveris config list # View all settings with sources
|
|
@@ -302,6 +451,11 @@ qveris completions fish | source
|
|
|
302
451
|
| `--api-key <key>` | | Override API key |
|
|
303
452
|
| `--timeout <seconds>` | | Request timeout |
|
|
304
453
|
| `--max-size <bytes>` | | Response size limit (-1 = unlimited) |
|
|
454
|
+
| `--target <target>` | | MCP target |
|
|
455
|
+
| `--output <path>` | | MCP config output path |
|
|
456
|
+
| `--write` | | Write MCP config to disk |
|
|
457
|
+
| `--include-key` | | Include resolved API key instead of a placeholder |
|
|
458
|
+
| `--probe` | | Live-probe MCP tools during validation |
|
|
305
459
|
| `--no-color` | | Disable colors |
|
|
306
460
|
| `--version` | `-V` | Print version |
|
|
307
461
|
| `--help` | `-h` | Show help |
|
|
@@ -319,8 +473,22 @@ When used by agents or in scripts, the CLI auto-detects non-TTY environments:
|
|
|
319
473
|
|
|
320
474
|
```bash
|
|
321
475
|
# Agent workflow: discover → select → call → parse
|
|
476
|
+
|
|
477
|
+
# Linux / macOS
|
|
322
478
|
TOOL=$(qveris discover "weather" --json | jq -r '.results[0].tool_id')
|
|
323
479
|
qveris call "$TOOL" --params '{"city":"London"}' --json | jq '.result.data'
|
|
480
|
+
|
|
481
|
+
# Windows (PowerShell)
|
|
482
|
+
$TOOL = qveris discover "weather" --json | ConvertFrom-Json | Select-Object -ExpandProperty results | Select-Object -First 1 | Select-Object -ExpandProperty tool_id
|
|
483
|
+
qveris call $TOOL --params '{"city":"London"}' --json | ConvertFrom-Json | Select-Object -ExpandProperty result | Select-Object -ExpandProperty data
|
|
484
|
+
|
|
485
|
+
# Windows (CMD, interactive) - requires jq for Windows
|
|
486
|
+
for /f "tokens=*" %i in ('qveris discover "weather" --json ^| jq -r ".results[0].tool_id"') do set TOOL=%i
|
|
487
|
+
qveris call %TOOL% --params "{\"city\":\"London\"}" --json | jq ".result.data"
|
|
488
|
+
|
|
489
|
+
# Windows (.bat/.cmd script) - use %%i instead of %i
|
|
490
|
+
for /f "tokens=*" %%i in ('qveris discover "weather" --json ^| jq -r ".results[0].tool_id"') do set TOOL=%%i
|
|
491
|
+
qveris call %TOOL% --params "{\"city\":\"London\"}" --json | jq ".result.data"
|
|
324
492
|
```
|
|
325
493
|
|
|
326
494
|
## Exit Codes
|
|
@@ -343,7 +511,7 @@ QVeris CLI uses only Node.js built-in APIs. No `chalk`, no `commander`, no `yarg
|
|
|
343
511
|
- Website: https://qveris.ai (global) / https://qveris.cn (China)
|
|
344
512
|
- API Docs: https://qveris.ai/docs
|
|
345
513
|
- Get API Key: https://qveris.ai/account?page=api-keys (global) / https://qveris.cn/account?page=api-keys (China)
|
|
346
|
-
- GitHub: https://github.com/QVerisAI/
|
|
514
|
+
- GitHub: https://github.com/QVerisAI/qveris-agent-toolkit
|
|
347
515
|
|
|
348
516
|
## License
|
|
349
517
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qverisai/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "QVeris CLI
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "QVeris CLI for agent capability discovery, inspection, calling, and audit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"qveris": "./bin/qveris.mjs"
|
|
8
8
|
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test"
|
|
11
|
+
},
|
|
9
12
|
"engines": {
|
|
10
13
|
"node": ">=18"
|
|
11
14
|
},
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
],
|
|
25
28
|
"repository": {
|
|
26
29
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/QVerisAI/
|
|
30
|
+
"url": "https://github.com/QVerisAI/qveris-agent-toolkit",
|
|
28
31
|
"directory": "packages/cli"
|
|
29
32
|
},
|
|
30
33
|
"homepage": "https://qveris.ai",
|
package/src/client/api.mjs
CHANGED
|
@@ -36,7 +36,12 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
|
|
|
36
36
|
errorDetail = jsonBody.error_message || jsonBody.message || null;
|
|
37
37
|
} catch { /* not JSON */ }
|
|
38
38
|
if (status === 401 || status === 403) throw new CliError("AUTH_INVALID_KEY", errorDetail);
|
|
39
|
-
if (status === 402)
|
|
39
|
+
if (status === 402) {
|
|
40
|
+
const pricingHost = baseUrl.includes("qveris.cn") ? "https://qveris.cn" : "https://qveris.ai";
|
|
41
|
+
const err = new CliError("CREDITS_INSUFFICIENT", errorDetail);
|
|
42
|
+
err.hint = `Purchase credits at ${pricingHost}/pricing`;
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
40
45
|
if (status === 429) throw new CliError("RATE_LIMITED", errorDetail);
|
|
41
46
|
const err = new CliError("API_ERROR", `HTTP ${status}: ${errorDetail || rawText}`);
|
|
42
47
|
if (jsonBody) err.responseData = jsonBody;
|
|
@@ -57,6 +62,21 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
|
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
65
|
+
export function unwrapApiResponse(response) {
|
|
66
|
+
if (
|
|
67
|
+
response &&
|
|
68
|
+
typeof response === "object" &&
|
|
69
|
+
Object.prototype.hasOwnProperty.call(response, "status") &&
|
|
70
|
+
Object.prototype.hasOwnProperty.call(response, "data")
|
|
71
|
+
) {
|
|
72
|
+
if (response.status === "failure") {
|
|
73
|
+
throw new CliError("API_ERROR", response.message || "API request failed");
|
|
74
|
+
}
|
|
75
|
+
return response.data;
|
|
76
|
+
}
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
|
|
60
80
|
export async function discoverTools({ apiKey, baseUrl: baseUrlFlag, query, limit = 5, timeoutMs = 30000 }) {
|
|
61
81
|
const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
|
|
62
82
|
return requestJson("/search", { apiKey, baseUrl, body: { query, limit }, timeoutMs });
|
|
@@ -91,3 +111,45 @@ export async function callTool({
|
|
|
91
111
|
timeoutMs,
|
|
92
112
|
});
|
|
93
113
|
}
|
|
114
|
+
|
|
115
|
+
export async function getCredits({ apiKey, baseUrl: baseUrlFlag, timeoutMs = 30000 }) {
|
|
116
|
+
const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
|
|
117
|
+
return requestJson("/auth/credits", {
|
|
118
|
+
method: "GET",
|
|
119
|
+
apiKey,
|
|
120
|
+
baseUrl,
|
|
121
|
+
timeoutMs,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function getUsageHistory({
|
|
126
|
+
apiKey,
|
|
127
|
+
baseUrl: baseUrlFlag,
|
|
128
|
+
query = {},
|
|
129
|
+
timeoutMs = 30000,
|
|
130
|
+
}) {
|
|
131
|
+
const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
|
|
132
|
+
return requestJson("/auth/usage/history/v2", {
|
|
133
|
+
method: "GET",
|
|
134
|
+
apiKey,
|
|
135
|
+
baseUrl,
|
|
136
|
+
query,
|
|
137
|
+
timeoutMs,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function getCreditsLedger({
|
|
142
|
+
apiKey,
|
|
143
|
+
baseUrl: baseUrlFlag,
|
|
144
|
+
query = {},
|
|
145
|
+
timeoutMs = 30000,
|
|
146
|
+
}) {
|
|
147
|
+
const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
|
|
148
|
+
return requestJson("/auth/credits/ledger", {
|
|
149
|
+
method: "GET",
|
|
150
|
+
apiKey,
|
|
151
|
+
baseUrl,
|
|
152
|
+
query,
|
|
153
|
+
timeoutMs,
|
|
154
|
+
});
|
|
155
|
+
}
|
package/src/client/auth.mjs
CHANGED
|
@@ -16,11 +16,15 @@ export function resolveApiKey(flagValue) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const trimmed = value.trim();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
throw new CliError("AUTH_MISSING_KEY", "API key appears to be a placeholder. Set a real key.");
|
|
22
|
-
}
|
|
19
|
+
if (isPlaceholderApiKey(trimmed)) {
|
|
20
|
+
throw new CliError("AUTH_MISSING_KEY", "API key appears to be a placeholder. Set a real key.");
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
return trimmed;
|
|
26
24
|
}
|
|
25
|
+
|
|
26
|
+
export function isPlaceholderApiKey(value) {
|
|
27
|
+
if (typeof value !== "string") return true;
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
return PLACEHOLDER_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
30
|
+
}
|
package/src/commands/credits.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveApiKey } from "../client/auth.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { getCredits, unwrapApiResponse } from "../client/api.mjs";
|
|
3
3
|
import { resolveBaseUrl, getSiteUrl } from "../config/region.mjs";
|
|
4
4
|
import { bold, dim, cyan, yellow, green } from "../output/colors.mjs";
|
|
5
5
|
import { outputJson } from "../output/json.mjs";
|
|
@@ -13,16 +13,19 @@ export async function runCredits(flags) {
|
|
|
13
13
|
const spinner = flags.json ? { stop() {} } : createSpinner("Checking credits...");
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
|
-
|
|
17
|
-
const result = await discoverTools({ apiKey, baseUrl: flags.baseUrl, query: "credit balance", limit: 1, timeoutMs: 10000 });
|
|
16
|
+
const result = unwrapApiResponse(await getCredits({ apiKey, baseUrl, timeoutMs: 10000 }));
|
|
18
17
|
spinner.stop();
|
|
19
18
|
|
|
20
19
|
const credits = result.remaining_credits;
|
|
21
20
|
|
|
22
21
|
if (flags.json) {
|
|
23
|
-
outputJson(
|
|
22
|
+
outputJson(result);
|
|
24
23
|
} else if (credits !== undefined && credits !== null) {
|
|
25
24
|
console.log(`\n ${green("\u2713")} Credits remaining: ${bold(yellow(String(credits)))}`);
|
|
25
|
+
printBucket("Daily free", result.daily_free, "remaining", "total");
|
|
26
|
+
printBucket("Invite reward", result.invite_reward, "remaining", "total");
|
|
27
|
+
printBucket("Welcome bonus", result.welcome_bonus, "remaining", "initial");
|
|
28
|
+
printBucket("Purchased", result.purchased, "remaining", "total");
|
|
26
29
|
console.log(` ${dim("Manage at:")} ${cyan(accountUrl)}\n`);
|
|
27
30
|
} else {
|
|
28
31
|
console.log(`\n ${dim("Credit balance not available in API response.")}`);
|
|
@@ -33,3 +36,14 @@ export async function runCredits(flags) {
|
|
|
33
36
|
throw err;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
39
|
+
|
|
40
|
+
function printBucket(label, bucket, remainingKey, totalKey) {
|
|
41
|
+
if (!bucket || typeof bucket !== "object") return;
|
|
42
|
+
const remaining = bucket[remainingKey];
|
|
43
|
+
const total = bucket[totalKey];
|
|
44
|
+
if (remaining === undefined && total === undefined) return;
|
|
45
|
+
const parts = [];
|
|
46
|
+
if (remaining !== undefined) parts.push(`remaining ${yellow(String(remaining))}`);
|
|
47
|
+
if (total !== undefined) parts.push(`${dim("of")} ${String(total)}`);
|
|
48
|
+
console.log(` ${dim(label + ":")} ${parts.join(" ")}`);
|
|
49
|
+
}
|
|
@@ -8,6 +8,7 @@ import { createSpinner } from "../output/spinner.mjs";
|
|
|
8
8
|
|
|
9
9
|
export async function runDiscover(query, flags) {
|
|
10
10
|
const apiKey = resolveApiKey(flags.apiKey);
|
|
11
|
+
const { region, baseUrl: resolvedBaseUrl } = resolveBaseUrl({ baseUrlFlag: flags.baseUrl, apiKey });
|
|
11
12
|
const limit = parseInt(flags.limit, 10) || 5;
|
|
12
13
|
const timeoutMs = (parseInt(flags.timeout, 10) || 30) * 1000;
|
|
13
14
|
|
|
@@ -16,7 +17,7 @@ export async function runDiscover(query, flags) {
|
|
|
16
17
|
try {
|
|
17
18
|
const result = await discoverTools({
|
|
18
19
|
apiKey,
|
|
19
|
-
baseUrl:
|
|
20
|
+
baseUrl: resolvedBaseUrl,
|
|
20
21
|
query,
|
|
21
22
|
limit,
|
|
22
23
|
timeoutMs,
|
|
@@ -26,7 +27,6 @@ export async function runDiscover(query, flags) {
|
|
|
26
27
|
|
|
27
28
|
// Store richer session data for index resolution
|
|
28
29
|
const tools = result.results ?? [];
|
|
29
|
-
const { region, baseUrl: resolvedBaseUrl } = resolveBaseUrl({ baseUrlFlag: flags.baseUrl, apiKey });
|
|
30
30
|
writeSession({
|
|
31
31
|
discoveryId: result.search_id,
|
|
32
32
|
query,
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -30,7 +30,7 @@ export async function runDoctor(flags) {
|
|
|
30
30
|
// Test connectivity
|
|
31
31
|
process.stderr.write(` \u2026 Testing API connectivity...\r`);
|
|
32
32
|
try {
|
|
33
|
-
await discoverTools({ apiKey, baseUrl
|
|
33
|
+
await discoverTools({ apiKey, baseUrl, query: "test", limit: 1, timeoutMs: 10000 });
|
|
34
34
|
console.log(` ${green("\u2713")} API connectivity OK `);
|
|
35
35
|
} catch (err) {
|
|
36
36
|
console.log(` ${red("\u2718")} API connectivity failed: ${err.message} `);
|