@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 +3 -3
- package/dist/commands/fav.js +127 -3
- package/package.json +1 -1
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
|
|
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(`
|
|
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 "
|
|
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,
|
package/dist/commands/fav.js
CHANGED
|
@@ -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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
+
}
|