@gloablehive/celphone-wechat-plugin 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 (45) hide show
  1. package/INSTALL.md +231 -0
  2. package/README.md +259 -0
  3. package/dist/index-simple.js +9 -0
  4. package/dist/index.d.ts +16 -0
  5. package/dist/index.js +77 -0
  6. package/dist/mock-server.d.ts +6 -0
  7. package/dist/mock-server.js +203 -0
  8. package/dist/openclaw.plugin.json +96 -0
  9. package/dist/setup-entry.d.ts +9 -0
  10. package/dist/setup-entry.js +8 -0
  11. package/dist/src/cache/compactor.d.ts +36 -0
  12. package/dist/src/cache/compactor.js +154 -0
  13. package/dist/src/cache/extractor.d.ts +48 -0
  14. package/dist/src/cache/extractor.js +120 -0
  15. package/dist/src/cache/index.d.ts +15 -0
  16. package/dist/src/cache/index.js +16 -0
  17. package/dist/src/cache/indexer.d.ts +41 -0
  18. package/dist/src/cache/indexer.js +262 -0
  19. package/dist/src/cache/manager.d.ts +113 -0
  20. package/dist/src/cache/manager.js +271 -0
  21. package/dist/src/cache/message-queue.d.ts +59 -0
  22. package/dist/src/cache/message-queue.js +147 -0
  23. package/dist/src/cache/saas-connector.d.ts +94 -0
  24. package/dist/src/cache/saas-connector.js +289 -0
  25. package/dist/src/cache/syncer.d.ts +60 -0
  26. package/dist/src/cache/syncer.js +177 -0
  27. package/dist/src/cache/types.d.ts +198 -0
  28. package/dist/src/cache/types.js +43 -0
  29. package/dist/src/cache/writer.d.ts +81 -0
  30. package/dist/src/cache/writer.js +461 -0
  31. package/dist/src/channel.d.ts +65 -0
  32. package/dist/src/channel.js +334 -0
  33. package/dist/src/client.d.ts +280 -0
  34. package/dist/src/client.js +248 -0
  35. package/index-simple.ts +11 -0
  36. package/index.ts +89 -0
  37. package/mock-server.ts +237 -0
  38. package/openclaw.plugin.json +98 -0
  39. package/package.json +37 -0
  40. package/setup-entry.ts +10 -0
  41. package/src/channel.ts +398 -0
  42. package/src/client.ts +412 -0
  43. package/test-cache.ts +260 -0
  44. package/test-integration.ts +319 -0
  45. package/tsconfig.json +22 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Cache Module Exports
3
+ *
4
+ * All cache-related modules for WeChat Channel Plugin
5
+ */
6
+ export * from './types.js';
7
+ export * from './writer.js';
8
+ export * from './indexer.js';
9
+ export * from './extractor.js';
10
+ export * from './compactor.js';
11
+ export * from './syncer.js';
12
+ export * from './saas-connector.js';
13
+ export * from './message-queue.js';
14
+ export * from './manager.js';
15
+ // Main export
16
+ export { createCacheManager, CacheManager } from './manager.js';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Cache Indexer - MEMORY.md index management
3
+ *
4
+ * Aligned with Claude Code:
5
+ * - MEMORY.md is index, not memory itself
6
+ * - Max 200 lines, 25KB
7
+ * - Multi-level: global → account → session
8
+ */
9
+ import { IndexEntry, WeChatAccount } from './types.js';
10
+ /**
11
+ * Build global index (across all accounts)
12
+ */
13
+ export declare function buildGlobalIndex(basePath: string, accounts: WeChatAccount[]): Promise<string>;
14
+ /**
15
+ * Write global index
16
+ */
17
+ export declare function writeGlobalIndex(basePath: string, content: string): Promise<void>;
18
+ /**
19
+ * Build account-level index
20
+ */
21
+ export declare function buildAccountIndex(basePath: string, account: WeChatAccount, friends: IndexEntry[], chatrooms: IndexEntry[], profiles: IndexEntry[]): Promise<string>;
22
+ /**
23
+ * Write account index
24
+ */
25
+ export declare function writeAccountIndex(basePath: string, accountId: string, content: string): Promise<void>;
26
+ /**
27
+ * Scan account directory for entries
28
+ */
29
+ export declare function scanAccountEntries(basePath: string, accountId: string): Promise<{
30
+ friends: IndexEntry[];
31
+ chatrooms: IndexEntry[];
32
+ profiles: IndexEntry[];
33
+ }>;
34
+ /**
35
+ * Rebuild all indexes for an account
36
+ */
37
+ export declare function rebuildAccountIndex(basePath: string, account: WeChatAccount): Promise<void>;
38
+ /**
39
+ * Rebuild all indexes for all accounts
40
+ */
41
+ export declare function rebuildAllIndexes(basePath: string, accounts: WeChatAccount[]): Promise<void>;
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Cache Indexer - MEMORY.md index management
3
+ *
4
+ * Aligned with Claude Code:
5
+ * - MEMORY.md is index, not memory itself
6
+ * - Max 200 lines, 25KB
7
+ * - Multi-level: global → account → session
8
+ */
9
+ import * as fs from 'fs/promises';
10
+ import * as path from 'path';
11
+ import { getAccountCachePath, } from './types.js';
12
+ // ========== Constants ==========
13
+ const MAX_INDEX_LINES = 200;
14
+ const MAX_INDEX_BYTES = 25000;
15
+ const ENCODING = 'utf-8';
16
+ // ========== Global Index ==========
17
+ /**
18
+ * Build global index (across all accounts)
19
+ */
20
+ export async function buildGlobalIndex(basePath, accounts) {
21
+ const lines = [
22
+ '# WeChat Cache Index - All Accounts',
23
+ '',
24
+ '## 账号',
25
+ ];
26
+ for (const account of accounts) {
27
+ const accountIndexPath = path.join(getAccountCachePath(basePath, account.accountId), 'MEMORY.md');
28
+ lines.push(`- [${account.nickName}](accounts/${account.accountId}/MEMORY.md) - ${account.wechatId}`);
29
+ }
30
+ lines.push('');
31
+ lines.push('---');
32
+ lines.push('');
33
+ lines.push('*Last updated: ' + new Date().toISOString() + '*');
34
+ const content = lines.join('\n');
35
+ return truncateIfNeeded(content);
36
+ }
37
+ /**
38
+ * Write global index
39
+ */
40
+ export async function writeGlobalIndex(basePath, content) {
41
+ const indexPath = path.join(basePath, 'MEMORY.md');
42
+ await fs.writeFile(indexPath, content, ENCODING);
43
+ }
44
+ // ========== Account Index ==========
45
+ /**
46
+ * Build account-level index
47
+ */
48
+ export async function buildAccountIndex(basePath, account, friends, chatrooms, profiles) {
49
+ const lines = [
50
+ `# WeChat Cache Index - ${account.nickName}`,
51
+ '',
52
+ account.enabled ? '✅ 在线' : '❌ 离线',
53
+ '',
54
+ ];
55
+ // Recent conversations
56
+ lines.push('## 最近对话');
57
+ const recentFriends = friends.slice(0, 5);
58
+ const recentChatrooms = chatrooms.slice(0, 5);
59
+ for (const friend of recentFriends) {
60
+ lines.push(`- [${friend.name}](friends/${friend.path}) — ${friend.messageCount}条消息`);
61
+ }
62
+ for (const chatroom of recentChatrooms) {
63
+ lines.push(`- [${chatroom.name}](chatrooms/${chatroom.path}) — ${chatroom.messageCount}条消息`);
64
+ }
65
+ lines.push('');
66
+ // User profiles
67
+ lines.push('## 用户画像');
68
+ for (const profile of profiles.slice(0, 10)) {
69
+ const tags = profile.description.split(', ').slice(0, 3).join(', ');
70
+ lines.push(`- [${profile.name}](profiles/${profile.path}) — ${tags}`);
71
+ }
72
+ lines.push('');
73
+ lines.push('---');
74
+ lines.push('');
75
+ // All friends
76
+ lines.push(`## 全部好友 (${friends.length})`);
77
+ for (const friend of friends.slice(0, 50)) {
78
+ lines.push(`- [${friend.name}](friends/${friend.path})`);
79
+ }
80
+ if (friends.length > 50) {
81
+ lines.push(`- ... 还有 ${friends.length - 50} 个`);
82
+ }
83
+ lines.push('');
84
+ // All chatrooms
85
+ lines.push(`## 全部群聊 (${chatrooms.length})`);
86
+ for (const chatroom of chatrooms.slice(0, 50)) {
87
+ lines.push(`- [${chatroom.name}](chatrooms/${chatroom.path})`);
88
+ }
89
+ if (chatrooms.length > 50) {
90
+ lines.push(`- ... 还有 ${chatrooms.length - 50} 个`);
91
+ }
92
+ lines.push('');
93
+ lines.push('---');
94
+ lines.push('');
95
+ lines.push(`*最后更新: ${new Date().toISOString()}*`);
96
+ const content = lines.join('\n');
97
+ return truncateIfNeeded(content);
98
+ }
99
+ /**
100
+ * Write account index
101
+ */
102
+ export async function writeAccountIndex(basePath, accountId, content) {
103
+ const indexPath = path.join(getAccountCachePath(basePath, accountId), 'MEMORY.md');
104
+ await fs.mkdir(path.dirname(indexPath), { recursive: true });
105
+ await fs.writeFile(indexPath, content, ENCODING);
106
+ }
107
+ // ========== Scan Functions ==========
108
+ /**
109
+ * Scan account directory for entries
110
+ */
111
+ export async function scanAccountEntries(basePath, accountId) {
112
+ const accountPath = getAccountCachePath(basePath, accountId);
113
+ const friends = await scanDirectory(path.join(accountPath, 'friends'), 'friend');
114
+ const chatrooms = await scanDirectory(path.join(accountPath, 'chatrooms'), 'chatroom');
115
+ const profiles = await scanDirectory(path.join(accountPath, 'profiles'), 'profile');
116
+ return { friends, chatrooms, profiles };
117
+ }
118
+ /**
119
+ * Scan directory for entries
120
+ */
121
+ async function scanDirectory(dirPath, type) {
122
+ const entries = [];
123
+ try {
124
+ const items = await fs.readdir(dirPath, { withFileTypes: true });
125
+ for (const item of items) {
126
+ if (!item.isDirectory() && type !== 'profile')
127
+ continue;
128
+ if (type === 'profile' && !item.name.endsWith('.md'))
129
+ continue;
130
+ const itemPath = type === 'profile'
131
+ ? item.name
132
+ : item.name;
133
+ // Try to read frontmatter
134
+ const fullPath = path.join(dirPath, item.name, 'memory');
135
+ try {
136
+ const files = await fs.readdir(fullPath);
137
+ const latestFile = files
138
+ .filter(f => f.endsWith('.md'))
139
+ .sort()
140
+ .pop();
141
+ if (latestFile) {
142
+ const content = await fs.readFile(path.join(fullPath, latestFile), ENCODING);
143
+ const fm = parseFrontmatter(content);
144
+ entries.push({
145
+ path: itemPath,
146
+ name: fm?.name || item.name,
147
+ description: fm?.description || '',
148
+ type: 'conversation',
149
+ lastContact: fm?.lastContact,
150
+ messageCount: fm?.messageCount || 0,
151
+ });
152
+ }
153
+ }
154
+ catch {
155
+ // If no memory dir, try profile file
156
+ if (type === 'profile') {
157
+ try {
158
+ const content = await fs.readFile(path.join(dirPath, item.name), ENCODING);
159
+ const fm = parseFrontmatter(content);
160
+ entries.push({
161
+ path: item.name,
162
+ name: fm?.name || item.name.replace('.md', ''),
163
+ description: fm?.description || '',
164
+ type: 'user',
165
+ lastContact: fm?.lastContact,
166
+ });
167
+ }
168
+ catch {
169
+ // Skip
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ catch {
176
+ // Directory doesn't exist
177
+ }
178
+ // Sort by lastContact
179
+ entries.sort((a, b) => {
180
+ if (!a.lastContact)
181
+ return 1;
182
+ if (!b.lastContact)
183
+ return -1;
184
+ return new Date(b.lastContact).getTime() - new Date(a.lastContact).getTime();
185
+ });
186
+ return entries;
187
+ }
188
+ /**
189
+ * Parse frontmatter from markdown content
190
+ */
191
+ function parseFrontmatter(content) {
192
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
193
+ if (!match)
194
+ return null;
195
+ const fm = {};
196
+ const lines = match[1].split('\n');
197
+ for (const line of lines) {
198
+ const colonIdx = line.indexOf(':');
199
+ if (colonIdx === -1)
200
+ continue;
201
+ const key = line.slice(0, colonIdx).trim();
202
+ let value = line.slice(colonIdx + 1).trim();
203
+ // Parse value type
204
+ if (value === 'true')
205
+ value = true;
206
+ else if (value === 'false')
207
+ value = false;
208
+ else if (/^\d+$/.test(value))
209
+ value = parseInt(value, 10);
210
+ else if (value.startsWith('"'))
211
+ value = value.replace(/"/g, '');
212
+ fm[key] = value;
213
+ }
214
+ return fm;
215
+ }
216
+ // ========== Utility Functions ==========
217
+ /**
218
+ * Truncate content if exceeds limits (aligned with Claude Code)
219
+ */
220
+ function truncateIfNeeded(content) {
221
+ const lines = content.split('\n');
222
+ if (lines.length > MAX_INDEX_LINES) {
223
+ const truncated = lines.slice(0, MAX_INDEX_LINES);
224
+ truncated.push('');
225
+ truncated.push('---');
226
+ truncated.push('');
227
+ truncated.push('*Index truncated. Showing first 200 entries.*');
228
+ return truncated.join('\n');
229
+ }
230
+ if (content.length > MAX_INDEX_BYTES) {
231
+ let truncated = '';
232
+ for (const line of lines) {
233
+ if ((truncated + line + '\n').length > MAX_INDEX_BYTES) {
234
+ break;
235
+ }
236
+ truncated += line + '\n';
237
+ }
238
+ truncated += '\n---\n\n*Index truncated. Showing first 25KB.*';
239
+ return truncated;
240
+ }
241
+ return content;
242
+ }
243
+ /**
244
+ * Rebuild all indexes for an account
245
+ */
246
+ export async function rebuildAccountIndex(basePath, account) {
247
+ const { friends, chatrooms, profiles } = await scanAccountEntries(basePath, account.accountId);
248
+ const content = await buildAccountIndex(basePath, account, friends, chatrooms, profiles);
249
+ await writeAccountIndex(basePath, account.accountId, content);
250
+ }
251
+ /**
252
+ * Rebuild all indexes for all accounts
253
+ */
254
+ export async function rebuildAllIndexes(basePath, accounts) {
255
+ // Rebuild each account index
256
+ for (const account of accounts) {
257
+ await rebuildAccountIndex(basePath, account);
258
+ }
259
+ // Rebuild global index
260
+ const globalContent = await buildGlobalIndex(basePath, accounts);
261
+ await writeGlobalIndex(basePath, globalContent);
262
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Cache Manager - Main cache management
3
+ *
4
+ * Ties together all cache modules:
5
+ * - Writer (file operations)
6
+ * - Indexer (MEMORY.md)
7
+ * - Extractor (AI summary)
8
+ * - Compactor (4-layer compression)
9
+ * - Syncer (cloud sync)
10
+ * - SAAS Connector (offline fallback)
11
+ * - Message Queue (offline retry)
12
+ */
13
+ import type { WeChatAccount, WeChatMessage, UserProfile, SyncConfig, SAASConfig, ExtractorConfig, CompactConfig } from './types.js';
14
+ import * as writer from './writer.js';
15
+ import * as indexer from './indexer.js';
16
+ import * as extractor from './extractor.js';
17
+ import * as compactor from './compactor.js';
18
+ import { CloudSyncer } from './syncer.js';
19
+ import { SAASConnector } from './saas-connector.js';
20
+ import { MessageQueue } from './message-queue.js';
21
+ /**
22
+ * Cache Manager Options
23
+ */
24
+ export interface CacheManagerOptions {
25
+ basePath: string;
26
+ accounts: WeChatAccount[];
27
+ syncConfig?: Partial<SyncConfig>;
28
+ saasConfig?: Partial<SAASConfig>;
29
+ extractorConfig?: Partial<ExtractorConfig>;
30
+ compactConfig?: Partial<CompactConfig>;
31
+ }
32
+ /**
33
+ * Cache Manager class
34
+ */
35
+ export declare class CacheManager {
36
+ private basePath;
37
+ private accounts;
38
+ private compactConfig;
39
+ private extractorConfig;
40
+ private syncer;
41
+ private saasConnector;
42
+ private messageQueue;
43
+ private messageCounts;
44
+ private lastExtractionCounts;
45
+ constructor(options: CacheManagerOptions);
46
+ /**
47
+ * Initialize cache manager
48
+ */
49
+ init(): Promise<void>;
50
+ /**
51
+ * Handle incoming message
52
+ */
53
+ onMessage(message: WeChatMessage): Promise<void>;
54
+ /**
55
+ * Check if extraction should run
56
+ */
57
+ private checkExtraction;
58
+ /**
59
+ * Check if compaction should run
60
+ */
61
+ private checkCompaction;
62
+ /**
63
+ * Get user profile (with offline fallback)
64
+ */
65
+ getProfile(accountId: string, wechatId: string): Promise<UserProfile | null>;
66
+ /**
67
+ * Update user profile
68
+ */
69
+ updateProfile(accountId: string, wechatId: string, updates: Partial<UserProfile>): Promise<void>;
70
+ /**
71
+ * Write session memory
72
+ */
73
+ writeSessionMemory(accountId: string, conversationId: string, session: any): Promise<void>;
74
+ /**
75
+ * Read session memory
76
+ */
77
+ readSessionMemory(accountId: string, conversationId: string): Promise<any | null>;
78
+ /**
79
+ * Get SAAS connection status
80
+ */
81
+ getConnectionStatus(): import("./types.js").ConnectionStatus;
82
+ /**
83
+ * Check if SAAS is online
84
+ */
85
+ isSAASOnline(): boolean;
86
+ /**
87
+ * Get offline message
88
+ */
89
+ getOfflineMessage(): string;
90
+ /**
91
+ * Run sync
92
+ */
93
+ runSync(): Promise<void>;
94
+ /**
95
+ * Process message queue
96
+ */
97
+ processQueue(): Promise<void>;
98
+ /**
99
+ * Rebuild all indexes
100
+ */
101
+ rebuildIndexes(): Promise<void>;
102
+ /**
103
+ * Shutdown cache manager
104
+ */
105
+ shutdown(): Promise<void>;
106
+ private options?;
107
+ }
108
+ /**
109
+ * Create cache manager instance
110
+ */
111
+ export declare function createCacheManager(options: CacheManagerOptions): CacheManager;
112
+ export { writer, indexer, extractor, compactor };
113
+ export { CloudSyncer, SAASConnector, MessageQueue };