@p-sw/brainbox 0.2.2 → 0.2.3-beta.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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/dist/index.js +148 -109
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -22,6 +22,9 @@ Then she runs on a Discord or Telegram account. She greets you. She replies when
22
22
 
23
23
  ### Getting started
24
24
 
25
+
26
+ > **Requires [Bun](https://bun.sh).** Channel message history uses `bun:sqlite`, so run BrainBox with Bun (`bun install`, `bun run …`, or install the CLI under Bun). Node alone is not enough.
27
+
25
28
  **1. Install**
26
29
 
27
30
  ```
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
+ // @bun
2
3
 
3
4
  // src/index.ts
4
5
  import { Command } from "commander";
5
6
  import { readFileSync as readFileSync2 } from "fs";
6
7
  import { fileURLToPath as fileURLToPath2 } from "url";
7
- import { dirname as dirname3, join as join6 } from "path";
8
+ import { dirname as dirname3, join as join7 } from "path";
8
9
 
9
10
  // src/utils/logger.ts
10
11
  import chalk from "chalk";
@@ -24,12 +25,12 @@ var LEVELS = {
24
25
  fatal: { rank: 5, color: chalk.bgRed.white, stderr: true }
25
26
  };
26
27
  var ICONS = {
27
- debug: "",
28
- info: "",
29
- success: "",
30
- warn: "",
31
- error: "",
32
- fatal: ""
28
+ debug: "\u25C6",
29
+ info: "\u2139",
30
+ success: "\u2714",
31
+ warn: "\u26A0",
32
+ error: "\u2716",
33
+ fatal: "\u25B2"
33
34
  };
34
35
  function pad2(n) {
35
36
  return n < 10 ? `0${n}` : `${n}`;
@@ -444,7 +445,7 @@ class BrainDBManager {
444
445
  async isBrainAvailable(brainId) {
445
446
  const item = await this.loadBrain(brainId);
446
447
  const ok = item !== undefined && this.isBrainReady(item);
447
- log.debug(`isBrainAvailable: id=${brainId} ${ok}`);
448
+ log.debug(`isBrainAvailable: id=${brainId} \u2192 ${ok}`);
448
449
  return ok;
449
450
  }
450
451
  isBrainReady(item) {
@@ -474,7 +475,7 @@ class BrainDBManager {
474
475
  var brainManager = new BrainDBManager;
475
476
 
476
477
  // src/brain/index.ts
477
- import { randomUUID } from "node:crypto";
478
+ import { randomUUID } from "crypto";
478
479
  import Supermemory from "supermemory";
479
480
 
480
481
  // src/provider/llm.ts
@@ -1915,7 +1916,7 @@ class AnthropicExecutor extends LLMExecutor {
1915
1916
  }
1916
1917
 
1917
1918
  // src/provider/providers/bedrock.ts
1918
- import { createHmac, createHash } from "node:crypto";
1919
+ import { createHmac, createHash } from "crypto";
1919
1920
  var pad22 = (n) => n.toString().padStart(2, "0");
1920
1921
  function amzDate(now) {
1921
1922
  return {
@@ -2627,7 +2628,7 @@ async function loadPrompt(promptKey) {
2627
2628
  const filePath = path.join(PROMPTS_DIR, fileName(promptKey));
2628
2629
  log5.debug(`loadPrompt: ${promptKey} (${filePath})`);
2629
2630
  const content = await readFile2(filePath, "utf-8");
2630
- log5.debug(`loadPrompt: ${promptKey} ${content.length} chars`);
2631
+ log5.debug(`loadPrompt: ${promptKey} \u2192 ${content.length} chars`);
2631
2632
  return content;
2632
2633
  }
2633
2634
 
@@ -2733,13 +2734,49 @@ var baseSystemPromptSchema = {
2733
2734
  import { BadRequestResponseError } from "@openrouter/sdk/models/errors";
2734
2735
 
2735
2736
  // src/brain/messageHistory.ts
2737
+ import { Database } from "bun:sqlite";
2738
+ import { mkdirSync as mkdirSync3 } from "fs";
2739
+ import { join as join4 } from "path";
2740
+ var db = null;
2741
+ function getDb() {
2742
+ if (db)
2743
+ return db;
2744
+ mkdirSync3(brainboxRoot, { recursive: true });
2745
+ db = new Database(join4(brainboxRoot, "message-history.sqlite"));
2746
+ db.run(`
2747
+ CREATE TABLE IF NOT EXISTS messages (
2748
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
2749
+ brain_id TEXT NOT NULL,
2750
+ sender TEXT NOT NULL,
2751
+ time INTEGER NOT NULL,
2752
+ content TEXT NOT NULL
2753
+ );
2754
+ CREATE INDEX IF NOT EXISTS idx_messages_brain_time
2755
+ ON messages(brain_id, time);
2756
+ `);
2757
+ return db;
2758
+ }
2759
+ function appendMessageHistory(brainId, entry) {
2760
+ getDb().prepare(`INSERT INTO messages (brain_id, sender, time, content)
2761
+ VALUES (?, ?, ?, ?)`).run(brainId, entry.sender, entry.time.getTime(), entry.content);
2762
+ }
2763
+ function getMessageHistory(brainId, start, end) {
2764
+ const rows = getDb().prepare(`SELECT sender, time, content FROM messages
2765
+ WHERE brain_id = ? AND time >= ? AND time <= ?
2766
+ ORDER BY time ASC, id ASC`).all(brainId, start.getTime(), end.getTime());
2767
+ return rows.map((row) => ({
2768
+ sender: row.sender,
2769
+ time: new Date(row.time),
2770
+ content: row.content
2771
+ }));
2772
+ }
2736
2773
  function formatTime(time) {
2737
2774
  const pad = (n) => n.toString().padStart(2, "0");
2738
2775
  return `${time.getFullYear()}-${pad(time.getMonth() + 1)}-${pad(time.getDate())} ${pad(time.getHours())}:${pad(time.getMinutes())}:${pad(time.getSeconds())}`;
2739
2776
  }
2740
2777
  function translateMessageHistory(personaName, entries) {
2741
2778
  return entries.map((entry) => {
2742
- const label = entry.sender === "persona" ? personaName : "사용자";
2779
+ const label = entry.sender === "persona" ? personaName : "\uC0AC\uC6A9\uC790";
2743
2780
  return `${label}@${formatTime(entry.time)}: ${entry.content}`;
2744
2781
  }).join(`
2745
2782
  `);
@@ -2762,8 +2799,8 @@ var log6 = logger.child("memory");
2762
2799
  class Memory {
2763
2800
  db;
2764
2801
  space;
2765
- constructor(db, space) {
2766
- this.db = db;
2802
+ constructor(db2, space) {
2803
+ this.db = db2;
2767
2804
  this.space = space;
2768
2805
  log6.debug(`Memory constructed for space=${space.name}`);
2769
2806
  }
@@ -2843,8 +2880,8 @@ class Brain {
2843
2880
  brainbase;
2844
2881
  availabilityCache = new Map;
2845
2882
  memory;
2846
- constructor(db, space, brainbase, memory) {
2847
- this.db = db;
2883
+ constructor(db2, space, brainbase, memory) {
2884
+ this.db = db2;
2848
2885
  this.space = space;
2849
2886
  this.brainbase = brainbase;
2850
2887
  this.memory = memory ?? new Memory(this.db, this.space);
@@ -2926,7 +2963,7 @@ class Brain {
2926
2963
  const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
2927
2964
  const monthly = await this.createMonthlySchedule(nextMonth);
2928
2965
  if (!monthly) {
2929
- log7.debug(`regenerateSchedules: skip daily monthly schedule generation failed`);
2966
+ log7.debug(`regenerateSchedules: skip daily \u2014 monthly schedule generation failed`);
2930
2967
  return;
2931
2968
  }
2932
2969
  const tomorrow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
@@ -3112,7 +3149,7 @@ class Brain {
3112
3149
  return slice;
3113
3150
  }
3114
3151
  async deriveAvailabilityFromSchedule(schedule) {
3115
- log7.debug(`deriveAvailabilityFromSchedule: ${schedule.items.length} slots model`);
3152
+ log7.debug(`deriveAvailabilityFromSchedule: ${schedule.items.length} slots \u2192 model`);
3116
3153
  try {
3117
3154
  const instruction = await loadPrompt("SCHEDULE_AVAILABILITY");
3118
3155
  const promptMessage = JSON.stringify({
@@ -3184,7 +3221,7 @@ class Brain {
3184
3221
  `Conversation so far:`,
3185
3222
  historyBlock.length > 0 ? historyBlock : "(no prior messages)",
3186
3223
  `New user message(s) to which you must reply:`,
3187
- newBlock.length > 0 ? newBlock : "(none open turn)"
3224
+ newBlock.length > 0 ? newBlock : "(none \u2014 open turn)"
3188
3225
  ].join(`
3189
3226
 
3190
3227
  `);
@@ -3382,7 +3419,7 @@ ${blocks.join(`
3382
3419
  const baseSystemPrompt = `${generated.baseSystemPrompt}
3383
3420
 
3384
3421
  ${personaSystemFixed}`;
3385
- const db = new Supermemory({ apiKey: config.supermemoryApiKey });
3422
+ const db2 = new Supermemory({ apiKey: config.supermemoryApiKey });
3386
3423
  const brainId = randomUUID();
3387
3424
  const space = {
3388
3425
  name: `brain:${brainId}`,
@@ -3399,7 +3436,7 @@ ${personaSystemFixed}`;
3399
3436
  startConversationTimeThreshold: generated.startConversationTimeThreshold,
3400
3437
  activated: false
3401
3438
  };
3402
- const memory = new Memory(db, space);
3439
+ const memory = new Memory(db2, space);
3403
3440
  await memory.add({
3404
3441
  customId: "persona",
3405
3442
  content: description,
@@ -3408,7 +3445,7 @@ ${personaSystemFixed}`;
3408
3445
  log7.debug(`Brain.create: persona description stored`);
3409
3446
  await brainManager.saveBrain(brainId, brainbase);
3410
3447
  log7.debug(`Brain.create: brainbase saved (id=${brainId})`);
3411
- return { brainId, brain: new Brain(db, space, brainbase, memory) };
3448
+ return { brainId, brain: new Brain(db2, space, brainbase, memory) };
3412
3449
  } catch (error) {
3413
3450
  let reason = error instanceof Error ? `${error.message} (${error.name})` : String(error);
3414
3451
  if (error instanceof BadRequestResponseError) {
@@ -3425,8 +3462,8 @@ ${personaSystemFixed}`;
3425
3462
  log7.debug(`Brain.delete: no brainbase found`);
3426
3463
  return false;
3427
3464
  }
3428
- const db = new Supermemory({ apiKey: config.supermemoryApiKey });
3429
- const memory = new Memory(db, { name: brainbase.spaceName });
3465
+ const db2 = new Supermemory({ apiKey: config.supermemoryApiKey });
3466
+ const memory = new Memory(db2, { name: brainbase.spaceName });
3430
3467
  try {
3431
3468
  await memory.clear();
3432
3469
  } catch (error) {
@@ -3444,10 +3481,10 @@ ${personaSystemFixed}`;
3444
3481
  log7.debug(`Brain.load: not loadable (missing or not ready)`);
3445
3482
  return null;
3446
3483
  }
3447
- const db = new Supermemory({ apiKey: config.supermemoryApiKey });
3484
+ const db2 = new Supermemory({ apiKey: config.supermemoryApiKey });
3448
3485
  const space = { name: brainbase.spaceName };
3449
3486
  log7.debug(`Brain.load: ready (channel=${brainbase.channel})`);
3450
- return new Brain(db, space, brainbase);
3487
+ return new Brain(db2, space, brainbase);
3451
3488
  }
3452
3489
  }
3453
3490
  function formatDatetime(now) {
@@ -3545,7 +3582,9 @@ var VIEW_THINGS = [
3545
3582
  "monthly-schedule",
3546
3583
  "sending-queue",
3547
3584
  "deferred-queue",
3548
- "today-availability"
3585
+ "today-availability",
3586
+ "persona",
3587
+ "base-system-prompt"
3549
3588
  ];
3550
3589
 
3551
3590
  class BaseChannel {
@@ -3576,16 +3615,16 @@ class BaseChannel {
3576
3615
  if (!force) {
3577
3616
  const availability = await this.brain.getAvailability();
3578
3617
  if (availability.status !== "offline") {
3579
- logger.debug(`sleepMemory cron: skip availability=${availability.status}`);
3618
+ logger.debug(`sleepMemory cron: skip \u2014 availability=${availability.status}`);
3580
3619
  return;
3581
3620
  }
3582
3621
  const existing = await this.brain.memory.get(`daily-journal:${dateKey2}`);
3583
3622
  if (existing) {
3584
- logger.debug(`sleepMemory cron: skip journal for ${dateKey2} exists`);
3623
+ logger.debug(`sleepMemory cron: skip \u2014 journal for ${dateKey2} exists`);
3585
3624
  return;
3586
3625
  }
3587
3626
  }
3588
- const history = await this.getMessageHistoryBetween(new Date(Date.now() - 24 * 60 * 60 * 1000), new Date);
3627
+ const history = this.getMessageHistory(new Date(Date.now() - 24 * 60 * 60 * 1000), new Date);
3589
3628
  await this.brain.sleepMemory(new Date, history);
3590
3629
  }
3591
3630
  async regenerateSchedules() {
@@ -3595,11 +3634,11 @@ class BaseChannel {
3595
3634
  }
3596
3635
  async runStartConversation() {
3597
3636
  if (!this.isReady) {
3598
- logger.debug("startConversation: skip not ready");
3637
+ logger.debug("startConversation: skip \u2014 not ready");
3599
3638
  return;
3600
3639
  }
3601
3640
  if (this.isChatting || this.startConversationTimeout) {
3602
- logger.debug("startConversation: skip chat in progress");
3641
+ logger.debug("startConversation: skip \u2014 chat in progress");
3603
3642
  return;
3604
3643
  }
3605
3644
  const availability = await this.brain.getAvailability();
@@ -3613,11 +3652,11 @@ class BaseChannel {
3613
3652
  if (count >= countThreshold)
3614
3653
  return;
3615
3654
  const nowMs = now.getTime();
3616
- const history = await this.getMessageHistoryBetween(new Date(nowMs - 24 * 60 * 60 * 1000), now);
3655
+ const history = this.getMessageHistory(new Date(nowMs - 24 * 60 * 60 * 1000), now);
3617
3656
  try {
3618
3657
  const replies = await this.brain.sendMessage(history, [], {
3619
3658
  initiate: true,
3620
- send: this.send.bind(this)
3659
+ send: this.sendAndRecord.bind(this)
3621
3660
  });
3622
3661
  if (replies.length === 0)
3623
3662
  return;
@@ -3709,10 +3748,15 @@ class BaseChannel {
3709
3748
  twoDaysAgo.setDate(now.getDate() - 2);
3710
3749
  this.isSending = true;
3711
3750
  try {
3712
- await this.brain.sendMessage(await this.getMessageHistoryBetween(twoDaysAgo, now), newUserMessages, { send: this.send.bind(this) });
3751
+ const history = this.getMessageHistory(twoDaysAgo, now);
3752
+ for (const m of newUserMessages)
3753
+ this.saveMessageHistory(m);
3754
+ await this.brain.sendMessage(history, newUserMessages, {
3755
+ send: this.sendAndRecord.bind(this)
3756
+ });
3713
3757
  } catch (e) {
3714
3758
  logger.error(`Error while sending message: ${e}`);
3715
- logger.debug(`onMessage: sendMessage threw ${e instanceof Error ? e.stack : String(e)}`);
3759
+ logger.debug(`onMessage: sendMessage threw \u2014 ${e instanceof Error ? e.stack : String(e)}`);
3716
3760
  } finally {
3717
3761
  this.isSending = false;
3718
3762
  if (this.isSendingQueue.length > 0) {
@@ -3730,7 +3774,7 @@ class BaseChannel {
3730
3774
  }
3731
3775
  }, MESSAGE_DEBOUNCE_MS);
3732
3776
  } else {
3733
- logger.debug(`onMessage: isSending buffering into isSendingQueue (size=${this.isSendingQueue.length + 1})`);
3777
+ logger.debug(`onMessage: isSending \u2014 buffering into isSendingQueue (size=${this.isSendingQueue.length + 1})`);
3734
3778
  this.isSendingQueue.push(message);
3735
3779
  }
3736
3780
  this.isChatting = true;
@@ -3756,7 +3800,7 @@ class BaseChannel {
3756
3800
  const current = await this.brain.getAvailability();
3757
3801
  const prev = this.previousAvailability;
3758
3802
  this.previousAvailability = current.status;
3759
- logger.debug(`availabilityWatcher: ${prev ?? "(initial)"} ${current.status}`);
3803
+ logger.debug(`availabilityWatcher: ${prev ?? "(initial)"} \u2192 ${current.status}`);
3760
3804
  if (prev !== current.status) {
3761
3805
  await this.setAvailability(current.status);
3762
3806
  }
@@ -3772,7 +3816,7 @@ class BaseChannel {
3772
3816
  }
3773
3817
  const current = await this.brain.getAvailability();
3774
3818
  if (current.status !== "online") {
3775
- logger.debug(`flushDeferred: skip still ${current.status} (deferred size=${this.deferredQueue.length})`);
3819
+ logger.debug(`flushDeferred: skip \u2014 still ${current.status} (deferred size=${this.deferredQueue.length})`);
3776
3820
  return;
3777
3821
  }
3778
3822
  const drained = this.deferredQueue.splice(0, this.deferredQueue.length);
@@ -3888,6 +3932,12 @@ class BaseChannel {
3888
3932
  }));
3889
3933
  case "today-availability":
3890
3934
  return await this.brain.getTodayScheduledAvailability(now);
3935
+ case "persona": {
3936
+ const stored = await this.brain.memory.get("persona");
3937
+ return stored?.content ?? null;
3938
+ }
3939
+ case "base-system-prompt":
3940
+ return this.brain.brainbase.baseSystemPrompt;
3891
3941
  }
3892
3942
  }
3893
3943
  static async shutdownAll() {
@@ -3972,10 +4022,23 @@ class BaseChannel {
3972
4022
  displayName: channel.brain.brainbase.displayName
3973
4023
  };
3974
4024
  }
4025
+ getMessageHistory(start, end) {
4026
+ return getMessageHistory(this.brain.brainbase.brainId, start, end);
4027
+ }
4028
+ saveMessageHistory(entry) {
4029
+ appendMessageHistory(this.brain.brainbase.brainId, entry);
4030
+ }
4031
+ async sendAndRecord(text) {
4032
+ await this.send(text);
4033
+ this.saveMessageHistory({
4034
+ sender: "persona",
4035
+ time: new Date,
4036
+ content: text
4037
+ });
4038
+ }
3975
4039
  }
3976
4040
 
3977
4041
  // src/channel/discord.ts
3978
- var HISTORY_CAP = 1000;
3979
4042
  var AVAILABILITY_STATUS_MAP = {
3980
4043
  online: "online",
3981
4044
  "do-not-disturb": "dnd",
@@ -3985,7 +4048,6 @@ var AVAILABILITY_STATUS_MAP = {
3985
4048
  class DiscordChannel extends BaseChannel {
3986
4049
  client;
3987
4050
  targetChannel;
3988
- history = [];
3989
4051
  constructor(brain) {
3990
4052
  super(brain);
3991
4053
  }
@@ -4037,14 +4099,12 @@ class DiscordChannel extends BaseChannel {
4037
4099
  this.onPairing(inbound);
4038
4100
  return;
4039
4101
  }
4040
- const entry = {
4102
+ logger.debug(`MessageCreate: dispatching (channel=${msg.channelId})`);
4103
+ this.onMessage({
4041
4104
  sender: "user",
4042
4105
  time: msg.createdAt,
4043
4106
  content
4044
- };
4045
- this.pushHistory(entry);
4046
- logger.debug(`MessageCreate: stored in history, dispatching (channel=${msg.channelId})`);
4047
- this.onMessage(entry);
4107
+ });
4048
4108
  });
4049
4109
  logger.debug(`DiscordChannel.init: logging in`);
4050
4110
  await this.client.login(this.brain.brainbase.discord.token);
@@ -4076,7 +4136,7 @@ class DiscordChannel extends BaseChannel {
4076
4136
  this.targetChannel = channel;
4077
4137
  }
4078
4138
  }
4079
- logger.success(`Discord channel bound: ${this.brain.brainbase.displayName} ${entry.channelId}`);
4139
+ logger.success(`Discord channel bound: ${this.brain.brainbase.displayName} \u2192 ${entry.channelId}`);
4080
4140
  }
4081
4141
  await super.completePairing(entry);
4082
4142
  }
@@ -4101,17 +4161,9 @@ class DiscordChannel extends BaseChannel {
4101
4161
  return;
4102
4162
  }
4103
4163
  const mapped = AVAILABILITY_STATUS_MAP[status];
4104
- logger.debug(`setAvailability: ${status} ${mapped}`);
4164
+ logger.debug(`setAvailability: ${status} \u2192 ${mapped}`);
4105
4165
  this.client.user.setStatus(mapped);
4106
4166
  }
4107
- async getMessageHistoryBetween(start, end) {
4108
- return this.history.filter((m) => m.time >= start && m.time <= end);
4109
- }
4110
- pushHistory(entry) {
4111
- this.history.push(entry);
4112
- if (this.history.length > HISTORY_CAP)
4113
- this.history.shift();
4114
- }
4115
4167
  async resolveSendChannel() {
4116
4168
  if (this.targetChannel) {
4117
4169
  logger.debug(`resolveSendChannel: cache hit`);
@@ -4160,12 +4212,9 @@ class DiscordChannel extends BaseChannel {
4160
4212
 
4161
4213
  // src/channel/telegram.ts
4162
4214
  import { Bot } from "gramio";
4163
- var HISTORY_CAP2 = 1000;
4164
-
4165
4215
  class TelegramChannel extends BaseChannel {
4166
4216
  bot;
4167
4217
  chatId;
4168
- history = [];
4169
4218
  constructor(brain) {
4170
4219
  super(brain);
4171
4220
  }
@@ -4207,14 +4256,12 @@ class TelegramChannel extends BaseChannel {
4207
4256
  return;
4208
4257
  }
4209
4258
  this.chatId = ctx.chat.id;
4210
- const entry = {
4259
+ logger.debug(`Telegram message: dispatching (chat=${ctx.chat.id})`);
4260
+ this.onMessage({
4211
4261
  sender: "user",
4212
4262
  time: inbound.time,
4213
4263
  content: text
4214
- };
4215
- this.pushHistory(entry);
4216
- logger.debug(`Telegram message: stored in history, dispatching (chat=${ctx.chat.id})`);
4217
- this.onMessage(entry);
4264
+ });
4218
4265
  });
4219
4266
  logger.debug(`TelegramChannel.init: starting bot`);
4220
4267
  await this.bot.start();
@@ -4236,7 +4283,7 @@ class TelegramChannel extends BaseChannel {
4236
4283
  this.brain.brainbase.telegram.chatId = entry.chatId;
4237
4284
  this.chatId = entry.chatId;
4238
4285
  await this.brain.persistBrainBase();
4239
- logger.success(`Telegram chat bound: ${this.brain.brainbase.displayName} ${entry.chatId}`);
4286
+ logger.success(`Telegram chat bound: ${this.brain.brainbase.displayName} \u2192 ${entry.chatId}`);
4240
4287
  }
4241
4288
  await super.completePairing(entry);
4242
4289
  }
@@ -4254,14 +4301,6 @@ class TelegramChannel extends BaseChannel {
4254
4301
  async setAvailability(_status) {
4255
4302
  logger.debug(`setAvailability: ${_status} (no-op, Telegram has no bot presence)`);
4256
4303
  }
4257
- async getMessageHistoryBetween(start, end) {
4258
- return this.history.filter((m) => m.time >= start && m.time <= end);
4259
- }
4260
- pushHistory(entry) {
4261
- this.history.push(entry);
4262
- if (this.history.length > HISTORY_CAP2)
4263
- this.history.shift();
4264
- }
4265
4304
  async teardownClient() {
4266
4305
  if (!this.bot) {
4267
4306
  logger.debug(`teardownClient: no bot, nothing to stop`);
@@ -4274,9 +4313,9 @@ class TelegramChannel extends BaseChannel {
4274
4313
  }
4275
4314
 
4276
4315
  // src/utils/daemonClient.ts
4277
- import { connect } from "node:net";
4278
- import { join as join4 } from "node:path";
4279
- var DAEMON_SOCKET_PATH = join4(config.brainboxRoot, "daemon.sock");
4316
+ import { connect } from "net";
4317
+ import { join as join5 } from "path";
4318
+ var DAEMON_SOCKET_PATH = join5(config.brainboxRoot, "daemon.sock");
4280
4319
  async function sendToDaemon(payload) {
4281
4320
  logger.debug(`sendToDaemon: command="${payload.command}" args=${JSON.stringify(payload.args) ?? "null"}`);
4282
4321
  let reply;
@@ -4336,9 +4375,9 @@ function exchangeOnce(payload) {
4336
4375
  }
4337
4376
 
4338
4377
  // src/commands/daemon.ts
4339
- import { createServer } from "node:net";
4340
- import { chmodSync, unlinkSync } from "node:fs";
4341
- import { join as join5 } from "node:path";
4378
+ import { createServer } from "net";
4379
+ import { chmodSync, unlinkSync } from "fs";
4380
+ import { join as join6 } from "path";
4342
4381
 
4343
4382
  // src/commands/daemon/commands.ts
4344
4383
  var log8 = logger.child("daemon-cmd");
@@ -4362,7 +4401,7 @@ async function dispatch(payload) {
4362
4401
  log8.debug(`dispatch: unknown command "${command}"`);
4363
4402
  return { ok: false, error: `unknown command: ${command}` };
4364
4403
  }
4365
- log8.debug(`dispatch: "${command}" handler (args type=${typeof args})`);
4404
+ log8.debug(`dispatch: "${command}" \u2192 handler (args type=${typeof args})`);
4366
4405
  try {
4367
4406
  return await entry.handler(args);
4368
4407
  } catch (error) {
@@ -4490,9 +4529,9 @@ async function startChannels() {
4490
4529
  return started;
4491
4530
  }
4492
4531
  async function daemon() {
4493
- const logDir = join5(config.brainboxRoot, "logs");
4532
+ const logDir = join6(config.brainboxRoot, "logs");
4494
4533
  logger.configure({ logDir });
4495
- configureLlmLog(config.debug ? join5(logDir, "llm") : undefined);
4534
+ configureLlmLog(config.debug ? join6(logDir, "llm") : undefined);
4496
4535
  logger.debug(`daemon: boot (debug=${config.debug}, logDir=${logDir}, llmLog=${config.debug})`);
4497
4536
  const started = await startChannels();
4498
4537
  if (started === 0) {
@@ -4608,7 +4647,7 @@ async function listBrains() {
4608
4647
  }
4609
4648
  }
4610
4649
  async function setActivated(brainId, activated) {
4611
- logger.debug(`setActivated: id=${brainId} ${activated}`);
4650
+ logger.debug(`setActivated: id=${brainId} \u2192 ${activated}`);
4612
4651
  const brain = await brainManager.loadBrain(brainId);
4613
4652
  if (!brain) {
4614
4653
  logger.error(`Brain not found: ${brainId}`);
@@ -4685,7 +4724,7 @@ async function viewThing(thing, brainId) {
4685
4724
  args: { thing, brainId }
4686
4725
  });
4687
4726
  const name = response.result?.displayName ?? brainId;
4688
- logger.info(`${thing} "${name}" (${brainId})`);
4727
+ logger.info(`${thing} \u2014 "${name}" (${brainId})`);
4689
4728
  console.log(JSON.stringify(response.result?.value ?? null, null, 2));
4690
4729
  }
4691
4730
  function register3(program) {
@@ -5255,7 +5294,7 @@ function ModelApp({
5255
5294
  children: [
5256
5295
  "Known providers: ",
5257
5296
  listProviderNames().slice(0, 12).join(", "),
5258
- ""
5297
+ "\u2026"
5259
5298
  ]
5260
5299
  }, undefined, true, undefined, this)
5261
5300
  ]
@@ -5473,7 +5512,7 @@ function RawSelect({
5473
5512
  filtered.length,
5474
5513
  "/",
5475
5514
  items.length,
5476
- ") · ↑↓ · type to filter · enter"
5515
+ ") \xB7 \u2191\u2193 \xB7 type to filter \xB7 enter"
5477
5516
  ]
5478
5517
  }, undefined, true, undefined, this),
5479
5518
  filtered.length === 0 ? /* @__PURE__ */ jsxDEV4(Text4, {
@@ -5483,7 +5522,7 @@ function RawSelect({
5483
5522
  children: [
5484
5523
  start > 0 && /* @__PURE__ */ jsxDEV4(Text4, {
5485
5524
  dimColor: true,
5486
- children: " "
5525
+ children: " \u2026"
5487
5526
  }, undefined, false, undefined, this),
5488
5527
  visible.map((item, i) => {
5489
5528
  const idx = start + i;
@@ -5498,7 +5537,7 @@ function RawSelect({
5498
5537
  }),
5499
5538
  start + visible.length < filtered.length && /* @__PURE__ */ jsxDEV4(Text4, {
5500
5539
  dimColor: true,
5501
- children: " "
5540
+ children: " \u2026"
5502
5541
  }, undefined, false, undefined, this)
5503
5542
  ]
5504
5543
  }, undefined, true, undefined, this)
@@ -5520,7 +5559,7 @@ function Select(props) {
5520
5559
  // src/commands/onboard.tsx
5521
5560
  import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
5522
5561
  function ok(msg) {
5523
- console.log(chalk3.green(`✔ ${msg}`));
5562
+ console.log(chalk3.green(`\u2714 ${msg}`));
5524
5563
  }
5525
5564
  function info(msg) {
5526
5565
  console.log(msg);
@@ -5545,12 +5584,12 @@ function ProviderApp({
5545
5584
  /* @__PURE__ */ jsxDEV5(Text5, {
5546
5585
  children: [
5547
5586
  chalk3.bold("Step 1/4"),
5548
- " Choose your first provider"
5587
+ " \u2014 Choose your first provider"
5549
5588
  ]
5550
5589
  }, undefined, true, undefined, this),
5551
5590
  /* @__PURE__ */ jsxDEV5(Text5, {
5552
5591
  dimColor: true,
5553
- children: "↑↓ to move, type to filter, enter to select"
5592
+ children: "\u2191\u2193 to move, type to filter, enter to select"
5554
5593
  }, undefined, false, undefined, this),
5555
5594
  /* @__PURE__ */ jsxDEV5(Select, {
5556
5595
  prompt: "provider> ",
@@ -5575,7 +5614,7 @@ function ProviderApp({
5575
5614
  /* @__PURE__ */ jsxDEV5(Text5, {
5576
5615
  children: [
5577
5616
  chalk3.bold("Step 1/4"),
5578
- " ",
5617
+ " \u2014 ",
5579
5618
  /* @__PURE__ */ jsxDEV5(Text5, {
5580
5619
  color: "cyan",
5581
5620
  children: stage.provider
@@ -5629,7 +5668,7 @@ function ProviderApp({
5629
5668
  /* @__PURE__ */ jsxDEV5(Text5, {
5630
5669
  children: [
5631
5670
  chalk3.bold("Step 1/4"),
5632
- " ",
5671
+ " \u2014 ",
5633
5672
  stage.provider,
5634
5673
  " extra:",
5635
5674
  " ",
@@ -5675,7 +5714,7 @@ function ModelApp2({
5675
5714
  /* @__PURE__ */ jsxDEV5(Text5, {
5676
5715
  children: [
5677
5716
  chalk3.bold("Step 1/4"),
5678
- " Default model (both slots)"
5717
+ " \u2014 Default model (both slots)"
5679
5718
  ]
5680
5719
  }, undefined, true, undefined, this),
5681
5720
  /* @__PURE__ */ jsxDEV5(Text5, {
@@ -5689,7 +5728,7 @@ function ModelApp2({
5689
5728
  "/"
5690
5729
  ]
5691
5730
  }, undefined, true, undefined, this),
5692
- "model fine-tune later with ",
5731
+ "model \u2014 fine-tune later with ",
5693
5732
  /* @__PURE__ */ jsxDEV5(Text5, {
5694
5733
  color: "cyan",
5695
5734
  children: "brainbox model"
@@ -5732,7 +5771,7 @@ function SuperMemoryApp({
5732
5771
  /* @__PURE__ */ jsxDEV5(Text5, {
5733
5772
  children: [
5734
5773
  chalk3.bold("Step 2/4"),
5735
- " Supermemory API key"
5774
+ " \u2014 Supermemory API key"
5736
5775
  ]
5737
5776
  }, undefined, true, undefined, this),
5738
5777
  /* @__PURE__ */ jsxDEV5(Text5, {
@@ -5771,7 +5810,7 @@ function BrainApp({
5771
5810
  /* @__PURE__ */ jsxDEV5(Text5, {
5772
5811
  children: [
5773
5812
  chalk3.bold("Step 3/4"),
5774
- " Brain name"
5813
+ " \u2014 Brain name"
5775
5814
  ]
5776
5815
  }, undefined, true, undefined, this),
5777
5816
  /* @__PURE__ */ jsxDEV5(Text5, {
@@ -5804,7 +5843,7 @@ function BrainApp({
5804
5843
  /* @__PURE__ */ jsxDEV5(Text5, {
5805
5844
  children: [
5806
5845
  chalk3.bold("Step 3/4"),
5807
- " Language for",
5846
+ " \u2014 Language for",
5808
5847
  " ",
5809
5848
  /* @__PURE__ */ jsxDEV5(Text5, {
5810
5849
  color: "cyan",
@@ -5814,7 +5853,7 @@ function BrainApp({
5814
5853
  }, undefined, true, undefined, this),
5815
5854
  /* @__PURE__ */ jsxDEV5(Text5, {
5816
5855
  dimColor: true,
5817
- children: "↑↓ to move, type to filter, enter to select"
5856
+ children: "\u2191\u2193 to move, type to filter, enter to select"
5818
5857
  }, undefined, false, undefined, this),
5819
5858
  /* @__PURE__ */ jsxDEV5(Select, {
5820
5859
  prompt: "language> ",
@@ -5857,7 +5896,7 @@ function BrainApp({
5857
5896
  /* @__PURE__ */ jsxDEV5(Text5, {
5858
5897
  children: [
5859
5898
  chalk3.bold("Step 3/4"),
5860
- " Seed for",
5899
+ " \u2014 Seed for",
5861
5900
  " ",
5862
5901
  /* @__PURE__ */ jsxDEV5(Text5, {
5863
5902
  color: "cyan",
@@ -5880,7 +5919,7 @@ function BrainApp({
5880
5919
  }, undefined, false, undefined, this),
5881
5920
  busy ? /* @__PURE__ */ jsxDEV5(Text5, {
5882
5921
  dimColor: true,
5883
- children: "Creating brain (This can take few minutes depending on the model's response speed)"
5922
+ children: "Creating brain\u2026 (This can take few minutes depending on the model's response speed)"
5884
5923
  }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV5(TextInput, {
5885
5924
  prompt: "seed> ",
5886
5925
  onSubmit: (raw) => {
@@ -5931,7 +5970,7 @@ function ChannelApp({
5931
5970
  /* @__PURE__ */ jsxDEV5(Text5, {
5932
5971
  children: [
5933
5972
  chalk3.bold("Step 4/4"),
5934
- " Channel for",
5973
+ " \u2014 Channel for",
5935
5974
  " ",
5936
5975
  /* @__PURE__ */ jsxDEV5(Text5, {
5937
5976
  color: "cyan",
@@ -5941,7 +5980,7 @@ function ChannelApp({
5941
5980
  }, undefined, true, undefined, this),
5942
5981
  /* @__PURE__ */ jsxDEV5(Text5, {
5943
5982
  dimColor: true,
5944
- children: "↑↓ to move, type to filter, enter to select"
5983
+ children: "\u2191\u2193 to move, type to filter, enter to select"
5945
5984
  }, undefined, false, undefined, this),
5946
5985
  /* @__PURE__ */ jsxDEV5(Select, {
5947
5986
  prompt: "channel> ",
@@ -5969,7 +6008,7 @@ function ChannelApp({
5969
6008
  /* @__PURE__ */ jsxDEV5(Text5, {
5970
6009
  children: [
5971
6010
  chalk3.bold("Step 4/4"),
5972
- " ",
6011
+ " \u2014 ",
5973
6012
  stage.kind_,
5974
6013
  " bot token"
5975
6014
  ]
@@ -5999,7 +6038,7 @@ function ChannelApp({
5999
6038
  /* @__PURE__ */ jsxDEV5(Text5, {
6000
6039
  children: [
6001
6040
  chalk3.bold("Step 4/4"),
6002
- " Optional",
6041
+ " \u2014 Optional",
6003
6042
  " ",
6004
6043
  stage.kind_ === "discord" ? "channelId" : "chatId",
6005
6044
  " (blank = pair later)"
@@ -6040,7 +6079,7 @@ function ChannelApp({
6040
6079
  };
6041
6080
  }
6042
6081
  await brainManager.saveBrain(brainId, updated);
6043
- onDone(`Bound ${displayName} ${stage.kind_}${target ? ` (${target})` : " (pairing mode)"}`);
6082
+ onDone(`Bound ${displayName} \u2192 ${stage.kind_}${target ? ` (${target})` : " (pairing mode)"}`);
6044
6083
  })();
6045
6084
  }
6046
6085
  }, undefined, false, undefined, this),
@@ -6053,7 +6092,7 @@ function ChannelApp({
6053
6092
  }
6054
6093
  async function runOnboard() {
6055
6094
  console.clear();
6056
- info(`Welcome let's get ${chalk3.bold("brainbox")} ready.`);
6095
+ info(`Welcome \u2014 let's get ${chalk3.bold("brainbox")} ready.`);
6057
6096
  const providers = listProviderNames().slice().sort();
6058
6097
  const { promise, resolve: resolve2 } = Promise.withResolvers();
6059
6098
  const active = { current: null };
@@ -6114,7 +6153,7 @@ logger.configure({ level: config.debug ? "debug" : "info" });
6114
6153
  logger.debug(`brainbox starting (debug=${config.debug}, root=${config.brainboxRoot})`);
6115
6154
  function getVersion() {
6116
6155
  try {
6117
- const pkgPath = join6(__dirname3, "..", "package.json");
6156
+ const pkgPath = join7(__dirname3, "..", "package.json");
6118
6157
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
6119
6158
  return pkg.version ?? "0.0.0";
6120
6159
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p-sw/brainbox",
3
- "version": "0.2.2",
3
+ "version": "0.2.3-beta.1",
4
4
  "module": "dist/index.js",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -10,10 +10,11 @@
10
10
  "scripts": {
11
11
  "cli": "bun run src/index.ts",
12
12
  "format": "bun run prettier 'src/**/*.ts' -w",
13
- "build": "rm -rf dist && bun build ./src/index.ts --outdir dist --target node --packages external && cp -r prompts dist/prompts && chmod +x dist/index.js",
13
+ "build": "rm -rf dist && bun build ./src/index.ts --outdir dist --target bun --packages external && sed -i '1s|#!/usr/bin/env node|#!/usr/bin/env bun|' dist/index.js && cp -r prompts dist/prompts && chmod +x dist/index.js",
14
14
  "prepack": "bun run build"
15
15
  },
16
16
  "devDependencies": {
17
+ "@types/bun": "^1.3.14",
17
18
  "@types/node": "^25.9.1"
18
19
  },
19
20
  "peerDependencies": {