@theowlops/boombot 1.1.0 → 1.1.1

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.
@@ -129,59 +129,69 @@ export function browserHost(host) {
129
129
  return host === '0.0.0.0' || host === '::' ? '127.0.0.1' : host;
130
130
  }
131
131
  export async function runSetup() {
132
- console.log('🦉 BoomBot setup\n');
132
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
133
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
134
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
135
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
136
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
137
+ console.log(`
138
+ ${cyan('╭───────────────────────────────────────────────╮')}
139
+ ${cyan('│')} ${bold('🦉 BoomBot — First-Run Setup Wizard')} ${cyan('│')}
140
+ ${cyan('│')} ${dim('Thiết lập nhanh Gateway, API Key & LLM')} ${cyan('│')}
141
+ ${cyan('╰───────────────────────────────────────────────╯')}
142
+ `);
133
143
  const existing = loadConfig();
134
- const host = await askDefault('Gateway host', existing?.host ?? '127.0.0.1');
135
- const defaultPort = existing?.port ?? 8787;
136
- const port = parsePort(await ask(`Gateway port [${defaultPort}]: `), defaultPort);
137
- const dataDir = await askDefault('Data directory', existing?.dataDir ?? '~/.boombot/data');
144
+ console.log(`${bold('1. Cấu hình mạng & đường dẫn (Network & Paths)')}`);
145
+ const host = await askDefault(` ${cyan('•')} Gateway host`, existing?.host ?? '127.0.0.1');
146
+ const defaultPort = existing?.port ?? 8788;
147
+ const port = parsePort(await ask(` ${cyan('•')} Gateway port [${defaultPort}]: `), defaultPort);
148
+ const dataDir = await askDefault(` ${cyan('•')} Thư mục dữ liệu (Data directory)`, existing?.dataDir ?? '~/.boombot/data');
138
149
  // API key — kept at <config-dir>/api.key (0600) so it stays recoverable; the
139
150
  // config only ever holds its sha256. An existing key is reused, never rotated.
151
+ console.log(`\n${bold('2. Khóa bảo mật Gateway (Security Keys)')}`);
140
152
  const { apiKey, apiKeyHash, keyPath, created, fromEnv } = ensureApiKey();
141
153
  if (fromEnv) {
142
- console.log('\nUsing the API key from BOOMBOT_API_KEY (no key file written).\n');
154
+ console.log(` ${green('✔')} Sử dụng API key từ biến môi trường BOOMBOT_API_KEY`);
143
155
  }
144
156
  else if (created) {
145
- console.log('\n────────────────────────────────────────');
146
- console.log('Your gateway API key (save it somewhere safe):');
147
- console.log(`\n ${apiKey}\n`);
148
- console.log(`Also stored at ${keyPath} (mode 0600) — \`boombot gateway\` reads it from`);
149
- console.log('there and signs your browser in automatically, so you can ignore this');
150
- console.log('notice. Only the sha256 hash is written to config.json.');
151
- console.log('────────────────────────────────────────\n');
157
+ console.log(` ${yellow('┌─ Gateway API Key của bạn (Lưu vào nơi an toàn) ───────────────┐')}`);
158
+ console.log(` ${yellow('│')} ${bold(apiKey)}`);
159
+ console.log(` ${yellow('└───────────────────────────────────────────────────────────────┘')}`);
160
+ console.log(` ${dim(`Đã lưu tại: ${keyPath} (tự động đăng nhập web)`)}`);
152
161
  }
153
162
  else {
154
- console.log(`\nKeeping the existing gateway API key from ${keyPath}.`);
155
- console.log('(delete that file and re-run setup to rotate it)\n');
163
+ console.log(` ${green('✔')} Giữ API key hiện có tại: ${dim(keyPath)}`);
156
164
  }
157
- // Master key — encrypts provider API keys at rest. The gateway needs it
158
- // as BOOMBOT_MASTER_KEY; `boombot gateway` loads this file automatically.
165
+ // Master key — encrypts provider API keys at rest.
159
166
  const masterKeyPath = resolveMasterKeyPath();
160
167
  const masterKey = process.env['BOOMBOT_MASTER_KEY'] || ensureMasterKey(masterKeyPath);
161
- console.log(`Master key for provider encryption: ${masterKeyPath}`);
162
- console.log('(mode 0600 — it decrypts your provider keys, keep it safe)\n');
168
+ console.log(` ${green('✔')} Master key mã hóa AES-256: ${dim(masterKeyPath)}`);
163
169
  // Telegram (optional).
170
+ console.log(`\n${bold('3. Kênh kết nối Chat (Messaging Channels)')}`);
164
171
  let telegram = existing?.telegram;
165
- if (await askYesNo('Configure Telegram bot?', false)) {
166
- const botToken = await readSecret('Telegram bot token: ');
167
- const allowlist = parseAllowlist(await ask('Allowlisted chat IDs (comma-separated, empty = none): '));
172
+ if (await askYesNo(` ${cyan('?')} Cấu hình bot Telegram?`, false)) {
173
+ const botToken = await readSecret(' Token bot Telegram: ');
174
+ const allowlist = parseAllowlist(await ask(' Chat IDs được phép (phân cách bằng dấu phẩy, để trống = tất cả): '));
168
175
  telegram = { botToken: botToken.trim(), allowlistChatIds: allowlist };
176
+ console.log(` ${green('✔')} Đã cấu hình Telegram.`);
169
177
  }
170
- // Default provider → the real encrypted store (<dataDir>/providers.json),
171
- // the same store the gateway reads. The key is pasted once, masked, and
172
- // never written in plaintext anywhere.
173
- let providerSaved = false;
174
- if (await askYesNo('Configure a default LLM provider?', true)) {
178
+ else {
179
+ console.log(` ${dim('Bỏ qua kết nối Telegram (có thể cấu hình sau trên Web UI).')}`);
180
+ }
181
+ // Default provider → the real encrypted store (<dataDir>/providers.json)
182
+ console.log(`\n${bold('4. Nhà cung cấp AI (LLM Provider)')}`);
183
+ if (await askYesNo(` ${cyan('?')} Thiết lập nhà cung cấp LLM mặc định?`, true)) {
184
+ console.log(` ${dim(`Các loại hỗ trợ: ${PROVIDER_KINDS.join(', ')}`)}`);
175
185
  const kind = await askProviderKind();
176
186
  if (kind !== null) {
177
- const model = await askRequired('Default model: ');
187
+ const model = await askRequired(' Model mặc định (vd: gpt-4o, claude-3-7-sonnet): ');
178
188
  const needsBaseUrl = kind === 'openai-compatible' || kind === 'local';
179
189
  const baseUrl = needsBaseUrl
180
- ? await askRequired('Base URL (required for this provider kind): ')
181
- : (await ask('Base URL (empty = provider default): ')).trim();
190
+ ? await askRequired(' Base URL (bắt buộc cho loại này): ')
191
+ : (await ask(' Base URL (để trống = mặc định): ')).trim();
182
192
  const providerApiKey = kind === 'local'
183
- ? (await readSecret('Provider API key (empty = none): ')).trim()
184
- : await readRequiredSecret('Provider API key: ');
193
+ ? (await readSecret(' Provider API key (để trống nếu không có): ')).trim()
194
+ : await readRequiredSecret(' Provider API key: ');
185
195
  const service = createProviderService({
186
196
  storagePath: path.join(expandTilde(dataDir), 'providers.json'),
187
197
  masterKey,
@@ -195,11 +205,13 @@ export async function runSetup() {
195
205
  ...(providerApiKey !== '' ? { apiKey: providerApiKey } : {}),
196
206
  active: true,
197
207
  });
198
- providerSaved = true;
199
- const keyNote = providerApiKey !== '' ? ' — API key stored encrypted' : ' — no API key stored';
200
- console.log(`\nSaved default provider '${saved.name}' (${saved.kind})${keyNote}.`);
208
+ const keyNote = providerApiKey !== '' ? ' (đã mã hóa an toàn)' : '';
209
+ console.log(` ${green('✔')} Đã lưu provider '${saved.name}' [${saved.kind}]${keyNote}`);
201
210
  }
202
211
  }
212
+ else {
213
+ console.log(` ${dim('Bỏ qua cấu hình LLM (bạn có thể thêm trực tiếp trên Web UI).')}`);
214
+ }
203
215
  const config = {
204
216
  ...existing,
205
217
  host,
@@ -210,21 +222,23 @@ export async function runSetup() {
210
222
  };
211
223
  const problems = validateConfig(config);
212
224
  if (problems.length > 0) {
213
- console.error(`\nInvalid config: ${problems.join('; ')}`);
225
+ console.error(`\n❌ Cấu hình không hợp lệ: ${problems.join('; ')}`);
214
226
  process.exitCode = 1;
215
227
  return;
216
228
  }
217
229
  const configPath = resolveConfigPath();
218
230
  saveConfig(config, configPath);
219
- const permsNote = process.platform === 'win32'
220
- ? 'permissions NOT enforced on Windows (chmod is a no-op) — see SECURITY.md §5'
221
- : 'mode 0600 — secrets protected';
222
- console.log(`\nSaved config to ${configPath} (${permsNote}).`);
223
231
  const url = `http://${browserHost(host)}:${port}`;
224
- console.log('\nNext steps:');
225
- console.log(` 1. boombot gateway # start the gateway — opens ${url} in your browser`);
226
- console.log(' 2. boombot doctor # health check, if anything looks off');
227
- if (!providerSaved) {
228
- console.log(' (tip: re-run `boombot setup` any time to add a default provider)');
229
- }
232
+ console.log(`
233
+ ${green('╭───────────────────────────────────────────────╮')}
234
+ ${green('│')} ${bold('🎉 CÀI ĐẶT HOÀN TẤT THÀNH CÔNG!')} ${green('│')}
235
+ ${green('╰───────────────────────────────────────────────╯')}
236
+
237
+ ${bold('Tệp cấu hình:')} ${dim(configPath)}
238
+ ${bold('Giao diện Web:')} ${cyan(url)}
239
+
240
+ ${bold('Bắt đầu sử dụng:')}
241
+ ${cyan('1.')} ${bold('boombot')} Khởi động Gateway và mở Web UI
242
+ ${cyan('2.')} ${bold('boombot doctor')} Kiểm tra toàn diện tình trạng hệ thống
243
+ `);
230
244
  }
package/dist/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@theowlops/boombot",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "type": "module"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theowlops/boombot",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"