@mneme-ai/core 1.73.0 β 1.75.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.
- package/dist/agent_manifest.d.ts +1 -1
- package/dist/agent_manifest.d.ts.map +1 -1
- package/dist/agent_manifest.js +19 -1
- package/dist/agent_manifest.js.map +1 -1
- package/dist/genesplice/soul_prompt.d.ts +6 -0
- package/dist/genesplice/soul_prompt.d.ts.map +1 -1
- package/dist/genesplice/soul_prompt.js +2 -0
- package/dist/genesplice/soul_prompt.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -1
- package/dist/permeate/bookmarklet_generator.d.ts +26 -0
- package/dist/permeate/bookmarklet_generator.d.ts.map +1 -0
- package/dist/permeate/bookmarklet_generator.js +56 -0
- package/dist/permeate/bookmarklet_generator.js.map +1 -0
- package/dist/permeate/editor_integration_map.d.ts +43 -0
- package/dist/permeate/editor_integration_map.d.ts.map +1 -0
- package/dist/permeate/editor_integration_map.js +180 -0
- package/dist/permeate/editor_integration_map.js.map +1 -0
- package/dist/permeate/index.d.ts +25 -0
- package/dist/permeate/index.d.ts.map +1 -0
- package/dist/permeate/index.js +25 -0
- package/dist/permeate/index.js.map +1 -0
- package/dist/permeate/permeate.test.d.ts +5 -0
- package/dist/permeate/permeate.test.d.ts.map +1 -0
- package/dist/permeate/permeate.test.js +141 -0
- package/dist/permeate/permeate.test.js.map +1 -0
- package/dist/permeate/transport_menu.d.ts +35 -0
- package/dist/permeate/transport_menu.d.ts.map +1 -0
- package/dist/permeate/transport_menu.js +96 -0
- package/dist/permeate/transport_menu.js.map +1 -0
- package/dist/permeate/userscript_generator.d.ts +42 -0
- package/dist/permeate/userscript_generator.d.ts.map +1 -0
- package/dist/permeate/userscript_generator.js +170 -0
- package/dist/permeate/userscript_generator.js.map +1 -0
- package/dist/telepathy/heartbeat.d.ts +46 -0
- package/dist/telepathy/heartbeat.d.ts.map +1 -0
- package/dist/telepathy/heartbeat.js +175 -0
- package/dist/telepathy/heartbeat.js.map +1 -0
- package/dist/telepathy/index.d.ts +2 -0
- package/dist/telepathy/index.d.ts.map +1 -0
- package/dist/telepathy/index.js +2 -0
- package/dist/telepathy/index.js.map +1 -0
- package/dist/telepathy/telepathy.test.d.ts +2 -0
- package/dist/telepathy/telepathy.test.d.ts.map +1 -0
- package/dist/telepathy/telepathy.test.js +195 -0
- package/dist/telepathy/telepathy.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1.74.0 -- PERMEATE P1: USERSCRIPT GENERATOR.
|
|
3
|
+
*
|
|
4
|
+
* Routes around Chrome Web Store / Firefox Addons / Safari approval
|
|
5
|
+
* by emitting a Tampermonkey/Greasemonkey/Violentmonkey-compatible
|
|
6
|
+
* userscript. User installs the userscript manager once (free,
|
|
7
|
+
* single click) and the Mneme userscript adds a floating "π
|
|
8
|
+
* Inject Mneme Soul" button to ChatGPT / Gemini / Claude.ai /
|
|
9
|
+
* Copilot / DeepSeek chats.
|
|
10
|
+
*
|
|
11
|
+
* The userscript is PURE BROWSER JS -- no Node, no HTTP, no
|
|
12
|
+
* backend. Reads soul prompt from clipboard, finds the chat input
|
|
13
|
+
* element via known selectors, injects + submits.
|
|
14
|
+
*
|
|
15
|
+
* Selectors per site (kept conservative; fallbacks included):
|
|
16
|
+
* ChatGPT div#prompt-textarea / textarea
|
|
17
|
+
* Gemini rich-textarea / textarea
|
|
18
|
+
* Claude.ai div[contenteditable="true"]
|
|
19
|
+
* Copilot textarea[aria-label]
|
|
20
|
+
* DeepSeek textarea#chat-input
|
|
21
|
+
*/
|
|
22
|
+
export function generateUserscript(opts) {
|
|
23
|
+
const header = [
|
|
24
|
+
"// ==UserScript==",
|
|
25
|
+
"// @name Mneme Soul Injector",
|
|
26
|
+
"// @namespace https://github.com/patsa2561-art/mneme-ai",
|
|
27
|
+
`// @version ${opts.mnemeVersion}`,
|
|
28
|
+
"// @description Inject Mneme cross-vendor brain (soul prompt) into ChatGPT, Gemini, Claude.ai, Copilot, DeepSeek. No store approval needed.",
|
|
29
|
+
"// @author Mneme",
|
|
30
|
+
"// @match https://chatgpt.com/*",
|
|
31
|
+
"// @match https://chat.openai.com/*",
|
|
32
|
+
"// @match https://gemini.google.com/*",
|
|
33
|
+
"// @match https://aistudio.google.com/*",
|
|
34
|
+
"// @match https://claude.ai/*",
|
|
35
|
+
"// @match https://copilot.microsoft.com/*",
|
|
36
|
+
"// @match https://chat.deepseek.com/*",
|
|
37
|
+
"// @match https://chat.qwenlm.ai/*",
|
|
38
|
+
"// @grant GM_setClipboard",
|
|
39
|
+
"// @grant GM_xmlhttpRequest",
|
|
40
|
+
"// @run-at document-idle",
|
|
41
|
+
"// ==/UserScript==",
|
|
42
|
+
].join("\n");
|
|
43
|
+
const bridgeBlock = opts.bridgeUrl
|
|
44
|
+
? `
|
|
45
|
+
// ββ BRIDGE FETCH (when local Mneme HTTP bridge is reachable) βββββ
|
|
46
|
+
const BRIDGE_URL = ${JSON.stringify(opts.bridgeUrl)};
|
|
47
|
+
const BRIDGE_TOKEN = ${JSON.stringify(opts.bridgeToken ?? "")};
|
|
48
|
+
async function fetchSoulFromBridge() {
|
|
49
|
+
try {
|
|
50
|
+
const res = await fetch(BRIDGE_URL + "/v1/health", {
|
|
51
|
+
headers: BRIDGE_TOKEN ? { Authorization: "Bearer " + BRIDGE_TOKEN } : {}
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) return null;
|
|
54
|
+
// The user must transmit the soul prompt server-side; bridge
|
|
55
|
+
// doesn't yet have a /v1/soul GET. Future: add endpoint.
|
|
56
|
+
return null;
|
|
57
|
+
} catch { return null; }
|
|
58
|
+
}
|
|
59
|
+
`
|
|
60
|
+
: "";
|
|
61
|
+
const body = `
|
|
62
|
+
(function() {
|
|
63
|
+
'use strict';
|
|
64
|
+
|
|
65
|
+
const SITE = (() => {
|
|
66
|
+
const h = location.hostname;
|
|
67
|
+
if (h.includes('chatgpt.com') || h.includes('openai.com')) return 'chatgpt';
|
|
68
|
+
if (h.includes('gemini.google.com') || h.includes('aistudio.google.com')) return 'gemini';
|
|
69
|
+
if (h.includes('claude.ai')) return 'claude-ai';
|
|
70
|
+
if (h.includes('copilot.microsoft.com')) return 'copilot';
|
|
71
|
+
if (h.includes('deepseek.com')) return 'deepseek';
|
|
72
|
+
if (h.includes('qwenlm.ai')) return 'qwen';
|
|
73
|
+
return 'unknown';
|
|
74
|
+
})();
|
|
75
|
+
|
|
76
|
+
// Selector ladder per site -- try each, use the first that matches.
|
|
77
|
+
const SELECTORS = {
|
|
78
|
+
chatgpt: ['div#prompt-textarea', 'textarea[data-id="root"]', 'textarea#prompt-textarea', 'div[contenteditable="true"]'],
|
|
79
|
+
gemini: ['rich-textarea div[contenteditable="true"]', 'rich-textarea', 'textarea'],
|
|
80
|
+
'claude-ai':['div[contenteditable="true"]', 'div[data-placeholder]'],
|
|
81
|
+
copilot: ['textarea[aria-label*="Ask"]', 'textarea'],
|
|
82
|
+
deepseek: ['textarea#chat-input', 'textarea'],
|
|
83
|
+
qwen: ['textarea', 'div[contenteditable="true"]'],
|
|
84
|
+
unknown: ['textarea', 'div[contenteditable="true"]'],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function findInput() {
|
|
88
|
+
const candidates = SELECTORS[SITE] || SELECTORS.unknown;
|
|
89
|
+
for (const sel of candidates) {
|
|
90
|
+
const el = document.querySelector(sel);
|
|
91
|
+
if (el) return el;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function injectText(el, text) {
|
|
97
|
+
if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {
|
|
98
|
+
const setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set
|
|
99
|
+
|| Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
|
|
100
|
+
if (setter) setter.call(el, text);
|
|
101
|
+
else el.value = text;
|
|
102
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
103
|
+
} else {
|
|
104
|
+
// contenteditable -- use execCommand for compat with React-controlled fields
|
|
105
|
+
el.focus();
|
|
106
|
+
try {
|
|
107
|
+
document.execCommand('selectAll', false);
|
|
108
|
+
document.execCommand('insertText', false, text);
|
|
109
|
+
} catch {
|
|
110
|
+
el.textContent = text;
|
|
111
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function makeButton() {
|
|
117
|
+
const btn = document.createElement('button');
|
|
118
|
+
btn.textContent = 'π Inject Mneme Soul';
|
|
119
|
+
btn.style.cssText = \`
|
|
120
|
+
position: fixed; top: 12px; right: 12px; z-index: 99999;
|
|
121
|
+
background: linear-gradient(135deg,#7c3aed,#ec4899);
|
|
122
|
+
color: #fff; font-weight: 600; border: 0; border-radius: 8px;
|
|
123
|
+
padding: 8px 14px; cursor: pointer;
|
|
124
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
125
|
+
box-shadow: 0 4px 14px rgba(124,58,237,0.4);
|
|
126
|
+
font-size: 13px;
|
|
127
|
+
\`;
|
|
128
|
+
btn.title = 'Paste a Mneme soul prompt to inject into the chat input';
|
|
129
|
+
btn.onclick = async () => {
|
|
130
|
+
let soul;
|
|
131
|
+
try { soul = await navigator.clipboard.readText(); }
|
|
132
|
+
catch { soul = prompt('Paste your Mneme soul prompt:'); }
|
|
133
|
+
if (!soul || !soul.includes('MNEME SOUL PROMPT')) {
|
|
134
|
+
alert('That does not look like a Mneme soul prompt. Copy the full text starting with "# 𧬠MNEME SOUL PROMPT".');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const el = findInput();
|
|
138
|
+
if (!el) {
|
|
139
|
+
alert('Could not find the chat input on this page. The site UI may have changed.');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
injectText(el, soul);
|
|
143
|
+
btn.textContent = 'β Soul injected';
|
|
144
|
+
setTimeout(() => { btn.textContent = 'π Inject Mneme Soul'; }, 2500);
|
|
145
|
+
};
|
|
146
|
+
return btn;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function ensureButton() {
|
|
150
|
+
if (document.getElementById('mneme-inject-btn')) return;
|
|
151
|
+
const btn = makeButton();
|
|
152
|
+
btn.id = 'mneme-inject-btn';
|
|
153
|
+
document.body.appendChild(btn);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// React-rendered sites re-mount; observe DOM and re-attach if needed.
|
|
157
|
+
const obs = new MutationObserver(() => ensureButton());
|
|
158
|
+
obs.observe(document.body, { childList: true, subtree: false });
|
|
159
|
+
ensureButton();
|
|
160
|
+
console.log('[Mneme] Soul injector ready -- site=' + SITE);
|
|
161
|
+
})();
|
|
162
|
+
`.trim();
|
|
163
|
+
const content = `${header}\n\n${bridgeBlock}${body}\n`;
|
|
164
|
+
return {
|
|
165
|
+
content,
|
|
166
|
+
filename: `mneme-soul-injector-${opts.mnemeVersion}.user.js`,
|
|
167
|
+
installNote: `Install: (1) Add the Tampermonkey/Violentmonkey extension to your browser. (2) Click the .user.js file -- Tampermonkey will prompt to install. (3) Open ChatGPT/Gemini/Claude.ai -- the π button appears top-right. (4) Copy a Mneme soul prompt, click the button, soul is injected into the chat.`,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=userscript_generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userscript_generator.js","sourceRoot":"","sources":["../../src/permeate/userscript_generator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAuBH,MAAM,UAAU,kBAAkB,CAAC,IAAuB;IACxD,MAAM,MAAM,GAAG;QACb,mBAAmB;QACnB,sCAAsC;QACtC,4DAA4D;QAC5D,oBAAoB,IAAI,CAAC,YAAY,EAAE;QACvC,8IAA8I;QAC9I,wBAAwB;QACxB,wCAAwC;QACxC,4CAA4C;QAC5C,8CAA8C;QAC9C,gDAAgD;QAChD,sCAAsC;QACtC,kDAAkD;QAClD,8CAA8C;QAC9C,2CAA2C;QAC3C,kCAAkC;QAClC,oCAAoC;QACpC,gCAAgC;QAChC,oBAAoB;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS;QAChC,CAAC,CAAC;;qBAEe,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;uBAC5B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;;;;;;;;;;;;CAY5D;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqGd,CAAC,IAAI,EAAE,CAAC;IAEP,MAAM,OAAO,GAAG,GAAG,MAAM,OAAO,WAAW,GAAG,IAAI,IAAI,CAAC;IACvD,OAAO;QACL,OAAO;QACP,QAAQ,EAAE,uBAAuB,IAAI,CAAC,YAAY,UAAU;QAC5D,WAAW,EAAE,sSAAsS;KACpT,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1.75.0 -- VERSION TELEPATHY: Mneme's heartbeat survives the cross-vendor jump.
|
|
3
|
+
*
|
|
4
|
+
* The breakthrough: every soul prompt now carries a compact
|
|
5
|
+
* `## Mneme Heartbeat` section -- local version, npm latest, daemon
|
|
6
|
+
* state, vaccine count, repo fingerprint. The receiving AI (even one
|
|
7
|
+
* that has NEVER seen Mneme) reads the heartbeat and can answer
|
|
8
|
+
* "what version is Mneme on this machine?" / "is there a newer one?"
|
|
9
|
+
* without running any command.
|
|
10
|
+
*
|
|
11
|
+
* npm-latest is cached 1h in `.mneme/telepathy/npm-cache.json` so we
|
|
12
|
+
* don't hammer the registry. Works offline -- if the cache is stale
|
|
13
|
+
* and the network is down, sync_status falls back to "unknown" and
|
|
14
|
+
* the heartbeat still ships.
|
|
15
|
+
*/
|
|
16
|
+
export interface Heartbeat {
|
|
17
|
+
localVersion: string;
|
|
18
|
+
npmLatest: string | null;
|
|
19
|
+
syncStatus: "in-sync" | "behind" | "ahead" | "unknown";
|
|
20
|
+
daemonRunning: boolean;
|
|
21
|
+
vaccineCount: number;
|
|
22
|
+
inboxUnsent: number;
|
|
23
|
+
repoFingerprint: string;
|
|
24
|
+
checkedAt: string;
|
|
25
|
+
}
|
|
26
|
+
export interface HeartbeatInput {
|
|
27
|
+
localVersion: string;
|
|
28
|
+
repoFingerprint: string;
|
|
29
|
+
daemonRunning?: boolean;
|
|
30
|
+
vaccineCount?: number;
|
|
31
|
+
inboxUnsent?: number;
|
|
32
|
+
/** Cache directory. Default `.mneme/telepathy`. */
|
|
33
|
+
cacheDir?: string;
|
|
34
|
+
/** TTL for npm-latest cache. Default 1 hour. */
|
|
35
|
+
cacheTtlMs?: number;
|
|
36
|
+
/** HTTPS timeout when hitting npm registry. Default 1500ms. */
|
|
37
|
+
npmCheckTimeoutMs?: number;
|
|
38
|
+
/** When true, skip network entirely; use only cached npm-latest. */
|
|
39
|
+
offline?: boolean;
|
|
40
|
+
/** Test seam: inject a fake npm fetcher (returns the latest version or null). */
|
|
41
|
+
fetchOverride?: () => Promise<string | null>;
|
|
42
|
+
}
|
|
43
|
+
export declare function generateHeartbeat(input: HeartbeatInput): Promise<Heartbeat>;
|
|
44
|
+
export declare function renderHeartbeatMarkdown(h: Heartbeat): string;
|
|
45
|
+
export declare function parseHeartbeat(text: string): Heartbeat | null;
|
|
46
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../src/telepathy/heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IACvD,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9C;AAwCD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,CA+CjF;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAmC5D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAoC7D"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1.75.0 -- VERSION TELEPATHY: Mneme's heartbeat survives the cross-vendor jump.
|
|
3
|
+
*
|
|
4
|
+
* The breakthrough: every soul prompt now carries a compact
|
|
5
|
+
* `## Mneme Heartbeat` section -- local version, npm latest, daemon
|
|
6
|
+
* state, vaccine count, repo fingerprint. The receiving AI (even one
|
|
7
|
+
* that has NEVER seen Mneme) reads the heartbeat and can answer
|
|
8
|
+
* "what version is Mneme on this machine?" / "is there a newer one?"
|
|
9
|
+
* without running any command.
|
|
10
|
+
*
|
|
11
|
+
* npm-latest is cached 1h in `.mneme/telepathy/npm-cache.json` so we
|
|
12
|
+
* don't hammer the registry. Works offline -- if the cache is stale
|
|
13
|
+
* and the network is down, sync_status falls back to "unknown" and
|
|
14
|
+
* the heartbeat still ships.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { request } from "node:https";
|
|
19
|
+
function cmpVersion(a, b) {
|
|
20
|
+
const ap = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
21
|
+
const bp = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
22
|
+
for (let i = 0; i < Math.max(ap.length, bp.length); i++) {
|
|
23
|
+
const ai = ap[i] ?? 0;
|
|
24
|
+
const bi = bp[i] ?? 0;
|
|
25
|
+
if (ai !== bi)
|
|
26
|
+
return ai < bi ? -1 : 1;
|
|
27
|
+
}
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
function fetchNpmLatest(timeoutMs) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const req = request("https://registry.npmjs.org/mneme-ai/latest", { method: "GET", timeout: timeoutMs }, (res) => {
|
|
33
|
+
let body = "";
|
|
34
|
+
res.on("data", (c) => (body += c));
|
|
35
|
+
res.on("end", () => {
|
|
36
|
+
try {
|
|
37
|
+
const j = JSON.parse(body);
|
|
38
|
+
resolve(typeof j.version === "string" ? j.version : null);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
resolve(null);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
req.on("error", () => resolve(null));
|
|
46
|
+
req.on("timeout", () => {
|
|
47
|
+
req.destroy();
|
|
48
|
+
resolve(null);
|
|
49
|
+
});
|
|
50
|
+
req.end();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export async function generateHeartbeat(input) {
|
|
54
|
+
const cacheDir = input.cacheDir ?? ".mneme/telepathy";
|
|
55
|
+
const cachePath = join(cacheDir, "npm-cache.json");
|
|
56
|
+
const ttl = input.cacheTtlMs ?? 60 * 60 * 1000;
|
|
57
|
+
let npmLatest = null;
|
|
58
|
+
if (existsSync(cachePath)) {
|
|
59
|
+
try {
|
|
60
|
+
const c = JSON.parse(readFileSync(cachePath, "utf8"));
|
|
61
|
+
if (typeof c.version === "string" && typeof c.savedAt === "number" && Date.now() - c.savedAt < ttl) {
|
|
62
|
+
npmLatest = c.version;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// corrupt cache -- ignore, will re-fetch
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (npmLatest === null && !input.offline) {
|
|
70
|
+
npmLatest = input.fetchOverride
|
|
71
|
+
? await input.fetchOverride()
|
|
72
|
+
: await fetchNpmLatest(input.npmCheckTimeoutMs ?? 1500);
|
|
73
|
+
if (npmLatest) {
|
|
74
|
+
try {
|
|
75
|
+
if (!existsSync(cacheDir))
|
|
76
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
77
|
+
writeFileSync(cachePath, JSON.stringify({ version: npmLatest, savedAt: Date.now() }), "utf8");
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// ignore cache-write failures
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let syncStatus = "unknown";
|
|
85
|
+
if (npmLatest) {
|
|
86
|
+
const c = cmpVersion(input.localVersion, npmLatest);
|
|
87
|
+
syncStatus = c === 0 ? "in-sync" : c < 0 ? "behind" : "ahead";
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
localVersion: input.localVersion,
|
|
91
|
+
npmLatest,
|
|
92
|
+
syncStatus,
|
|
93
|
+
daemonRunning: input.daemonRunning ?? false,
|
|
94
|
+
vaccineCount: input.vaccineCount ?? 0,
|
|
95
|
+
inboxUnsent: input.inboxUnsent ?? 0,
|
|
96
|
+
repoFingerprint: input.repoFingerprint,
|
|
97
|
+
checkedAt: new Date().toISOString(),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function renderHeartbeatMarkdown(h) {
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push(`## Mneme Heartbeat (version telepathy)`);
|
|
103
|
+
lines.push(`local_version: ${h.localVersion}`);
|
|
104
|
+
lines.push(`npm_latest: ${h.npmLatest ?? "(unknown)"}`);
|
|
105
|
+
const statusLabel = h.syncStatus === "in-sync"
|
|
106
|
+
? "in-sync β"
|
|
107
|
+
: h.syncStatus === "behind"
|
|
108
|
+
? "BEHIND β "
|
|
109
|
+
: h.syncStatus === "ahead"
|
|
110
|
+
? "ahead (pre-release)"
|
|
111
|
+
: "unknown";
|
|
112
|
+
lines.push(`sync_status: ${statusLabel}`);
|
|
113
|
+
lines.push(`daemon: ${h.daemonRunning ? "running" : "stopped"}`);
|
|
114
|
+
lines.push(`vaccines: ${h.vaccineCount}`);
|
|
115
|
+
lines.push(`inbox_unsent: ${h.inboxUnsent}`);
|
|
116
|
+
lines.push(`repo_fingerprint: ${h.repoFingerprint}`);
|
|
117
|
+
lines.push(`checked_at: ${h.checkedAt}`);
|
|
118
|
+
lines.push(``);
|
|
119
|
+
lines.push(`### What this means (receiving AI, READ THIS):`);
|
|
120
|
+
if (h.syncStatus === "behind") {
|
|
121
|
+
lines.push(`The user's local Mneme is at v${h.localVersion}, but npm latest is v${h.npmLatest}. Tell the user: "your local Mneme is behind -- ask your editor AI to run mneme.system.upgrade".`);
|
|
122
|
+
}
|
|
123
|
+
else if (h.syncStatus === "in-sync") {
|
|
124
|
+
lines.push(`The user's local Mneme is in-sync with npm. The command catalog in this soul prompt is current.`);
|
|
125
|
+
}
|
|
126
|
+
else if (h.syncStatus === "ahead") {
|
|
127
|
+
lines.push(`The user's local Mneme is ahead of npm latest -- they're running a dev build. New commands may not be documented in the npm-published manifest yet.`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
lines.push(`Could not reach npm; treat the local version as authoritative.`);
|
|
131
|
+
}
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
134
|
+
export function parseHeartbeat(text) {
|
|
135
|
+
// Find the heartbeat section; stop at next "## " heading or "---" divider.
|
|
136
|
+
const startIdx = text.indexOf("## Mneme Heartbeat");
|
|
137
|
+
if (startIdx < 0)
|
|
138
|
+
return null;
|
|
139
|
+
const tail = text.slice(startIdx);
|
|
140
|
+
const stopIdx = tail.search(/^(##\s|---)/m);
|
|
141
|
+
// skip the heading itself when looking for stop, so search from offset 1
|
|
142
|
+
const subTail = tail.slice(1);
|
|
143
|
+
const stop = subTail.search(/^(##\s|---)/m);
|
|
144
|
+
const body = stop >= 0 ? tail.slice(0, stop + 1) : tail;
|
|
145
|
+
void stopIdx;
|
|
146
|
+
const get = (key) => {
|
|
147
|
+
const m = body.match(new RegExp(`^${key}:\\s*(.+)$`, "m"));
|
|
148
|
+
return m ? m[1].trim() : null;
|
|
149
|
+
};
|
|
150
|
+
const localVersion = get("local_version");
|
|
151
|
+
const fingerprint = get("repo_fingerprint");
|
|
152
|
+
if (!localVersion || !fingerprint)
|
|
153
|
+
return null;
|
|
154
|
+
const npmRaw = get("npm_latest");
|
|
155
|
+
const npmLatest = npmRaw && npmRaw !== "(unknown)" ? npmRaw : null;
|
|
156
|
+
const statusRaw = get("sync_status") ?? "unknown";
|
|
157
|
+
let syncStatus = "unknown";
|
|
158
|
+
if (statusRaw.startsWith("in-sync"))
|
|
159
|
+
syncStatus = "in-sync";
|
|
160
|
+
else if (statusRaw.startsWith("BEHIND"))
|
|
161
|
+
syncStatus = "behind";
|
|
162
|
+
else if (statusRaw.startsWith("ahead"))
|
|
163
|
+
syncStatus = "ahead";
|
|
164
|
+
return {
|
|
165
|
+
localVersion,
|
|
166
|
+
npmLatest,
|
|
167
|
+
syncStatus,
|
|
168
|
+
daemonRunning: get("daemon") === "running",
|
|
169
|
+
vaccineCount: parseInt(get("vaccines") ?? "0", 10) || 0,
|
|
170
|
+
inboxUnsent: parseInt(get("inbox_unsent") ?? "0", 10) || 0,
|
|
171
|
+
repoFingerprint: fingerprint,
|
|
172
|
+
checkedAt: get("checked_at") ?? new Date().toISOString(),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/telepathy/heartbeat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telepathy/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/telepathy/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telepathy.test.d.ts","sourceRoot":"","sources":["../../src/telepathy/telepathy.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mkdtempSync, writeFileSync, mkdirSync, readFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { generateHeartbeat, renderHeartbeatMarkdown, parseHeartbeat } from "./heartbeat.js";
|
|
6
|
+
function tmpCacheDir() {
|
|
7
|
+
const d = mkdtempSync(join(tmpdir(), "mneme-telepathy-"));
|
|
8
|
+
return join(d, "telepathy");
|
|
9
|
+
}
|
|
10
|
+
describe("VERSION TELEPATHY heartbeat", () => {
|
|
11
|
+
it("offline mode returns unknown sync status", async () => {
|
|
12
|
+
const h = await generateHeartbeat({
|
|
13
|
+
localVersion: "1.75.0",
|
|
14
|
+
repoFingerprint: "abc123",
|
|
15
|
+
offline: true,
|
|
16
|
+
cacheDir: tmpCacheDir(),
|
|
17
|
+
});
|
|
18
|
+
expect(h.syncStatus).toBe("unknown");
|
|
19
|
+
expect(h.npmLatest).toBeNull();
|
|
20
|
+
expect(h.localVersion).toBe("1.75.0");
|
|
21
|
+
});
|
|
22
|
+
it("in-sync when local == npm latest (from cache)", async () => {
|
|
23
|
+
const dir = tmpCacheDir();
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
writeFileSync(join(dir, "npm-cache.json"), JSON.stringify({ version: "1.75.0", savedAt: Date.now() }));
|
|
26
|
+
const h = await generateHeartbeat({ localVersion: "1.75.0", repoFingerprint: "abc", cacheDir: dir, offline: true });
|
|
27
|
+
expect(h.syncStatus).toBe("in-sync");
|
|
28
|
+
expect(h.npmLatest).toBe("1.75.0");
|
|
29
|
+
});
|
|
30
|
+
it("behind when local < npm latest", async () => {
|
|
31
|
+
const dir = tmpCacheDir();
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
writeFileSync(join(dir, "npm-cache.json"), JSON.stringify({ version: "2.0.0", savedAt: Date.now() }));
|
|
34
|
+
const h = await generateHeartbeat({ localVersion: "1.75.0", repoFingerprint: "abc", cacheDir: dir, offline: true });
|
|
35
|
+
expect(h.syncStatus).toBe("behind");
|
|
36
|
+
});
|
|
37
|
+
it("ahead when local > npm latest (dev build)", async () => {
|
|
38
|
+
const dir = tmpCacheDir();
|
|
39
|
+
mkdirSync(dir, { recursive: true });
|
|
40
|
+
writeFileSync(join(dir, "npm-cache.json"), JSON.stringify({ version: "1.74.0", savedAt: Date.now() }));
|
|
41
|
+
const h = await generateHeartbeat({ localVersion: "1.75.0", repoFingerprint: "abc", cacheDir: dir, offline: true });
|
|
42
|
+
expect(h.syncStatus).toBe("ahead");
|
|
43
|
+
});
|
|
44
|
+
it("expired cache + offline = unknown (cache TTL respected)", async () => {
|
|
45
|
+
const dir = tmpCacheDir();
|
|
46
|
+
mkdirSync(dir, { recursive: true });
|
|
47
|
+
writeFileSync(join(dir, "npm-cache.json"), JSON.stringify({ version: "1.75.0", savedAt: Date.now() - 60 * 60 * 1000 * 2 }));
|
|
48
|
+
const h = await generateHeartbeat({
|
|
49
|
+
localVersion: "1.75.0",
|
|
50
|
+
repoFingerprint: "abc",
|
|
51
|
+
cacheDir: dir,
|
|
52
|
+
offline: true,
|
|
53
|
+
cacheTtlMs: 60 * 60 * 1000,
|
|
54
|
+
});
|
|
55
|
+
expect(h.npmLatest).toBeNull();
|
|
56
|
+
expect(h.syncStatus).toBe("unknown");
|
|
57
|
+
});
|
|
58
|
+
it("corrupt cache falls back gracefully (no throw)", async () => {
|
|
59
|
+
const dir = tmpCacheDir();
|
|
60
|
+
mkdirSync(dir, { recursive: true });
|
|
61
|
+
writeFileSync(join(dir, "npm-cache.json"), "{not valid json");
|
|
62
|
+
const h = await generateHeartbeat({ localVersion: "1.75.0", repoFingerprint: "abc", cacheDir: dir, offline: true });
|
|
63
|
+
expect(h.syncStatus).toBe("unknown");
|
|
64
|
+
});
|
|
65
|
+
it("fetchOverride is called and cache is populated", async () => {
|
|
66
|
+
const dir = tmpCacheDir();
|
|
67
|
+
let calls = 0;
|
|
68
|
+
const h = await generateHeartbeat({
|
|
69
|
+
localVersion: "1.75.0",
|
|
70
|
+
repoFingerprint: "abc",
|
|
71
|
+
cacheDir: dir,
|
|
72
|
+
fetchOverride: async () => {
|
|
73
|
+
calls++;
|
|
74
|
+
return "1.99.0";
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
expect(calls).toBe(1);
|
|
78
|
+
expect(h.npmLatest).toBe("1.99.0");
|
|
79
|
+
expect(h.syncStatus).toBe("behind");
|
|
80
|
+
expect(existsSync(join(dir, "npm-cache.json"))).toBe(true);
|
|
81
|
+
const cached = JSON.parse(readFileSync(join(dir, "npm-cache.json"), "utf8"));
|
|
82
|
+
expect(cached.version).toBe("1.99.0");
|
|
83
|
+
});
|
|
84
|
+
it("fetchOverride returning null still produces a heartbeat", async () => {
|
|
85
|
+
const dir = tmpCacheDir();
|
|
86
|
+
const h = await generateHeartbeat({
|
|
87
|
+
localVersion: "1.75.0",
|
|
88
|
+
repoFingerprint: "abc",
|
|
89
|
+
cacheDir: dir,
|
|
90
|
+
fetchOverride: async () => null,
|
|
91
|
+
});
|
|
92
|
+
expect(h.npmLatest).toBeNull();
|
|
93
|
+
expect(h.syncStatus).toBe("unknown");
|
|
94
|
+
});
|
|
95
|
+
it("renderHeartbeatMarkdown includes all key fields", () => {
|
|
96
|
+
const md = renderHeartbeatMarkdown({
|
|
97
|
+
localVersion: "1.75.0",
|
|
98
|
+
npmLatest: "1.75.0",
|
|
99
|
+
syncStatus: "in-sync",
|
|
100
|
+
daemonRunning: true,
|
|
101
|
+
vaccineCount: 8,
|
|
102
|
+
inboxUnsent: 2,
|
|
103
|
+
repoFingerprint: "abc123",
|
|
104
|
+
checkedAt: "2026-05-12T15:00:00.000Z",
|
|
105
|
+
});
|
|
106
|
+
expect(md).toContain("local_version: 1.75.0");
|
|
107
|
+
expect(md).toContain("npm_latest: 1.75.0");
|
|
108
|
+
expect(md).toContain("in-sync");
|
|
109
|
+
expect(md).toContain("repo_fingerprint: abc123");
|
|
110
|
+
expect(md).toContain("vaccines: 8");
|
|
111
|
+
});
|
|
112
|
+
it("renderHeartbeatMarkdown behind status surfaces upgrade instruction", () => {
|
|
113
|
+
const md = renderHeartbeatMarkdown({
|
|
114
|
+
localVersion: "1.50.0",
|
|
115
|
+
npmLatest: "1.75.0",
|
|
116
|
+
syncStatus: "behind",
|
|
117
|
+
daemonRunning: false,
|
|
118
|
+
vaccineCount: 0,
|
|
119
|
+
inboxUnsent: 0,
|
|
120
|
+
repoFingerprint: "x",
|
|
121
|
+
checkedAt: "2026-05-12T15:00:00.000Z",
|
|
122
|
+
});
|
|
123
|
+
expect(md).toContain("BEHIND");
|
|
124
|
+
expect(md).toMatch(/mneme\.system\.upgrade/i);
|
|
125
|
+
});
|
|
126
|
+
it("parseHeartbeat round-trips a rendered heartbeat", () => {
|
|
127
|
+
const h = {
|
|
128
|
+
localVersion: "1.75.0",
|
|
129
|
+
npmLatest: "1.75.0",
|
|
130
|
+
syncStatus: "in-sync",
|
|
131
|
+
daemonRunning: true,
|
|
132
|
+
vaccineCount: 8,
|
|
133
|
+
inboxUnsent: 2,
|
|
134
|
+
repoFingerprint: "abc123",
|
|
135
|
+
checkedAt: "2026-05-12T15:00:00.000Z",
|
|
136
|
+
};
|
|
137
|
+
const md = renderHeartbeatMarkdown(h);
|
|
138
|
+
const parsed = parseHeartbeat(md);
|
|
139
|
+
expect(parsed).not.toBeNull();
|
|
140
|
+
expect(parsed.localVersion).toBe("1.75.0");
|
|
141
|
+
expect(parsed.npmLatest).toBe("1.75.0");
|
|
142
|
+
expect(parsed.syncStatus).toBe("in-sync");
|
|
143
|
+
expect(parsed.daemonRunning).toBe(true);
|
|
144
|
+
expect(parsed.vaccineCount).toBe(8);
|
|
145
|
+
expect(parsed.repoFingerprint).toBe("abc123");
|
|
146
|
+
});
|
|
147
|
+
it("parseHeartbeat returns null when section is missing", () => {
|
|
148
|
+
expect(parseHeartbeat("no heartbeat here")).toBeNull();
|
|
149
|
+
});
|
|
150
|
+
it("parseHeartbeat handles npm_latest=(unknown)", () => {
|
|
151
|
+
const md = renderHeartbeatMarkdown({
|
|
152
|
+
localVersion: "1.75.0",
|
|
153
|
+
npmLatest: null,
|
|
154
|
+
syncStatus: "unknown",
|
|
155
|
+
daemonRunning: false,
|
|
156
|
+
vaccineCount: 0,
|
|
157
|
+
inboxUnsent: 0,
|
|
158
|
+
repoFingerprint: "x",
|
|
159
|
+
checkedAt: "2026-05-12T15:00:00.000Z",
|
|
160
|
+
});
|
|
161
|
+
const parsed = parseHeartbeat(md);
|
|
162
|
+
expect(parsed).not.toBeNull();
|
|
163
|
+
expect(parsed.npmLatest).toBeNull();
|
|
164
|
+
expect(parsed.syncStatus).toBe("unknown");
|
|
165
|
+
});
|
|
166
|
+
it("parseHeartbeat extracts from a longer text with surrounding sections", () => {
|
|
167
|
+
const text = [
|
|
168
|
+
"# 𧬠MNEME SOUL PROMPT",
|
|
169
|
+
"",
|
|
170
|
+
"## Context",
|
|
171
|
+
"blah blah",
|
|
172
|
+
"",
|
|
173
|
+
"## Mneme Heartbeat (version telepathy)",
|
|
174
|
+
"local_version: 1.75.0",
|
|
175
|
+
"npm_latest: 1.75.0",
|
|
176
|
+
"sync_status: in-sync β",
|
|
177
|
+
"daemon: running",
|
|
178
|
+
"vaccines: 8",
|
|
179
|
+
"inbox_unsent: 2",
|
|
180
|
+
"repo_fingerprint: zz9plural",
|
|
181
|
+
"checked_at: 2026-05-12T15:00:00.000Z",
|
|
182
|
+
"",
|
|
183
|
+
"### What this meansβ¦",
|
|
184
|
+
"irrelevant",
|
|
185
|
+
"",
|
|
186
|
+
"---",
|
|
187
|
+
"ID: deadbeef",
|
|
188
|
+
].join("\n");
|
|
189
|
+
const p = parseHeartbeat(text);
|
|
190
|
+
expect(p).not.toBeNull();
|
|
191
|
+
expect(p.localVersion).toBe("1.75.0");
|
|
192
|
+
expect(p.repoFingerprint).toBe("zz9plural");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=telepathy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telepathy.test.js","sourceRoot":"","sources":["../../src/telepathy/telepathy.test.ts"],"names":[],"mappings":"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"}
|