memex-mvp 0.10.4 → 0.10.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.
- package/lib/notify-click-action.js +9 -4
- package/lib/telegram-notify.js +20 -1
- package/package.json +1 -1
|
@@ -213,8 +213,11 @@ function shellQuote(s) {
|
|
|
213
213
|
*
|
|
214
214
|
* opts = {
|
|
215
215
|
* title, subtitle, message,
|
|
216
|
-
* target,
|
|
217
|
-
* env,
|
|
216
|
+
* target, // 'auto' | 'claude-cli' | 'claude-desktop' | 'terminal' | 'none'
|
|
217
|
+
* env, // optional override of detected env (for tests)
|
|
218
|
+
* dryRun, // if true → compute backend+target+command but DON'T spawn.
|
|
219
|
+
* // Used by unit tests so `npm test` doesn't spam real
|
|
220
|
+
* // macOS notifications. Also honors env MEMEX_NO_FIRE=1.
|
|
218
221
|
* }
|
|
219
222
|
*
|
|
220
223
|
* Returns { backend: 'terminal-notifier' | 'osascript' | 'noop',
|
|
@@ -226,13 +229,14 @@ export function fireClickableNotification(opts = {}) {
|
|
|
226
229
|
|
|
227
230
|
const target = pickTarget(opts.target || 'auto', env);
|
|
228
231
|
const click = buildClickCommand(target, env);
|
|
232
|
+
const dryRun = opts.dryRun === true || process.env.MEMEX_NO_FIRE === '1';
|
|
229
233
|
|
|
230
234
|
const title = opts.title || 'memex';
|
|
231
235
|
const subtitle = opts.subtitle || '';
|
|
232
236
|
const message = opts.message || '';
|
|
233
237
|
|
|
234
238
|
if (env.terminal_notifier && click) {
|
|
235
|
-
|
|
239
|
+
if (dryRun) return { backend: 'terminal-notifier', target, click_command: click };
|
|
236
240
|
const args = [
|
|
237
241
|
'-title', title,
|
|
238
242
|
'-message', message,
|
|
@@ -240,7 +244,7 @@ export function fireClickableNotification(opts = {}) {
|
|
|
240
244
|
];
|
|
241
245
|
if (subtitle) { args.push('-subtitle'); args.push(subtitle); }
|
|
242
246
|
args.push('-sound', 'Pop');
|
|
243
|
-
args.push('-sender', 'com.apple.Terminal');
|
|
247
|
+
args.push('-sender', 'com.apple.Terminal');
|
|
244
248
|
try {
|
|
245
249
|
spawn(env.terminal_notifier, args, { detached: true, stdio: 'ignore' }).unref();
|
|
246
250
|
return { backend: 'terminal-notifier', target, click_command: click };
|
|
@@ -248,6 +252,7 @@ export function fireClickableNotification(opts = {}) {
|
|
|
248
252
|
}
|
|
249
253
|
|
|
250
254
|
// Plain osascript fallback — banner is not clickable but text is informative
|
|
255
|
+
if (dryRun) return { backend: 'osascript', target: 'none', click_command: null };
|
|
251
256
|
const esc = (s) => String(s || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
252
257
|
const sub = subtitle ? ` subtitle "${esc(subtitle)}"` : '';
|
|
253
258
|
const script = `display notification "${esc(message)}" with title "${esc(title)}"${sub} sound name "Pop"`;
|
package/lib/telegram-notify.js
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
writeFileSync,
|
|
34
34
|
renameSync,
|
|
35
35
|
mkdirSync,
|
|
36
|
+
statSync,
|
|
36
37
|
} from 'node:fs';
|
|
37
38
|
import { createHash } from 'node:crypto';
|
|
38
39
|
import { join, dirname } from 'node:path';
|
|
@@ -106,8 +107,26 @@ export function markCliTipShown(state, now = new Date()) {
|
|
|
106
107
|
|
|
107
108
|
// ---------------------- Notification dedup ----------------------
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Stable hash of a pending export for notification dedup.
|
|
112
|
+
*
|
|
113
|
+
* v0.10.5+: hash now incorporates the file's mtime in addition to path.
|
|
114
|
+
*
|
|
115
|
+
* Why: Telegram Desktop reuses the same folder name on same-day re-exports
|
|
116
|
+
* (e.g. ChatExport_2026-05-16). After memex imports & removes that folder
|
|
117
|
+
* from pending, a fresh export with the same date creates the same path
|
|
118
|
+
* again. Path-only hash collided → notification was incorrectly deduped
|
|
119
|
+
* as "already shown".
|
|
120
|
+
*
|
|
121
|
+
* Including mtime makes the hash content-aware: same path + different
|
|
122
|
+
* mtime → fresh hash → notification fires. If the path doesn't exist
|
|
123
|
+
* (file was deleted), we fall back to path-only — it's an edge case
|
|
124
|
+
* (notifShownFor check before fire, file should exist).
|
|
125
|
+
*/
|
|
109
126
|
export function notifIdFor(path) {
|
|
110
|
-
|
|
127
|
+
let mtimeKey = '';
|
|
128
|
+
try { mtimeKey = String(Math.floor(statSync(path).mtimeMs)); } catch (_) { /* path missing — fall back to path-only */ }
|
|
129
|
+
return createHash('sha256').update(String(path) + ':' + mtimeKey).digest('hex').slice(0, 16);
|
|
111
130
|
}
|
|
112
131
|
|
|
113
132
|
export function notifShownFor(state, path) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memex-mvp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.6",
|
|
4
4
|
"description": "Local-first MCP server for cross-agent AI memory. One SQLite + FTS5 corpus across Claude Code, Cowork, Cursor, Continue, Zed, Obsidian, and Telegram — passively captured, verbatim, searchable from any MCP-compatible client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|