@qverisai/cli 0.3.0 → 0.5.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 +59 -14
- package/package.json +1 -1
- package/src/client/api.mjs +63 -1
- package/src/commands/completions.mjs +1 -1
- package/src/commands/config.mjs +9 -1
- package/src/commands/credits.mjs +23 -6
- package/src/commands/discover.mjs +5 -1
- package/src/commands/doctor.mjs +6 -1
- package/src/commands/interactive.mjs +6 -5
- package/src/commands/ledger.mjs +186 -0
- package/src/commands/login.mjs +77 -9
- package/src/commands/usage.mjs +190 -0
- package/src/compat/aliases.mjs +4 -0
- package/src/config/region.mjs +20 -0
- package/src/main.mjs +59 -1
- package/src/output/audit.mjs +536 -0
- package/src/output/formatter.mjs +59 -6
package/src/commands/login.mjs
CHANGED
|
@@ -5,11 +5,10 @@ import { resolve } from "../config/resolve.mjs";
|
|
|
5
5
|
import { setConfigValue, deleteConfigValue } from "../config/store.mjs";
|
|
6
6
|
import { discoverTools } from "../client/api.mjs";
|
|
7
7
|
import { VERSION } from "../config/defaults.mjs";
|
|
8
|
+
import { resolveBaseUrl, getSiteUrl } from "../config/region.mjs";
|
|
8
9
|
import { bold, green, red, dim, cyan } from "../output/colors.mjs";
|
|
9
10
|
import { printLoginBanner } from "../output/banner.mjs";
|
|
10
11
|
|
|
11
|
-
const ACCOUNT_URL = "https://qveris.ai/account?page=api-keys";
|
|
12
|
-
|
|
13
12
|
function openBrowser(url) {
|
|
14
13
|
const cmds = { darwin: "open", win32: "cmd", linux: "xdg-open" };
|
|
15
14
|
const cmd = cmds[platform()] || "xdg-open";
|
|
@@ -87,9 +86,54 @@ function prompt(question) {
|
|
|
87
86
|
});
|
|
88
87
|
}
|
|
89
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Prompt user to select region interactively.
|
|
91
|
+
* Skipped if region is already determined via --base-url, QVERIS_REGION, or QVERIS_BASE_URL.
|
|
92
|
+
*/
|
|
93
|
+
function promptRegion() {
|
|
94
|
+
return new Promise((pResolve, pReject) => {
|
|
95
|
+
console.log(` ${bold("Select your region / 选择站点区域:")}\n`);
|
|
96
|
+
console.log(` ${cyan("1)")} Global — qveris.ai (International users)`);
|
|
97
|
+
console.log(` ${cyan("2)")} China — qveris.cn (中国大陆用户)\n`);
|
|
98
|
+
|
|
99
|
+
if (!process.stdin.isTTY) {
|
|
100
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
101
|
+
rl.question(" Enter 1 or 2: ", (answer) => { rl.close(); pResolve(answer.trim()); });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
process.stderr.write(" Enter 1 or 2: ");
|
|
106
|
+
|
|
107
|
+
const cleanup = () => {
|
|
108
|
+
process.stdin.removeListener("data", onData);
|
|
109
|
+
try { process.stdin.setRawMode(false); } catch {}
|
|
110
|
+
process.stdin.pause();
|
|
111
|
+
};
|
|
112
|
+
const onExit = () => { try { process.stdin.setRawMode(false); } catch {} };
|
|
113
|
+
process.once("exit", onExit);
|
|
114
|
+
|
|
115
|
+
process.stdin.setRawMode(true);
|
|
116
|
+
process.stdin.resume();
|
|
117
|
+
process.stdin.setEncoding("utf-8");
|
|
118
|
+
|
|
119
|
+
const onData = (chunk) => {
|
|
120
|
+
const ch = chunk[0];
|
|
121
|
+
if (ch === "\x03") { cleanup(); process.removeListener("exit", onExit); process.stderr.write("\n"); pReject(new Error("Aborted")); return; }
|
|
122
|
+
if (ch === "1" || ch === "2") {
|
|
123
|
+
cleanup();
|
|
124
|
+
process.removeListener("exit", onExit);
|
|
125
|
+
process.stderr.write(`${ch}\n`);
|
|
126
|
+
pResolve(ch);
|
|
127
|
+
}
|
|
128
|
+
// Ignore other keys
|
|
129
|
+
};
|
|
130
|
+
process.stdin.on("data", onData);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
90
134
|
export async function runLogin(flags) {
|
|
91
135
|
if (flags.token) {
|
|
92
|
-
await validateAndSave(flags.token);
|
|
136
|
+
await validateAndSave(flags.token, flags.baseUrl);
|
|
93
137
|
return;
|
|
94
138
|
}
|
|
95
139
|
|
|
@@ -97,10 +141,29 @@ export async function runLogin(flags) {
|
|
|
97
141
|
printLoginBanner({ version: VERSION, noColor: flags.noColor });
|
|
98
142
|
}
|
|
99
143
|
|
|
100
|
-
|
|
144
|
+
// Determine region: if already set via flag/env, use that; otherwise ask the user.
|
|
145
|
+
const { region: presetRegion, baseUrl: presetBaseUrl, source: regionSource } = resolveBaseUrl({ baseUrlFlag: flags.baseUrl });
|
|
146
|
+
let region;
|
|
147
|
+
let baseUrl = presetBaseUrl;
|
|
148
|
+
if (regionSource !== "default") {
|
|
149
|
+
// Region explicitly configured — use it directly
|
|
150
|
+
region = presetRegion;
|
|
151
|
+
} else {
|
|
152
|
+
// No explicit region — let user choose interactively
|
|
153
|
+
let choice;
|
|
154
|
+
try {
|
|
155
|
+
choice = await promptRegion();
|
|
156
|
+
} catch {
|
|
157
|
+
return; // Ctrl+C
|
|
158
|
+
}
|
|
159
|
+
region = choice === "2" ? "cn" : "global";
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const accountUrl = `${getSiteUrl(region, baseUrl)}/account?page=api-keys`;
|
|
163
|
+
console.log(`\n Get your API key at: ${cyan(accountUrl)}\n`);
|
|
101
164
|
|
|
102
165
|
if (!flags.noBrowser) {
|
|
103
|
-
openBrowser(
|
|
166
|
+
openBrowser(accountUrl);
|
|
104
167
|
}
|
|
105
168
|
|
|
106
169
|
let key;
|
|
@@ -116,18 +179,21 @@ export async function runLogin(flags) {
|
|
|
116
179
|
return;
|
|
117
180
|
}
|
|
118
181
|
|
|
119
|
-
await validateAndSave(key);
|
|
182
|
+
await validateAndSave(key, flags.baseUrl);
|
|
120
183
|
}
|
|
121
184
|
|
|
122
|
-
async function validateAndSave(key) {
|
|
185
|
+
async function validateAndSave(key, baseUrlFlag) {
|
|
123
186
|
process.stderr.write(` Validating key...`);
|
|
124
187
|
|
|
188
|
+
const { baseUrl, region, source } = resolveBaseUrl({ baseUrlFlag, apiKey: key });
|
|
189
|
+
|
|
125
190
|
try {
|
|
126
|
-
await discoverTools({ apiKey: key, query: "test", limit: 1, timeoutMs: 10000 });
|
|
191
|
+
await discoverTools({ apiKey: key, baseUrl, query: "test", limit: 1, timeoutMs: 10000 });
|
|
127
192
|
setConfigValue("api_key", key);
|
|
128
193
|
const masked = key.slice(0, 6) + "..." + key.slice(-4);
|
|
129
194
|
console.error(`\r\x1b[K`);
|
|
130
195
|
console.log(` ${green("\u2713")} Authenticated as ${bold(masked)}`);
|
|
196
|
+
console.log(` ${dim("Region:")} ${region} ${dim(`(${source})`)}`);
|
|
131
197
|
console.log(` ${dim("Key saved to config.")}`);
|
|
132
198
|
} catch {
|
|
133
199
|
console.error(`\r\x1b[K`);
|
|
@@ -151,15 +217,17 @@ export async function runWhoami(flags) {
|
|
|
151
217
|
}
|
|
152
218
|
|
|
153
219
|
const masked = key.slice(0, 6) + "..." + key.slice(-4);
|
|
220
|
+
const { baseUrl, region, source: regionSource } = resolveBaseUrl({ baseUrlFlag: flags.baseUrl, apiKey: key });
|
|
154
221
|
|
|
155
222
|
process.stderr.write(` Validating...`);
|
|
156
223
|
|
|
157
224
|
try {
|
|
158
|
-
await discoverTools({ apiKey: key, query: "test", limit: 1, timeoutMs: 10000 });
|
|
225
|
+
await discoverTools({ apiKey: key, baseUrl, query: "test", limit: 1, timeoutMs: 10000 });
|
|
159
226
|
process.stderr.write("\r\x1b[K");
|
|
160
227
|
console.log(`\n ${green("\u2713")} Authenticated`);
|
|
161
228
|
console.log(` Key: ${bold(masked)}`);
|
|
162
229
|
console.log(` Source: ${dim(source)}`);
|
|
230
|
+
console.log(` Region: ${region} ${dim(`(${regionSource})`)}`);
|
|
163
231
|
} catch {
|
|
164
232
|
console.error(`\r\x1b[K`);
|
|
165
233
|
console.log(`\n ${red("\u2718")} Key ${masked} is ${red("invalid")}`);
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { resolveApiKey } from "../client/auth.mjs";
|
|
2
|
+
import { getUsageHistory, unwrapApiResponse } from "../client/api.mjs";
|
|
3
|
+
import { outputJson } from "../output/json.mjs";
|
|
4
|
+
import { createSpinner } from "../output/spinner.mjs";
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_PAGE_SIZE,
|
|
7
|
+
MAX_EXPORT_ROWS,
|
|
8
|
+
MAX_SEARCH_SCAN_ROWS,
|
|
9
|
+
MAX_SUMMARY_ROWS,
|
|
10
|
+
buildUsageQuery,
|
|
11
|
+
buildUsageSummary,
|
|
12
|
+
buildUsageSummaryFromServer,
|
|
13
|
+
chooseBucket,
|
|
14
|
+
clampLimit,
|
|
15
|
+
extractItems,
|
|
16
|
+
extractTotal,
|
|
17
|
+
formatExportMetadata,
|
|
18
|
+
formatUsageRows,
|
|
19
|
+
formatUsageSummary,
|
|
20
|
+
matchesUsageFilters,
|
|
21
|
+
pickUsageRow,
|
|
22
|
+
resolveDateRange,
|
|
23
|
+
resolveMode,
|
|
24
|
+
writeJsonlExport,
|
|
25
|
+
} from "../output/audit.mjs";
|
|
26
|
+
|
|
27
|
+
export async function runUsage(flags) {
|
|
28
|
+
const apiKey = resolveApiKey(flags.apiKey);
|
|
29
|
+
const mode = resolveMode(flags.mode);
|
|
30
|
+
const timeoutMs = (parseInt(flags.timeout, 10) || 30) * 1000;
|
|
31
|
+
const limit = clampLimit(flags.limit);
|
|
32
|
+
const { startDate, endDate } = resolveDateRange(flags);
|
|
33
|
+
const bucket = chooseBucket(startDate, endDate, flags.bucket);
|
|
34
|
+
|
|
35
|
+
const spinner = flags.json ? { stop() {} } : createSpinner("Querying usage history...");
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
if (mode === "search") {
|
|
39
|
+
const { rows, total, partial } = await collectUsageRows({
|
|
40
|
+
apiKey,
|
|
41
|
+
flags,
|
|
42
|
+
timeoutMs,
|
|
43
|
+
limit,
|
|
44
|
+
maxRows: MAX_SEARCH_SCAN_ROWS,
|
|
45
|
+
stopWhenLimitReached: true,
|
|
46
|
+
});
|
|
47
|
+
spinner.stop();
|
|
48
|
+
const payload = {
|
|
49
|
+
mode,
|
|
50
|
+
start_date: startDate,
|
|
51
|
+
end_date: endDate,
|
|
52
|
+
shown_records: rows.length,
|
|
53
|
+
matched_records: total,
|
|
54
|
+
truncated: partial,
|
|
55
|
+
items: rows.map(pickUsageRow),
|
|
56
|
+
};
|
|
57
|
+
if (flags.json) outputJson(payload);
|
|
58
|
+
else console.log(formatUsageRows(rows, { total, partial }));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (mode === "export_file") {
|
|
63
|
+
const { rows, total, partial } = await collectUsageRows({
|
|
64
|
+
apiKey,
|
|
65
|
+
flags,
|
|
66
|
+
timeoutMs,
|
|
67
|
+
limit: MAX_EXPORT_ROWS,
|
|
68
|
+
maxRows: MAX_EXPORT_ROWS,
|
|
69
|
+
stopWhenLimitReached: false,
|
|
70
|
+
});
|
|
71
|
+
spinner.stop();
|
|
72
|
+
const metadata = writeJsonlExport("usage_history", rows, {
|
|
73
|
+
start_date: startDate,
|
|
74
|
+
end_date: endDate,
|
|
75
|
+
matched_records: total,
|
|
76
|
+
truncated: partial,
|
|
77
|
+
filters: usageFilters(flags),
|
|
78
|
+
});
|
|
79
|
+
if (flags.json) outputJson(metadata);
|
|
80
|
+
else console.log(formatExportMetadata(metadata));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const summaryResponse = unwrapApiResponse(await getUsageHistory({
|
|
85
|
+
apiKey,
|
|
86
|
+
baseUrl: flags.baseUrl,
|
|
87
|
+
query: buildUsageQuery(flags, {
|
|
88
|
+
page: 1,
|
|
89
|
+
pageSize: limit,
|
|
90
|
+
mode: "summary",
|
|
91
|
+
}),
|
|
92
|
+
timeoutMs,
|
|
93
|
+
}));
|
|
94
|
+
if (summaryResponse?.summary) {
|
|
95
|
+
spinner.stop();
|
|
96
|
+
const rows = extractItems(summaryResponse);
|
|
97
|
+
const total = extractTotal(summaryResponse, rows.length);
|
|
98
|
+
const summary = buildUsageSummaryFromServer(summaryResponse.summary, { startDate, endDate, bucket });
|
|
99
|
+
if (flags.json) {
|
|
100
|
+
outputJson({
|
|
101
|
+
mode,
|
|
102
|
+
summary,
|
|
103
|
+
scanned_records: rows.length,
|
|
104
|
+
matched_records: total,
|
|
105
|
+
truncated: false,
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
console.log(formatUsageSummary(summary, { scannedRows: rows.length, total, partial: false }));
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { rows, total, partial, scannedRows } = await collectUsageRows({
|
|
114
|
+
apiKey,
|
|
115
|
+
flags,
|
|
116
|
+
timeoutMs,
|
|
117
|
+
limit: MAX_SUMMARY_ROWS,
|
|
118
|
+
maxRows: MAX_SUMMARY_ROWS,
|
|
119
|
+
stopWhenLimitReached: false,
|
|
120
|
+
});
|
|
121
|
+
spinner.stop();
|
|
122
|
+
const summary = buildUsageSummary(rows, { startDate, endDate, bucket });
|
|
123
|
+
if (flags.json) {
|
|
124
|
+
outputJson({
|
|
125
|
+
mode,
|
|
126
|
+
summary,
|
|
127
|
+
scanned_records: scannedRows,
|
|
128
|
+
matched_records: total,
|
|
129
|
+
truncated: partial,
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
console.log(formatUsageSummary(summary, { scannedRows, total, partial }));
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
spinner.stop();
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function collectUsageRows({ apiKey, flags, timeoutMs, limit, maxRows, stopWhenLimitReached }) {
|
|
141
|
+
const rows = [];
|
|
142
|
+
let page = 1;
|
|
143
|
+
let total;
|
|
144
|
+
let scannedRows = 0;
|
|
145
|
+
let partial = false;
|
|
146
|
+
|
|
147
|
+
while (rows.length < limit && scannedRows < maxRows) {
|
|
148
|
+
const query = buildUsageQuery(flags, { page, pageSize: Math.min(DEFAULT_PAGE_SIZE, maxRows - scannedRows) });
|
|
149
|
+
const response = unwrapApiResponse(await getUsageHistory({
|
|
150
|
+
apiKey,
|
|
151
|
+
baseUrl: flags.baseUrl,
|
|
152
|
+
query,
|
|
153
|
+
timeoutMs,
|
|
154
|
+
}));
|
|
155
|
+
const items = extractItems(response);
|
|
156
|
+
if (total === undefined) total = extractTotal(response, undefined);
|
|
157
|
+
if (items.length === 0) break;
|
|
158
|
+
scannedRows += items.length;
|
|
159
|
+
for (const item of items) {
|
|
160
|
+
if (!matchesUsageFilters(item, flags)) continue;
|
|
161
|
+
rows.push(item);
|
|
162
|
+
if (stopWhenLimitReached && rows.length >= limit) break;
|
|
163
|
+
}
|
|
164
|
+
if (stopWhenLimitReached && rows.length >= limit) {
|
|
165
|
+
partial = Boolean(total && total > scannedRows);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
if (total !== undefined && scannedRows >= total) break;
|
|
169
|
+
if (items.length < DEFAULT_PAGE_SIZE) break;
|
|
170
|
+
page += 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (total !== undefined && scannedRows < total) partial = true;
|
|
174
|
+
if (scannedRows >= maxRows && (total === undefined || scannedRows < total)) partial = true;
|
|
175
|
+
|
|
176
|
+
return { rows: rows.slice(0, limit), total, partial, scannedRows };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function usageFilters(flags) {
|
|
180
|
+
return {
|
|
181
|
+
execution_id: flags.executionId,
|
|
182
|
+
search_id: flags.searchId,
|
|
183
|
+
event_type: flags.eventType,
|
|
184
|
+
kind: flags.kind,
|
|
185
|
+
success: flags.success,
|
|
186
|
+
charge_outcome: flags.chargeOutcome,
|
|
187
|
+
min_credits: flags.minCredits,
|
|
188
|
+
max_credits: flags.maxCredits,
|
|
189
|
+
};
|
|
190
|
+
}
|
package/src/compat/aliases.mjs
CHANGED
|
@@ -13,6 +13,7 @@ const FLAG_ALIASES = {
|
|
|
13
13
|
export function normalizeLegacyArgs(args) {
|
|
14
14
|
const result = [...args];
|
|
15
15
|
const warnings = [];
|
|
16
|
+
const originalCommand = result[0];
|
|
16
17
|
|
|
17
18
|
if (result.length > 0 && COMMAND_ALIASES[result[0]]) {
|
|
18
19
|
warnings.push(`'${result[0]}' is deprecated; use '${COMMAND_ALIASES[result[0]]}' instead.`);
|
|
@@ -20,6 +21,9 @@ export function normalizeLegacyArgs(args) {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
for (let i = 0; i < result.length; i++) {
|
|
24
|
+
if (originalCommand === "usage" && result[i] === "--search-id") {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
23
27
|
if (FLAG_ALIASES[result[i]]) {
|
|
24
28
|
warnings.push(`'${result[i]}' is deprecated; use '${FLAG_ALIASES[result[i]]}' instead.`);
|
|
25
29
|
result[i] = FLAG_ALIASES[result[i]];
|
package/src/config/region.mjs
CHANGED
|
@@ -13,6 +13,26 @@ const REGION_URLS = {
|
|
|
13
13
|
cn: "https://qveris.cn/api/v1",
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
const SITE_URLS = {
|
|
17
|
+
global: "https://qveris.ai",
|
|
18
|
+
cn: "https://qveris.cn",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get the site URL (not API URL) for a given region.
|
|
23
|
+
* For custom regions, attempts to infer from the base URL domain.
|
|
24
|
+
* @param {string} region
|
|
25
|
+
* @param {string} [baseUrl] - The resolved API base URL (used to infer site for custom regions)
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
export function getSiteUrl(region, baseUrl) {
|
|
29
|
+
if (region === "custom" && baseUrl) {
|
|
30
|
+
if (baseUrl.includes("qveris.cn")) return SITE_URLS.cn;
|
|
31
|
+
if (baseUrl.includes("qveris.ai")) return SITE_URLS.global;
|
|
32
|
+
}
|
|
33
|
+
return SITE_URLS[region] || SITE_URLS.global;
|
|
34
|
+
}
|
|
35
|
+
|
|
16
36
|
/**
|
|
17
37
|
* Detect region from API key prefix.
|
|
18
38
|
* @param {string} apiKey
|
package/src/main.mjs
CHANGED
|
@@ -72,6 +72,16 @@ export async function main(argv) {
|
|
|
72
72
|
await runCredits(flags);
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
|
+
case "usage": {
|
|
76
|
+
const { runUsage } = await import("./commands/usage.mjs");
|
|
77
|
+
await runUsage(flags);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "ledger": {
|
|
81
|
+
const { runLedger } = await import("./commands/ledger.mjs");
|
|
82
|
+
await runLedger(flags);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
75
85
|
case "config": {
|
|
76
86
|
const subcommand = rest[0];
|
|
77
87
|
const subArgs = rest.slice(1);
|
|
@@ -119,6 +129,11 @@ const VALUE_FLAGS = {
|
|
|
119
129
|
"api-key": "apiKey", "base-url": "baseUrl", timeout: "timeout",
|
|
120
130
|
limit: "limit", "discovery-id": "discoveryId", params: "params",
|
|
121
131
|
"max-size": "maxSize", codegen: "codegen", token: "token",
|
|
132
|
+
mode: "mode", "start-date": "startDate", "end-date": "endDate",
|
|
133
|
+
bucket: "bucket", "execution-id": "executionId", "search-id": "searchId",
|
|
134
|
+
"event-type": "eventType", kind: "kind", success: "success",
|
|
135
|
+
"charge-outcome": "chargeOutcome", "entry-type": "entryType",
|
|
136
|
+
direction: "direction", "min-credits": "minCredits", "max-credits": "maxCredits",
|
|
122
137
|
};
|
|
123
138
|
|
|
124
139
|
function takeNext(args, i, flag) {
|
|
@@ -199,6 +214,34 @@ function extractGlobalFlags(args) {
|
|
|
199
214
|
flags.codegen = takeNext(args, i++, arg); break;
|
|
200
215
|
case "--token":
|
|
201
216
|
flags.token = takeNext(args, i++, arg); break;
|
|
217
|
+
case "--mode":
|
|
218
|
+
flags.mode = takeNext(args, i++, arg); break;
|
|
219
|
+
case "--start-date":
|
|
220
|
+
flags.startDate = takeNext(args, i++, arg); break;
|
|
221
|
+
case "--end-date":
|
|
222
|
+
flags.endDate = takeNext(args, i++, arg); break;
|
|
223
|
+
case "--bucket":
|
|
224
|
+
flags.bucket = takeNext(args, i++, arg); break;
|
|
225
|
+
case "--execution-id":
|
|
226
|
+
flags.executionId = takeNext(args, i++, arg); break;
|
|
227
|
+
case "--search-id":
|
|
228
|
+
flags.searchId = takeNext(args, i++, arg); break;
|
|
229
|
+
case "--event-type":
|
|
230
|
+
flags.eventType = takeNext(args, i++, arg); break;
|
|
231
|
+
case "--kind":
|
|
232
|
+
flags.kind = takeNext(args, i++, arg); break;
|
|
233
|
+
case "--success":
|
|
234
|
+
flags.success = takeNext(args, i++, arg); break;
|
|
235
|
+
case "--charge-outcome":
|
|
236
|
+
flags.chargeOutcome = takeNext(args, i++, arg); break;
|
|
237
|
+
case "--entry-type":
|
|
238
|
+
flags.entryType = takeNext(args, i++, arg); break;
|
|
239
|
+
case "--direction":
|
|
240
|
+
flags.direction = takeNext(args, i++, arg); break;
|
|
241
|
+
case "--min-credits":
|
|
242
|
+
flags.minCredits = takeNext(args, i++, arg); break;
|
|
243
|
+
case "--max-credits":
|
|
244
|
+
flags.maxCredits = takeNext(args, i++, arg); break;
|
|
202
245
|
default:
|
|
203
246
|
flags._positional.push(arg);
|
|
204
247
|
}
|
|
@@ -228,6 +271,8 @@ function printUsage(flags = {}) {
|
|
|
228
271
|
${cyan("logout")} Remove stored API key
|
|
229
272
|
${cyan("whoami")} Show current auth status
|
|
230
273
|
${cyan("credits")} Show credit balance
|
|
274
|
+
${cyan("usage")} Summarize or search usage audit history
|
|
275
|
+
${cyan("ledger")} Summarize or search credit ledger entries
|
|
231
276
|
|
|
232
277
|
${bold("Configuration:")}
|
|
233
278
|
${cyan("config")} set|get|list|reset Manage CLI settings
|
|
@@ -241,19 +286,32 @@ function printUsage(flags = {}) {
|
|
|
241
286
|
${bold("Global Flags:")}
|
|
242
287
|
--json, -j Output raw JSON
|
|
243
288
|
--api-key <key> Override API key
|
|
289
|
+
--base-url <url> Override API base URL
|
|
244
290
|
--timeout <seconds> Request timeout
|
|
291
|
+
--mode <mode> summary | search | export-file for usage/ledger
|
|
292
|
+
--start-date <date> Usage/ledger range start (YYYY-MM-DD)
|
|
293
|
+
--end-date <date> Usage/ledger range end (YYYY-MM-DD)
|
|
294
|
+
--min-credits <n> Usage/ledger amount lower bound
|
|
295
|
+
--max-credits <n> Usage/ledger amount upper bound
|
|
245
296
|
--no-color Disable colors
|
|
246
297
|
--verbose, -v Show request details
|
|
247
298
|
--version, -V Print version
|
|
248
299
|
--help, -h Show help
|
|
249
300
|
|
|
301
|
+
${bold("Environment Variables:")}
|
|
302
|
+
QVERIS_API_KEY API key
|
|
303
|
+
QVERIS_REGION Region override (global | cn)
|
|
304
|
+
QVERIS_BASE_URL Custom API base URL
|
|
305
|
+
|
|
250
306
|
${bold("Examples:")}
|
|
251
307
|
qveris discover "weather forecast API"
|
|
252
308
|
qveris inspect 1
|
|
253
309
|
qveris call 1 --params '{"city": "London"}'
|
|
254
310
|
qveris call 1 --params @params.json --codegen curl
|
|
311
|
+
qveris usage --mode search --execution-id <id>
|
|
312
|
+
qveris ledger --mode search --min-credits 50 --direction consume
|
|
255
313
|
qveris interactive
|
|
256
314
|
|
|
257
|
-
${dim("https://qveris.ai")}
|
|
315
|
+
${dim("https://qveris.ai (global) / https://qveris.cn (China)")}
|
|
258
316
|
`);
|
|
259
317
|
}
|