slashvibe-mcp 0.3.20 → 0.3.21

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 (167) hide show
  1. package/README.md +47 -252
  2. package/analytics.js +107 -0
  3. package/auth-store.js +148 -0
  4. package/auto-update.js +130 -0
  5. package/bridges/bridge-monitor.js +388 -0
  6. package/bridges/discord-bot.js +431 -0
  7. package/bridges/farcaster.js +299 -0
  8. package/bridges/telegram.js +261 -0
  9. package/bridges/webhook-health.js +420 -0
  10. package/bridges/webhook-server.js +437 -0
  11. package/bridges/whatsapp.js +441 -0
  12. package/bridges/x-webhook.js +423 -0
  13. package/config.js +27 -15
  14. package/games/arcade.js +406 -0
  15. package/games/chess.js +451 -0
  16. package/games/colorguess.js +343 -0
  17. package/games/crossword-words.js +171 -0
  18. package/games/crossword.js +461 -0
  19. package/games/drawing.js +347 -0
  20. package/games/gameroulette.js +300 -0
  21. package/games/gamerouter.js +336 -0
  22. package/games/gamestatus.js +337 -0
  23. package/games/guessnumber.js +209 -0
  24. package/games/hangman.js +279 -0
  25. package/games/memory.js +338 -0
  26. package/games/multiplayer-tictactoe.js +389 -0
  27. package/games/pixelart.js +399 -0
  28. package/games/quickduel.js +354 -0
  29. package/games/riddle.js +371 -0
  30. package/games/rockpaperscissors.js +291 -0
  31. package/games/snake.js +406 -0
  32. package/games/storybuilder.js +343 -0
  33. package/games/tictactoe.js +345 -0
  34. package/games/twentyquestions.js +286 -0
  35. package/games/twotruths.js +207 -0
  36. package/games/werewolf.js +508 -0
  37. package/games/wordassociation.js +247 -0
  38. package/games/wordchain.js +135 -0
  39. package/index.js +116 -159
  40. package/intelligence/index.js +9 -2
  41. package/intelligence/interests.js +369 -0
  42. package/notification-emitter.js +77 -0
  43. package/notify.js +5 -1
  44. package/package.json +21 -16
  45. package/prompts.js +1 -1
  46. package/protocol/index.js +73 -0
  47. package/setup.js +480 -0
  48. package/smart-inbox.js +276 -0
  49. package/store/api.js +536 -215
  50. package/store/profiles.js +160 -12
  51. package/tools/_actions.js +362 -21
  52. package/tools/_discovery.js +119 -26
  53. package/tools/_shared/index.js +64 -0
  54. package/tools/_shared.js +234 -0
  55. package/tools/_work-context.js +338 -0
  56. package/tools/_work-context.manual-test.js +199 -0
  57. package/tools/_work-context.test.js +260 -0
  58. package/tools/activity.js +220 -0
  59. package/tools/analytics.js +191 -0
  60. package/tools/approve.js +197 -0
  61. package/tools/artifact-create.js +14 -3
  62. package/tools/artifacts-price.js +107 -0
  63. package/tools/available.js +120 -0
  64. package/tools/broadcast.js +325 -0
  65. package/tools/chat.js +202 -0
  66. package/tools/collaborative-drawing.js +1 -1
  67. package/tools/connection-status.js +178 -0
  68. package/tools/discover.js +350 -34
  69. package/tools/dm.js +80 -8
  70. package/tools/earnings.js +126 -0
  71. package/tools/feed.js +35 -4
  72. package/tools/follow.js +224 -0
  73. package/tools/friends.js +207 -0
  74. package/tools/gig-browse.js +206 -0
  75. package/tools/gig-complete.js +144 -0
  76. package/tools/health.js +87 -0
  77. package/tools/help.js +3 -3
  78. package/tools/idea.js +9 -2
  79. package/tools/inbox.js +289 -105
  80. package/tools/init.js +131 -34
  81. package/tools/invite.js +15 -4
  82. package/tools/leaderboard.js +117 -0
  83. package/tools/lib/git-apply.js +206 -0
  84. package/tools/lib/git-bundle.js +407 -0
  85. package/tools/migrate.js +3 -3
  86. package/tools/multiplayer-game.js +1 -1
  87. package/tools/onboarding.js +7 -7
  88. package/tools/open.js +143 -12
  89. package/tools/party-game.js +1 -1
  90. package/tools/plan.js +225 -0
  91. package/tools/proof-of-work.js +144 -0
  92. package/tools/reply.js +166 -0
  93. package/tools/report.js +1 -1
  94. package/tools/request.js +17 -3
  95. package/tools/schedule.js +367 -0
  96. package/tools/search-messages.js +123 -0
  97. package/tools/session.js +467 -0
  98. package/tools/session_price.js +128 -0
  99. package/tools/settings.js +90 -2
  100. package/tools/ship.js +30 -7
  101. package/tools/smart-check.js +201 -0
  102. package/tools/start.js +147 -12
  103. package/tools/status.js +53 -6
  104. package/tools/streak.js +147 -0
  105. package/tools/stuck.js +297 -0
  106. package/tools/subscribe.js +148 -0
  107. package/tools/subscriptions.js +134 -0
  108. package/tools/suggest-tags.js +6 -8
  109. package/tools/tag-suggestions.js +1 -1
  110. package/tools/tip.js +150 -77
  111. package/tools/token.js +4 -4
  112. package/tools/update.js +1 -1
  113. package/tools/wallet.js +221 -79
  114. package/tools/watch.js +157 -0
  115. package/tools/who.js +30 -1
  116. package/tools/withdraw.js +145 -0
  117. package/tools/work-summary.js +96 -0
  118. package/version.json +10 -8
  119. package/LICENSE +0 -21
  120. package/store/sqlite.js +0 -347
  121. /package/tools/{auto-suggest-connections.js → _deprecated/auto-suggest-connections.js} +0 -0
  122. /package/tools/{away.js → _deprecated/away.js} +0 -0
  123. /package/tools/{back.js → _deprecated/back.js} +0 -0
  124. /package/tools/{bootstrap-skills.js → _deprecated/bootstrap-skills.js} +0 -0
  125. /package/tools/{bridge-dashboard.js → _deprecated/bridge-dashboard.js} +0 -0
  126. /package/tools/{bridge-health.js → _deprecated/bridge-health.js} +0 -0
  127. /package/tools/{bridge-live.js → _deprecated/bridge-live.js} +0 -0
  128. /package/tools/{bridges.js → _deprecated/bridges.js} +0 -0
  129. /package/tools/{colorguess.js → _deprecated/colorguess.js} +0 -0
  130. /package/tools/{discover-insights.js → _deprecated/discover-insights.js} +0 -0
  131. /package/tools/{discover-momentum.js → _deprecated/discover-momentum.js} +0 -0
  132. /package/tools/{discovery-analytics.js → _deprecated/discovery-analytics.js} +0 -0
  133. /package/tools/{discovery-auto-suggest.js → _deprecated/discovery-auto-suggest.js} +0 -0
  134. /package/tools/{discovery-bootstrap.js → _deprecated/discovery-bootstrap.js} +0 -0
  135. /package/tools/{discovery-daily.js → _deprecated/discovery-daily.js} +0 -0
  136. /package/tools/{discovery-dashboard.js → _deprecated/discovery-dashboard.js} +0 -0
  137. /package/tools/{discovery-digest.js → _deprecated/discovery-digest.js} +0 -0
  138. /package/tools/{discovery-hub.js → _deprecated/discovery-hub.js} +0 -0
  139. /package/tools/{discovery-insights.js → _deprecated/discovery-insights.js} +0 -0
  140. /package/tools/{discovery-momentum.js → _deprecated/discovery-momentum.js} +0 -0
  141. /package/tools/{discovery-monitor.js → _deprecated/discovery-monitor.js} +0 -0
  142. /package/tools/{discovery-proactive.js → _deprecated/discovery-proactive.js} +0 -0
  143. /package/tools/{draw.js → _deprecated/draw.js} +0 -0
  144. /package/tools/{farcaster.js → _deprecated/farcaster.js} +0 -0
  145. /package/tools/{forget.js → _deprecated/forget.js} +0 -0
  146. /package/tools/{games-catalog.js → _deprecated/games-catalog.js} +0 -0
  147. /package/tools/{games.js → _deprecated/games.js} +0 -0
  148. /package/tools/{guessnumber.js → _deprecated/guessnumber.js} +0 -0
  149. /package/tools/{hangman.js → _deprecated/hangman.js} +0 -0
  150. /package/tools/{multiplayer-tictactoe.js → _deprecated/multiplayer-tictactoe.js} +0 -0
  151. /package/tools/{mute.js → _deprecated/mute.js} +0 -0
  152. /package/tools/{recall.js → _deprecated/recall.js} +0 -0
  153. /package/tools/{remember.js → _deprecated/remember.js} +0 -0
  154. /package/tools/{riddle.js → _deprecated/riddle.js} +0 -0
  155. /package/tools/{run-bootstrap.js → _deprecated/run-bootstrap.js} +0 -0
  156. /package/tools/{skills-analytics.js → _deprecated/skills-analytics.js} +0 -0
  157. /package/tools/{skills-bootstrap.js → _deprecated/skills-bootstrap.js} +0 -0
  158. /package/tools/{skills-dashboard.js → _deprecated/skills-dashboard.js} +0 -0
  159. /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
  160. /package/tools/{skills.js → _deprecated/skills.js} +0 -0
  161. /package/tools/{smart-intro.js → _deprecated/smart-intro.js} +0 -0
  162. /package/tools/{storybuilder.js → _deprecated/storybuilder.js} +0 -0
  163. /package/tools/{telegram-bot.js → _deprecated/telegram-bot.js} +0 -0
  164. /package/tools/{telegram-setup.js → _deprecated/telegram-setup.js} +0 -0
  165. /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
  166. /package/tools/{twentyquestions.js → _deprecated/twentyquestions.js} +0 -0
  167. /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
@@ -0,0 +1,96 @@
1
+ /**
2
+ * vibe_work_summary — Standalone tool for explicit context gathering
3
+ *
4
+ * Use this when you need to:
5
+ * - Draft a message about what you're working on
6
+ * - Share your progress with someone
7
+ * - Get context for composing a "ship" post
8
+ *
9
+ * This tool returns structured data about:
10
+ * - Git state (branch, recent commits, changed files)
11
+ * - Project info (name, type)
12
+ * - Pre-formatted suggestions ready for messages/presence
13
+ */
14
+
15
+ const { gatherWorkContext } = require('./_work-context');
16
+
17
+ const definition = {
18
+ name: 'vibe_work_summary',
19
+ description: 'Get a summary of what you\'re working on (git state, project info). Use before composing messages about your work.',
20
+ inputSchema: {
21
+ type: 'object',
22
+ properties: {
23
+ detail_level: {
24
+ type: 'string',
25
+ enum: ['brief', 'detailed'],
26
+ description: 'brief = one-liner, detailed = include commits/files'
27
+ }
28
+ }
29
+ }
30
+ };
31
+
32
+ async function handler(args) {
33
+ const { detail_level = 'brief' } = args;
34
+
35
+ // Gather all context
36
+ const context = gatherWorkContext();
37
+
38
+ // Build display based on detail level
39
+ let display = '## Work Context\n\n';
40
+
41
+ // Project info
42
+ display += `**Project:** ${context.project.name}`;
43
+ if (context.project.type !== 'unknown') {
44
+ display += ` (${context.project.type})`;
45
+ }
46
+ display += '\n';
47
+
48
+ // Git info
49
+ if (context.git) {
50
+ display += `**Branch:** ${context.git.branch}\n`;
51
+
52
+ if (detail_level === 'detailed') {
53
+ // Show recent commits
54
+ if (context.git.recentCommits.length > 0) {
55
+ display += '\n**Recent commits:**\n';
56
+ context.git.recentCommits.forEach(c => {
57
+ display += `- \`${c.hash}\` ${c.message}\n`;
58
+ });
59
+ }
60
+
61
+ // Show changed files
62
+ if (context.git.changedFiles.length > 0) {
63
+ display += '\n**Changed files:**\n';
64
+ context.git.changedFiles.forEach(f => {
65
+ display += `- ${f}\n`;
66
+ });
67
+ }
68
+
69
+ // Uncommitted changes indicator
70
+ if (context.git.hasUncommitted) {
71
+ display += '\n⚠️ _Uncommitted changes_\n';
72
+ }
73
+ }
74
+ } else {
75
+ display += '_Not in a git repository_\n';
76
+ }
77
+
78
+ // Suggestions
79
+ display += '\n---\n\n';
80
+ display += '**Ready to use:**\n';
81
+ display += `- Brief: "${context.suggestions.brief}"\n`;
82
+ if (detail_level === 'detailed' && context.suggestions.detailed !== context.suggestions.brief) {
83
+ display += `- Detailed: "${context.suggestions.detailed}"\n`;
84
+ }
85
+
86
+ display += '\n_Use these in messages, status updates, or ship posts._';
87
+
88
+ return {
89
+ display,
90
+ // Structured data for Claude to use programmatically
91
+ ...context,
92
+ usage_hint: 'Use suggestions.brief for messages, suggestions.detailed for updates.'
93
+ };
94
+ }
95
+
96
+ module.exports = { definition, handler };
package/version.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
- "version": "0.3.0",
3
- "updated": "2026-01-10",
4
- "changelog": "Artifacts system - create guides, learnings, workspaces with vibe_create_artifact",
2
+ "version": "0.3.16",
3
+ "updated": "2026-01-26",
4
+ "changelog": "MCP cleanup: _shared utilities, version sync, deprecated tool archive, ESM->CommonJS",
5
5
  "features": [
6
- "vibe_create_artifact - Create social artifacts from conversations",
7
- "vibe_view_artifact - View and list artifacts",
8
- "Dual-write to KV + Postgres",
9
- "HTML rendering at /a/:slug",
10
- "Structured blocks: places, schedules, checklists, callouts"
6
+ "Smart inbox checking - triggers on commits, tests, builds, natural breaks",
7
+ "Tech stack auto-detection for better connection matching",
8
+ "Enhanced streak system - 5 badge tiers, freeze preservation",
9
+ "Leaderboard 2.1 - category rankings, position tracking, movers",
10
+ "Featured sessions in browse",
11
+ "Gig recommendations based on proven skills",
12
+ "Mobile dashboard API for fast loading"
11
13
  ],
12
14
  "breaking": false,
13
15
  "updateUrl": "https://www.slashvibe.dev/api/version"
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Slash Vibe, Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/store/sqlite.js DELETED
@@ -1,347 +0,0 @@
1
- /**
2
- * SQLite Message Store - Local persistence for v2 messaging
3
- *
4
- * Shares database with Vibe Terminal app at ~/.vibecodings/sessions.db
5
- * Schema matches src-tauri/src/db.rs exactly (LocalMessage struct)
6
- */
7
-
8
- const Database = require('better-sqlite3');
9
- const path = require('path');
10
- const os = require('os');
11
- const fs = require('fs');
12
- const { randomUUID } = require('crypto');
13
-
14
- const DB_PATH = path.join(os.homedir(), '.vibecodings', 'sessions.db');
15
-
16
- class MessageStore {
17
- constructor() {
18
- // Ensure directory exists
19
- const dir = path.dirname(DB_PATH);
20
- if (!fs.existsSync(dir)) {
21
- fs.mkdirSync(dir, { recursive: true });
22
- }
23
-
24
- this.db = new Database(DB_PATH);
25
-
26
- // Enable WAL mode for better concurrency with Tauri app
27
- this.db.pragma('journal_mode = WAL');
28
- this.db.pragma('busy_timeout = 5000');
29
- this.db.pragma('synchronous = NORMAL');
30
-
31
- // Ensure messages table exists (should already exist from Tauri, but just in case)
32
- this.ensureSchema();
33
-
34
- // Prepare statements for performance
35
- this.prepareStatements();
36
- }
37
-
38
- ensureSchema() {
39
- // This matches the Tauri schema + V2 Postgres fields
40
- this.db.exec(`
41
- CREATE TABLE IF NOT EXISTS messages (
42
- local_id TEXT PRIMARY KEY,
43
- server_id TEXT,
44
- thread_id TEXT,
45
- from_handle TEXT NOT NULL,
46
- to_handle TEXT NOT NULL,
47
- content TEXT NOT NULL,
48
- created_at TEXT NOT NULL,
49
- status TEXT NOT NULL CHECK(status IN ('pending', 'sent', 'delivered', 'read', 'failed')),
50
- sent_at TEXT,
51
- delivered_at TEXT,
52
- read_at TEXT,
53
- synced_at TEXT,
54
- retry_count INTEGER DEFAULT 0
55
- );
56
-
57
- CREATE INDEX IF NOT EXISTS idx_messages_thread
58
- ON messages(from_handle, to_handle, created_at);
59
-
60
- CREATE INDEX IF NOT EXISTS idx_messages_thread_id
61
- ON messages(thread_id);
62
-
63
- CREATE INDEX IF NOT EXISTS idx_messages_server_id
64
- ON messages(server_id);
65
-
66
- CREATE INDEX IF NOT EXISTS idx_messages_status
67
- ON messages(status);
68
-
69
- CREATE INDEX IF NOT EXISTS idx_messages_synced
70
- ON messages(synced_at);
71
- `);
72
- }
73
-
74
- prepareStatements() {
75
- this.stmts = {
76
- insert: this.db.prepare(`
77
- INSERT OR REPLACE INTO messages
78
- (local_id, server_id, thread_id, from_handle, to_handle, content, created_at, status,
79
- sent_at, delivered_at, read_at, synced_at, retry_count)
80
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
81
- `),
82
-
83
- getThread: this.db.prepare(`
84
- SELECT local_id, server_id, thread_id, from_handle, to_handle, content, created_at,
85
- status, sent_at, delivered_at, read_at, synced_at, retry_count
86
- FROM messages
87
- WHERE (from_handle = ? AND to_handle = ?)
88
- OR (from_handle = ? AND to_handle = ?)
89
- ORDER BY created_at ASC
90
- LIMIT ?
91
- `),
92
-
93
- updateStatus: this.db.prepare(`
94
- UPDATE messages
95
- SET status = ?, server_id = COALESCE(?, server_id), thread_id = COALESCE(?, thread_id), sent_at = COALESCE(?, sent_at)
96
- WHERE local_id = ?
97
- `),
98
-
99
- getInboxThreads: this.db.prepare(`
100
- WITH thread_partners AS (
101
- SELECT DISTINCT
102
- CASE WHEN from_handle = ? THEN to_handle ELSE from_handle END as partner
103
- FROM messages
104
- WHERE from_handle = ? OR to_handle = ?
105
- ),
106
- latest_messages AS (
107
- SELECT
108
- CASE WHEN from_handle = ? THEN to_handle ELSE from_handle END as partner,
109
- local_id, server_id, from_handle, to_handle, content, created_at,
110
- status, sent_at, delivered_at, read_at, synced_at, retry_count,
111
- ROW_NUMBER() OVER (PARTITION BY CASE WHEN from_handle = ? THEN to_handle ELSE from_handle END
112
- ORDER BY created_at DESC) as rn
113
- FROM messages
114
- WHERE from_handle = ? OR to_handle = ?
115
- ),
116
- unread_counts AS (
117
- SELECT
118
- CASE WHEN to_handle = ? THEN from_handle ELSE to_handle END as partner,
119
- COUNT(*) as unread
120
- FROM messages
121
- WHERE to_handle = ? AND status IN ('sent', 'delivered')
122
- GROUP BY partner
123
- )
124
- SELECT
125
- lm.partner, lm.local_id, lm.server_id, lm.from_handle, lm.to_handle,
126
- lm.content, lm.created_at, lm.status, lm.sent_at, lm.delivered_at,
127
- lm.read_at, lm.synced_at, lm.retry_count,
128
- COALESCE(uc.unread, 0) as unread_count
129
- FROM latest_messages lm
130
- LEFT JOIN unread_counts uc ON lm.partner = uc.partner
131
- WHERE lm.rn = 1
132
- ORDER BY lm.created_at DESC
133
- `),
134
-
135
- markThreadRead: this.db.prepare(`
136
- UPDATE messages
137
- SET status = 'read', read_at = ?
138
- WHERE from_handle = ? AND to_handle = ? AND status IN ('sent', 'delivered')
139
- `),
140
- };
141
- }
142
-
143
- /**
144
- * Save a local message (optimistic - before server confirmation)
145
- */
146
- saveLocalMessage(message) {
147
- const {
148
- local_id = randomUUID(),
149
- server_id = null,
150
- thread_id = null,
151
- from_handle,
152
- to_handle,
153
- content,
154
- created_at = new Date().toISOString(),
155
- status = 'pending',
156
- sent_at = null,
157
- delivered_at = null,
158
- read_at = null,
159
- synced_at = null,
160
- retry_count = 0
161
- } = message;
162
-
163
- this.stmts.insert.run(
164
- local_id,
165
- server_id,
166
- thread_id,
167
- from_handle,
168
- to_handle,
169
- content,
170
- created_at,
171
- status,
172
- sent_at,
173
- delivered_at,
174
- read_at,
175
- synced_at,
176
- retry_count
177
- );
178
-
179
- return local_id;
180
- }
181
-
182
- /**
183
- * Get messages for a thread between two users
184
- */
185
- getThreadMessages(handle1, handle2, limit = 100) {
186
- return this.stmts.getThread.all(handle1, handle2, handle2, handle1, limit).map(row => ({
187
- local_id: row.local_id,
188
- server_id: row.server_id,
189
- thread_id: row.thread_id,
190
- from_handle: row.from_handle,
191
- to_handle: row.to_handle,
192
- content: row.content,
193
- created_at: row.created_at,
194
- status: row.status,
195
- sent_at: row.sent_at,
196
- delivered_at: row.delivered_at,
197
- read_at: row.read_at,
198
- synced_at: row.synced_at,
199
- retry_count: row.retry_count
200
- }));
201
- }
202
-
203
- /**
204
- * Update message status after server response
205
- */
206
- updateMessageStatus(local_id, status, server_id = null, thread_id = null) {
207
- const sent_at = (status === 'sent' || status === 'delivered' || status === 'read')
208
- ? new Date().toISOString()
209
- : null;
210
-
211
- this.stmts.updateStatus.run(status, server_id, thread_id, sent_at, local_id);
212
- }
213
-
214
- /**
215
- * Merge server messages into local cache (V2 Postgres format)
216
- * Uses INSERT OR IGNORE to avoid overwriting local changes
217
- */
218
- mergeServerMessages(messages) {
219
- const insert = this.db.prepare(`
220
- INSERT OR IGNORE INTO messages
221
- (local_id, server_id, thread_id, from_handle, to_handle, content, created_at, status,
222
- sent_at, delivered_at, read_at, synced_at, retry_count)
223
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
224
- `);
225
-
226
- const transaction = this.db.transaction((msgs) => {
227
- for (const msg of msgs) {
228
- insert.run(
229
- msg.local_id || msg.id || randomUUID(), // Handle both formats
230
- msg.server_id || msg.id,
231
- msg.thread_id, // V2 Postgres thread_id
232
- msg.from_handle || msg.from,
233
- msg.to_handle || msg.to,
234
- msg.content || msg.body || msg.text || '',
235
- msg.created_at || msg.createdAt || new Date().toISOString(),
236
- msg.status || 'delivered',
237
- msg.sent_at || msg.sentAt || msg.created_at || msg.createdAt,
238
- msg.delivered_at || msg.deliveredAt,
239
- msg.read_at || msg.readAt,
240
- new Date().toISOString(), // synced_at
241
- 0 // retry_count
242
- );
243
- }
244
- });
245
-
246
- transaction(messages);
247
- return messages.length;
248
- }
249
-
250
- /**
251
- * Get inbox threads for a user
252
- */
253
- getInboxThreads(handle) {
254
- const rows = this.stmts.getInboxThreads.all(
255
- handle, handle, handle, // thread_partners CTE
256
- handle, handle, handle, handle, // latest_messages CTE
257
- handle, handle // unread_counts CTE
258
- );
259
-
260
- return rows.map(row => ({
261
- partner: row.partner,
262
- latestMessage: {
263
- local_id: row.local_id,
264
- server_id: row.server_id,
265
- from_handle: row.from_handle,
266
- to_handle: row.to_handle,
267
- content: row.content,
268
- created_at: row.created_at,
269
- status: row.status,
270
- sent_at: row.sent_at,
271
- delivered_at: row.delivered_at,
272
- read_at: row.read_at,
273
- synced_at: row.synced_at,
274
- retry_count: row.retry_count
275
- },
276
- unreadCount: row.unread_count
277
- }));
278
- }
279
-
280
- /**
281
- * Mark all messages in a thread as read
282
- */
283
- markThreadRead(my_handle, other_handle) {
284
- const now = new Date().toISOString();
285
- const result = this.stmts.markThreadRead.run(now, other_handle, my_handle);
286
- return result.changes;
287
- }
288
-
289
- /**
290
- * Get pending/failed messages for retry
291
- */
292
- getPendingMessages() {
293
- const rows = this.db.prepare(`
294
- SELECT local_id, server_id, from_handle, to_handle, content, created_at,
295
- status, sent_at, delivered_at, read_at, synced_at, retry_count
296
- FROM messages
297
- WHERE status = 'pending' OR status = 'failed'
298
- ORDER BY created_at ASC
299
- `).all();
300
-
301
- return rows.map(row => ({
302
- local_id: row.local_id,
303
- server_id: row.server_id,
304
- from_handle: row.from_handle,
305
- to_handle: row.to_handle,
306
- content: row.content,
307
- created_at: row.created_at,
308
- status: row.status,
309
- sent_at: row.sent_at,
310
- delivered_at: row.delivered_at,
311
- read_at: row.read_at,
312
- synced_at: row.synced_at,
313
- retry_count: row.retry_count
314
- }));
315
- }
316
-
317
- close() {
318
- this.db.close();
319
- }
320
- }
321
-
322
- // Export singleton instance
323
- let instance = null;
324
-
325
- function getInstance() {
326
- if (!instance) {
327
- try {
328
- instance = new MessageStore();
329
- } catch (error) {
330
- console.error('[SQLite] Failed to initialize:', error.message);
331
- // Return stub with no-op methods if SQLite fails
332
- return {
333
- saveLocalMessage: () => randomUUID(),
334
- getThreadMessages: () => [],
335
- updateMessageStatus: () => {},
336
- mergeServerMessages: () => 0,
337
- getInboxThreads: () => [],
338
- markThreadRead: () => 0,
339
- getPendingMessages: () => [],
340
- close: () => {}
341
- };
342
- }
343
- }
344
- return instance;
345
- }
346
-
347
- module.exports = getInstance();
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes