opencode-mailbox 0.0.0 → 0.0.2

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 +7 -4
  2. package/dist/index.js +63 -44
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -4,7 +4,9 @@ A simple mailbox system for sending and receiving messages between sessions.
4
4
 
5
5
  ## Description
6
6
 
7
- This OpenCode plugin provides a lightweight mailbox system that allows sessions to send messages to each other asynchronously. Messages are stored efficiently, indexed first by recipient and then by timestamp.
7
+ This OpenCode plugin provides a lightweight mailbox system that allows sessions to send messages to each other asynchronously. Messages are stored in a SQLite database with proper indexing for fast lookups.
8
+
9
+ **NOTE: Mail is stored in `~/.config/opencode/mailbox.db`**
8
10
 
9
11
  ## Installation
10
12
 
@@ -58,9 +60,10 @@ Stop all mail watching for the current session.
58
60
 
59
61
  ## Storage
60
62
 
61
- Mail data is persisted in the OpenCode config directory as `mailbox.json`. The storage is optimized with an index structure:
62
- - First index: recipient (who the mail is to)
63
- - Second index: timestamp (when the mail was received)
63
+ Mail data is persisted in a SQLite database at `~/.config/opencode/mailbox.db`. The database includes:
64
+ - Indexed `recipient` column for fast recipient lookups
65
+ - Indexed `read` status for efficient watch queries
66
+ - WAL (Write-Ahead Logging) mode for better concurrency
64
67
 
65
68
  ## Session Management
66
69
 
package/dist/index.js CHANGED
@@ -12677,30 +12677,68 @@ var init_dist = __esm(() => {
12677
12677
  });
12678
12678
 
12679
12679
  // index.ts
12680
- import * as fs from "fs";
12681
12680
  import * as path from "path";
12682
- var mailboxFile = null;
12681
+ import Database from "better-sqlite3";
12682
+ var dbFile = null;
12683
+ var db = null;
12683
12684
  var activeWatches = new Map;
12684
- async function getMailboxFile(client) {
12685
- if (!mailboxFile) {
12685
+ async function getDbFile(client) {
12686
+ if (!dbFile) {
12686
12687
  const result = await client.path.get();
12687
- mailboxFile = path.join(result.data.config, "mailbox.json");
12688
- }
12689
- return mailboxFile;
12690
- }
12691
- async function loadMailbox(client) {
12692
- const file2 = await getMailboxFile(client);
12693
- try {
12694
- if (fs.existsSync(file2)) {
12695
- const data = JSON.parse(fs.readFileSync(file2, "utf-8"));
12696
- return data;
12697
- }
12698
- } catch {}
12699
- return {};
12700
- }
12701
- async function saveMailbox(client, mailbox) {
12702
- const file2 = await getMailboxFile(client);
12703
- fs.writeFileSync(file2, JSON.stringify(mailbox, null, 2));
12688
+ dbFile = path.join(result.data.config, "mailbox.db");
12689
+ }
12690
+ return dbFile;
12691
+ }
12692
+ async function getDatabase(client) {
12693
+ if (!db) {
12694
+ const file2 = await getDbFile(client);
12695
+ db = new Database(file2);
12696
+ db.pragma("journal_mode = WAL");
12697
+ db.exec(`
12698
+ CREATE TABLE IF NOT EXISTS messages (
12699
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12700
+ recipient TEXT NOT NULL,
12701
+ sender TEXT NOT NULL,
12702
+ message TEXT NOT NULL,
12703
+ timestamp INTEGER NOT NULL,
12704
+ read INTEGER NOT NULL DEFAULT 0
12705
+ )
12706
+ `);
12707
+ db.exec(`
12708
+ CREATE INDEX IF NOT EXISTS idx_messages_recipient ON messages(recipient)
12709
+ `);
12710
+ db.exec(`
12711
+ CREATE INDEX IF NOT EXISTS idx_messages_read ON messages(recipient, read)
12712
+ `);
12713
+ }
12714
+ return db;
12715
+ }
12716
+ async function addMessage(client, recipient, sender, message, timestamp) {
12717
+ const database = await getDatabase(client);
12718
+ const stmt = database.prepare(`
12719
+ INSERT INTO messages (recipient, sender, message, timestamp, read)
12720
+ VALUES (?, ?, ?, ?, 0)
12721
+ `);
12722
+ stmt.run(recipient.toLowerCase(), sender.toLowerCase(), message, timestamp);
12723
+ }
12724
+ async function getUnreadMessages(client, recipient) {
12725
+ const database = await getDatabase(client);
12726
+ const stmt = database.prepare(`
12727
+ SELECT sender as from, message, timestamp, read
12728
+ FROM messages
12729
+ WHERE recipient = ? AND read = 0
12730
+ ORDER BY timestamp ASC
12731
+ `);
12732
+ return stmt.all(recipient.toLowerCase());
12733
+ }
12734
+ async function markMessageAsRead(client, recipient, timestamp) {
12735
+ const database = await getDatabase(client);
12736
+ const stmt = database.prepare(`
12737
+ UPDATE messages
12738
+ SET read = 1
12739
+ WHERE recipient = ? AND timestamp = ?
12740
+ `);
12741
+ stmt.run(recipient.toLowerCase(), timestamp);
12704
12742
  }
12705
12743
  function startMailWatch(client, recipient, sessionId, instructions) {
12706
12744
  if (activeWatches.has(recipient)) {
@@ -12708,25 +12746,16 @@ function startMailWatch(client, recipient, sessionId, instructions) {
12708
12746
  }
12709
12747
  const interval = setInterval(async () => {
12710
12748
  try {
12711
- const mailbox = await loadMailbox(client);
12712
12749
  const watch = activeWatches.get(recipient);
12713
12750
  if (!watch) {
12714
12751
  return;
12715
12752
  }
12716
- const recipientMailbox = mailbox[recipient];
12717
- if (!recipientMailbox) {
12753
+ const unreadMessages = await getUnreadMessages(client, recipient);
12754
+ if (unreadMessages.length === 0) {
12718
12755
  return;
12719
12756
  }
12720
- const unreadMessages = [];
12721
- for (const [timestamp, message] of Object.entries(recipientMailbox)) {
12722
- if (!message.read) {
12723
- unreadMessages.push(message);
12724
- }
12725
- }
12726
- unreadMessages.sort((a, b) => a.timestamp - b.timestamp);
12727
12757
  for (const message of unreadMessages) {
12728
- recipientMailbox[message.timestamp].read = true;
12729
- await saveMailbox(client, mailbox);
12758
+ await markMessageAsRead(client, recipient, message.timestamp);
12730
12759
  await injectMailMessage(client, sessionId, recipient, message, instructions);
12731
12760
  }
12732
12761
  } catch (error45) {
@@ -12793,20 +12822,10 @@ var mailboxPlugin = async (ctx) => {
12793
12822
  message: z.string().describe("Message content to send")
12794
12823
  },
12795
12824
  async execute(args) {
12796
- const mailbox = await loadMailbox(client);
12797
12825
  const to = args.to.toLowerCase();
12798
12826
  const from = args.from.toLowerCase();
12799
12827
  const timestamp = Date.now();
12800
- if (!mailbox[to]) {
12801
- mailbox[to] = {};
12802
- }
12803
- mailbox[to][timestamp] = {
12804
- from,
12805
- message: args.message,
12806
- timestamp,
12807
- read: false
12808
- };
12809
- await saveMailbox(client, mailbox);
12828
+ await addMessage(client, to, from, args.message, timestamp);
12810
12829
  return `Mail sent to "${args.to}" from "${args.from}" at ${new Date(timestamp).toISOString()}`;
12811
12830
  }
12812
12831
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-mailbox",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "A simple mailbox system for sending and receiving messages between sessions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,12 +16,13 @@
16
16
  "README.md"
17
17
  ],
18
18
  "scripts": {
19
- "build": "bun build ./index.ts --outdir ./dist --target bun && bun run build:types",
19
+ "build": "bun build ./index.ts --outdir ./dist --target bun --external better-sqlite3 && bun run build:types",
20
20
  "build:types": "tsc --emitDeclarationOnly",
21
21
  "start": "bun run build",
22
22
  "prepublishOnly": "bun run build"
23
23
  },
24
24
  "devDependencies": {
25
+ "@types/better-sqlite3": "^7.6.12",
25
26
  "@types/node": "^25.0.10",
26
27
  "electron": "^28.0.0",
27
28
  "typescript": "^5.9.3"
@@ -30,6 +31,7 @@
30
31
  "@opencode-ai/plugin": "^1.1.25"
31
32
  },
32
33
  "dependencies": {
34
+ "better-sqlite3": "^12.1.0",
33
35
  "dotenv": "^17.2.3"
34
36
  },
35
37
  "keywords": [