parallelclaw 1.0.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 (62) hide show
  1. package/CHANGELOG.md +204 -0
  2. package/HELP.md +600 -0
  3. package/LICENSE +21 -0
  4. package/MULTI_MACHINE.md +152 -0
  5. package/README.md +417 -0
  6. package/README.ru.md +740 -0
  7. package/SYNC.md +844 -0
  8. package/bot/README.md +173 -0
  9. package/bot/config.js +66 -0
  10. package/bot/inbox.js +153 -0
  11. package/bot/index.js +294 -0
  12. package/bot/nexara.js +61 -0
  13. package/bot/poll.js +304 -0
  14. package/bot/search.js +155 -0
  15. package/bot/telegram.js +96 -0
  16. package/ingest.js +2712 -0
  17. package/lib/cli/index.js +1987 -0
  18. package/lib/config.js +220 -0
  19. package/lib/db-init.js +158 -0
  20. package/lib/hook/install.js +268 -0
  21. package/lib/import-telegram.js +158 -0
  22. package/lib/ingest-file.js +779 -0
  23. package/lib/notify-click-action.js +281 -0
  24. package/lib/openclaw-channel.js +643 -0
  25. package/lib/parse-cursor.js +172 -0
  26. package/lib/parse-obsidian.js +256 -0
  27. package/lib/parse-telegram-html.js +384 -0
  28. package/lib/parse.js +175 -0
  29. package/lib/render-markdown.js +0 -0
  30. package/lib/store-doc/canonicalize.js +116 -0
  31. package/lib/store-doc/detect.js +209 -0
  32. package/lib/store-doc/extract-title.js +162 -0
  33. package/lib/sync/auth.js +80 -0
  34. package/lib/sync/cert.js +144 -0
  35. package/lib/sync/cli.js +906 -0
  36. package/lib/sync/client.js +138 -0
  37. package/lib/sync/config.js +130 -0
  38. package/lib/sync/pair.js +145 -0
  39. package/lib/sync/pull.js +158 -0
  40. package/lib/sync/push.js +305 -0
  41. package/lib/sync/replicate.js +335 -0
  42. package/lib/sync/server.js +224 -0
  43. package/lib/sync/service.js +726 -0
  44. package/lib/tasks.js +215 -0
  45. package/lib/telegram-decisions.js +165 -0
  46. package/lib/telegram-discovery.js +373 -0
  47. package/lib/telegram-notify.js +272 -0
  48. package/lib/telegram-pending.js +200 -0
  49. package/lib/web/index.js +265 -0
  50. package/lib/web/routes/conversation.js +193 -0
  51. package/lib/web/routes/conversations.js +180 -0
  52. package/lib/web/routes/dashboard.js +175 -0
  53. package/lib/web/routes/pending.js +277 -0
  54. package/lib/web/routes/settings.js +226 -0
  55. package/lib/web/static/style.css +393 -0
  56. package/lib/web/templates.js +234 -0
  57. package/package.json +84 -0
  58. package/server.js +3816 -0
  59. package/skills/install-memex/README.md +109 -0
  60. package/skills/install-memex/SKILL.md +342 -0
  61. package/skills/install-memex/examples.md +294 -0
  62. package/skills/install-memex-claw/SKILL.md +423 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Shared Telegram-importer logic — used by both server.js (live, via inbox
3
+ * watcher) and the CLI (`memex telegram import` direct write).
4
+ *
5
+ * Takes a better-sqlite3 Database connection (write mode) and a parsed
6
+ * Telegram object (the shape produced by Telegram Desktop JSON export OR
7
+ * lib/parse-telegram-html.js).
8
+ *
9
+ * Returns:
10
+ * {
11
+ * totalImported: <int>, // messages inserted across all chats
12
+ * chats: [{
13
+ * conversation_id, title, msg_count, first_ts, last_ts
14
+ * }, ...]
15
+ * }
16
+ *
17
+ * UNIQUE(source, conversation_id, msg_id) handles dedupe — re-importing the
18
+ * same chat (e.g. a fresh export with newer messages) only adds the delta.
19
+ */
20
+
21
+ import { readFileSync } from 'node:fs';
22
+ import { getOrigin } from './config.js';
23
+
24
+ export function importTelegramRaw(db, raw) {
25
+ if (typeof raw === 'string') {
26
+ raw = JSON.parse(readFileSync(raw, 'utf-8'));
27
+ }
28
+
29
+ const insertMessage = db.prepare(`
30
+ INSERT INTO messages (source, conversation_id, msg_id, role, sender, text, ts, metadata, edited_at, uuid, origin)
31
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '${getOrigin()}')
32
+ ON CONFLICT(source, conversation_id, msg_id) DO UPDATE SET
33
+ text = CASE
34
+ WHEN excluded.edited_at IS NOT NULL
35
+ AND (messages.edited_at IS NULL OR excluded.edited_at > messages.edited_at)
36
+ THEN excluded.text ELSE messages.text END,
37
+ edited_at = CASE
38
+ WHEN excluded.edited_at IS NOT NULL
39
+ AND (messages.edited_at IS NULL OR excluded.edited_at > messages.edited_at)
40
+ THEN excluded.edited_at ELSE messages.edited_at END,
41
+ uuid = COALESCE(messages.uuid, excluded.uuid),
42
+ origin = COALESCE(messages.origin, excluded.origin)
43
+ `);
44
+
45
+ const upsertConversation = db.prepare(`
46
+ INSERT INTO conversations (conversation_id, source, title, first_ts, last_ts, message_count, parent_conversation_id, project_path)
47
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
48
+ ON CONFLICT(conversation_id) DO UPDATE SET
49
+ title = excluded.title,
50
+ first_ts = MIN(first_ts, excluded.first_ts),
51
+ last_ts = MAX(last_ts, excluded.last_ts),
52
+ parent_conversation_id = COALESCE(excluded.parent_conversation_id, parent_conversation_id),
53
+ project_path = COALESCE(excluded.project_path, project_path),
54
+ message_count = (
55
+ SELECT COUNT(*) FROM messages
56
+ WHERE messages.conversation_id = conversations.conversation_id
57
+ )
58
+ `);
59
+
60
+ const chats = Array.isArray(raw.chats?.list)
61
+ ? raw.chats.list
62
+ : Array.isArray(raw.list)
63
+ ? raw.list
64
+ : raw.messages
65
+ ? [raw]
66
+ : [];
67
+
68
+ const myUserId = String(raw?.personal_information?.user_id || raw?.user_id || '');
69
+ let totalImported = 0;
70
+ const chatSummaries = [];
71
+
72
+ const tx = db.transaction((chatList) => {
73
+ for (const chat of chatList) {
74
+ if (!Array.isArray(chat.messages)) continue;
75
+
76
+ const conversationId = `tg-${chat.id ?? chat.name ?? 'unknown'}`;
77
+ const title =
78
+ chat.name ||
79
+ (chat.type === 'saved_messages' ? 'Saved Messages' : `Telegram chat ${chat.id}`);
80
+
81
+ let first_ts = Infinity;
82
+ let last_ts = 0;
83
+ let chatMsgs = 0;
84
+
85
+ for (const msg of chat.messages) {
86
+ if (msg.type !== 'message') continue;
87
+
88
+ let text = '';
89
+ if (typeof msg.text === 'string') {
90
+ text = msg.text;
91
+ } else if (Array.isArray(msg.text)) {
92
+ text = msg.text
93
+ .map((f) => (typeof f === 'string' ? f : f.text || ''))
94
+ .join('');
95
+ }
96
+ if (!text || !text.trim()) continue;
97
+
98
+ const ts = parseInt(msg.date_unixtime || '0', 10);
99
+ if (ts) {
100
+ first_ts = Math.min(first_ts, ts);
101
+ last_ts = Math.max(last_ts, ts);
102
+ }
103
+
104
+ const editedAt = msg.edited_unixtime
105
+ ? parseInt(msg.edited_unixtime, 10) || null
106
+ : null;
107
+
108
+ const fromId = String(msg.from_id || '');
109
+ const isMe =
110
+ (myUserId && fromId === `user${myUserId}`) ||
111
+ (myUserId && fromId === myUserId);
112
+ const role = isMe ? 'user' : 'assistant';
113
+
114
+ insertMessage.run(
115
+ 'telegram',
116
+ conversationId,
117
+ String(msg.id),
118
+ role,
119
+ msg.from || (isMe ? 'me' : 'bot'),
120
+ text,
121
+ ts,
122
+ JSON.stringify({
123
+ chat_name: chat.name,
124
+ chat_type: chat.type,
125
+ reply_to: msg.reply_to_message_id || null,
126
+ }),
127
+ editedAt,
128
+ null
129
+ );
130
+ chatMsgs += 1;
131
+ }
132
+
133
+ if (chatMsgs > 0) {
134
+ upsertConversation.run(
135
+ conversationId,
136
+ 'telegram',
137
+ title,
138
+ isFinite(first_ts) ? first_ts : null,
139
+ last_ts || null,
140
+ chatMsgs,
141
+ null,
142
+ null
143
+ );
144
+ totalImported += chatMsgs;
145
+ chatSummaries.push({
146
+ conversation_id: conversationId,
147
+ title,
148
+ msg_count: chatMsgs,
149
+ first_ts: isFinite(first_ts) ? first_ts : null,
150
+ last_ts: last_ts || null,
151
+ });
152
+ }
153
+ }
154
+ });
155
+
156
+ tx(chats);
157
+ return { totalImported, chats: chatSummaries };
158
+ }