@qverisai/cli 0.1.0 → 0.2.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 CHANGED
@@ -242,6 +242,28 @@ qveris discover "weather" --api-key "sk-1_..."
242
242
 
243
243
  **Resolution order:** `--api-key` flag > `QVERIS_API_KEY` env > config file
244
244
 
245
+ ### Region
246
+
247
+ The API region is auto-detected from your key prefix:
248
+
249
+ | Key prefix | Region | Base URL |
250
+ |------------|--------|----------|
251
+ | `sk-xxx` | Global | `https://qveris.ai/api/v1` |
252
+ | `sk-cn-xxx` | China | `https://qveris.cn/api/v1` |
253
+
254
+ No extra configuration needed. To override manually:
255
+
256
+ ```bash
257
+ # Via environment variable
258
+ export QVERIS_REGION=cn
259
+
260
+ # Or set a custom base URL
261
+ export QVERIS_BASE_URL=https://custom.endpoint/api/v1
262
+
263
+ # Or per-command
264
+ qveris discover "weather" --base-url https://qveris.cn/api/v1
265
+ ```
266
+
245
267
  ### Config File
246
268
 
247
269
  Located at `~/.config/qveris/config.json` (respects `XDG_CONFIG_HOME`).
@@ -313,9 +335,9 @@ QVeris CLI uses only Node.js built-in APIs. No `chalk`, no `commander`, no `yarg
313
335
 
314
336
  ## Links
315
337
 
316
- - Website: https://qveris.ai
338
+ - Website: https://qveris.ai (global) / https://qveris.cn (China)
317
339
  - API Docs: https://qveris.ai/docs
318
- - Get API Key: https://qveris.ai/account?page=api-keys
340
+ - Get API Key: https://qveris.ai/account?page=api-keys (global) / https://qveris.cn/account?page=api-keys (China)
319
341
  - GitHub: https://github.com/QVerisAI/QVerisAI
320
342
 
321
343
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qverisai/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "QVeris CLI -- discover, inspect, and call 10,000+ capabilities",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +1,8 @@
1
- import { resolve } from "../config/resolve.mjs";
1
+ import { resolveBaseUrl } from "../config/region.mjs";
2
2
  import { CliError } from "../errors/handler.mjs";
3
3
 
4
- function getBaseUrl(flagValue) {
5
- return resolve("base_url", flagValue).value;
4
+ function getBaseUrl(baseUrlFlag, apiKey) {
5
+ return resolveBaseUrl({ baseUrlFlag, apiKey }).baseUrl;
6
6
  }
7
7
 
8
8
  async function requestJson(path, { method = "POST", query = {}, body, timeoutMs = 30000, apiKey, baseUrl }) {
@@ -10,7 +10,7 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
10
10
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
11
11
 
12
12
  try {
13
- const url = new URL(baseUrl.replace(/\/$/, "") + path);
13
+ const url = new URL(baseUrl.replace(/\/+$/, "") + path);
14
14
  for (const [key, value] of Object.entries(query)) {
15
15
  if (value !== undefined && value !== null) {
16
16
  url.searchParams.set(key, String(value));
@@ -35,8 +35,6 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
35
35
  jsonBody = JSON.parse(rawText);
36
36
  errorDetail = jsonBody.error_message || jsonBody.message || null;
37
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
38
  if (status === 401 || status === 403) throw new CliError("AUTH_INVALID_KEY", errorDetail);
41
39
  if (status === 402) throw new CliError("CREDITS_INSUFFICIENT", errorDetail);
42
40
  if (status === 429) throw new CliError("RATE_LIMITED", errorDetail);
@@ -60,12 +58,12 @@ async function requestJson(path, { method = "POST", query = {}, body, timeoutMs
60
58
  }
61
59
 
62
60
  export async function discoverTools({ apiKey, baseUrl: baseUrlFlag, query, limit = 5, timeoutMs = 30000 }) {
63
- const baseUrl = getBaseUrl(baseUrlFlag);
61
+ const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
64
62
  return requestJson("/search", { apiKey, baseUrl, body: { query, limit }, timeoutMs });
65
63
  }
66
64
 
67
65
  export async function inspectToolsByIds({ apiKey, baseUrl: baseUrlFlag, toolIds, discoveryId, timeoutMs = 30000 }) {
68
- const baseUrl = getBaseUrl(baseUrlFlag);
66
+ const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
69
67
  const body = { tool_ids: toolIds };
70
68
  if (discoveryId) body.search_id = discoveryId;
71
69
  return requestJson("/tools/by-ids", { apiKey, baseUrl, body, timeoutMs });
@@ -80,7 +78,7 @@ export async function callTool({
80
78
  maxResponseSize = 102400,
81
79
  timeoutMs = 120000,
82
80
  }) {
83
- const baseUrl = getBaseUrl(baseUrlFlag);
81
+ const baseUrl = getBaseUrl(baseUrlFlag, apiKey);
84
82
  return requestJson("/tools/execute", {
85
83
  apiKey,
86
84
  baseUrl,
@@ -93,4 +91,3 @@ export async function callTool({
93
91
  timeoutMs,
94
92
  });
95
93
  }
96
-
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Region resolution for QVeris API.
3
+ *
4
+ * Priority: --base-url flag > QVERIS_BASE_URL env > QVERIS_REGION env > key prefix auto-detect > default (global)
5
+ *
6
+ * Key prefix convention:
7
+ * sk-cn-xxx → cn region (qveris.cn)
8
+ * sk-xxx → global (qveris.ai)
9
+ */
10
+
11
+ const REGION_URLS = {
12
+ global: "https://qveris.ai/api/v1",
13
+ cn: "https://qveris.cn/api/v1",
14
+ };
15
+
16
+ /**
17
+ * Detect region from API key prefix.
18
+ * @param {string} apiKey
19
+ * @returns {"cn"|"global"}
20
+ */
21
+ export function detectRegionFromKey(apiKey) {
22
+ if (typeof apiKey === "string" && apiKey.startsWith("sk-cn-")) return "cn";
23
+ return "global";
24
+ }
25
+
26
+ /**
27
+ * Resolve the base URL for the QVeris API.
28
+ *
29
+ * Priority:
30
+ * 1. Explicit base URL (--base-url flag or QVERIS_BASE_URL env)
31
+ * 2. Explicit region (QVERIS_REGION env or config)
32
+ * 3. Auto-detect from API key prefix
33
+ * 4. Default: global (qveris.ai)
34
+ *
35
+ * @param {{ baseUrlFlag?: string, apiKey?: string }} options
36
+ * @returns {{ baseUrl: string, region: string, source: string }}
37
+ */
38
+ export function resolveBaseUrl({ baseUrlFlag, apiKey } = {}) {
39
+ // 1. Explicit base URL flag or env var
40
+ if (baseUrlFlag) {
41
+ return { baseUrl: baseUrlFlag.replace(/\/+$/, ""), region: "custom", source: "flag" };
42
+ }
43
+ if (process.env.QVERIS_BASE_URL) {
44
+ return { baseUrl: process.env.QVERIS_BASE_URL.replace(/\/+$/, ""), region: "custom", source: "env (QVERIS_BASE_URL)" };
45
+ }
46
+
47
+ // 2. Explicit region env var
48
+ if (process.env.QVERIS_REGION) {
49
+ const region = process.env.QVERIS_REGION.toLowerCase();
50
+ const url = REGION_URLS[region] || REGION_URLS.global;
51
+ return { baseUrl: url, region, source: "env (QVERIS_REGION)" };
52
+ }
53
+
54
+ // 3. Auto-detect from API key prefix
55
+ if (apiKey) {
56
+ const region = detectRegionFromKey(apiKey);
57
+ return { baseUrl: REGION_URLS[region], region, source: "auto (key prefix)" };
58
+ }
59
+
60
+ // 4. Default
61
+ return { baseUrl: REGION_URLS.global, region: "global", source: "default" };
62
+ }
63
+
64
+ export { REGION_URLS };