@rikaidev/yomi 0.4.2 → 0.5.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 (41) hide show
  1. package/README.md +18 -4
  2. package/dist/attention/policy.js +95 -0
  3. package/dist/auth/credential-store.js +32 -11
  4. package/dist/auth/keychain.js +7 -54
  5. package/dist/cli/query.js +668 -0
  6. package/dist/insight/relationships.js +1 -1
  7. package/dist/line/auth/protocol/pwless-login-flow.js +12 -0
  8. package/dist/line/auth/pwless/index.js +1 -1
  9. package/dist/line/client/index.js +0 -2
  10. package/dist/line/client/obs-media-client.js +37 -0
  11. package/dist/line/client/parsers.js +1 -0
  12. package/dist/line/client/sticker-meta.js +32 -0
  13. package/dist/line/client/sync-service/client.js +31 -3
  14. package/dist/line/client/talk-service/contact-query.js +8 -1
  15. package/dist/line/client/talk-service/message-box-query.js +17 -0
  16. package/dist/line/client/talk-service/query-client.js +32 -26
  17. package/dist/line/client/talk-service/requests.js +1 -11
  18. package/dist/line/core/auth-session-runtime.js +16 -1
  19. package/dist/line/core/chat-event.js +33 -0
  20. package/dist/line/core/chat-runtime-service.js +0 -20
  21. package/dist/line/core/directory-runtime-service.js +10 -2
  22. package/dist/line/core/e2ee/recovery/bootstrap-keys.js +8 -16
  23. package/dist/line/core/flex-message.js +130 -0
  24. package/dist/line/core/mention.js +1 -1
  25. package/dist/line/core/message-preview.js +60 -0
  26. package/dist/line/core/message-query-service.js +0 -20
  27. package/dist/line/core/service.js +1 -2
  28. package/dist/mcp/handlers/conversations.js +37 -4
  29. package/dist/mcp/handlers/login.js +1 -1
  30. package/dist/mcp/handlers/message-actions.js +1 -1
  31. package/dist/mcp/handlers/search.js +1 -1
  32. package/dist/mcp/handlers/shared.js +1 -1
  33. package/dist/mcp/integrator.js +168 -0
  34. package/dist/mcp/media.js +34 -2
  35. package/dist/mcp/phi-guard.js +2 -2
  36. package/dist/search/bigram.js +2 -2
  37. package/dist/search/collector.js +1 -1
  38. package/dist/search/embedder.js +1 -1
  39. package/dist/version.js +1 -1
  40. package/package.json +4 -1
  41. package/run.mjs +218 -2
package/run.mjs CHANGED
@@ -10,7 +10,8 @@
10
10
  * yomi version Print version
11
11
  */
12
12
 
13
- import { existsSync } from 'node:fs';
13
+ import { existsSync, readdirSync, statSync } from 'node:fs';
14
+ import { join } from 'node:path';
14
15
  import { fileURLToPath } from 'node:url';
15
16
 
16
17
  // Load the compiled `dist/` build when it exists (this is what the npm
@@ -22,7 +23,23 @@ import { fileURLToPath } from 'node:url';
22
23
  // STRIPPING), which is exactly how every 0.1.x release failed to start.
23
24
  // `files` in package.json must therefore always ship `dist/`.
24
25
  const distUrl = new URL('./dist/', import.meta.url);
25
- const built = existsSync(fileURLToPath(new URL('mcp/server.js', distUrl)));
26
+ const sourceRoot = fileURLToPath(new URL('./src/', import.meta.url));
27
+
28
+ function newestMtime(directory) {
29
+ if (!existsSync(directory)) return 0;
30
+ let newest = 0;
31
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
32
+ const path = join(directory, entry.name);
33
+ if (entry.isDirectory()) newest = Math.max(newest, newestMtime(path));
34
+ else if (entry.name.endsWith('.ts')) newest = Math.max(newest, statSync(path).mtimeMs);
35
+ }
36
+ return newest;
37
+ }
38
+
39
+ const distServerPath = fileURLToPath(new URL('mcp/server.js', distUrl));
40
+ const distMtime = existsSync(distServerPath) ? statSync(distServerPath).mtimeMs : 0;
41
+ const sourceIsNewer = existsSync(sourceRoot) && newestMtime(sourceRoot) > distMtime;
42
+ const built = existsSync(distServerPath) && process.env.YOMI_DEV_SOURCE !== '1' && !sourceIsNewer;
26
43
  const base = built ? distUrl : new URL('./src/', import.meta.url);
27
44
  const ext = built ? '.js' : '.ts';
28
45
 
@@ -38,6 +55,205 @@ switch (cmd) {
38
55
  const code = await cliLogin(args.slice(1));
39
56
  process.exit(code);
40
57
  }
58
+ case 'setup-mcp':
59
+ case 'integrate': {
60
+ const { integrateAllClients, integrateClient, getClientTargets } = await load('mcp/integrator');
61
+ const targetArg = args[1];
62
+ if (targetArg) {
63
+ const targets = getClientTargets();
64
+ const matched = targets.find(t => t.id === targetArg || t.name.toLowerCase().includes(targetArg.toLowerCase()));
65
+ if (matched) {
66
+ const res = integrateClient(matched);
67
+ console.log(JSON.stringify(res));
68
+ } else {
69
+ console.error(`Target client "${targetArg}" not recognized.`);
70
+ process.exit(1);
71
+ }
72
+ } else {
73
+ const report = integrateAllClients();
74
+ console.log(JSON.stringify(report));
75
+ }
76
+ process.exit(0);
77
+ }
78
+ case 'chats':
79
+ case 'conversations': {
80
+ const { cliListConversationsJson } = await load('cli/query');
81
+ const limit = args[1] && args[1] !== 'all' ? parseInt(args[1], 10) : undefined;
82
+ const attentionOnly = args.includes('--attention-only');
83
+ const firstPage = args.includes('--first-page');
84
+ const semantic = args.includes('--semantic');
85
+ const json = await cliListConversationsJson({ limit, attentionOnly, firstPage, semantic });
86
+ console.log(JSON.stringify(json));
87
+ process.exit(0);
88
+ }
89
+ case 'messages':
90
+ case 'chat-messages': {
91
+ const { cliGetChatMessagesJson } = await load('cli/query');
92
+ const chatId = args[1];
93
+ const count = args[2] ? parseInt(args[2], 10) : 30;
94
+ if (!chatId) {
95
+ console.error(JSON.stringify({ error: 'chatId required' }));
96
+ process.exit(1);
97
+ }
98
+ const json = await cliGetChatMessagesJson(chatId, count);
99
+ console.log(JSON.stringify(json));
100
+ process.exit(0);
101
+ }
102
+ case 'attention-classify': {
103
+ const { cliClassifyAttentionJson } = await load('cli/query');
104
+ let input = '';
105
+ for await (const chunk of process.stdin) input += chunk;
106
+ const items = JSON.parse(input || '[]');
107
+ console.log(JSON.stringify(await cliClassifyAttentionJson(items)));
108
+ process.exit(0);
109
+ }
110
+ case 'list-stickers': {
111
+ const { cliListStickersJson } = await load('cli/query');
112
+ const language = args[1] || 'zh_TW';
113
+ console.log(JSON.stringify(await cliListStickersJson(language)));
114
+ process.exit(0);
115
+ }
116
+ case 'sticker-previews': {
117
+ const { cliStickerPreviewsJson } = await load('cli/query');
118
+ const packageId = args[1];
119
+ const limit = args[2] ? parseInt(args[2], 10) : 12;
120
+ const language = args[3] || 'en';
121
+ if (!packageId) {
122
+ console.error(JSON.stringify({ error: 'packageId required' }));
123
+ process.exit(1);
124
+ }
125
+ console.log(JSON.stringify(await cliStickerPreviewsJson(packageId, limit, language)));
126
+ process.exit(0);
127
+ }
128
+ case 'send-sticker': {
129
+ const chatId = args[1];
130
+ const stickerId = args[2];
131
+ const packageId = args[3];
132
+ const version = args[4] || '1';
133
+ if (!chatId || !stickerId || !packageId) {
134
+ console.error(JSON.stringify({ error: 'chatId, stickerId and packageId required' }));
135
+ process.exit(1);
136
+ }
137
+ const { LineProtocolService } = await load('line/core/service');
138
+ const service = new LineProtocolService();
139
+ if (!(await service.resumeSession())) process.exit(1);
140
+ const sent = await service.sendSticker(chatId, stickerId, packageId, version);
141
+ console.log(JSON.stringify({ sent: true, messageId: sent?.id ?? sent?.messageId ?? null }));
142
+ process.exit(0);
143
+ }
144
+ case 'send-message': {
145
+ const chatId = args[1];
146
+ const text = args.slice(2).join(' ').trim();
147
+ if (!chatId || !text) {
148
+ console.error(JSON.stringify({ error: 'chatId and text required' }));
149
+ process.exit(1);
150
+ }
151
+ const { LineProtocolService } = await load('line/core/service');
152
+ const { buildMentionMetadata } = await load('line/core/mention');
153
+ const service = new LineProtocolService();
154
+ if (!(await service.resumeSession())) process.exit(1);
155
+ const targets = JSON.parse(process.env.YOMI_MENTIONS_JSON || '[]');
156
+ const mentions = targets.flatMap(({ mid, name }) => {
157
+ const start = text.indexOf(`@${name}`);
158
+ return start < 0 ? [] : [{ mid, start, end: start + name.length + 1 }];
159
+ });
160
+ const metadata = mentions.length ? { MENTION: buildMentionMetadata(text, mentions) } : undefined;
161
+ const sent = await service.sendMessage(chatId, text, metadata);
162
+ console.log(JSON.stringify({
163
+ sent: true,
164
+ messageId: sent?.id ?? sent?.messageId ?? null,
165
+ }));
166
+ process.exit(0);
167
+ }
168
+ case 'send-image': {
169
+ const chatId = args[1];
170
+ const imagePath = args[2];
171
+ if (!chatId || !imagePath) {
172
+ console.error(JSON.stringify({ error: 'chatId and imagePath required' }));
173
+ process.exit(1);
174
+ }
175
+ const { LineProtocolService } = await load('line/core/service');
176
+ const { readFile } = await import('node:fs/promises');
177
+ const { basename } = await import('node:path');
178
+ const service = new LineProtocolService();
179
+ if (!(await service.resumeSession())) process.exit(1);
180
+ const bytes = await readFile(imagePath);
181
+ if (bytes.length === 0) {
182
+ console.error(JSON.stringify({ error: 'image is empty' }));
183
+ process.exit(1);
184
+ }
185
+ const sent = await service.sendImage(chatId, bytes, basename(imagePath));
186
+ console.log(JSON.stringify({
187
+ sent: true,
188
+ messageId: sent?.messageId ?? sent?.id ?? null,
189
+ oid: sent?.oid ?? null,
190
+ }));
191
+ process.exit(0);
192
+ }
193
+ case 'send-file': {
194
+ const chatId = args[1];
195
+ const filePath = args[2];
196
+ if (!chatId || !filePath) {
197
+ console.error(JSON.stringify({ error: 'chatId and filePath required' }));
198
+ process.exit(1);
199
+ }
200
+ const { LineProtocolService } = await load('line/core/service');
201
+ const { readFile } = await import('node:fs/promises');
202
+ const { basename } = await import('node:path');
203
+ const service = new LineProtocolService();
204
+ if (!(await service.resumeSession())) process.exit(1);
205
+ const bytes = await readFile(filePath);
206
+ if (bytes.length === 0) {
207
+ console.error(JSON.stringify({ error: 'file is empty' }));
208
+ process.exit(1);
209
+ }
210
+ const sent = await service.sendFile(chatId, bytes, basename(filePath));
211
+ console.log(JSON.stringify({
212
+ sent: true,
213
+ messageId: sent?.messageId ?? sent?.id ?? null,
214
+ oid: sent?.oid ?? null,
215
+ }));
216
+ process.exit(0);
217
+ }
218
+ case 'media-preview': {
219
+ const chatId = args[1];
220
+ const messageId = args[2];
221
+ if (!chatId || !messageId) {
222
+ console.error(JSON.stringify({ error: 'chatId and messageId required' }));
223
+ process.exit(1);
224
+ }
225
+ const { LineProtocolService } = await load('line/core/service');
226
+ const { fetchLineMessageMedia } = await load('mcp/media');
227
+ const service = new LineProtocolService();
228
+ if (!(await service.resumeSession())) process.exit(1);
229
+ const result = await fetchLineMessageMedia(service, chatId, messageId, args[3] !== 'original');
230
+ console.log(JSON.stringify({
231
+ data: result.bytes.toString('base64'),
232
+ mimeType: result.mimeType,
233
+ fileName: result.fileName ?? null,
234
+ }));
235
+ process.exit(0);
236
+ }
237
+ case 'mark-read': {
238
+ const { cliMarkReadJson } = await load('cli/query');
239
+ const chatId = args[1];
240
+ const msgId = args[2];
241
+ if (!chatId) {
242
+ console.error(JSON.stringify({ error: 'chatId required' }));
243
+ process.exit(1);
244
+ }
245
+ const res = await cliMarkReadJson(chatId, msgId);
246
+ console.log(JSON.stringify(res));
247
+ process.exit(0);
248
+ }
249
+ case 'summary':
250
+ case 'recent':
251
+ case 'query': {
252
+ const { cliRecentSummary } = await load('cli/query');
253
+ const summary = await cliRecentSummary();
254
+ console.log(summary);
255
+ process.exit(0);
256
+ }
41
257
  case 'serve':
42
258
  await load('mcp/server');
43
259
  break;