@rikaidev/yomi 0.2.6

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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +81 -0
  3. package/PRIVACY.md +39 -0
  4. package/README.md +659 -0
  5. package/dist/auth/credential-store.js +395 -0
  6. package/dist/auth/keychain.js +256 -0
  7. package/dist/cli/login.js +142 -0
  8. package/dist/line/auth/protocol/e2ee-bootstrap.js +87 -0
  9. package/dist/line/auth/protocol/index.js +3 -0
  10. package/dist/line/auth/protocol/login-metadata.js +159 -0
  11. package/dist/line/auth/protocol/pwless-login-flow.js +83 -0
  12. package/dist/line/auth/pwless/index.js +163 -0
  13. package/dist/line/auth/pwless/steps.js +213 -0
  14. package/dist/line/client/auth-service/client.js +240 -0
  15. package/dist/line/client/e2ee-service/client.js +69 -0
  16. package/dist/line/client/e2ee-service/requests.js +92 -0
  17. package/dist/line/client/e2ee-service/shared-key-log.js +24 -0
  18. package/dist/line/client/e2ee-service/shared-key-payload.js +35 -0
  19. package/dist/line/client/index.js +213 -0
  20. package/dist/line/client/obs-media-client.js +140 -0
  21. package/dist/line/client/parsers.js +69 -0
  22. package/dist/line/client/shop-service/client.js +63 -0
  23. package/dist/line/client/sticker-meta.js +46 -0
  24. package/dist/line/client/sync-service/client.js +317 -0
  25. package/dist/line/client/talk-service/chat-query.js +122 -0
  26. package/dist/line/client/talk-service/command-client.js +242 -0
  27. package/dist/line/client/talk-service/contact-query.js +77 -0
  28. package/dist/line/client/talk-service/message-box-query.js +137 -0
  29. package/dist/line/client/talk-service/message-id-query.js +241 -0
  30. package/dist/line/client/talk-service/query-client.js +219 -0
  31. package/dist/line/client/talk-service/requests.js +383 -0
  32. package/dist/line/client/transport.js +138 -0
  33. package/dist/line/core/auth-session-logout.js +55 -0
  34. package/dist/line/core/auth-session-runtime.js +231 -0
  35. package/dist/line/core/auth-session-service.js +85 -0
  36. package/dist/line/core/chat-runtime-service.js +343 -0
  37. package/dist/line/core/client-runtime.js +17 -0
  38. package/dist/line/core/config.js +67 -0
  39. package/dist/line/core/constants.js +56 -0
  40. package/dist/line/core/contact-query-service.js +20 -0
  41. package/dist/line/core/directory-runtime-service.js +83 -0
  42. package/dist/line/core/e2ee/bootstrap/keychain.js +41 -0
  43. package/dist/line/core/e2ee/crypto/crypto-primitives.js +98 -0
  44. package/dist/line/core/e2ee/index.js +6 -0
  45. package/dist/line/core/e2ee/keys/decrypt.js +110 -0
  46. package/dist/line/core/e2ee/keys/encrypt.js +117 -0
  47. package/dist/line/core/e2ee/keys/group-key.js +260 -0
  48. package/dist/line/core/e2ee/keys/key-manager.js +231 -0
  49. package/dist/line/core/e2ee/keys/key-payload.js +130 -0
  50. package/dist/line/core/e2ee/keys/key-types.js +1 -0
  51. package/dist/line/core/e2ee/keys/message-crypto.js +146 -0
  52. package/dist/line/core/e2ee/keys/message-decrypt.js +390 -0
  53. package/dist/line/core/e2ee/keys/peer-key.js +191 -0
  54. package/dist/line/core/e2ee/recovery/bootstrap-keys.js +161 -0
  55. package/dist/line/core/e2ee/types.js +4 -0
  56. package/dist/line/core/group-query-service.js +20 -0
  57. package/dist/line/core/media-decrypt.js +78 -0
  58. package/dist/line/core/media-encrypt.js +97 -0
  59. package/dist/line/core/mention.js +89 -0
  60. package/dist/line/core/message-command-service.js +454 -0
  61. package/dist/line/core/message-query-service.js +161 -0
  62. package/dist/line/core/service.js +134 -0
  63. package/dist/line/core/session-state/index.js +243 -0
  64. package/dist/line/core/session-state/persist.js +192 -0
  65. package/dist/line/core/store.js +8 -0
  66. package/dist/line/core/thrift/binary/binary-reader.js +151 -0
  67. package/dist/line/core/thrift/binary/binary-writer.js +157 -0
  68. package/dist/line/core/thrift/binary/type-codec-dsl.js +42 -0
  69. package/dist/line/core/thrift/compact/compact-reader.js +180 -0
  70. package/dist/line/core/thrift/compact/compact-writer.js +161 -0
  71. package/dist/line/core/thrift/compact/type-codec-dsl.js +66 -0
  72. package/dist/line/core/thrift/fields/builders.js +124 -0
  73. package/dist/line/core/thrift/index.js +12 -0
  74. package/dist/line/core/thrift/message/decoder.js +55 -0
  75. package/dist/line/core/thrift/message/encoder.js +25 -0
  76. package/dist/line/core/thrift/primitives/varint.js +64 -0
  77. package/dist/line/core/thrift/struct/reader.js +172 -0
  78. package/dist/line/core/thrift/struct/writer.js +189 -0
  79. package/dist/line/core/thrift/types.js +23 -0
  80. package/dist/line/core/video-thumbnail.js +104 -0
  81. package/dist/mcp/handlers/contacts.js +166 -0
  82. package/dist/mcp/handlers/conversations.js +193 -0
  83. package/dist/mcp/handlers/groups.js +123 -0
  84. package/dist/mcp/handlers/index.js +15 -0
  85. package/dist/mcp/handlers/login.js +473 -0
  86. package/dist/mcp/handlers/media.js +69 -0
  87. package/dist/mcp/handlers/message-actions.js +62 -0
  88. package/dist/mcp/handlers/scope.js +120 -0
  89. package/dist/mcp/handlers/search.js +196 -0
  90. package/dist/mcp/handlers/send.js +523 -0
  91. package/dist/mcp/handlers/shared.js +84 -0
  92. package/dist/mcp/handlers/stickers.js +87 -0
  93. package/dist/mcp/media.js +228 -0
  94. package/dist/mcp/names.js +133 -0
  95. package/dist/mcp/phi-guard.js +105 -0
  96. package/dist/mcp/policy.js +34 -0
  97. package/dist/mcp/server.js +301 -0
  98. package/dist/mcp/tools.js +761 -0
  99. package/dist/mcp/ui/capability.js +26 -0
  100. package/dist/mcp/ui/login-app-protocol.js +118 -0
  101. package/dist/mcp/ui/login-app-styles.js +142 -0
  102. package/dist/mcp/ui/login-app-view.js +361 -0
  103. package/dist/mcp/ui/login-app.js +72 -0
  104. package/dist/mcp/ui/login-structured-content.js +1 -0
  105. package/dist/mcp/ui/resource.js +35 -0
  106. package/dist/mcp/ui/tools-with-ui.js +40 -0
  107. package/dist/search/bigram.js +86 -0
  108. package/dist/search/capture.js +140 -0
  109. package/dist/search/collector.js +176 -0
  110. package/dist/search/default-embedder.js +101 -0
  111. package/dist/search/embedder.js +13 -0
  112. package/dist/search/scope.js +142 -0
  113. package/dist/search/sqlite.js +141 -0
  114. package/dist/search/store.js +469 -0
  115. package/dist/util/data-dir.js +55 -0
  116. package/dist/util/log.js +86 -0
  117. package/dist/version.js +7 -0
  118. package/package.json +63 -0
  119. package/run.mjs +71 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Yomi conversation scoping — a DENYLIST of chats excluded from capture.
3
+ *
4
+ * Default behavior is capture-everything: every conversation is indexed by
5
+ * ../search/collector.ts unless its chatId appears in the `excluded_chats`
6
+ * table this module owns. Excluding a chat does two things, not one:
7
+ * 1. Blocks future capture — collectMessages drops excluded chatIds
8
+ * before fetching (see ../search/collector.ts).
9
+ * 2. Purges past capture — deleteMessagesForChats removes that chat's
10
+ * already-indexed messages AND their embeddings, so excluding a
11
+ * conversation actually removes what was already learned about it
12
+ * rather than just freezing the index in place. A denylist that only
13
+ * blocked future capture while leaving old data behind would be fake
14
+ * privacy.
15
+ *
16
+ * Shares the same node:sqlite connection as ../search/store.ts via the
17
+ * exported getDb(), so this table lives in the same search-index.db file
18
+ * rather than a second database.
19
+ */
20
+ import { getDb } from './store.js';
21
+ let tableEnsured = false;
22
+ /**
23
+ * Create the `excluded_chats` table if it does not already exist. Memoized
24
+ * per process (mirrors getDb()'s own memoization) so every public function
25
+ * in this module can safely call it without repeating the DDL on every
26
+ * invocation.
27
+ */
28
+ function ensureExcludedTable() {
29
+ if (tableEnsured) {
30
+ return;
31
+ }
32
+ const handle = getDb();
33
+ handle.exec(`
34
+ CREATE TABLE IF NOT EXISTS excluded_chats (
35
+ chatId TEXT PRIMARY KEY NOT NULL
36
+ );
37
+ `);
38
+ tableEnsured = true;
39
+ }
40
+ /**
41
+ * Build a `(?, ?, ...)` placeholder group sized to `values.length`, for use
42
+ * in an `IN (...)` clause. Callers must pass `values` itself (in the same
43
+ * order) as the bound parameters — this never string-interpolates the
44
+ * values themselves, only the `?` count.
45
+ *
46
+ * @param values - Values the IN-clause will be matched against.
47
+ * @returns Placeholder group, e.g. `(?, ?, ?)`.
48
+ */
49
+ function buildPlaceholders(values) {
50
+ return `(${values.map(() => '?').join(', ')})`;
51
+ }
52
+ /**
53
+ * List every chatId currently on the exclusion denylist.
54
+ *
55
+ * @returns Set of excluded chatIds (empty when the denylist is empty).
56
+ */
57
+ export function getExcludedChatIds() {
58
+ ensureExcludedTable();
59
+ const handle = getDb();
60
+ const rows = handle
61
+ .prepare('SELECT chatId FROM excluded_chats;')
62
+ .all();
63
+ return new Set(rows.map((row) => row.chatId));
64
+ }
65
+ /**
66
+ * Add chatIds to the exclusion denylist. Idempotent: chatIds already
67
+ * present are silently skipped (INSERT OR IGNORE), never duplicated or
68
+ * erroring.
69
+ *
70
+ * @param chatIds - Chat MIDs to exclude from future capture.
71
+ * @returns Number of chatIds now covered by this call (rows inserted or
72
+ * already present among the input).
73
+ */
74
+ export function addExcludedChatIds(chatIds) {
75
+ if (chatIds.length === 0) {
76
+ return 0;
77
+ }
78
+ ensureExcludedTable();
79
+ const handle = getDb();
80
+ const stmt = handle.prepare('INSERT OR IGNORE INTO excluded_chats (chatId) VALUES (?);');
81
+ const insertAll = handle.transaction((ids) => {
82
+ for (const id of ids) {
83
+ stmt.run(id);
84
+ }
85
+ });
86
+ insertAll(chatIds);
87
+ return chatIds.length;
88
+ }
89
+ /**
90
+ * Remove chatIds from the exclusion denylist, re-allowing future capture.
91
+ * Does NOT re-fetch or re-index anything that was previously purged — the
92
+ * next `collect_messages`/`search_messages` auto-collect will pick the
93
+ * chat back up.
94
+ *
95
+ * @param chatIds - Chat MIDs to remove from the denylist.
96
+ * @returns Number of rows actually removed.
97
+ */
98
+ export function removeExcludedChatIds(chatIds) {
99
+ if (chatIds.length === 0) {
100
+ return 0;
101
+ }
102
+ ensureExcludedTable();
103
+ const handle = getDb();
104
+ const placeholders = buildPlaceholders(chatIds);
105
+ const result = handle
106
+ .prepare(`DELETE FROM excluded_chats WHERE chatId IN ${placeholders};`)
107
+ .run(...chatIds);
108
+ return result.changes;
109
+ }
110
+ /**
111
+ * Purge already-indexed data for the given chats: their embeddings, then
112
+ * their messages, in one transaction. This is what makes exclusion an
113
+ * actual privacy action rather than just a future-capture filter — a chat
114
+ * that gets excluded loses what was already learned about it, not just
115
+ * what would have been learned next. The `messages_fts` external-content
116
+ * index stays in sync automatically via the AFTER DELETE trigger on
117
+ * `messages` (see ../search/store.ts), so it is never touched directly
118
+ * here.
119
+ *
120
+ * @param chatIds - Chat MIDs whose indexed messages + embeddings should be deleted.
121
+ * @returns Number of `messages` rows deleted.
122
+ */
123
+ export function deleteMessagesForChats(chatIds) {
124
+ if (chatIds.length === 0) {
125
+ return 0;
126
+ }
127
+ const handle = getDb();
128
+ const placeholders = buildPlaceholders(chatIds);
129
+ const purge = handle.transaction((ids) => {
130
+ // Count the target rows FIRST: the DELETE's own `.changes` on `messages`
131
+ // is inflated by the AFTER DELETE trigger that maintains messages_fts
132
+ // (one fts write per deleted row), so it cannot serve as the purged
133
+ // count. An explicit COUNT is trigger-independent and honest.
134
+ const counted = handle
135
+ .prepare(`SELECT COUNT(*) as c FROM messages WHERE chatId IN ${placeholders};`)
136
+ .get(...ids);
137
+ handle.exec(`DELETE FROM embeddings WHERE messageId IN (SELECT messageId FROM messages WHERE chatId IN ${placeholders});`, ids);
138
+ handle.exec(`DELETE FROM messages WHERE chatId IN ${placeholders};`, ids);
139
+ return counted?.c ?? 0;
140
+ });
141
+ return purge(chatIds);
142
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Runtime-agnostic SQLite wrapper — uses bun:sqlite in bun, node:sqlite in node.
3
+ *
4
+ * The APIs differ:
5
+ * - bun: Database → .run(sql, params?) .query(sql).get(params?) .query(sql).all(params?) .transaction(fn)
6
+ * - node: DatabaseSync → .exec(sql) .prepare(sql).get(params?) .prepare(sql).all(params?) (no .transaction)
7
+ *
8
+ * This module exports a unified `Database` class that normalizes both to the
9
+ * same surface, so the rest of the codebase never cares which runtime it's on.
10
+ */
11
+ import { createRequire } from 'node:module';
12
+ const isBun = typeof process.versions.bun !== 'undefined';
13
+ // The sqlite binding has to be picked at runtime, not import time: `bun:sqlite`
14
+ // does not exist under node and vice versa, so a static import would fail on
15
+ // whichever runtime is not running. That means a require — but this package is
16
+ // `"type": "module"`, and a bare `require` in ESM only works because bun allows
17
+ // it as an extension. Under node it is a ReferenceError, which is why every
18
+ // index-backed tool (search, scope, capture, policy) died with
19
+ // "require is not defined" once the server ran on node rather than bun.
20
+ // createRequire gives ESM a real require that both runtimes honor.
21
+ const runtimeRequire = createRequire(import.meta.url);
22
+ function wrapBunDb(raw) {
23
+ return {
24
+ exec(sql) {
25
+ raw.run(sql);
26
+ },
27
+ prepare(sql) {
28
+ const q = raw.query(sql);
29
+ return {
30
+ run(...params) {
31
+ return q.get(...params);
32
+ },
33
+ get(...params) {
34
+ return q.get(...params);
35
+ },
36
+ all(...params) {
37
+ return q.all(...params);
38
+ },
39
+ };
40
+ },
41
+ transaction: raw.transaction.bind(raw),
42
+ close() {
43
+ raw.close();
44
+ },
45
+ };
46
+ }
47
+ function wrapNodeDb(raw) {
48
+ return {
49
+ exec(sql, params) {
50
+ if (params && params.length > 0) {
51
+ const stmt = raw.prepare(sql);
52
+ stmt.run(...params);
53
+ }
54
+ else {
55
+ raw.exec(sql);
56
+ }
57
+ },
58
+ prepare(sql) {
59
+ const stmt = raw.prepare(sql);
60
+ return {
61
+ run(...params) {
62
+ return stmt.run(...params);
63
+ },
64
+ get(...params) {
65
+ return stmt.get(...params);
66
+ },
67
+ all(...params) {
68
+ return stmt.all(...params);
69
+ },
70
+ };
71
+ },
72
+ transaction(fn) {
73
+ const wrapped = (...args) => {
74
+ raw.exec('BEGIN');
75
+ try {
76
+ const result = fn(...args);
77
+ raw.exec('COMMIT');
78
+ return result;
79
+ }
80
+ catch (e) {
81
+ raw.exec('ROLLBACK');
82
+ throw e;
83
+ }
84
+ };
85
+ return wrapped;
86
+ },
87
+ close() {
88
+ raw.close();
89
+ },
90
+ };
91
+ }
92
+ /**
93
+ * Node versions carrying an unflagged `node:sqlite`.
94
+ *
95
+ * It was unflagged in 22.13.0 and 23.4.0 (nodejs/node#55890), so the 23 line
96
+ * splits: 23.0–23.3 are NEWER than 22.13 yet still lack it, which a plain
97
+ * `>=22.13.0` floor would wrongly admit. Keep this in step with
98
+ * package.json's `engines.node`.
99
+ */
100
+ const NODE_SQLITE_REQUIREMENT = '22.13+ (or 23.4+ on the 23.x line)';
101
+ /** Thrown when the running runtime has no SQLite module at all. */
102
+ export class SqliteUnavailableError extends Error {
103
+ constructor(message) {
104
+ super(message);
105
+ this.name = 'SqliteUnavailableError';
106
+ }
107
+ }
108
+ /**
109
+ * Open a SQLite database at the given path. Creates the file if it doesn't exist.
110
+ *
111
+ * @param path - Database file path.
112
+ * @returns Runtime-agnostic database handle.
113
+ * @throws SqliteUnavailableError when the runtime predates `node:sqlite`.
114
+ */
115
+ export function openDatabase(path) {
116
+ if (isBun) {
117
+ const mod = runtimeRequire('bun:sqlite');
118
+ return wrapBunDb(new mod.Database(path));
119
+ }
120
+ let mod;
121
+ try {
122
+ mod = runtimeRequire('node:sqlite');
123
+ }
124
+ catch (error) {
125
+ // Runtimes without it throw "No such built-in module: node:sqlite", which
126
+ // tells a user nothing about what to change. `engines` does not help: npm
127
+ // only warns at install time, and npx ignores it entirely.
128
+ //
129
+ // The trap worth naming is that the node running this is often not the one
130
+ // the user configured. An MCP client launches the server through `npx`,
131
+ // whose `#!/usr/bin/env node` resolves against the CLIENT's PATH — so the
132
+ // runtime can be any node on that PATH regardless of which npx was named.
133
+ // Report the actual execPath rather than assume the user can guess it.
134
+ throw new SqliteUnavailableError(`This Node cannot open Yomi's message index: ${process.version} has no built-in \`node:sqlite\` (needs ${NODE_SQLITE_REQUIREMENT}). ` +
135
+ `Live tools still work; search, scope and capture do not. ` +
136
+ `The runtime is ${process.execPath} — an MCP client resolves \`node\` from its own PATH, so this may not be the node you expect. ` +
137
+ `Point the client at a supported node and restart it. ` +
138
+ `Original error: ${error?.message ?? String(error)}`);
139
+ }
140
+ return wrapNodeDb(new mod.DatabaseSync(path));
141
+ }