ispbills-icli 5.0.0 → 5.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/bin/icli.js +79 -3
- package/package.json +1 -1
- package/src/chat.js +2 -0
- package/src/config.js +11 -1
package/bin/icli.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createInterface, emitKeypressEvents } from 'readline';
|
|
3
|
-
import { stdin as input, stdout as output, argv, exit } from 'process';
|
|
3
|
+
import { stdin as input, stdout as output, argv, exit, platform } from 'process';
|
|
4
|
+
import { spawnSync } from 'child_process';
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { dirname, join } from 'path';
|
|
4
8
|
import chalk from 'chalk';
|
|
5
|
-
import { loadConfig, configPath } from '../src/config.js';
|
|
9
|
+
import { loadConfig, configPath, clearConfig } from '../src/config.js';
|
|
6
10
|
import { loginFlow } from '../src/auth.js';
|
|
7
11
|
import { inner, onResize, boxTop, boxBot, boxMid, boxRow, bottomPalette } from '../src/layout.js';
|
|
8
12
|
import {
|
|
@@ -10,6 +14,45 @@ import {
|
|
|
10
14
|
SLASH_COMMANDS, slashCompleter,
|
|
11
15
|
} from '../src/chat.js';
|
|
12
16
|
|
|
17
|
+
const PKG_NAME = 'ispbills-icli';
|
|
18
|
+
|
|
19
|
+
/** Read this CLI's installed version from its package.json. */
|
|
20
|
+
function pkgVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const p = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
|
|
23
|
+
return JSON.parse(readFileSync(p, 'utf8')).version ?? 'unknown';
|
|
24
|
+
} catch {
|
|
25
|
+
return 'unknown';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Self-update: install the latest published version globally via npm.
|
|
31
|
+
* Returns true on success. The running process keeps the old code until the
|
|
32
|
+
* next launch, so callers should tell the user to restart.
|
|
33
|
+
*/
|
|
34
|
+
function selfUpdate(P) {
|
|
35
|
+
const cur = pkgVersion();
|
|
36
|
+
process.stdout.write('\n ' + P.dim('Current version: ') + chalk.white(cur) + '\n');
|
|
37
|
+
process.stdout.write(' ' + P.muted('Updating ' + PKG_NAME + ' to the latest version…') + '\n\n');
|
|
38
|
+
|
|
39
|
+
const npm = platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
40
|
+
const res = spawnSync(npm, ['install', '-g', `${PKG_NAME}@latest`], {
|
|
41
|
+
stdio: 'inherit',
|
|
42
|
+
shell: platform === 'win32',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (res.status === 0) {
|
|
46
|
+
process.stdout.write('\n ' + P.ok('✓') + ' iCli updated. ' +
|
|
47
|
+
P.dim('Restart iCli to use the new version.') + '\n\n');
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
process.stdout.write('\n ' + P.err('✖') + ' Update failed. Try manually:\n');
|
|
51
|
+
process.stdout.write(' ' + P.accent(`npm install -g ${PKG_NAME}@latest`) + '\n');
|
|
52
|
+
process.stdout.write(' ' + P.dim('On some systems this needs elevated privileges (sudo / Administrator).') + '\n\n');
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
13
56
|
const P = {
|
|
14
57
|
border: chalk.hex('#303134'),
|
|
15
58
|
dim: chalk.hex('#9AA0A6'),
|
|
@@ -44,6 +87,8 @@ function printHelp() {
|
|
|
44
87
|
['icli', 'Interactive REPL with slash commands'],
|
|
45
88
|
['icli "question"', 'Single-shot query then exit'],
|
|
46
89
|
['icli login', 'Authenticate to your IspBills instance'],
|
|
90
|
+
['icli logout', 'Log out and clear saved credentials'],
|
|
91
|
+
['icli update', 'Update iCli to the latest version'],
|
|
47
92
|
['icli whoami', 'Show current session info'],
|
|
48
93
|
['icli --help', 'Show this help'],
|
|
49
94
|
].forEach(([c, d]) =>
|
|
@@ -163,7 +208,7 @@ async function interactiveRepl(cfg) {
|
|
|
163
208
|
turns.forEach((m, i) =>
|
|
164
209
|
process.stdout.write(
|
|
165
210
|
boxRow(P.dim(String(i + 1).padStart(2) + '.') + ' ' +
|
|
166
|
-
chalk.white(m.content.replace(/\s+/g, ' ').slice(0, inner() - 5)), P.border) + '\n'
|
|
211
|
+
chalk.white(m.content.replace(/\s+/g, ' ').slice(0, inner() - 5)), P.border) + '\n')
|
|
167
212
|
);
|
|
168
213
|
process.stdout.write(boxBot(P.border) + '\n\n');
|
|
169
214
|
}
|
|
@@ -221,6 +266,21 @@ async function interactiveRepl(cfg) {
|
|
|
221
266
|
rl.prompt();
|
|
222
267
|
return;
|
|
223
268
|
}
|
|
269
|
+
if (q === '/update' || q === 'update') {
|
|
270
|
+
rl.pause();
|
|
271
|
+
selfUpdate(P);
|
|
272
|
+
rl.resume();
|
|
273
|
+
rl.prompt();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (q === '/logout' || q === 'logout') {
|
|
277
|
+
const cleared = clearConfig();
|
|
278
|
+
process.stdout.write('\n ' + (cleared ? P.ok('✓') + ' Logged out — credentials cleared.'
|
|
279
|
+
: P.warn('Already logged out.')) +
|
|
280
|
+
'\n ' + P.dim('Run ') + P.accent('icli login') + P.dim(' to sign in again.') + '\n\n');
|
|
281
|
+
rl.close();
|
|
282
|
+
exit(0);
|
|
283
|
+
}
|
|
224
284
|
if (q === '/help' || q === 'help') { printHelp(); rl.prompt(); return; }
|
|
225
285
|
if (q === '/history' || q === 'history') {
|
|
226
286
|
const turns = history.filter(m => m.role === 'user');
|
|
@@ -278,6 +338,22 @@ async function main() {
|
|
|
278
338
|
return;
|
|
279
339
|
}
|
|
280
340
|
|
|
341
|
+
if (cmd === 'update') {
|
|
342
|
+
const ok = selfUpdate(P);
|
|
343
|
+
exit(ok ? 0 : 1);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (cmd === 'logout') {
|
|
347
|
+
const cleared = clearConfig();
|
|
348
|
+
if (cleared) {
|
|
349
|
+
process.stdout.write('\n ' + P.ok('✓') + ' Logged out — credentials cleared.\n');
|
|
350
|
+
process.stdout.write(' ' + P.dim('Run ') + P.accent('icli login') + P.dim(' to sign in again.') + '\n\n');
|
|
351
|
+
} else {
|
|
352
|
+
process.stdout.write('\n ' + P.warn('Not logged in.') + ' Nothing to clear.\n\n');
|
|
353
|
+
}
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
281
357
|
if (cmd === 'whoami') {
|
|
282
358
|
const cfg = loadConfig();
|
|
283
359
|
if (!cfg) {
|
package/package.json
CHANGED
package/src/chat.js
CHANGED
|
@@ -119,6 +119,8 @@ export const SLASH_COMMANDS = [
|
|
|
119
119
|
{ cmd: '/online', hint: '', desc: 'List online sessions' },
|
|
120
120
|
{ cmd: '/history', hint: '', desc: 'Show conversation history' },
|
|
121
121
|
{ cmd: '/clear', hint: '', desc: 'Clear the screen' },
|
|
122
|
+
{ cmd: '/update', hint: '', desc: 'Update iCli to the latest version'},
|
|
123
|
+
{ cmd: '/logout', hint: '', desc: 'Log out and clear credentials'},
|
|
122
124
|
{ cmd: '/help', hint: '', desc: 'Show help' },
|
|
123
125
|
{ cmd: '/exit', hint: '', desc: 'Exit iCli' },
|
|
124
126
|
];
|
package/src/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync } from 'fs';
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
@@ -21,3 +21,13 @@ export function saveConfig(cfg) {
|
|
|
21
21
|
export function configPath() {
|
|
22
22
|
return CONFIG_FILE;
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Remove the saved credentials (log out). Returns true if a config file was
|
|
27
|
+
* present and deleted, false if there was nothing to clear.
|
|
28
|
+
*/
|
|
29
|
+
export function clearConfig() {
|
|
30
|
+
if (!existsSync(CONFIG_FILE)) return false;
|
|
31
|
+
rmSync(CONFIG_FILE, { force: true });
|
|
32
|
+
return true;
|
|
33
|
+
}
|