@caravo/cli 0.2.0 → 0.2.2
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 +1 -1
- package/dist/cli.js +20 -3
- package/dist/commands/fav.js +15 -4
- package/dist/commands/login.js +67 -0
- package/dist/lib/auth.js +17 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ caravo review EXECUTION_ID --rating 5 --comment "Great quality"
|
|
|
29
29
|
# Upvote an existing review
|
|
30
30
|
caravo upvote REVIEW_ID --exec EXECUTION_ID
|
|
31
31
|
|
|
32
|
-
# Manage favorites (
|
|
32
|
+
# Manage favorites (works with or without API key)
|
|
33
33
|
caravo fav list
|
|
34
34
|
caravo fav add fal-ai/flux/schnell
|
|
35
35
|
caravo fav rm fal-ai/flux/schnell
|
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ Commands:
|
|
|
22
22
|
request --title <t> --desc <d>
|
|
23
23
|
Submit a tool request
|
|
24
24
|
request-upvote <req-id> Upvote a tool request
|
|
25
|
+
login Connect your Caravo account via browser (saves API key)
|
|
25
26
|
wallet Show wallet + balance info
|
|
26
27
|
fetch [METHOD] <url> Raw x402 HTTP request
|
|
27
28
|
|
|
@@ -169,11 +170,16 @@ function parseArgs(argv) {
|
|
|
169
170
|
else if (!arg.startsWith("-")) {
|
|
170
171
|
args.positional.push(arg);
|
|
171
172
|
}
|
|
173
|
+
else {
|
|
174
|
+
// Unknown flag
|
|
175
|
+
process.stderr.write(`[caravo] unknown option: ${arg}\n`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
172
178
|
i++;
|
|
173
179
|
}
|
|
174
180
|
return args;
|
|
175
181
|
}
|
|
176
|
-
const VERSION = "0.2.
|
|
182
|
+
const VERSION = "0.2.2";
|
|
177
183
|
async function main() {
|
|
178
184
|
const args = parseArgs(process.argv.slice(2));
|
|
179
185
|
if (args.version) {
|
|
@@ -218,8 +224,14 @@ async function main() {
|
|
|
218
224
|
break;
|
|
219
225
|
}
|
|
220
226
|
case "exec": {
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
if (args.dryRun) {
|
|
228
|
+
const { runDryRun } = await import("./commands/exec.js");
|
|
229
|
+
await runDryRun(args.positional[0], args.data, auth, args.compact);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const { run } = await import("./commands/exec.js");
|
|
233
|
+
await run(args.positional[0], args.data, auth, args.compact);
|
|
234
|
+
}
|
|
223
235
|
break;
|
|
224
236
|
}
|
|
225
237
|
case "dry-run": {
|
|
@@ -271,6 +283,11 @@ async function main() {
|
|
|
271
283
|
await runReqUpvote(args.positional[0], args.exec, auth, args.compact);
|
|
272
284
|
break;
|
|
273
285
|
}
|
|
286
|
+
case "login": {
|
|
287
|
+
const { runLogin } = await import("./commands/login.js");
|
|
288
|
+
await runLogin(auth.baseUrl);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
274
291
|
case "wallet": {
|
|
275
292
|
const { run } = await import("./commands/wallet-cmd.js");
|
|
276
293
|
await run(auth, args.compact);
|
package/dist/commands/fav.js
CHANGED
|
@@ -26,7 +26,18 @@ function localList(compact) {
|
|
|
26
26
|
const data = { data: favorites.map((id) => ({ id })), total: favorites.length };
|
|
27
27
|
outputJson(data, compact);
|
|
28
28
|
}
|
|
29
|
-
function localAdd(toolId) {
|
|
29
|
+
async function localAdd(toolId, baseUrl) {
|
|
30
|
+
// Verify tool exists on server before adding
|
|
31
|
+
try {
|
|
32
|
+
const r = await fetch(`${baseUrl}/api/tools/${toolId}`);
|
|
33
|
+
if (r.status === 404) {
|
|
34
|
+
log(`Tool not found: ${toolId}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Network error — allow offline add
|
|
40
|
+
}
|
|
30
41
|
const favs = readLocal();
|
|
31
42
|
if (favs.favorites.includes(toolId)) {
|
|
32
43
|
log(`${toolId} is already in favorites`);
|
|
@@ -90,7 +101,7 @@ export async function run(sub, toolId, auth, compact) {
|
|
|
90
101
|
}
|
|
91
102
|
// No API key → local mode
|
|
92
103
|
log("[local mode — set CARAVO_API_KEY to sync with server]");
|
|
93
|
-
return runLocal(sub, toolId, compact);
|
|
104
|
+
return runLocal(sub, toolId, compact, auth.baseUrl);
|
|
94
105
|
}
|
|
95
106
|
async function runServer(sub, toolId, auth, compact) {
|
|
96
107
|
switch (sub) {
|
|
@@ -132,7 +143,7 @@ async function runServer(sub, toolId, auth, compact) {
|
|
|
132
143
|
process.exit(1);
|
|
133
144
|
}
|
|
134
145
|
}
|
|
135
|
-
function runLocal(sub, toolId, compact) {
|
|
146
|
+
async function runLocal(sub, toolId, compact, baseUrl) {
|
|
136
147
|
switch (sub) {
|
|
137
148
|
case "list":
|
|
138
149
|
localList(compact);
|
|
@@ -147,7 +158,7 @@ function runLocal(sub, toolId, compact) {
|
|
|
147
158
|
log(err);
|
|
148
159
|
process.exit(1);
|
|
149
160
|
}
|
|
150
|
-
localAdd(toolId);
|
|
161
|
+
await localAdd(toolId, baseUrl);
|
|
151
162
|
break;
|
|
152
163
|
}
|
|
153
164
|
case "rm": {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { exec } from "child_process";
|
|
5
|
+
const CONFIG_DIR = join(homedir(), ".caravo");
|
|
6
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
7
|
+
function loadConfig() {
|
|
8
|
+
try {
|
|
9
|
+
if (!existsSync(CONFIG_FILE))
|
|
10
|
+
return {};
|
|
11
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function saveConfig(data) {
|
|
18
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
20
|
+
}
|
|
21
|
+
function openBrowser(url) {
|
|
22
|
+
const opener = process.platform === "darwin"
|
|
23
|
+
? `open "${url}"`
|
|
24
|
+
: process.platform === "win32"
|
|
25
|
+
? `start "" "${url}"`
|
|
26
|
+
: `xdg-open "${url}"`;
|
|
27
|
+
exec(opener);
|
|
28
|
+
}
|
|
29
|
+
export async function runLogin(baseUrl) {
|
|
30
|
+
// 1. Create session
|
|
31
|
+
process.stdout.write("Opening browser for login...\n");
|
|
32
|
+
const initRes = await fetch(`${baseUrl}/api/auth/mcp-session`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/json" },
|
|
35
|
+
});
|
|
36
|
+
if (!initRes.ok) {
|
|
37
|
+
process.stderr.write(`[caravo] login: server error ${initRes.status}\n`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const { token, url } = (await initRes.json());
|
|
41
|
+
// 2. Open browser
|
|
42
|
+
openBrowser(url);
|
|
43
|
+
process.stdout.write(`\nOpened: ${url}\n\nWaiting for login (5 min timeout)...\n`);
|
|
44
|
+
// 3. Poll every 2s
|
|
45
|
+
const deadline = Date.now() + 5 * 60 * 1000;
|
|
46
|
+
while (Date.now() < deadline) {
|
|
47
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
48
|
+
const pollRes = await fetch(`${baseUrl}/api/auth/mcp-session?token=${encodeURIComponent(token)}`);
|
|
49
|
+
const poll = (await pollRes.json());
|
|
50
|
+
if (poll.status === "completed" && poll.api_key) {
|
|
51
|
+
// 4. Save to config
|
|
52
|
+
const existing = loadConfig();
|
|
53
|
+
saveConfig({ ...existing, api_key: poll.api_key });
|
|
54
|
+
process.stdout.write(`\n✓ Logged in! API key saved to ${CONFIG_FILE}\n\n`);
|
|
55
|
+
process.stdout.write(`To use it immediately (this shell session):\n export CARAVO_API_KEY=${poll.api_key}\n\n`);
|
|
56
|
+
process.stdout.write(`To make it permanent, add to your shell profile (~/.zshrc / ~/.bashrc):\n export CARAVO_API_KEY=${poll.api_key}\n`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (poll.status === "expired") {
|
|
60
|
+
process.stderr.write("[caravo] login: session expired. Run `caravo login` again.\n");
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
process.stdout.write(".");
|
|
64
|
+
}
|
|
65
|
+
process.stderr.write("\n[caravo] login: timed out after 5 minutes.\n");
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
package/dist/lib/auth.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
1
4
|
import { loadOrCreateWallet } from "../wallet.js";
|
|
2
5
|
const DEFAULT_BASE_URL = "https://caravo.ai";
|
|
6
|
+
const CONFIG_FILE = join(homedir(), ".caravo", "config.json");
|
|
7
|
+
function readConfigApiKey() {
|
|
8
|
+
try {
|
|
9
|
+
if (!existsSync(CONFIG_FILE))
|
|
10
|
+
return undefined;
|
|
11
|
+
const c = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
12
|
+
return typeof c.api_key === "string" ? c.api_key : undefined;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
3
18
|
export function resolveAuth(args) {
|
|
4
|
-
|
|
19
|
+
// Priority: explicit --api-key flag → CARAVO_API_KEY env → ~/.caravo/config.json
|
|
20
|
+
const apiKey = args.apiKey || process.env.CARAVO_API_KEY || readConfigApiKey();
|
|
5
21
|
const baseUrl = args.baseUrl || process.env.CARAVO_URL || DEFAULT_BASE_URL;
|
|
6
22
|
let cached;
|
|
7
23
|
return {
|