satoridb 1.2.1 → 1.2.3
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/cli.js +220 -517
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -1,556 +1,259 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (char === ' ' && !inQuotes) {
|
|
41
|
-
if (current.trim()) {
|
|
42
|
-
args.push(current.trim());
|
|
43
|
-
current = '';
|
|
44
|
-
}
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
current += char;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (current.trim()) {
|
|
52
|
-
args.push(current.trim());
|
|
3
|
+
/**
|
|
4
|
+
* Satori Interactive CLI (Improved)
|
|
5
|
+
* - Command registry (easy to extend)
|
|
6
|
+
* - Robust arg parsing (quotes, escapes)
|
|
7
|
+
* - Fixed bugs (lecture args check, set_mindspace double call)
|
|
8
|
+
* - Better UX (history, tab completion, prompt context)
|
|
9
|
+
* - Centralized error handling
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const os = require("os");
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
const { spawnSync } = require("child_process");
|
|
16
|
+
const readline = require("readline");
|
|
17
|
+
const { Satori } = require("satori-node");
|
|
18
|
+
|
|
19
|
+
// -------------------------------
|
|
20
|
+
// Globals / State
|
|
21
|
+
// -------------------------------
|
|
22
|
+
const binName = os.platform() === "win32" ? "satori.exe" : "satori";
|
|
23
|
+
const binPath = path.join(os.homedir(), ".satori", "bin", binName);
|
|
24
|
+
|
|
25
|
+
let satori = null;
|
|
26
|
+
let session = {
|
|
27
|
+
host: null,
|
|
28
|
+
user: null,
|
|
29
|
+
password: null,
|
|
30
|
+
mindspace: null,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// -------------------------------
|
|
34
|
+
// Utils
|
|
35
|
+
// -------------------------------
|
|
36
|
+
function assertInstalled() {
|
|
37
|
+
if (!fs.existsSync(binPath)) {
|
|
38
|
+
console.error("❌ Satori binary not found. Reinstall the package.");
|
|
39
|
+
process.exit(1);
|
|
53
40
|
}
|
|
54
|
-
|
|
55
|
-
return args;
|
|
56
41
|
}
|
|
57
42
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Intentar parsear como número
|
|
64
|
-
if (!isNaN(trimmed) && trimmed !== '') {
|
|
65
|
-
let numValue = Number(trimmed);
|
|
66
|
-
if (Number.isInteger(numValue)) return parseInt(trimmed);
|
|
67
|
-
return numValue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Intentar parsear booleanos
|
|
71
|
-
if (trimmed.toLowerCase() === 'true') return true;
|
|
72
|
-
if (trimmed.toLowerCase() === 'false') return false;
|
|
73
|
-
|
|
74
|
-
// Intentar parsear JSON solo si empieza con { o [
|
|
75
|
-
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
76
|
-
try {
|
|
77
|
-
return JSON.parse(trimmed);
|
|
78
|
-
} catch (e) {
|
|
79
|
-
// Intentar evaluar como objeto JavaScript literal
|
|
80
|
-
try {
|
|
81
|
-
return new Function('return (' + trimmed + ')')();
|
|
82
|
-
} catch (evalError) {
|
|
83
|
-
console.log("⚠️ Could not parse as JSON or JavaScript object, returning original string");
|
|
84
|
-
return data;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return data;
|
|
43
|
+
function runBinary(args) {
|
|
44
|
+
assertInstalled();
|
|
45
|
+
const r = spawnSync(binPath, args, { stdio: "inherit" });
|
|
46
|
+
if (r.error) throw r.error;
|
|
90
47
|
}
|
|
91
48
|
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
let result = spawnSync(binPath, args, { stdio: "inherit" });
|
|
99
|
-
if (result.error) {
|
|
100
|
-
console.log("❌ Error executing:", result.error.message);
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
49
|
+
function parseArgs(line) {
|
|
50
|
+
const re = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|(\S+)/g;
|
|
51
|
+
const out = [];
|
|
52
|
+
let m;
|
|
53
|
+
while ((m = re.exec(line))) out.push(m[1] ?? m[2] ?? m[3]);
|
|
54
|
+
return out.map(v => v.replace(/\\"/g, '"').replace(/\\'/g, "'"));
|
|
103
55
|
}
|
|
104
56
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
57
|
+
function parseData(v) {
|
|
58
|
+
if (typeof v !== "string") return v;
|
|
59
|
+
const t = v.trim();
|
|
60
|
+
if (t === "true") return true;
|
|
61
|
+
if (t === "false") return false;
|
|
62
|
+
if (!isNaN(t) && t !== "") return Number(t);
|
|
63
|
+
if (t.startsWith("{") || t.startsWith("[")) {
|
|
64
|
+
try { return JSON.parse(t); } catch {}
|
|
112
65
|
}
|
|
113
|
-
return
|
|
66
|
+
return v;
|
|
114
67
|
}
|
|
115
68
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log(`🔗 Connecting to ${host}...`);
|
|
119
|
-
// Store credentials for use in all commands
|
|
120
|
-
currentUser = user;
|
|
121
|
-
currentPassword = password;
|
|
122
|
-
satoriInstance = new Satori({ host, user, password });
|
|
123
|
-
await satoriInstance.connect();
|
|
124
|
-
console.log("✅ Successfully connected to SatoriDB");
|
|
125
|
-
return true;
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.log("❌ Error connecting:", error.message);
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
69
|
+
function requireConn() {
|
|
70
|
+
if (!satori) throw new Error("No active connection. Use: connect <host> [user] [password]");
|
|
130
71
|
}
|
|
131
72
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return;
|
|
138
|
-
|
|
139
|
-
case "exit":
|
|
140
|
-
case "quit":
|
|
141
|
-
console.log("👋 ¡Hasta luego!");
|
|
142
|
-
process.exit(0);
|
|
143
|
-
return;
|
|
144
|
-
|
|
145
|
-
case "clear":
|
|
146
|
-
clearScreen();
|
|
147
|
-
return;
|
|
148
|
-
|
|
149
|
-
case "mindspace":
|
|
150
|
-
let cmd = args[0];
|
|
151
|
-
if (cmd == "select"){
|
|
152
|
-
let mId = args[1];
|
|
153
|
-
currentMindspace = mId;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
73
|
+
function withCreds(p = {}) {
|
|
74
|
+
if (session.user) p.user = session.user;
|
|
75
|
+
if (session.password) p.password = session.password;
|
|
76
|
+
return p;
|
|
77
|
+
}
|
|
156
78
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
79
|
+
function promptLabel() {
|
|
80
|
+
return `satori${session.mindspace ? `:${session.mindspace}` : ""}> `;
|
|
81
|
+
}
|
|
162
82
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
let [putKey, replaceField, replaceValue, encryption_key_put] = args;
|
|
186
|
-
// Parsear JSON en el valor si es necesario
|
|
187
|
-
replaceValue = parseData(replaceValue);
|
|
188
|
-
await satoriInstance.put(addCredentials({ key: putKey, replace_field: replaceField, replace_value: replaceValue, encryption_key: encryption_key_put }));
|
|
189
|
-
console.log(`✅ Field updated: ${putKey}.${replaceField} = ${replaceValue}`);
|
|
190
|
-
break;
|
|
191
|
-
|
|
192
|
-
case "get":
|
|
193
|
-
if (args.length >= 1) {
|
|
194
|
-
let getKey = args[0];
|
|
195
|
-
let encryption_key_get = args[1] || null;
|
|
196
|
-
let result = await satoriInstance.get(addCredentials({ key: getKey, encryption_key: encryption_key_get }));
|
|
197
|
-
console.log(`📄 Data from ${getKey}:`, JSON.stringify(result, null, 2));
|
|
198
|
-
break;
|
|
199
|
-
} else {
|
|
200
|
-
let result = await satoriInstance.get(addCredentials({}));
|
|
201
|
-
console.log(`📄 Data from get:`, JSON.stringify(result, null, 2));
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
case "set_mindspace":
|
|
206
|
-
if (args.length == 1) {
|
|
207
|
-
let profile = args[0];
|
|
208
|
-
let res = null;
|
|
209
|
-
if (currentMindspace != null){
|
|
210
|
-
|
|
211
|
-
res = await satoriInstance.setMindspace({
|
|
212
|
-
config: profile,
|
|
213
|
-
mindspace_id: currentMindspace
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
res = await satoriInstance.setMindspace({
|
|
218
|
-
config: profile
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
console.log(`✅ Mindspace set: ${res.data}`);
|
|
222
|
-
break;
|
|
223
|
-
} else if (args.length == 2) {
|
|
224
|
-
let profile = args[0];
|
|
225
|
-
let mindspace_id = args[1];
|
|
226
|
-
|
|
227
|
-
let res = await satoriInstance.setMindspace({
|
|
228
|
-
mindspace_id: mindspace_id,
|
|
229
|
-
config: profile
|
|
230
|
-
})
|
|
231
|
-
console.log(`✅ Mindspace set: ${res.data}`);
|
|
232
|
-
break;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
case "delete_mindspace":
|
|
236
|
-
if (args.length == 1) {
|
|
237
|
-
let mid = args[0];
|
|
238
|
-
|
|
239
|
-
let res = await satoriInstance.deleteMindspace({
|
|
240
|
-
mindspace_id: mid
|
|
241
|
-
})
|
|
242
|
-
console.log(`✅ Mindspace deleted: ${mid}`);
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
case "chat":
|
|
247
|
-
if (args.length == 1) {
|
|
248
|
-
if(currentMindspace == null){
|
|
249
|
-
console.log("Select a mindspace with: mindspace select <mindspace_id>")
|
|
250
|
-
}
|
|
251
|
-
let mid = currentMindspace;
|
|
252
|
-
let message = args[0]
|
|
253
|
-
|
|
254
|
-
let res = await satoriInstance.chatMindspace({
|
|
255
|
-
mindspace_id: mid,
|
|
256
|
-
message: message
|
|
257
|
-
})
|
|
258
|
-
console.log(`${res.data}`);
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
case "lecture":
|
|
263
|
-
if(args == 1){
|
|
264
|
-
if(currentMindspace == null){
|
|
265
|
-
console.log("Select a mindspace with: mindspace select <mindspace_id>")
|
|
266
|
-
}
|
|
267
|
-
let mid = currentMindspace;
|
|
268
|
-
let message = args[0]
|
|
269
|
-
|
|
270
|
-
let res = await satoriInstance.lectureMindspace({
|
|
271
|
-
mindspace_id: mid,
|
|
272
|
-
corpus: message
|
|
273
|
-
})
|
|
274
|
-
console.log(`${res.data}`);
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
case "delete":
|
|
279
|
-
if (args.length < 1) {
|
|
280
|
-
console.log("❌ Uso: delete <key> [encryption_key]");
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
let deleteKey = args[0];
|
|
284
|
-
await satoriInstance.delete(addCredentials({ key: deleteKey }));
|
|
285
|
-
console.log(`🗑️ Key deleted: ${deleteKey}`);
|
|
286
|
-
break;
|
|
287
|
-
|
|
288
|
-
case "ask":
|
|
289
|
-
if (args.length < 1) {
|
|
290
|
-
console.log("❌ Usage: ask \"<question>\" [backend]");
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
let question = args[0];
|
|
294
|
-
let backend = args[1] || null;
|
|
295
|
-
let result = await satoriInstance.ask(addCredentials({ question, backend }));
|
|
296
|
-
console.log("🔍 Search results:", JSON.stringify(result, null, 2));
|
|
297
|
-
break;
|
|
298
|
-
|
|
299
|
-
case "query":
|
|
300
|
-
if (args.length < 1) {
|
|
301
|
-
console.log("❌ Usage: query \"<query>\" [backend]");
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
let query = args[0];
|
|
305
|
-
let backend_q = args[1] || null;
|
|
306
|
-
let result_q = await satoriInstance.query(addCredentials({ query, backend: backend_q }));
|
|
307
|
-
console.log("🔍 Search results:", JSON.stringify(result_q, null, 2));
|
|
308
|
-
break;
|
|
309
|
-
|
|
310
|
-
case "dfs":
|
|
311
|
-
if (args.length < 1) {
|
|
312
|
-
console.log("❌ Uso: dfs <node> [relation] [encryption_key]");
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
let node = args[0];
|
|
316
|
-
let relation = args[1] || null;
|
|
317
|
-
let encryption_key = args[2] || null;
|
|
318
|
-
let result_dfs = await satoriInstance.dfs(addCredentials({ node, relation, encryption_key }));
|
|
319
|
-
console.log("🔍 Search results:", JSON.stringify(result_dfs, null, 2));
|
|
320
|
-
break;
|
|
321
|
-
|
|
322
|
-
case "encrypt":
|
|
323
|
-
if (args.length < 1) {
|
|
324
|
-
console.log("❌ Uso: encrypt <key> [encryption_key]");
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
let key_encrypt = args[0];
|
|
328
|
-
let encryption_key_encrypt = args[1] || null;
|
|
329
|
-
let result_encrypt = await satoriInstance.encrypt(addCredentials({ key: key_encrypt, encryption_key: encryption_key_encrypt }));
|
|
330
|
-
console.log("🔍 Encryption results:", JSON.stringify(result_encrypt, null, 2));
|
|
331
|
-
break;
|
|
332
|
-
|
|
333
|
-
case "decrypt":
|
|
334
|
-
if (args.length < 1) {
|
|
335
|
-
console.log("❌ Uso: decrypt <key> [encryption_key]");
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
let key_decrypt = args[0];
|
|
339
|
-
let encryption_key_decrypt = args[1] || null;
|
|
340
|
-
let result_decrypt = await satoriInstance.decrypt(addCredentials({ key: key_decrypt, encryption_key: encryption_key_decrypt }));
|
|
341
|
-
console.log("🔍 Decryption results:", JSON.stringify(result_decrypt, null, 2));
|
|
342
|
-
break;
|
|
343
|
-
|
|
344
|
-
case "push":
|
|
345
|
-
if (args.length < 3) {
|
|
346
|
-
console.log("❌ Uso: push <key> <array> <value> [encryption_key]");
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
let key_push = args[0];
|
|
350
|
-
let array_push = args[1];
|
|
351
|
-
let value_push = args[2];
|
|
352
|
-
let encryption_key_push = args[3] || null;
|
|
353
|
-
// Parsear JSON en el valor si es necesario
|
|
354
|
-
value_push = parseData(value_push);
|
|
355
|
-
let result_push = await satoriInstance.push(addCredentials({ key: key_push, array: array_push, value: value_push, encryption_key: encryption_key_push }));
|
|
356
|
-
console.log("🔍 Insertion results:", JSON.stringify(result_push, null, 2));
|
|
357
|
-
break;
|
|
358
|
-
|
|
359
|
-
case "pop":
|
|
360
|
-
if (args.length < 2) {
|
|
361
|
-
console.log("❌ Uso: pop <key> <array> [encryption_key]");
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
let key_pop = args[0];
|
|
365
|
-
let array_pop = args[1];
|
|
366
|
-
let encryption_key_pop = args[2] || null;
|
|
367
|
-
let result_pop = await satoriInstance.pop(addCredentials({ key: key_pop, array: array_pop, encryption_key: encryption_key_pop }));
|
|
368
|
-
console.log("🔍 Removal results:", JSON.stringify(result_pop, null, 2));
|
|
369
|
-
break;
|
|
370
|
-
|
|
371
|
-
case "splice":
|
|
372
|
-
if (args.length < 2) {
|
|
373
|
-
console.log("❌ Uso: splice <key> <array> [encryption_key]");
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
let key_splice = args[0];
|
|
377
|
-
let array_splice = args[1];
|
|
378
|
-
let encryption_key_splice = args[2] || null;
|
|
379
|
-
let result_splice = await satoriInstance.splice(addCredentials({ key: key_splice, array: array_splice, encryption_key: encryption_key_splice }));
|
|
380
|
-
console.log("🔍 Insertion results:", JSON.stringify(result_splice, null, 2));
|
|
381
|
-
break;
|
|
382
|
-
|
|
383
|
-
case "remove":
|
|
384
|
-
if (args.length < 3) {
|
|
385
|
-
console.log("❌ Uso: remove <key> <array> <value> [encryption_key]");
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
let key_remove = args[0];
|
|
389
|
-
let array_remove = args[1];
|
|
390
|
-
let value_remove = args[2];
|
|
391
|
-
let encryption_key_remove = args[3] || null;
|
|
392
|
-
// Parsear JSON en el valor si es necesario
|
|
393
|
-
value_remove = parseData(value_remove);
|
|
394
|
-
let result_remove = await satoriInstance.remove(addCredentials({ key: key_remove, array: array_remove, value: value_remove, encryption_key: encryption_key_remove }));
|
|
395
|
-
console.log("🔍 Removal results:", JSON.stringify(result_remove, null, 2));
|
|
396
|
-
break;
|
|
397
|
-
|
|
398
|
-
case "set_vertex":
|
|
399
|
-
if (args.length < 2) {
|
|
400
|
-
console.log("❌ Uso: set_vertex <key> <vertex> [encryption_key]");
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
let key_set_vertex = args[0];
|
|
404
|
-
let vertex_set_vertex = args[1];
|
|
405
|
-
let encryption_key_set_vertex = args[2] || null;
|
|
406
|
-
// Parsear JSON en el vertex si es necesario
|
|
407
|
-
vertex_set_vertex = parseData(vertex_set_vertex);
|
|
408
|
-
let result_set_vertex = await satoriInstance.set_vertex(addCredentials({ key: key_set_vertex, vertex: vertex_set_vertex, encryption_key: encryption_key_set_vertex }));
|
|
409
|
-
console.log("🔍 Insertion results:", JSON.stringify(result_set_vertex, null, 2));
|
|
410
|
-
break;
|
|
411
|
-
|
|
412
|
-
case "get_vertex":
|
|
413
|
-
if (args.length < 1) {
|
|
414
|
-
console.log("❌ Uso: get_vertex <key> [encryption_key]");
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
let key_get_vertex = args[0];
|
|
418
|
-
let encryption_key_get_vertex = args[1] || null;
|
|
419
|
-
let result_get_vertex = await satoriInstance.get_vertex(addCredentials({ key: key_get_vertex, encryption_key: encryption_key_get_vertex }));
|
|
420
|
-
console.log("🔍 Insertion results:", JSON.stringify(result_get_vertex, null, 2));
|
|
421
|
-
break;
|
|
422
|
-
|
|
423
|
-
case "delete_vertex":
|
|
424
|
-
if (args.length < 2) {
|
|
425
|
-
console.log("❌ Uso: delete_vertex <key> <vertex> [encryption_key]");
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
let key_delete_vertex = args[0];
|
|
429
|
-
let vertex_delete_vertex = args[1];
|
|
430
|
-
let encryption_key_delete_vertex = args[2] || null;
|
|
431
|
-
// Parsear JSON en el vertex si es necesario
|
|
432
|
-
vertex_delete_vertex = parseData(vertex_delete_vertex);
|
|
433
|
-
let result_delete_vertex = await satoriInstance.delete_vertex(addCredentials({ key: key_delete_vertex, vertex: vertex_delete_vertex, encryption_key: encryption_key_delete_vertex }));
|
|
434
|
-
console.log("🔍 Removal results:", JSON.stringify(result_delete_vertex, null, 2));
|
|
435
|
-
break;
|
|
436
|
-
|
|
437
|
-
default:
|
|
438
|
-
console.log("❌ Command not recognized. Use 'help' to see available commands.");
|
|
83
|
+
// -------------------------------
|
|
84
|
+
// Commands Registry
|
|
85
|
+
// -------------------------------
|
|
86
|
+
const commands = {
|
|
87
|
+
help: {
|
|
88
|
+
desc: "Show help",
|
|
89
|
+
run() { showHelp(); }
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
exit: { run() { process.exit(0); } },
|
|
93
|
+
quit: { run() { process.exit(0); } },
|
|
94
|
+
clear: { run() { console.clear(); } },
|
|
95
|
+
|
|
96
|
+
connect: {
|
|
97
|
+
desc: "connect <host> [user] [password]",
|
|
98
|
+
async run([host, user = null, password = null]) {
|
|
99
|
+
if (!host) throw new Error("Usage: connect <host> [user] [password]");
|
|
100
|
+
console.log(`🔗 Connecting to ${host}...`);
|
|
101
|
+
satori = new Satori({ host, user, password });
|
|
102
|
+
await satori.connect();
|
|
103
|
+
Object.assign(session, { host, user, password });
|
|
104
|
+
console.log("✅ Connected");
|
|
439
105
|
}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
mindspace: {
|
|
109
|
+
desc: "mindspace select <id>",
|
|
110
|
+
run([sub, id]) {
|
|
111
|
+
if (sub !== "select" || !id) throw new Error("Usage: mindspace select <id>");
|
|
112
|
+
session.mindspace = id;
|
|
113
|
+
console.log(`🧠 Mindspace selected: ${id}`);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
set: {
|
|
118
|
+
desc: "set <key> <data>",
|
|
119
|
+
async run([key, ...rest]) {
|
|
120
|
+
requireConn();
|
|
121
|
+
if (!key || !rest.length) throw new Error("Usage: set <key> <data>");
|
|
122
|
+
const data = parseData(rest.join(" "));
|
|
123
|
+
await satori.set(withCreds({ key, data }));
|
|
124
|
+
console.log(`✅ Saved: ${key}`);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
get: {
|
|
129
|
+
desc: "get <key>",
|
|
130
|
+
async run([key]) {
|
|
131
|
+
requireConn();
|
|
132
|
+
const r = await satori.get(withCreds(key ? { key } : {}));
|
|
133
|
+
console.log(JSON.stringify(r, null, 2));
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
put: {
|
|
138
|
+
desc: "put <key> <field> <value>",
|
|
139
|
+
async run([key, field, value]) {
|
|
140
|
+
requireConn();
|
|
141
|
+
if (!key || !field) throw new Error("Usage: put <key> <field> <value>");
|
|
142
|
+
await satori.put(withCreds({ key, replace_field: field, replace_value: parseData(value) }));
|
|
143
|
+
console.log("✅ Updated");
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
delete: {
|
|
148
|
+
desc: "delete <key>",
|
|
149
|
+
async run([key]) {
|
|
150
|
+
requireConn();
|
|
151
|
+
if (!key) throw new Error("Usage: delete <key>");
|
|
152
|
+
await satori.delete(withCreds({ key }));
|
|
153
|
+
console.log(`🗑️ Deleted: ${key}`);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
ask: {
|
|
158
|
+
desc: "ask \"<question>\"",
|
|
159
|
+
async run([q, backend = null]) {
|
|
160
|
+
requireConn();
|
|
161
|
+
if (!q) throw new Error("Usage: ask \"<question>\"");
|
|
162
|
+
const r = await satori.ask(withCreds({ question: q, backend }));
|
|
163
|
+
console.log(JSON.stringify(r, null, 2));
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
query: {
|
|
168
|
+
desc: "query \"<query>\"",
|
|
169
|
+
async run([q, backend = null]) {
|
|
170
|
+
requireConn();
|
|
171
|
+
if (!q) throw new Error("Usage: query \"<query>\"");
|
|
172
|
+
const r = await satori.query(withCreds({ query: q, backend }));
|
|
173
|
+
console.log(JSON.stringify(r, null, 2));
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
chat: {
|
|
178
|
+
desc: "chat \"<message>\"",
|
|
179
|
+
async run([msg]) {
|
|
180
|
+
requireConn();
|
|
181
|
+
if (!session.mindspace) throw new Error("Select a mindspace first");
|
|
182
|
+
const r = await satori.chatMindspace({ mindspace_id: session.mindspace, message: msg });
|
|
183
|
+
console.log(r.data);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
lecture: {
|
|
188
|
+
desc: "lecture \"<corpus>\"",
|
|
189
|
+
async run([corpus]) {
|
|
190
|
+
requireConn();
|
|
191
|
+
if (!session.mindspace) throw new Error("Select a mindspace first");
|
|
192
|
+
const r = await satori.lectureMindspace({ mindspace_id: session.mindspace, corpus });
|
|
193
|
+
console.log(r.data);
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
};
|
|
444
197
|
|
|
198
|
+
// -------------------------------
|
|
199
|
+
// Help
|
|
200
|
+
// -------------------------------
|
|
445
201
|
function showHelp() {
|
|
446
|
-
console.log(
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
📝 Data operations:
|
|
453
|
-
set <key> <data> - Save data to a key
|
|
454
|
-
put <key> <field> <value> - Update a specific field
|
|
455
|
-
get <key> - Get data from a key
|
|
456
|
-
delete <key> - Delete a key
|
|
457
|
-
ask "<question>" [backend] - Ask a question
|
|
458
|
-
query "<query>" [backend] - Make a query
|
|
459
|
-
|
|
460
|
-
🛠️ Others:
|
|
461
|
-
help - Show this help
|
|
462
|
-
exit / quit - Exit the CLI
|
|
463
|
-
clear - Clear screen
|
|
464
|
-
|
|
465
|
-
💡 Examples:
|
|
466
|
-
set user:123 '{"name": "John", "age": 25}'
|
|
467
|
-
ask "What is the weather like today?"
|
|
468
|
-
query "Find all users with age > 25"
|
|
469
|
-
`);
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function clearScreen() {
|
|
473
|
-
console.clear();
|
|
202
|
+
console.log("\n📚 Commands:\n");
|
|
203
|
+
Object.entries(commands).forEach(([k, v]) => {
|
|
204
|
+
if (v.desc) console.log(` ${k.padEnd(12)} ${v.desc}`);
|
|
205
|
+
});
|
|
206
|
+
console.log("");
|
|
474
207
|
}
|
|
475
208
|
|
|
476
|
-
|
|
477
|
-
|
|
209
|
+
// -------------------------------
|
|
210
|
+
// Interactive Shell
|
|
211
|
+
// -------------------------------
|
|
212
|
+
async function startREPL() {
|
|
213
|
+
const rl = readline.createInterface({
|
|
478
214
|
input: process.stdin,
|
|
479
215
|
output: process.stdout,
|
|
480
|
-
|
|
216
|
+
historySize: 500,
|
|
217
|
+
completer(line) {
|
|
218
|
+
const hits = Object.keys(commands).filter(c => c.startsWith(line));
|
|
219
|
+
return [hits.length ? hits : Object.keys(commands), line];
|
|
220
|
+
}
|
|
481
221
|
});
|
|
482
222
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
console.log("💡 Use 'connect <host> [user] [password]' to start\n");
|
|
486
|
-
|
|
223
|
+
const refreshPrompt = () => rl.setPrompt(promptLabel());
|
|
224
|
+
refreshPrompt();
|
|
487
225
|
rl.prompt();
|
|
488
226
|
|
|
489
|
-
rl.on(
|
|
490
|
-
|
|
227
|
+
rl.on("line", async (line) => {
|
|
228
|
+
const parts = parseArgs(line.trim());
|
|
229
|
+
if (!parts.length) return rl.prompt();
|
|
491
230
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
231
|
+
const [cmd, ...args] = parts;
|
|
232
|
+
const entry = commands[cmd];
|
|
496
233
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
if (command === 'connect') {
|
|
503
|
-
if (args.length < 1) {
|
|
504
|
-
console.log("❌ Uso: connect <host> [user] [password]");
|
|
505
|
-
rl.prompt();
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
let host = args[0];
|
|
509
|
-
let user = args[1] || null;
|
|
510
|
-
let password = args[2] || null;
|
|
511
|
-
let success = await connectToSatori(host, user, password);
|
|
512
|
-
if (success) {
|
|
513
|
-
console.log("✅ Now you can use commands like 'set', 'get', 'put', etc.");
|
|
514
|
-
}
|
|
515
|
-
} else {
|
|
516
|
-
await executeCommand(command, args);
|
|
234
|
+
try {
|
|
235
|
+
if (!entry) throw new Error("Unknown command. Type 'help'.");
|
|
236
|
+
await entry.run(args);
|
|
237
|
+
} catch (e) {
|
|
238
|
+
console.error("❌", e.message);
|
|
517
239
|
}
|
|
518
240
|
|
|
241
|
+
refreshPrompt();
|
|
519
242
|
rl.prompt();
|
|
520
243
|
});
|
|
521
|
-
|
|
522
|
-
rl.on('close', () => {
|
|
523
|
-
console.log('\n👋 Goodbye!');
|
|
524
|
-
process.exit(0);
|
|
525
|
-
});
|
|
526
244
|
}
|
|
527
245
|
|
|
528
|
-
|
|
246
|
+
// -------------------------------
|
|
247
|
+
// Entry
|
|
248
|
+
// -------------------------------
|
|
249
|
+
const argv = process.argv.slice(2);
|
|
529
250
|
|
|
530
|
-
if (
|
|
531
|
-
|
|
251
|
+
if (argv[0] === "run") {
|
|
252
|
+
runBinary(argv.slice(1));
|
|
253
|
+
} else if (argv[0] === "update") {
|
|
532
254
|
require("./postinstall");
|
|
533
|
-
} else if (
|
|
534
|
-
run(args.slice(1));
|
|
535
|
-
} else if (args[0] === "connect") {
|
|
536
|
-
if (args.length < 2) {
|
|
537
|
-
console.log("❌ Uso: satoridb connect <host> [user] [password]");
|
|
538
|
-
process.exit(1);
|
|
539
|
-
}
|
|
540
|
-
let host = args[1];
|
|
541
|
-
let user = args[2] || null;
|
|
542
|
-
let password = args[3] || null;
|
|
543
|
-
connectToSatori(host, user, password).then(success => {
|
|
544
|
-
if (success) {
|
|
545
|
-
startInteractiveCLI();
|
|
546
|
-
} else {
|
|
547
|
-
process.exit(1);
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
} else if (args[0] === "verify") {
|
|
551
|
-
console.log("🔍 Verifying installation...");
|
|
255
|
+
} else if (argv[0] === "verify") {
|
|
552
256
|
require("./verify-install");
|
|
553
257
|
} else {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
258
|
+
startREPL();
|
|
259
|
+
}
|