memhouse 0.4.4 → 0.7.0

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 (63) hide show
  1. package/README.md +244 -114
  2. package/bin/memhouse.js +1162 -230
  3. package/editors/base.js +6 -0
  4. package/editors/claude.js +44 -9
  5. package/editors/goose.js +4 -0
  6. package/editors/index.js +0 -0
  7. package/editors/opencode.js +3 -0
  8. package/{mem-house → memhouse}/COMPETITION.md +5 -5
  9. package/{mem-house → memhouse}/DESIGN.md +18 -12
  10. package/memhouse/RESUME-AND-UPDATE.md +185 -0
  11. package/{mem-house → memhouse}/delivery/AGENT-INSTALL-PROMPT.md +1 -1
  12. package/{mem-house → memhouse}/delivery/AGENT-INSTALL.md +9 -7
  13. package/{mem-house → memhouse}/delivery/PROMPT.md +8 -8
  14. package/{mem-house → memhouse}/delivery/kernel-install.md +3 -3
  15. package/{mem-house → memhouse}/delivery/plugin/.claude-plugin/plugin.json +1 -1
  16. package/{mem-house/delivery/skills/memhouse-search → memhouse/delivery/plugin/skills/search}/SKILL.md +60 -18
  17. package/memhouse/delivery/plugin/skills/sessions/SKILL.md +177 -0
  18. package/memhouse/delivery/plugin/skills/sql/SKILL.md +206 -0
  19. package/memhouse/host.js +101 -0
  20. package/{mem-house → memhouse}/per-member/INSTALL.md +59 -14
  21. package/{mem-house → memhouse}/per-member/SCHEMA.md +47 -0
  22. package/memhouse/per-member/provision.js +265 -0
  23. package/memhouse/per-member/rooms.js +235 -0
  24. package/{mem-house → memhouse}/per-member/schema-member.sql.tpl +18 -14
  25. package/{mem-house → memhouse}/per-member/schema-merge.sql.tpl +3 -3
  26. package/memhouse/resume.js +150 -0
  27. package/memhouse/self-update.js +150 -0
  28. package/{mem-house → memhouse}/server/queries.js +80 -34
  29. package/memhouse/server/server.js +451 -0
  30. package/{mem-house → memhouse}/service.js +48 -29
  31. package/memhouse/shipper/ship.js +949 -0
  32. package/package.json +5 -20
  33. package/public/assets/{index-CtH-5i95.js → index-CnWzrjF3.js} +10 -7
  34. package/public/index.html +2 -2
  35. package/ui/index.html +1 -1
  36. package/ui/src/App.jsx +1 -1
  37. package/ui/src/lib/api.js +29 -13
  38. package/ui/src/pages/Dashboard.jsx +8 -5
  39. package/ui/src/pages/Sessions.jsx +3 -1
  40. package/ui/src/pages/SqlViewer.jsx +6 -23
  41. package/cache.js +0 -850
  42. package/ch-ingest.js +0 -329
  43. package/ch-schema.js +0 -152
  44. package/clickhouse.js +0 -87
  45. package/index.js +0 -425
  46. package/mcp-server.js +0 -294
  47. package/mem-house/delivery/install.sh +0 -183
  48. package/mem-house/delivery/skills/memhouse-sessions/SKILL.md +0 -125
  49. package/mem-house/delivery/skills/memhouse-sql/SKILL.md +0 -146
  50. package/mem-house/per-member/provision.js +0 -142
  51. package/mem-house/per-member/rooms.js +0 -132
  52. package/mem-house/server/server.js +0 -228
  53. package/mem-house/shipper/ship.js +0 -566
  54. package/relay-client.js +0 -306
  55. package/relay-server.js +0 -577
  56. package/server.js +0 -860
  57. package/share-image.js +0 -367
  58. /package/{mem-house → memhouse}/deploy.js +0 -0
  59. /package/{mem-house → memhouse}/envfile.js +0 -0
  60. /package/{mem-house → memhouse}/per-member/PLAN.md +0 -0
  61. /package/{mem-house → memhouse}/per-member/PROVISIONING.md +0 -0
  62. /package/{mem-house → memhouse}/per-member/README.md +0 -0
  63. /package/{mem-house → memhouse}/per-member/SHARING.md +0 -0
package/relay-client.js DELETED
@@ -1,306 +0,0 @@
1
- const chalk = require('chalk');
2
- const http = require('http');
3
- const https = require('https');
4
- const readline = require('readline');
5
- const crypto = require('crypto');
6
-
7
- const cache = require('./cache');
8
-
9
- const SYNC_INTERVAL_MS = 30000; // 30 seconds
10
-
11
- const EDITOR_LABELS = {
12
- 'cursor': 'Cursor',
13
- 'devin': 'Devin',
14
- 'devin-next': 'Devin Next',
15
- 'antigravity': 'Antigravity',
16
- 'claude-code': 'Claude Code',
17
- 'claude': 'Claude Code',
18
- 'vscode': 'VS Code',
19
- 'vscode-insiders': 'VS Code Insiders',
20
- 'zed': 'Zed',
21
- 'opencode': 'OpenCode',
22
- 'codex': 'Codex CLI',
23
- 'gemini-cli': 'Gemini CLI',
24
- 'copilot-cli': 'GitHub Copilot',
25
- 'copilot-jetbrains': 'GitHub Copilot - JetBrains',
26
- 'cursor-agent': 'Cursor (Background Agent)',
27
- 'commandcode': 'CommandCode',
28
- };
29
-
30
- /**
31
- * Interactive project picker using readline (no external deps beyond Node built-ins).
32
- * Returns an array of selected folder paths.
33
- */
34
- async function pickProjects() {
35
- cache.initDb();
36
-
37
- // Scan to populate cache
38
- console.log(chalk.dim(' Scanning local sessions...'));
39
- cache.scanAll(() => {});
40
-
41
- const db = cache.getDb();
42
- const projects = db.prepare(`
43
- SELECT folder, COUNT(*) as count
44
- FROM chats WHERE folder IS NOT NULL
45
- GROUP BY folder ORDER BY count DESC
46
- `).all();
47
-
48
- if (projects.length === 0) {
49
- console.log(chalk.yellow(' No projects found in local cache.'));
50
- process.exit(1);
51
- }
52
-
53
- const cwd = process.cwd();
54
- const cwdMatch = projects.find(p => p.folder === cwd);
55
-
56
- // If cwd is a known project, offer quick share
57
- if (cwdMatch) {
58
- const name = cwdMatch.folder.split('/').pop();
59
- const editors = db.prepare(`
60
- SELECT source, COUNT(*) as count FROM chats
61
- WHERE folder = ? AND source IS NOT NULL
62
- GROUP BY source ORDER BY count DESC
63
- `).all(cwdMatch.folder);
64
- console.log('');
65
- console.log(chalk.cyan(` ${name}`) + chalk.dim(` — ${cwdMatch.count} sessions`));
66
- for (const e of editors) {
67
- console.log(chalk.yellow(` • ${EDITOR_LABELS[e.source] || e.source}`) + chalk.dim(` (${e.count} sessions)`));
68
- }
69
- console.log('');
70
-
71
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
72
- const answer = await new Promise(r => {
73
- rl.question(chalk.bold(' Share your sessions with your team? ') + chalk.dim('(Y/n) '), r);
74
- });
75
- rl.close();
76
-
77
- const trimmed = answer.trim().toLowerCase();
78
- if (trimmed === '' || trimmed === 'y' || trimmed === 'yes') {
79
- return [cwdMatch.folder];
80
- }
81
- // Fall through to full picker
82
- }
83
-
84
- console.log('');
85
- console.log(chalk.bold(' Select projects to share (comma-separated numbers, or "all"):'));
86
- console.log('');
87
- projects.forEach((p, i) => {
88
- const name = p.folder.split('/').pop();
89
- console.log(chalk.cyan(` ${i + 1}.`) + ` ${name} ${chalk.dim(`(${p.count} sessions) — ${p.folder}`)}`);
90
- });
91
- console.log('');
92
-
93
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
94
-
95
- return new Promise((resolve) => {
96
- rl2.question(chalk.bold(' > '), (answer) => {
97
- rl2.close();
98
- const trimmed = answer.trim().toLowerCase();
99
- if (trimmed === 'all' || trimmed === '*') {
100
- resolve(projects.map(p => p.folder));
101
- return;
102
- }
103
- const indices = trimmed.split(/[,\s]+/).map(s => parseInt(s.trim()) - 1).filter(i => i >= 0 && i < projects.length);
104
- if (indices.length === 0) {
105
- console.log(chalk.red(' No valid selection. Exiting.'));
106
- process.exit(1);
107
- }
108
- resolve(indices.map(i => projects[i].folder));
109
- });
110
- });
111
- }
112
-
113
- /**
114
- * Collect data for selected projects from local cache DB.
115
- */
116
- function collectProjectData(selectedFolders) {
117
- const db = cache.getDb();
118
- if (!db) return { chats: [], messages: [], stats: [] };
119
-
120
- const allChats = [];
121
- const allMessages = [];
122
- const allStats = [];
123
-
124
- for (const folder of selectedFolders) {
125
- // Get chats for this project
126
- const chats = db.prepare(`
127
- SELECT id, source, name, mode, folder, created_at, last_updated_at, bubble_count
128
- FROM chats WHERE folder = ?
129
- `).all(folder);
130
-
131
- for (const chat of chats) {
132
- allChats.push({
133
- id: chat.id,
134
- source: chat.source,
135
- name: chat.name,
136
- mode: chat.mode,
137
- folder: chat.folder,
138
- created_at: chat.created_at,
139
- last_updated_at: chat.last_updated_at,
140
- bubble_count: chat.bubble_count,
141
- });
142
-
143
- // Get messages
144
- const messages = db.prepare(
145
- 'SELECT chat_id, seq, role, content, model FROM messages WHERE chat_id = ? ORDER BY seq'
146
- ).all(chat.id);
147
- for (const m of messages) {
148
- allMessages.push({
149
- chat_id: m.chat_id,
150
- seq: m.seq,
151
- role: m.role,
152
- content: m.content,
153
- model: m.model,
154
- });
155
- }
156
-
157
- // Get stats
158
- const stat = db.prepare(
159
- 'SELECT * FROM chat_stats WHERE chat_id = ?'
160
- ).get(chat.id);
161
- if (stat) {
162
- allStats.push({
163
- chat_id: stat.chat_id,
164
- total_messages: stat.total_messages,
165
- user_messages: stat.user_messages,
166
- assistant_messages: stat.assistant_messages,
167
- tool_calls: JSON.parse(stat.tool_calls || '[]'),
168
- models: JSON.parse(stat.models || '[]'),
169
- total_input_tokens: stat.total_input_tokens,
170
- total_output_tokens: stat.total_output_tokens,
171
- });
172
- }
173
- }
174
- }
175
-
176
- return { chats: allChats, messages: allMessages, stats: allStats };
177
- }
178
-
179
- /**
180
- * POST data to relay server.
181
- */
182
- function postToRelay(scheme, host, port, username, data, authToken) {
183
- return new Promise((resolve, reject) => {
184
- const payload = JSON.stringify({
185
- username,
186
- projects: data.projects,
187
- chats: data.chats,
188
- messages: data.messages,
189
- stats: data.stats,
190
- });
191
-
192
- const headers = {
193
- 'Content-Type': 'application/json',
194
- 'Content-Length': Buffer.byteLength(payload),
195
- };
196
- if (authToken) headers['Authorization'] = `Bearer ${authToken}`;
197
-
198
- const options = {
199
- hostname: host,
200
- port: port,
201
- path: '/relay/sync',
202
- method: 'POST',
203
- headers,
204
- };
205
- const client = scheme === 'https' ? https : http
206
- const req = client.request(options, (res) => {
207
- let body = '';
208
- res.on('data', (chunk) => { body += chunk; });
209
- res.on('end', () => {
210
- try {
211
- resolve(JSON.parse(body));
212
- } catch {
213
- resolve({ raw: body });
214
- }
215
- });
216
- });
217
-
218
- req.on('error', reject);
219
- req.setTimeout(15000, () => {
220
- req.destroy(new Error('Request timed out'));
221
- });
222
- req.write(payload);
223
- req.end();
224
- });
225
- }
226
-
227
- /**
228
- * Main join client entry point.
229
- */
230
- async function startJoinClient(relayAddress, username) {
231
- console.log('');
232
- console.log(chalk.bold(' ⚡ Agentlytics Relay — Join'));
233
- console.log(chalk.dim(` Connecting to relay at ${relayAddress}`));
234
- console.log(chalk.dim(` Username: ${username}`));
235
- console.log('');
236
-
237
- // Parse host:port
238
- const parts = relayAddress.replace(/^https?:\/\//, '').split(':');
239
- const scheme = relayAddress.startsWith("https://") ? "https" : 'http'
240
- const host = parts[0] || 'localhost';
241
- const port = parseInt(parts[1]) || 4638;
242
-
243
- // Auth token from RELAY_PASSWORD env
244
- const relayPassword = process.env.RELAY_PASSWORD || null;
245
- const authToken = relayPassword
246
- ? crypto.createHmac('sha256', 'agentlytics-relay').update(relayPassword).digest('hex')
247
- : null;
248
-
249
- // Test connection
250
- try {
251
- const testResult = await postToRelay(scheme, host, port, username, { projects: [], chats: [], messages: [], stats: [] }, authToken);
252
- if (!testResult.ok) {
253
- const msg = testResult.error || 'unknown error';
254
- if (msg === 'Unauthorized') {
255
- console.log(chalk.red(' ✗ Relay requires a password. Set RELAY_PASSWORD env variable.'));
256
- } else {
257
- console.log(chalk.red(` ✗ Failed to connect: ${msg}`));
258
- }
259
- process.exit(1);
260
- }
261
- console.log(chalk.green(' ✓ Connected to relay'));
262
- } catch (err) {
263
- console.log(chalk.red(` ✗ Cannot reach relay at ${host}:${port}`));
264
- console.log(chalk.dim(` ${err.message}`));
265
- process.exit(1);
266
- }
267
-
268
- // Pick projects
269
- const selectedFolders = await pickProjects();
270
- console.log('');
271
- console.log(chalk.green(` ✓ Sharing ${selectedFolders.length} project(s):`));
272
- for (const f of selectedFolders) {
273
- console.log(chalk.dim(` • ${f.split('/').pop()} — ${f}`));
274
- }
275
- console.log('');
276
-
277
- // Initial sync
278
- async function sync() {
279
- try {
280
- // Rescan editors to pick up new/updated sessions (reset caches so fresh LS data is obtained)
281
- cache.scanAll(() => {}, { resetCaches: true });
282
- const data = collectProjectData(selectedFolders);
283
- data.projects = selectedFolders;
284
- const result = await postToRelay(scheme, host, port, username, data, authToken);
285
- if (result.ok) {
286
- const s = result.synced || {};
287
- process.stdout.write(chalk.dim(`\r ⟳ Synced: ${s.chats || 0} chats, ${s.messages || 0} messages — ${new Date().toLocaleTimeString()} `));
288
- } else {
289
- process.stdout.write(chalk.yellow(`\r ⚠ Sync issue: ${result.error || 'unknown'} `));
290
- }
291
- } catch (err) {
292
- process.stdout.write(chalk.red(`\r ✗ Sync failed: ${err.message} `));
293
- }
294
- }
295
-
296
- console.log(chalk.cyan(` ⟳ Syncing every ${SYNC_INTERVAL_MS / 1000}s (Ctrl+C to stop)`));
297
- console.log('');
298
-
299
- // Do first sync immediately
300
- await sync();
301
-
302
- // Then sync periodically
303
- setInterval(sync, SYNC_INTERVAL_MS);
304
- }
305
-
306
- module.exports = { startJoinClient };