bom-pi 0.0.5 → 0.0.6
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/package.json +1 -1
- package/packages/cli/src/bom-pi.mjs +78 -21
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Mọi lệnh conversation nhận --cid N (mặc định: conversation active của daemon).
|
|
6
6
|
import { connect } from 'node:net';
|
|
7
7
|
import { join, resolve } from 'node:path';
|
|
8
|
-
import { userInfo } from 'node:os';
|
|
8
|
+
import { userInfo, homedir, hostname } from 'node:os';
|
|
9
9
|
import { readFile, writeFile, mkdir, stat } from 'node:fs/promises';
|
|
10
10
|
import { execFile } from 'node:child_process';
|
|
11
11
|
import { promisify } from 'node:util';
|
|
@@ -27,10 +27,18 @@ const cid = arg('cid', null); // conversation id cho submit/transcript/reset/wat
|
|
|
27
27
|
const cidParams = () => (cid ? { conversationId: Number(cid) } : {});
|
|
28
28
|
const cmd = process.argv[2] ?? 'status';
|
|
29
29
|
|
|
30
|
-
// Remote (thiết bị khác — Windows/phone
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
const
|
|
30
|
+
// Remote (thiết bị khác — Windows/phone): pair MỘT lần bằng 'bom-pi pair <code> --url …'
|
|
31
|
+
// ⇒ CLI tự lưu {url, token} vào ~/.bom-pi.json và dùng mãi (env BOM_PI_URL/BOM_PI_TOKEN
|
|
32
|
+
// vẫn đè lên nếu đặt; --url/--token đè lên tất cả). CLI nói chuyện web /rpc thay unix socket.
|
|
33
|
+
const CONFIG_PATH = join(homedir(), '.bom-pi.json');
|
|
34
|
+
let cliConfig = {};
|
|
35
|
+
try { cliConfig = JSON.parse(await readFile(CONFIG_PATH, 'utf8')); } catch { /* chưa pair bao giờ */ }
|
|
36
|
+
async function saveCliConfig(next) {
|
|
37
|
+
cliConfig = next;
|
|
38
|
+
await writeFile(CONFIG_PATH, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
const REMOTE_URL = arg('url', process.env.BOM_PI_URL || cliConfig.url || null);
|
|
41
|
+
const REMOTE_TOKEN = arg('token', process.env.BOM_PI_TOKEN || cliConfig.token || null);
|
|
34
42
|
|
|
35
43
|
// -v/--version trước mọi thứ — user kiểm bản nào đang chạy (bài học: máy Windows kẹt bản cũ)
|
|
36
44
|
const VERSION = (await import('../../../package.json', { with: { type: 'json' } })).default.version;
|
|
@@ -47,20 +55,17 @@ function unreachable(e) {
|
|
|
47
55
|
return `${head}
|
|
48
56
|
|
|
49
57
|
bom-core chạy trên máy chủ Linux (unix socket + systemd) — CLI chưa chạy được trên Windows.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
set BOM_PI_URL=http://<ip>:8791 & set BOM_PI_TOKEN=bom_…
|
|
55
|
-
(PowerShell: $env:BOM_PI_URL=…; $env:BOM_PI_TOKEN=…) rồi chạy bom-pi status`;
|
|
58
|
+
Từ máy khác, pair MỘT LẦN bằng CLI (cần code in từ lệnh 'bom-pi pair' chạy TRÊN MÁY CHỦ):
|
|
59
|
+
bom-pi pair <CODE> --url http://<ip-máy-chủ>:8791
|
|
60
|
+
xong CLI tự nhớ (lưu ~/.bom-pi.json) — mọi lệnh chạy remote: bom-pi status / sessions …
|
|
61
|
+
(hoặc dùng WEB đầy đủ: mở http://<ip-máy-chủ>:8791 — pair bằng cùng code đó)`;
|
|
56
62
|
}
|
|
57
63
|
return `${head}
|
|
58
64
|
|
|
59
65
|
daemon chưa chạy? thử: systemctl --user start bom-core (xem trạng thái: bom-pi status)
|
|
60
66
|
chưa cài daemon?: bom-pi setup
|
|
61
|
-
ở THIẾT BỊ KHÁC?:
|
|
62
|
-
|
|
63
|
-
BOM_PI_URL=http://<ip>:8791 BOM_PI_TOKEN=bom_… bom-pi status`;
|
|
67
|
+
ở THIẾT BỊ KHÁC?: pair một lần rồi mọi lệnh chạy remote:
|
|
68
|
+
bom-pi pair <CODE> --url http://<ip-máy-chạy-daemon>:8791`;
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
function rpcSocket(method, params) {
|
|
@@ -101,11 +106,16 @@ const pretty = (o) => console.log(JSON.stringify(o, null, 2));
|
|
|
101
106
|
|
|
102
107
|
// Dispatcher: remote (/rpc qua HTTP) khi BOM_PI_URL đặt — thiết bị khác không có unix socket
|
|
103
108
|
async function rpcRemote(method, params) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
+
let r;
|
|
110
|
+
try {
|
|
111
|
+
r = await fetch(`${REMOTE_URL.replace(/\/+$/, '')}/rpc`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: { 'content-type': 'application/json', ...(REMOTE_TOKEN ? { Authorization: `Bearer ${REMOTE_TOKEN}` } : {}) },
|
|
114
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params: params ?? {} }),
|
|
115
|
+
});
|
|
116
|
+
} catch (e) {
|
|
117
|
+
throw new Error(`không nối được ${REMOTE_URL}: ${e.message} — kiểm tra URL và web daemon (bom-web) đang chạy ở đó`);
|
|
118
|
+
}
|
|
109
119
|
const j = await r.json().catch(() => ({}));
|
|
110
120
|
if (!r.ok) throw new Error(`bom-web ${REMOTE_URL} ${r.status}: ${j?.error?.message ?? r.statusText}`);
|
|
111
121
|
return j;
|
|
@@ -317,7 +327,45 @@ try {
|
|
|
317
327
|
// M8 + security: pairing 2 CHIỀU — thiết bị nhập code (chiều 1) ⇒ enrollment chờ;
|
|
318
328
|
// token chỉ cấp khi duyệt tại máy (chiều 2): pair approve <mã ghép>
|
|
319
329
|
const sub2 = process.argv[3];
|
|
320
|
-
if (
|
|
330
|
+
if (/^[A-Z2-7]{8}$/i.test(sub2 ?? '')) {
|
|
331
|
+
// THIẾT BỊ KHÁC pair từ CLI: code in ra từ máy chủ + URL web của máy chủ đó.
|
|
332
|
+
// /api/pair/start + /api/pair/poll công khai (code + operator duyệt tự vệ) —
|
|
333
|
+
// xong CLI TỰ LƯU {url, token} vào ~/.bom-pi.json, các lệnh sau chạy remote luôn.
|
|
334
|
+
const url = arg('url', REMOTE_URL || process.env.BOM_PI_URL || cliConfig.url || null);
|
|
335
|
+
if (!url) {
|
|
336
|
+
console.error('usage: bom-pi pair <code> --url http://<ip-máy-chủ>:8791 [--name tên-máy]\n (URL là máy CHẠY DAEMON — in từ lệnh \'bom-pi pair\' trên máy đó, vd https://bom.example.io.vn hoặc http://192.168.1.4:8791)');
|
|
337
|
+
process.exit(64);
|
|
338
|
+
}
|
|
339
|
+
const base = url.replace(/\/+$/, '');
|
|
340
|
+
const name = arg('name', `${hostname() || 'device'} (cli)`);
|
|
341
|
+
const post = async (path, body) => {
|
|
342
|
+
const r = await fetch(`${base}${path}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
|
|
343
|
+
const j = await r.json().catch(() => ({}));
|
|
344
|
+
if (!r.ok) throw new Error(`${path} ${r.status}: ${j?.error ?? r.statusText}`);
|
|
345
|
+
return j;
|
|
346
|
+
};
|
|
347
|
+
const st = await post('/api/pair/start', { code: sub2.toUpperCase(), name });
|
|
348
|
+
console.log(`đã gửi — chờ duyệt tại MÁY CHỦ. Ghép mã: ${st.displayId}`);
|
|
349
|
+
console.log(`(trên máy chủ: bom-pi pair approve ${st.displayId})`);
|
|
350
|
+
const t0 = Date.now();
|
|
351
|
+
for (;;) {
|
|
352
|
+
await new Promise((s) => setTimeout(s, st.pollAfterMs || 1500));
|
|
353
|
+
const p = await post('/api/pair/poll', { enrollId: st.enrollId });
|
|
354
|
+
if (p.status === 'approved') {
|
|
355
|
+
await saveCliConfig({ url: base, token: p.deviceToken, deviceId: p.deviceId, pairedAt: Date.now() });
|
|
356
|
+
console.log(`\nPAIRED ✓ — đã lưu ${CONFIG_PATH}`);
|
|
357
|
+
console.log(`mọi lệnh giờ tự chạy tới ${base}: bom-pi status / sessions / submit …`);
|
|
358
|
+
console.log(`(gỡ: bom-pi unpair)`);
|
|
359
|
+
process.exit(0);
|
|
360
|
+
}
|
|
361
|
+
if (p.status === 'rejected') { console.error('từ chối bởi operator tại máy chủ.'); process.exit(1); }
|
|
362
|
+
if (Date.now() - t0 > 5 * 60 * 1000) { console.error('hết thời gian chờ duyệt (5 phút) — chạy lại.'); process.exit(1); }
|
|
363
|
+
process.stdout.write('.');
|
|
364
|
+
}
|
|
365
|
+
} else if (!sub2 && REMOTE_URL) {
|
|
366
|
+
console.error(`máy này đã pair remote tới ${REMOTE_URL} (config ${CONFIG_PATH}).\n 'bom-pi pair' (in code/QR) là lệnh chạy TRÊN MÁY CHỦ — chạy đó, hoặc gỡ remote: bom-pi unpair.`);
|
|
367
|
+
process.exit(64);
|
|
368
|
+
} else if (!sub2) {
|
|
321
369
|
const r = await rpc('pair.code');
|
|
322
370
|
const nets = await import('node:os');
|
|
323
371
|
const lan = Object.values(nets.networkInterfaces() ?? {}).flat()
|
|
@@ -361,6 +409,15 @@ try {
|
|
|
361
409
|
console.log(`${d.revokedAtMs ? '○' : '●'} ${d.id} ${(d.name ?? '?').padEnd(18)} ${d.revokedAtMs ? 'revoked ' + new Date(d.revokedAtMs).toLocaleString() : 'paired ' + when} hash ${d.tokenHash8}`);
|
|
362
410
|
}
|
|
363
411
|
console.error(`# ${r.devices.filter((d) => !d.revokedAtMs).length} đang sống — thu hồi: bom-pi device rm <id>`);
|
|
412
|
+
} else if (cmd === 'unpair') {
|
|
413
|
+
// Gỡ remote khỏi máy này: thu hồi device TẠI MÁY CHỦ (best-effort) rồi xoá config local
|
|
414
|
+
if (!cliConfig.url || !cliConfig.token) { console.error('# máy này chưa pair remote (không có config)'); process.exit(0); }
|
|
415
|
+
try {
|
|
416
|
+
await rpcRemote('devices.remove', { id: cliConfig.deviceId });
|
|
417
|
+
console.log('đã thu hồi device tại máy chủ.');
|
|
418
|
+
} catch (e) { console.error(`# không thu hồi được tại máy chủ (${e.message}) — xoá config local thôi.`); }
|
|
419
|
+
await saveCliConfig({});
|
|
420
|
+
console.log(`đã xoá ${CONFIG_PATH} — CLI quay về local socket.`);
|
|
364
421
|
} else if (cmd === 'setup') {
|
|
365
422
|
// Sinh systemd user units đúng đường dẫn cài (npm global HOẶC working tree) + enable.
|
|
366
423
|
// root = 3 cấp trên file CLI này: packages/cli/src → gốc package (cả hai kiểu cài đều đúng)
|
|
@@ -495,7 +552,7 @@ WantedBy=default.target
|
|
|
495
552
|
} else if (cmd === 'shutdown') {
|
|
496
553
|
pretty(await rpc('shutdown'));
|
|
497
554
|
} else {
|
|
498
|
-
console.error('usage: bom-pi <hello|status|models|agents|sessions|new|use|submit|reset|transcript|approvals|approve|deny|schedules|schedule|unschedule|factory|limits|abort|watch|
|
|
555
|
+
console.error('usage: bom-pi <hello|status|models|agents|sessions|new|use|submit|reset|transcript|approvals|approve|deny|schedules|schedule|unschedule|factory|limits|abort|watch|unpair|version> [--socket PATH] [--cid N] [--url http://host:8791] [--token bom_…]\n thiết bị khác: bom-pi pair <CODE> --url http://host:8791 — pair một lần, CLI tự nhớ (xem bom-pi version)');
|
|
499
556
|
process.exit(64);
|
|
500
557
|
}
|
|
501
558
|
if (cmd !== 'watch') process.exit(0);
|