satoridb 1.2.2 → 1.2.4

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