satoridb 1.2.5 โ 1.2.7
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 +712 -278
- package/package.json +8 -3
- package/postinstall.js +20 -2
package/cli.js
CHANGED
|
@@ -1,278 +1,712 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Satori Interactive CLI
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Satori Interactive CLI
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const satori_node_1 = require("satori-node");
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const child_process_1 = require("child_process");
|
|
15
|
+
const readline_1 = __importDefault(require("readline"));
|
|
16
|
+
// โโ ANSI helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
17
|
+
const c = {
|
|
18
|
+
reset: '\x1b[0m',
|
|
19
|
+
dim: '\x1b[2m',
|
|
20
|
+
bold: '\x1b[1m',
|
|
21
|
+
cyan: '\x1b[96m',
|
|
22
|
+
green: '\x1b[92m',
|
|
23
|
+
yellow: '\x1b[93m',
|
|
24
|
+
red: '\x1b[91m',
|
|
25
|
+
blue: '\x1b[94m',
|
|
26
|
+
magenta: '\x1b[95m',
|
|
27
|
+
};
|
|
28
|
+
// โโ State โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
29
|
+
let VERSION = '1.2.6';
|
|
30
|
+
try {
|
|
31
|
+
VERSION = require('./package.json').version;
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
const binName = os_1.default.platform() === 'win32' ? 'satori.exe' : 'satori';
|
|
35
|
+
const binPath = path_1.default.join(os_1.default.homedir(), '.satori', 'bin', binName);
|
|
36
|
+
const historyDir = path_1.default.join(os_1.default.homedir(), '.satori');
|
|
37
|
+
const historyFile = path_1.default.join(historyDir, '.cli_history');
|
|
38
|
+
let satori = null;
|
|
39
|
+
let session = {
|
|
40
|
+
host: null,
|
|
41
|
+
user: null,
|
|
42
|
+
password: null,
|
|
43
|
+
mindspace: null,
|
|
44
|
+
};
|
|
45
|
+
// โโ Print helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
46
|
+
const ok = (msg) => console.log(` ${c.green}โ${c.reset} ${msg}`);
|
|
47
|
+
const fail = (msg) => console.error(` ${c.red}โ${c.reset} ${msg}`);
|
|
48
|
+
const info = (msg) => console.log(` ${c.dim}${msg}${c.reset}`);
|
|
49
|
+
function colorJson(obj) {
|
|
50
|
+
if (obj === null || obj === undefined)
|
|
51
|
+
return `${c.dim}null${c.reset}`;
|
|
52
|
+
if (typeof obj === 'string')
|
|
53
|
+
return obj;
|
|
54
|
+
if (typeof obj === 'number')
|
|
55
|
+
return `${c.yellow}${obj}${c.reset}`;
|
|
56
|
+
if (typeof obj === 'boolean')
|
|
57
|
+
return `${c.magenta}${obj}${c.reset}`;
|
|
58
|
+
return JSON.stringify(obj, null, 2)
|
|
59
|
+
.replace(/"([^"]+)":/g, `${c.cyan}"$1"${c.reset}:`)
|
|
60
|
+
.replace(/: "([^"]*)"/g, `: ${c.green}"$1"${c.reset}`)
|
|
61
|
+
.replace(/: (-?\d+(?:\.\d+)?)/g, `: ${c.yellow}$1${c.reset}`)
|
|
62
|
+
.replace(/: (true|false)/g, `: ${c.magenta}$1${c.reset}`)
|
|
63
|
+
.replace(/: null/g, `: ${c.dim}null${c.reset}`);
|
|
64
|
+
}
|
|
65
|
+
function print(obj) {
|
|
66
|
+
if (obj === null || obj === undefined)
|
|
67
|
+
return;
|
|
68
|
+
const s = colorJson(obj);
|
|
69
|
+
console.log('\n' + s.split('\n').map(l => ` ${l}`).join('\n') + '\n');
|
|
70
|
+
}
|
|
71
|
+
function spinner(text) {
|
|
72
|
+
if (!process.stdout.isTTY)
|
|
73
|
+
return () => { };
|
|
74
|
+
const frames = ['โ ', 'โ ', 'โ น', 'โ ธ', 'โ ผ', 'โ ด', 'โ ฆ', 'โ ง', 'โ ', 'โ '];
|
|
75
|
+
let i = 0;
|
|
76
|
+
const id = setInterval(() => {
|
|
77
|
+
process.stdout.write(`\r ${c.dim}${frames[i++ % frames.length]}${c.reset} ${c.dim}${text}${c.reset} `);
|
|
78
|
+
}, 80);
|
|
79
|
+
return () => {
|
|
80
|
+
clearInterval(id);
|
|
81
|
+
process.stdout.write('\r' + ' '.repeat(text.length + 10) + '\r');
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// โโ Banner โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
85
|
+
function showBanner() {
|
|
86
|
+
const logo = [
|
|
87
|
+
` ___ _ _ ____ ___ ____ ____`,
|
|
88
|
+
`/ __|| | |/ _ |/ || _ \\|_ _|`,
|
|
89
|
+
`\\__ \\| | |\\__ | / \\ || / | | `,
|
|
90
|
+
`|___/|___|/____||_| |_||_|\\_\\ |_| `,
|
|
91
|
+
];
|
|
92
|
+
console.log();
|
|
93
|
+
logo.forEach(l => console.log(` ${c.cyan}${c.bold}${l}${c.reset}`));
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(` ${c.dim}AI-Native Distributed Object Database ยท v${VERSION}${c.reset}`);
|
|
96
|
+
console.log(` ${c.dim}Type ${c.reset}${c.cyan}help${c.reset}` +
|
|
97
|
+
`${c.dim} for commands ยท ${c.reset}${c.cyan}connect <host>${c.reset}${c.dim} to begin${c.reset}`);
|
|
98
|
+
console.log();
|
|
99
|
+
}
|
|
100
|
+
// โโ Utilities โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
101
|
+
function assertInstalled() {
|
|
102
|
+
if (!fs_1.default.existsSync(binPath)) {
|
|
103
|
+
fail('Satori binary not found. Run: npm install -g satoridb');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function runBinary(args) {
|
|
108
|
+
assertInstalled();
|
|
109
|
+
const r = (0, child_process_1.spawnSync)(binPath, args, { stdio: 'inherit' });
|
|
110
|
+
if (r.error)
|
|
111
|
+
throw r.error;
|
|
112
|
+
}
|
|
113
|
+
/** Tokenises a shell-style line respecting single/double quotes and backslash escapes. */
|
|
114
|
+
function parseArgs(line) {
|
|
115
|
+
const re = /"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|(\S+)/g;
|
|
116
|
+
const out = [];
|
|
117
|
+
let m;
|
|
118
|
+
while ((m = re.exec(line)))
|
|
119
|
+
out.push(m[1] ?? m[2] ?? m[3]);
|
|
120
|
+
return out.map(v => v.replace(/\\"/g, '"').replace(/\\'/g, "'"));
|
|
121
|
+
}
|
|
122
|
+
/** Auto-casts string tokens to bool / number / JSON when unambiguous. */
|
|
123
|
+
function parseData(v) {
|
|
124
|
+
if (typeof v !== 'string')
|
|
125
|
+
return v;
|
|
126
|
+
const t = v.trim();
|
|
127
|
+
if (t === 'true')
|
|
128
|
+
return true;
|
|
129
|
+
if (t === 'false')
|
|
130
|
+
return false;
|
|
131
|
+
if (t !== '' && !isNaN(Number(t)))
|
|
132
|
+
return Number(t);
|
|
133
|
+
if (t.startsWith('{') || t.startsWith('[')) {
|
|
134
|
+
try {
|
|
135
|
+
return JSON.parse(t);
|
|
136
|
+
}
|
|
137
|
+
catch { }
|
|
138
|
+
}
|
|
139
|
+
return v;
|
|
140
|
+
}
|
|
141
|
+
function requireConn() {
|
|
142
|
+
if (!satori)
|
|
143
|
+
throw new Error('Not connected โ use: connect <host>');
|
|
144
|
+
}
|
|
145
|
+
function requireMindspace() {
|
|
146
|
+
requireConn();
|
|
147
|
+
if (!session.mindspace) {
|
|
148
|
+
throw new Error('No mindspace selected โ use: mindspace select <id>');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Returns `any` so the extra auth fields don't clash with typed SDK payloads.
|
|
152
|
+
// The satori-node already injects username/password via its constructor, but
|
|
153
|
+
// some server builds also accept them in the command body โ keep for compat.
|
|
154
|
+
function withCreds(p = {}) {
|
|
155
|
+
if (session.user)
|
|
156
|
+
p.user = session.user;
|
|
157
|
+
if (session.password)
|
|
158
|
+
p.password = session.password;
|
|
159
|
+
return p;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Plain-text prompt โ ANSI codes inside the prompt string break readline's
|
|
163
|
+
* cursor-position tracking, so keep it clean.
|
|
164
|
+
*/
|
|
165
|
+
function promptLabel() {
|
|
166
|
+
const host = session.host
|
|
167
|
+
? session.host.replace(/^wss?:\/\//, '').replace(/:\d+$/, '')
|
|
168
|
+
: null;
|
|
169
|
+
const ms = session.mindspace ? `[${session.mindspace}]` : '';
|
|
170
|
+
return host ? `${host}${ms} โบ ` : 'satori โบ ';
|
|
171
|
+
}
|
|
172
|
+
// โโ History โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
173
|
+
function loadHistory() {
|
|
174
|
+
try {
|
|
175
|
+
if (fs_1.default.existsSync(historyFile)) {
|
|
176
|
+
return fs_1.default.readFileSync(historyFile, 'utf8')
|
|
177
|
+
.split('\n').filter(Boolean).reverse().slice(0, 500);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch { }
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
function saveHistory(history) {
|
|
184
|
+
try {
|
|
185
|
+
fs_1.default.mkdirSync(historyDir, { recursive: true });
|
|
186
|
+
fs_1.default.writeFileSync(historyFile, [...history].reverse().join('\n') + '\n');
|
|
187
|
+
}
|
|
188
|
+
catch { }
|
|
189
|
+
}
|
|
190
|
+
const commands = {
|
|
191
|
+
// โโ Connection โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
192
|
+
connect: {
|
|
193
|
+
group: 'Connection', noConn: true,
|
|
194
|
+
desc: 'connect <host> [user] [password]',
|
|
195
|
+
async run([host, user, password]) {
|
|
196
|
+
if (!host)
|
|
197
|
+
throw new Error('Usage: connect <host> [user] [password]');
|
|
198
|
+
const stop = spinner(`Connecting to ${host}`);
|
|
199
|
+
try {
|
|
200
|
+
satori = (user && password)
|
|
201
|
+
? new satori_node_1.Satori({ host, username: user, password })
|
|
202
|
+
: new satori_node_1.Satori({ host, username: '', password: '' });
|
|
203
|
+
await satori.connect();
|
|
204
|
+
stop();
|
|
205
|
+
Object.assign(session, { host, user: user ?? null, password: password ?? null });
|
|
206
|
+
ok(`Connected ${c.dim}${host}${c.reset}`);
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
stop();
|
|
210
|
+
satori = null;
|
|
211
|
+
throw new Error(`Connection failed: ${e.message ?? e}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
disconnect: {
|
|
216
|
+
group: 'Connection',
|
|
217
|
+
desc: 'Disconnect from server',
|
|
218
|
+
run() {
|
|
219
|
+
requireConn();
|
|
220
|
+
const host = session.host;
|
|
221
|
+
satori = null;
|
|
222
|
+
Object.assign(session, { host: null, user: null, password: null, mindspace: null });
|
|
223
|
+
ok(`Disconnected from ${host}`);
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
status: {
|
|
227
|
+
group: 'Connection', noConn: true,
|
|
228
|
+
desc: 'Show connection info',
|
|
229
|
+
run() {
|
|
230
|
+
if (!satori) {
|
|
231
|
+
info('Not connected');
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
console.log();
|
|
235
|
+
console.log(` ${c.dim}Host ${c.reset}${c.cyan}${session.host}${c.reset}`);
|
|
236
|
+
if (session.user)
|
|
237
|
+
console.log(` ${c.dim}User ${c.reset}${session.user}`);
|
|
238
|
+
if (session.mindspace)
|
|
239
|
+
console.log(` ${c.dim}Mindspace ${c.reset}${c.green}${session.mindspace}${c.reset}`);
|
|
240
|
+
console.log(` ${c.dim}Status ${c.reset}${c.green}โ Connected${c.reset}`);
|
|
241
|
+
console.log();
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
// โโ Data โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
245
|
+
set: {
|
|
246
|
+
group: 'Data',
|
|
247
|
+
desc: 'set <key> <data>',
|
|
248
|
+
async run([key, ...rest]) {
|
|
249
|
+
requireConn();
|
|
250
|
+
if (!key || !rest.length)
|
|
251
|
+
throw new Error('Usage: set <key> <data>');
|
|
252
|
+
const stop = spinner('Writing...');
|
|
253
|
+
await satori.set(withCreds({ key, data: parseData(rest.join(' ')) }));
|
|
254
|
+
stop();
|
|
255
|
+
ok(`Saved ${c.cyan}${key}${c.reset}`);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
get: {
|
|
259
|
+
group: 'Data',
|
|
260
|
+
desc: 'get [key]',
|
|
261
|
+
async run([key]) {
|
|
262
|
+
requireConn();
|
|
263
|
+
const stop = spinner('Fetching...');
|
|
264
|
+
const r = await satori.get(withCreds(key ? { key } : {}));
|
|
265
|
+
stop();
|
|
266
|
+
print(r);
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
put: {
|
|
270
|
+
group: 'Data',
|
|
271
|
+
desc: 'put <key> <field> <value>',
|
|
272
|
+
async run([key, field, ...rest]) {
|
|
273
|
+
requireConn();
|
|
274
|
+
if (!key || !field || !rest.length)
|
|
275
|
+
throw new Error('Usage: put <key> <field> <value>');
|
|
276
|
+
const stop = spinner('Updating...');
|
|
277
|
+
await satori.put(withCreds({
|
|
278
|
+
key,
|
|
279
|
+
replace_field: field,
|
|
280
|
+
replace_value: parseData(rest.join(' ')),
|
|
281
|
+
}));
|
|
282
|
+
stop();
|
|
283
|
+
ok(`Updated ${c.cyan}${key}${c.reset}.${field}`);
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
delete: {
|
|
287
|
+
group: 'Data',
|
|
288
|
+
desc: 'delete <key>',
|
|
289
|
+
async run([key]) {
|
|
290
|
+
requireConn();
|
|
291
|
+
if (!key)
|
|
292
|
+
throw new Error('Usage: delete <key>');
|
|
293
|
+
const stop = spinner('Deleting...');
|
|
294
|
+
await satori.delete(withCreds({ key }));
|
|
295
|
+
stop();
|
|
296
|
+
ok(`Deleted ${c.cyan}${key}${c.reset}`);
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
push: {
|
|
300
|
+
group: 'Data',
|
|
301
|
+
desc: 'push <key> <array> <value> โ append to array field',
|
|
302
|
+
async run([key, array, ...rest]) {
|
|
303
|
+
requireConn();
|
|
304
|
+
if (!key || !array || !rest.length)
|
|
305
|
+
throw new Error('Usage: push <key> <array> <value>');
|
|
306
|
+
const stop = spinner('Pushing...');
|
|
307
|
+
const r = await satori.push(withCreds({ key, array, value: parseData(rest.join(' ')) }));
|
|
308
|
+
stop();
|
|
309
|
+
print(r);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
pop: {
|
|
313
|
+
group: 'Data',
|
|
314
|
+
desc: 'pop <key> <array> โ remove last array element',
|
|
315
|
+
async run([key, array]) {
|
|
316
|
+
requireConn();
|
|
317
|
+
if (!key || !array)
|
|
318
|
+
throw new Error('Usage: pop <key> <array>');
|
|
319
|
+
const stop = spinner('Popping...');
|
|
320
|
+
const r = await satori.pop(withCreds({ key, array }));
|
|
321
|
+
stop();
|
|
322
|
+
print(r);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
splice: {
|
|
326
|
+
group: 'Data',
|
|
327
|
+
desc: 'splice <key> <array> โ remove first array element',
|
|
328
|
+
async run([key, array]) {
|
|
329
|
+
requireConn();
|
|
330
|
+
if (!key || !array)
|
|
331
|
+
throw new Error('Usage: splice <key> <array>');
|
|
332
|
+
const stop = spinner('Splicing...');
|
|
333
|
+
const r = await satori.splice(withCreds({ key, array }));
|
|
334
|
+
stop();
|
|
335
|
+
print(r);
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
remove: {
|
|
339
|
+
group: 'Data',
|
|
340
|
+
desc: 'remove <key> <array> <value> โ remove specific value from array',
|
|
341
|
+
async run([key, array, ...rest]) {
|
|
342
|
+
requireConn();
|
|
343
|
+
if (!key || !array || !rest.length)
|
|
344
|
+
throw new Error('Usage: remove <key> <array> <value>');
|
|
345
|
+
const stop = spinner('Removing...');
|
|
346
|
+
const r = await satori.remove(withCreds({ key, array, value: parseData(rest.join(' ')) }));
|
|
347
|
+
stop();
|
|
348
|
+
print(r);
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
// โโ Graph โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
352
|
+
'set-vertex': {
|
|
353
|
+
group: 'Graph',
|
|
354
|
+
desc: 'set-vertex <from> <to> [weight]',
|
|
355
|
+
async run([from, to, weight]) {
|
|
356
|
+
requireConn();
|
|
357
|
+
if (!from || !to)
|
|
358
|
+
throw new Error('Usage: set-vertex <from> <to> [weight]');
|
|
359
|
+
const stop = spinner('Creating edge...');
|
|
360
|
+
await satori.setVertex(withCreds({
|
|
361
|
+
key: from, vertex: to,
|
|
362
|
+
weight: weight !== undefined ? Number(weight) : 1,
|
|
363
|
+
}));
|
|
364
|
+
stop();
|
|
365
|
+
ok(`Edge ${c.cyan}${from}${c.reset} โ ${c.cyan}${to}${c.reset}`);
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
'get-vertex': {
|
|
369
|
+
group: 'Graph',
|
|
370
|
+
desc: 'get-vertex <key>',
|
|
371
|
+
async run([key]) {
|
|
372
|
+
requireConn();
|
|
373
|
+
if (!key)
|
|
374
|
+
throw new Error('Usage: get-vertex <key>');
|
|
375
|
+
const stop = spinner('Fetching edges...');
|
|
376
|
+
const r = await satori.getVertex(withCreds({ key }));
|
|
377
|
+
stop();
|
|
378
|
+
print(r);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
'delete-vertex': {
|
|
382
|
+
group: 'Graph',
|
|
383
|
+
desc: 'delete-vertex <from> <to>',
|
|
384
|
+
async run([from, to]) {
|
|
385
|
+
requireConn();
|
|
386
|
+
if (!from || !to)
|
|
387
|
+
throw new Error('Usage: delete-vertex <from> <to>');
|
|
388
|
+
const stop = spinner('Removing edge...');
|
|
389
|
+
await satori.deleteVertex(withCreds({ key: from, vertex: to }));
|
|
390
|
+
stop();
|
|
391
|
+
ok(`Edge removed ${c.cyan}${from}${c.reset} โ ${c.cyan}${to}${c.reset}`);
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
dfs: {
|
|
395
|
+
group: 'Graph',
|
|
396
|
+
desc: 'dfs <node> โ depth-first traversal',
|
|
397
|
+
async run([node]) {
|
|
398
|
+
requireConn();
|
|
399
|
+
if (!node)
|
|
400
|
+
throw new Error('Usage: dfs <node>');
|
|
401
|
+
const stop = spinner('Traversing (DFS)...');
|
|
402
|
+
const r = await satori.dfs(withCreds({ node }));
|
|
403
|
+
stop();
|
|
404
|
+
print(r);
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
bfs: {
|
|
408
|
+
group: 'Graph',
|
|
409
|
+
desc: 'bfs <node> โ breadth-first traversal',
|
|
410
|
+
async run([node]) {
|
|
411
|
+
requireConn();
|
|
412
|
+
if (!node)
|
|
413
|
+
throw new Error('Usage: bfs <node>');
|
|
414
|
+
const stop = spinner('Traversing (BFS)...');
|
|
415
|
+
const r = await satori.graphBfs(withCreds({ node }));
|
|
416
|
+
stop();
|
|
417
|
+
print(r);
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
'shortest-path': {
|
|
421
|
+
group: 'Graph',
|
|
422
|
+
desc: 'shortest-path <node> <target>',
|
|
423
|
+
async run([node, target]) {
|
|
424
|
+
requireConn();
|
|
425
|
+
if (!node || !target)
|
|
426
|
+
throw new Error('Usage: shortest-path <node> <target>');
|
|
427
|
+
const stop = spinner('Computing...');
|
|
428
|
+
const r = await satori.graphShortestPath(withCreds({ node, target }));
|
|
429
|
+
stop();
|
|
430
|
+
print(r);
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
// โโ AI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
434
|
+
ask: {
|
|
435
|
+
group: 'AI',
|
|
436
|
+
desc: 'ask "<question>" [backend]',
|
|
437
|
+
async run([q, backend]) {
|
|
438
|
+
requireConn();
|
|
439
|
+
if (!q)
|
|
440
|
+
throw new Error('Usage: ask "<question>" [backend]');
|
|
441
|
+
const stop = spinner('Thinking...');
|
|
442
|
+
const r = await satori.ask(withCreds({
|
|
443
|
+
question: q,
|
|
444
|
+
...(backend ? { backend } : {}),
|
|
445
|
+
}));
|
|
446
|
+
stop();
|
|
447
|
+
const answer = r?.message ?? r?.data ?? r;
|
|
448
|
+
print(answer);
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
query: {
|
|
452
|
+
group: 'AI',
|
|
453
|
+
desc: 'query "<text>" [backend]',
|
|
454
|
+
async run([q, backend]) {
|
|
455
|
+
requireConn();
|
|
456
|
+
if (!q)
|
|
457
|
+
throw new Error('Usage: query "<text>" [backend]');
|
|
458
|
+
const stop = spinner('Searching...');
|
|
459
|
+
const r = await satori.query(withCreds({
|
|
460
|
+
query: q,
|
|
461
|
+
...(backend ? { backend } : {}),
|
|
462
|
+
}));
|
|
463
|
+
stop();
|
|
464
|
+
print(r);
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
// โโ Mindspace โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
468
|
+
mindspace: {
|
|
469
|
+
group: 'Mindspace', noConn: true,
|
|
470
|
+
desc: 'mindspace select <id> โ set active mindspace (local only)',
|
|
471
|
+
run([sub, id]) {
|
|
472
|
+
if (sub !== 'select' || !id)
|
|
473
|
+
throw new Error('Usage: mindspace select <id>');
|
|
474
|
+
session.mindspace = id;
|
|
475
|
+
ok(`Active mindspace: ${c.green}${id}${c.reset}`);
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
'set-mindspace': {
|
|
479
|
+
group: 'Mindspace',
|
|
480
|
+
desc: 'set-mindspace [id] โ create / activate mindspace on server',
|
|
481
|
+
async run([id]) {
|
|
482
|
+
requireConn();
|
|
483
|
+
const mid = id ?? session.mindspace;
|
|
484
|
+
if (!mid)
|
|
485
|
+
throw new Error('Provide an ID or select one first: mindspace select <id>');
|
|
486
|
+
const stop = spinner(`Activating ${mid}...`);
|
|
487
|
+
await satori.setMindspace(withCreds({ mindspace_id: mid, config: '' }));
|
|
488
|
+
stop();
|
|
489
|
+
session.mindspace = mid;
|
|
490
|
+
ok(`Mindspace active: ${c.green}${mid}${c.reset}`);
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
'delete-mindspace': {
|
|
494
|
+
group: 'Mindspace',
|
|
495
|
+
desc: 'delete-mindspace <id>',
|
|
496
|
+
async run([id]) {
|
|
497
|
+
requireConn();
|
|
498
|
+
if (!id)
|
|
499
|
+
throw new Error('Usage: delete-mindspace <id>');
|
|
500
|
+
const stop = spinner(`Deleting ${id}...`);
|
|
501
|
+
await satori.deleteMindspace(withCreds({ mindspace_id: id }));
|
|
502
|
+
stop();
|
|
503
|
+
if (session.mindspace === id)
|
|
504
|
+
session.mindspace = null;
|
|
505
|
+
ok(`Deleted mindspace: ${c.cyan}${id}${c.reset}`);
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
chat: {
|
|
509
|
+
group: 'Mindspace',
|
|
510
|
+
desc: 'chat "<message>"',
|
|
511
|
+
async run([msg]) {
|
|
512
|
+
requireMindspace();
|
|
513
|
+
if (!msg)
|
|
514
|
+
throw new Error('Usage: chat "<message>"');
|
|
515
|
+
const stop = spinner('Thinking...');
|
|
516
|
+
const r = await satori.chatMindspace(withCreds({ minspace_id: session.mindspace, message: msg }));
|
|
517
|
+
stop();
|
|
518
|
+
print(r);
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
lecture: {
|
|
522
|
+
group: 'Mindspace',
|
|
523
|
+
desc: 'lecture "<corpus>"',
|
|
524
|
+
async run([corpus]) {
|
|
525
|
+
requireMindspace();
|
|
526
|
+
if (!corpus)
|
|
527
|
+
throw new Error('Usage: lecture "<corpus>"');
|
|
528
|
+
const stop = spinner('Ingesting corpus...');
|
|
529
|
+
const r = await satori.lectureMindspace(withCreds({ mindspace_id: session.mindspace, corpus }));
|
|
530
|
+
stop();
|
|
531
|
+
const msg = r?.message;
|
|
532
|
+
if (msg)
|
|
533
|
+
ok(msg);
|
|
534
|
+
else
|
|
535
|
+
ok('Corpus ingested');
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
// โโ Crypto โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
539
|
+
encrypt: {
|
|
540
|
+
group: 'Crypto',
|
|
541
|
+
desc: 'encrypt <key> <encryption_key>',
|
|
542
|
+
async run([key, encryption_key]) {
|
|
543
|
+
requireConn();
|
|
544
|
+
if (!key || !encryption_key)
|
|
545
|
+
throw new Error('Usage: encrypt <key> <encryption_key>');
|
|
546
|
+
const stop = spinner('Encrypting...');
|
|
547
|
+
const r = await satori.encrypt(withCreds({ key, encryption_key }));
|
|
548
|
+
stop();
|
|
549
|
+
print(r);
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
decrypt: {
|
|
553
|
+
group: 'Crypto',
|
|
554
|
+
desc: 'decrypt <key> <encryption_key>',
|
|
555
|
+
async run([key, encryption_key]) {
|
|
556
|
+
requireConn();
|
|
557
|
+
if (!key || !encryption_key)
|
|
558
|
+
throw new Error('Usage: decrypt <key> <encryption_key>');
|
|
559
|
+
const stop = spinner('Decrypting...');
|
|
560
|
+
const r = await satori.decrypt(withCreds({ key, encryption_key }));
|
|
561
|
+
stop();
|
|
562
|
+
print(r);
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
// โโ Analytics โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
566
|
+
operations: {
|
|
567
|
+
group: 'Analytics',
|
|
568
|
+
desc: 'operations โ recent operation log',
|
|
569
|
+
async run() {
|
|
570
|
+
requireConn();
|
|
571
|
+
const stop = spinner('Fetching...');
|
|
572
|
+
const r = await satori.getOperations();
|
|
573
|
+
stop();
|
|
574
|
+
print(r);
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
freq: {
|
|
578
|
+
group: 'Analytics',
|
|
579
|
+
desc: 'freq <key> โ access frequency for a key',
|
|
580
|
+
async run([key]) {
|
|
581
|
+
requireConn();
|
|
582
|
+
if (!key)
|
|
583
|
+
throw new Error('Usage: freq <key>');
|
|
584
|
+
const stop = spinner('Fetching...');
|
|
585
|
+
const r = await satori.getAccessFrequency(key);
|
|
586
|
+
stop();
|
|
587
|
+
print(r);
|
|
588
|
+
}
|
|
589
|
+
},
|
|
590
|
+
// โโ Utility โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
591
|
+
help: {
|
|
592
|
+
group: 'Utility', noConn: true,
|
|
593
|
+
desc: 'Show help',
|
|
594
|
+
run() { showHelp(); }
|
|
595
|
+
},
|
|
596
|
+
clear: {
|
|
597
|
+
group: 'Utility', noConn: true,
|
|
598
|
+
desc: 'Clear screen',
|
|
599
|
+
run() { console.clear(); showBanner(); }
|
|
600
|
+
},
|
|
601
|
+
exit: {
|
|
602
|
+
group: 'Utility', noConn: true,
|
|
603
|
+
desc: 'Exit',
|
|
604
|
+
run() { process.exit(0); }
|
|
605
|
+
},
|
|
606
|
+
quit: {
|
|
607
|
+
group: 'Utility', noConn: true,
|
|
608
|
+
desc: 'Exit',
|
|
609
|
+
run() { process.exit(0); }
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
// Backward-compat aliases (old underscore variants kept for muscle memory)
|
|
613
|
+
const aliases = {
|
|
614
|
+
set_mindspace: 'set-mindspace',
|
|
615
|
+
delete_mindspace: 'delete-mindspace',
|
|
616
|
+
set_vertex: 'set-vertex',
|
|
617
|
+
get_vertex: 'get-vertex',
|
|
618
|
+
delete_vertex: 'delete-vertex',
|
|
619
|
+
shortest_path: 'shortest-path',
|
|
620
|
+
};
|
|
621
|
+
for (const [alias, canonical] of Object.entries(aliases)) {
|
|
622
|
+
commands[alias] = commands[canonical];
|
|
623
|
+
}
|
|
624
|
+
// โโ Help โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
625
|
+
function showHelp() {
|
|
626
|
+
var _a;
|
|
627
|
+
const hidden = new Set(['quit', ...Object.keys(aliases)]);
|
|
628
|
+
const groups = {};
|
|
629
|
+
for (const [name, cmd] of Object.entries(commands)) {
|
|
630
|
+
if (hidden.has(name))
|
|
631
|
+
continue;
|
|
632
|
+
(groups[_a = cmd.group] ?? (groups[_a] = [])).push([name, cmd.desc]);
|
|
633
|
+
}
|
|
634
|
+
console.log();
|
|
635
|
+
for (const [group, entries] of Object.entries(groups)) {
|
|
636
|
+
console.log(` ${c.yellow}${group}${c.reset}`);
|
|
637
|
+
for (const [name, desc] of entries) {
|
|
638
|
+
const argsPart = desc.startsWith(name) ? desc.slice(name.length).trim() : desc;
|
|
639
|
+
console.log(` ${c.cyan}${name.padEnd(20)}${c.reset}` +
|
|
640
|
+
`${c.dim}${argsPart}${c.reset}`);
|
|
641
|
+
}
|
|
642
|
+
console.log();
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// โโ REPL โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
646
|
+
async function startREPL() {
|
|
647
|
+
showBanner();
|
|
648
|
+
// Tab-completion list: canonical names only (no aliases, no quit duplicate)
|
|
649
|
+
const completionList = Object.keys(commands).filter(k => !aliases[k] && k !== 'quit');
|
|
650
|
+
const rl = readline_1.default.createInterface({
|
|
651
|
+
input: process.stdin,
|
|
652
|
+
output: process.stdout,
|
|
653
|
+
historySize: 500,
|
|
654
|
+
completer(line) {
|
|
655
|
+
const hits = completionList.filter(k => k.startsWith(line));
|
|
656
|
+
return [hits.length ? hits : completionList, line];
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
// Restore saved history into readline's internal array
|
|
660
|
+
const history = loadHistory();
|
|
661
|
+
if (history.length && rl.history) {
|
|
662
|
+
rl.history.push(...history);
|
|
663
|
+
}
|
|
664
|
+
const refreshPrompt = () => rl.setPrompt(promptLabel());
|
|
665
|
+
refreshPrompt();
|
|
666
|
+
rl.prompt();
|
|
667
|
+
rl.on('line', async (raw) => {
|
|
668
|
+
const line = raw.trim();
|
|
669
|
+
if (!line) {
|
|
670
|
+
rl.prompt();
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
const parts = parseArgs(line);
|
|
674
|
+
const [cmd, ...args] = parts;
|
|
675
|
+
const entry = commands[cmd];
|
|
676
|
+
try {
|
|
677
|
+
if (!entry) {
|
|
678
|
+
fail(`Unknown command: ${c.cyan}${cmd}${c.reset} โ type ${c.cyan}help${c.reset}`);
|
|
679
|
+
}
|
|
680
|
+
else if (!entry.noConn && !satori) {
|
|
681
|
+
fail(`Not connected โ use: ${c.cyan}connect <host>${c.reset}`);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
await entry.run(args);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
catch (e) {
|
|
688
|
+
fail(e.message ?? String(e));
|
|
689
|
+
}
|
|
690
|
+
refreshPrompt();
|
|
691
|
+
rl.prompt();
|
|
692
|
+
});
|
|
693
|
+
rl.on('close', () => {
|
|
694
|
+
saveHistory(rl.history ?? []);
|
|
695
|
+
console.log();
|
|
696
|
+
process.exit(0);
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
// โโ Entry โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
700
|
+
const argv = process.argv.slice(2);
|
|
701
|
+
if (argv[0] === 'run') {
|
|
702
|
+
runBinary(argv.slice(1));
|
|
703
|
+
}
|
|
704
|
+
else if (argv[0] === 'update') {
|
|
705
|
+
require('./postinstall');
|
|
706
|
+
}
|
|
707
|
+
else if (argv[0] === 'verify') {
|
|
708
|
+
require('./verify-install');
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
startREPL();
|
|
712
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "satoridb",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"description": "Install satori",
|
|
5
5
|
"bin": {
|
|
6
6
|
"satoridb": "./cli.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
+
"build": "tsc cli.ts --outDir . --target es2020 --module commonjs --esModuleInterop",
|
|
9
10
|
"postinstall": "node postinstall.js",
|
|
10
11
|
"verify": "node verify-install.js"
|
|
11
12
|
},
|
|
@@ -13,12 +14,16 @@
|
|
|
13
14
|
"license": "ISC",
|
|
14
15
|
"dependencies": {
|
|
15
16
|
"adm-zip": "^0.5.16",
|
|
16
|
-
"satori-node": "^1.1.13"
|
|
17
|
+
"satori-node": "^1.1.13",
|
|
18
|
+
"typescript": "^5.9.3"
|
|
17
19
|
},
|
|
18
20
|
"files": [
|
|
19
21
|
"postinstall.js",
|
|
20
22
|
"add-to-path.ps1",
|
|
21
23
|
"add-to-path.sh",
|
|
22
24
|
"verify-install.js"
|
|
23
|
-
]
|
|
25
|
+
],
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^25.1.0"
|
|
28
|
+
}
|
|
24
29
|
}
|
package/postinstall.js
CHANGED
|
@@ -10,14 +10,32 @@ let { spawnSync } = require("child_process");
|
|
|
10
10
|
let platform = os.platform();
|
|
11
11
|
let arch = os.arch();
|
|
12
12
|
|
|
13
|
+
// โโ CLI-only mode โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
14
|
+
// Set SATORI_NO_BINARY=1 to skip the binary download entirely.
|
|
15
|
+
// The interactive shell (satoridb) still works โ it only needs a running
|
|
16
|
+
// Satori server reachable over WebSocket.
|
|
17
|
+
//
|
|
18
|
+
// SATORI_NO_BINARY=1 npm install -g satoridb
|
|
19
|
+
//
|
|
20
|
+
if (process.env.SATORI_NO_BINARY === "1") {
|
|
21
|
+
console.log(" โน Skipping Satori binary download (SATORI_NO_BINARY=1)");
|
|
22
|
+
console.log(" ยท The CLI shell is ready: satoridb");
|
|
23
|
+
console.log(" ยท To download the binary later: satoridb update");
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
let baseURL = "https://www.satoridb.com";
|
|
14
28
|
let fileName;
|
|
15
29
|
|
|
16
30
|
if (platform === "linux") fileName = "lin/satori-linux.zip";
|
|
17
31
|
else if (platform === "win32") fileName = "win/satori-win.zip";
|
|
18
32
|
else {
|
|
19
|
-
|
|
20
|
-
|
|
33
|
+
// Unsupported platform โ warn but do not fail; CLI shell still works.
|
|
34
|
+
console.log(` โ No pre-built binary for platform: ${platform}`);
|
|
35
|
+
console.log(" ยท The CLI shell is still available: satoridb");
|
|
36
|
+
console.log(" ยท Connect to any Satori server via: connect <ws://host:port>");
|
|
37
|
+
console.log(" ยท To skip this message next time: SATORI_NO_BINARY=1 npm install -g satoridb");
|
|
38
|
+
process.exit(0);
|
|
21
39
|
}
|
|
22
40
|
|
|
23
41
|
let binName = platform === "win32" ? "satori.exe" : "satori";
|