@caravo/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/dist/cli.js CHANGED
@@ -173,11 +173,11 @@ function parseArgs(argv) {
173
173
  }
174
174
  return args;
175
175
  }
176
- const VERSION = "2.0.0";
176
+ const VERSION = "0.2.0";
177
177
  async function main() {
178
178
  const args = parseArgs(process.argv.slice(2));
179
179
  if (args.version) {
180
- process.stdout.write(`falm ${VERSION}\n`);
180
+ process.stdout.write(`caravo ${VERSION}\n`);
181
181
  process.exit(0);
182
182
  }
183
183
  if (args.help || !args.subcommand) {
@@ -192,7 +192,7 @@ async function main() {
192
192
  switch (args.subcommand) {
193
193
  case "search": {
194
194
  const { runSearch } = await import("./commands/search.js");
195
- // Join all positional args so "falm search image generation" works like "image generation"
195
+ // Join all positional args so "caravo search image generation" works like "image generation"
196
196
  const query = args.positional.length > 0 ? args.positional.join(" ") : undefined;
197
197
  await runSearch(query, {
198
198
  tag: args.tag,
@@ -1,10 +1,98 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
1
4
  import { apiGet, apiPost, apiDelete, validateToolId } from "../lib/api.js";
2
5
  import { outputJson, log } from "../lib/output.js";
6
+ // ─── Local favorites ────────────────────────────────────────────────────────
7
+ const FAV_DIR = join(homedir(), ".caravo");
8
+ const FAV_FILE = join(FAV_DIR, "favorites.json");
9
+ function readLocal() {
10
+ if (!existsSync(FAV_FILE))
11
+ return { version: "1.0.0", favorites: [] };
12
+ try {
13
+ const raw = JSON.parse(readFileSync(FAV_FILE, "utf-8"));
14
+ if (Array.isArray(raw.favorites))
15
+ return raw;
16
+ }
17
+ catch { }
18
+ return { version: "1.0.0", favorites: [] };
19
+ }
20
+ function writeLocal(favs) {
21
+ mkdirSync(FAV_DIR, { recursive: true });
22
+ writeFileSync(FAV_FILE, JSON.stringify(favs, null, 2) + "\n");
23
+ }
24
+ function localList(compact) {
25
+ const { favorites } = readLocal();
26
+ const data = { data: favorites.map((id) => ({ id })), total: favorites.length };
27
+ outputJson(data, compact);
28
+ }
29
+ function localAdd(toolId) {
30
+ const favs = readLocal();
31
+ if (favs.favorites.includes(toolId)) {
32
+ log(`${toolId} is already in favorites`);
33
+ return;
34
+ }
35
+ favs.favorites.push(toolId);
36
+ writeLocal(favs);
37
+ log(`✓ Added ${toolId} to local favorites`);
38
+ }
39
+ function localRm(toolId) {
40
+ const favs = readLocal();
41
+ const idx = favs.favorites.indexOf(toolId);
42
+ if (idx === -1) {
43
+ log(`${toolId} is not in favorites`);
44
+ return;
45
+ }
46
+ favs.favorites.splice(idx, 1);
47
+ writeLocal(favs);
48
+ log(`✓ Removed ${toolId} from local favorites`);
49
+ }
50
+ // ─── Auto-sync: merge local → server on first authenticated run ─────────────
51
+ async function autoSyncLocalToServer(auth) {
52
+ if (!existsSync(FAV_FILE))
53
+ return;
54
+ const local = readLocal();
55
+ if (local.favorites.length === 0)
56
+ return;
57
+ log(`Found ${local.favorites.length} local favorite(s), syncing to server...`);
58
+ // Get current server favorites to check connectivity and avoid duplicates
59
+ const serverData = (await apiGet("/api/favorites", auth));
60
+ if (serverData?.error || !serverData?.data) {
61
+ log("Server sync skipped (auth failed) — keeping local favorites");
62
+ return;
63
+ }
64
+ const serverIds = new Set(serverData.data.map((f) => f.id));
65
+ let added = 0;
66
+ for (const toolId of local.favorites) {
67
+ if (serverIds.has(toolId))
68
+ continue;
69
+ try {
70
+ await apiPost("/api/favorites", { tool_id: toolId }, auth);
71
+ added++;
72
+ }
73
+ catch {
74
+ // Tool may no longer exist — skip silently
75
+ }
76
+ }
77
+ // Only remove local file after successful sync
78
+ try {
79
+ unlinkSync(FAV_FILE);
80
+ }
81
+ catch { }
82
+ log(`✓ Synced ${added} new favorite(s) to server (local file removed)`);
83
+ }
84
+ // ─── Main entry ─────────────────────────────────────────────────────────────
3
85
  export async function run(sub, toolId, auth, compact) {
4
- if (auth.mode !== "apikey") {
5
- log("Favorites require an API key. Set $CARAVO_API_KEY or use --api-key.");
6
- process.exit(1);
86
+ // Authenticated → server mode (with auto-sync from local on first run)
87
+ if (auth.mode === "apikey") {
88
+ await autoSyncLocalToServer(auth);
89
+ return runServer(sub, toolId, auth, compact);
7
90
  }
91
+ // No API key → local mode
92
+ log("[local mode — set CARAVO_API_KEY to sync with server]");
93
+ return runLocal(sub, toolId, compact);
94
+ }
95
+ async function runServer(sub, toolId, auth, compact) {
8
96
  switch (sub) {
9
97
  case "list": {
10
98
  const data = await apiGet("/api/favorites", auth);
@@ -44,3 +132,39 @@ export async function run(sub, toolId, auth, compact) {
44
132
  process.exit(1);
45
133
  }
46
134
  }
135
+ function runLocal(sub, toolId, compact) {
136
+ switch (sub) {
137
+ case "list":
138
+ localList(compact);
139
+ break;
140
+ case "add": {
141
+ if (!toolId) {
142
+ log("Usage: caravo fav add <tool-id>");
143
+ process.exit(1);
144
+ }
145
+ const err = validateToolId(toolId);
146
+ if (err) {
147
+ log(err);
148
+ process.exit(1);
149
+ }
150
+ localAdd(toolId);
151
+ break;
152
+ }
153
+ case "rm": {
154
+ if (!toolId) {
155
+ log("Usage: caravo fav rm <tool-id>");
156
+ process.exit(1);
157
+ }
158
+ const err = validateToolId(toolId);
159
+ if (err) {
160
+ log(err);
161
+ process.exit(1);
162
+ }
163
+ localRm(toolId);
164
+ break;
165
+ }
166
+ default:
167
+ log("Usage: caravo fav <list|add|rm> [tool-id]");
168
+ process.exit(1);
169
+ }
170
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caravo/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Caravo CLI — search, execute, and review tools with API key or x402 USDC payments",
5
5
  "type": "module",
6
6
  "bin": {