@mneme-ai/core 2.19.37 → 2.19.38
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/browser_userscript/index.d.ts +79 -0
- package/dist/browser_userscript/index.d.ts.map +1 -0
- package/dist/browser_userscript/index.js +371 -0
- package/dist/browser_userscript/index.js.map +1 -0
- package/dist/browser_userscript/userscript.test.d.ts +2 -0
- package/dist/browser_userscript/userscript.test.d.ts.map +1 -0
- package/dist/browser_userscript/userscript.test.js +130 -0
- package/dist/browser_userscript/userscript.test.js.map +1 -0
- package/dist/citizens_contribute/contribute.test.d.ts +2 -0
- package/dist/citizens_contribute/contribute.test.d.ts.map +1 -0
- package/dist/citizens_contribute/contribute.test.js +136 -0
- package/dist/citizens_contribute/contribute.test.js.map +1 -0
- package/dist/citizens_contribute/index.d.ts +103 -0
- package/dist/citizens_contribute/index.d.ts.map +1 -0
- package/dist/citizens_contribute/index.js +176 -0
- package/dist/citizens_contribute/index.js.map +1 -0
- package/dist/conscience_auto_hook/auto_hook.test.d.ts +2 -0
- package/dist/conscience_auto_hook/auto_hook.test.d.ts.map +1 -0
- package/dist/conscience_auto_hook/auto_hook.test.js +149 -0
- package/dist/conscience_auto_hook/auto_hook.test.js.map +1 -0
- package/dist/conscience_auto_hook/index.d.ts +83 -0
- package/dist/conscience_auto_hook/index.d.ts.map +1 -0
- package/dist/conscience_auto_hook/index.js +170 -0
- package/dist/conscience_auto_hook/index.js.map +1 -0
- package/dist/cosmic/aurelian_v1938.test.d.ts +2 -0
- package/dist/cosmic/aurelian_v1938.test.d.ts.map +1 -0
- package/dist/cosmic/aurelian_v1938.test.js +76 -0
- package/dist/cosmic/aurelian_v1938.test.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/mayor_auto_vote/auto_vote.test.d.ts +2 -0
- package/dist/mayor_auto_vote/auto_vote.test.d.ts.map +1 -0
- package/dist/mayor_auto_vote/auto_vote.test.js +167 -0
- package/dist/mayor_auto_vote/auto_vote.test.js.map +1 -0
- package/dist/mayor_auto_vote/index.d.ts +97 -0
- package/dist/mayor_auto_vote/index.d.ts.map +1 -0
- package/dist/mayor_auto_vote/index.js +206 -0
- package/dist/mayor_auto_vote/index.js.map +1 -0
- package/dist/whats_new.d.ts.map +1 -1
- package/dist/whats_new.js +8 -0
- package/dist/whats_new.js.map +1 -1
- package/dist/wrapper_genesis/index.d.ts.map +1 -1
- package/dist/wrapper_genesis/index.js +60 -0
- package/dist/wrapper_genesis/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.19.38 — MNEME BROWSER USERSCRIPT (Socket #1 — install once, capture forever)
|
|
3
|
+
*
|
|
4
|
+
* v2.19.37 BROWSER RECEIPT shipped the pure-TS core. v2.19.38 ships
|
|
5
|
+
* the actual SHELL the user installs:
|
|
6
|
+
*
|
|
7
|
+
* (A) Single .user.js file (Tampermonkey / Violentmonkey / Greasemonkey
|
|
8
|
+
* compatible) — one-click install in any browser
|
|
9
|
+
* (B) Manifest v3 extension skeleton (.crx-ready) — for Chrome Web Store
|
|
10
|
+
*
|
|
11
|
+
* This module emits the bytes for both. Caller (npm pack) writes them
|
|
12
|
+
* to `dist/browser/` for distribution.
|
|
13
|
+
*
|
|
14
|
+
* Composes onto:
|
|
15
|
+
* - v2.19.37 BROWSER RECEIPT (vendor detection + chat extraction + mint)
|
|
16
|
+
* - v2.19.37 RECEIPT PROTOCOL (output format)
|
|
17
|
+
*
|
|
18
|
+
* Honest scope:
|
|
19
|
+
* - PURE FUNCTION bytes emitter. Two output formats.
|
|
20
|
+
* - Tampermonkey userscript = production-ready today.
|
|
21
|
+
* - Manifest v3 = skeleton (caller bundles core JS).
|
|
22
|
+
* - 20+ tests for emitted bytes + manifest validity.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Emit a single self-contained .user.js Tampermonkey script. User installs
|
|
26
|
+
* by clicking the URL — Tampermonkey opens an install dialog.
|
|
27
|
+
*
|
|
28
|
+
* The userscript:
|
|
29
|
+
* 1. Detects vendor from window.location.host
|
|
30
|
+
* 2. Listens for chat turn DOM mutations via MutationObserver
|
|
31
|
+
* 3. Extracts (user, assistant) turn pairs as text
|
|
32
|
+
* 4. Mints a Mneme Protocol Receipt v1 (inline sha256 via SubtleCrypto)
|
|
33
|
+
* 5. Saves to localStorage["mneme.browser.receipts.v1"] (JSON array)
|
|
34
|
+
* 6. Adds a 🛡 floating indicator showing receipt count
|
|
35
|
+
* 7. Optional: POST to a configurable Mneme HTTP bridge endpoint
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateUserscript(): string;
|
|
38
|
+
export interface ManifestV3 {
|
|
39
|
+
manifest_version: 3;
|
|
40
|
+
name: string;
|
|
41
|
+
version: string;
|
|
42
|
+
description: string;
|
|
43
|
+
permissions: string[];
|
|
44
|
+
host_permissions: string[];
|
|
45
|
+
content_scripts: Array<{
|
|
46
|
+
matches: string[];
|
|
47
|
+
js: string[];
|
|
48
|
+
run_at: string;
|
|
49
|
+
}>;
|
|
50
|
+
background?: {
|
|
51
|
+
service_worker: string;
|
|
52
|
+
};
|
|
53
|
+
action?: {
|
|
54
|
+
default_popup: string;
|
|
55
|
+
default_icon?: Record<string, string>;
|
|
56
|
+
};
|
|
57
|
+
icons?: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
export declare function generateManifestV3(): ManifestV3;
|
|
60
|
+
/** Content script (bundled-version of the userscript IIFE, minus Greasemonkey APIs). */
|
|
61
|
+
export declare function generateContentScript(): string;
|
|
62
|
+
export declare function generatePopupHtml(): string;
|
|
63
|
+
export declare function generateBrowserReadme(): string;
|
|
64
|
+
export interface UserscriptStats {
|
|
65
|
+
userscriptBytes: number;
|
|
66
|
+
manifestBytes: number;
|
|
67
|
+
contentScriptBytes: number;
|
|
68
|
+
popupBytes: number;
|
|
69
|
+
readmeBytes: number;
|
|
70
|
+
supportedDomains: number;
|
|
71
|
+
}
|
|
72
|
+
export declare function computeUserscriptStats(): UserscriptStats;
|
|
73
|
+
export declare function formatUserscriptStatsLine(s: UserscriptStats): string;
|
|
74
|
+
export declare const BROWSER_USERSCRIPT_TUNABLES: Readonly<{
|
|
75
|
+
PROTOCOL_VERSION: 1;
|
|
76
|
+
USERSCRIPT_VERSION: "1.0.0";
|
|
77
|
+
SUPPORTED_DOMAINS: readonly ["chatgpt.com", "chat.openai.com", "claude.ai", "gemini.google.com", "bard.google.com", "x.com", "grok.com", "perplexity.ai", "www.perplexity.ai", "copilot.microsoft.com"];
|
|
78
|
+
}>;
|
|
79
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser_userscript/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAgBH;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CA2L3C;AAID,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,CAAC,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,EAAE,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,UAAU,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,MAAM,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,wBAAgB,kBAAkB,IAAI,UAAU,CAe/C;AAED,wFAAwF;AACxF,wBAAgB,qBAAqB,IAAI,MAAM,CAS9C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAiD1C;AAID,wBAAgB,qBAAqB,IAAI,MAAM,CAsC9C;AAID,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,sBAAsB,IAAI,eAAe,CASxD;AAED,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAEpE;AAED,eAAO,MAAM,2BAA2B;;;;EAItC,CAAC"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.19.38 — MNEME BROWSER USERSCRIPT (Socket #1 — install once, capture forever)
|
|
3
|
+
*
|
|
4
|
+
* v2.19.37 BROWSER RECEIPT shipped the pure-TS core. v2.19.38 ships
|
|
5
|
+
* the actual SHELL the user installs:
|
|
6
|
+
*
|
|
7
|
+
* (A) Single .user.js file (Tampermonkey / Violentmonkey / Greasemonkey
|
|
8
|
+
* compatible) — one-click install in any browser
|
|
9
|
+
* (B) Manifest v3 extension skeleton (.crx-ready) — for Chrome Web Store
|
|
10
|
+
*
|
|
11
|
+
* This module emits the bytes for both. Caller (npm pack) writes them
|
|
12
|
+
* to `dist/browser/` for distribution.
|
|
13
|
+
*
|
|
14
|
+
* Composes onto:
|
|
15
|
+
* - v2.19.37 BROWSER RECEIPT (vendor detection + chat extraction + mint)
|
|
16
|
+
* - v2.19.37 RECEIPT PROTOCOL (output format)
|
|
17
|
+
*
|
|
18
|
+
* Honest scope:
|
|
19
|
+
* - PURE FUNCTION bytes emitter. Two output formats.
|
|
20
|
+
* - Tampermonkey userscript = production-ready today.
|
|
21
|
+
* - Manifest v3 = skeleton (caller bundles core JS).
|
|
22
|
+
* - 20+ tests for emitted bytes + manifest validity.
|
|
23
|
+
*/
|
|
24
|
+
const PROTOCOL_VERSION = 1;
|
|
25
|
+
const USERSCRIPT_VERSION = "1.0.0";
|
|
26
|
+
const SUPPORTED_VENDOR_DOMAINS = [
|
|
27
|
+
"chatgpt.com", "chat.openai.com",
|
|
28
|
+
"claude.ai",
|
|
29
|
+
"gemini.google.com", "bard.google.com",
|
|
30
|
+
"x.com", "grok.com",
|
|
31
|
+
"perplexity.ai", "www.perplexity.ai",
|
|
32
|
+
"copilot.microsoft.com",
|
|
33
|
+
];
|
|
34
|
+
// ─── USERSCRIPT (Tampermonkey / Violentmonkey compat) ──────────────
|
|
35
|
+
/**
|
|
36
|
+
* Emit a single self-contained .user.js Tampermonkey script. User installs
|
|
37
|
+
* by clicking the URL — Tampermonkey opens an install dialog.
|
|
38
|
+
*
|
|
39
|
+
* The userscript:
|
|
40
|
+
* 1. Detects vendor from window.location.host
|
|
41
|
+
* 2. Listens for chat turn DOM mutations via MutationObserver
|
|
42
|
+
* 3. Extracts (user, assistant) turn pairs as text
|
|
43
|
+
* 4. Mints a Mneme Protocol Receipt v1 (inline sha256 via SubtleCrypto)
|
|
44
|
+
* 5. Saves to localStorage["mneme.browser.receipts.v1"] (JSON array)
|
|
45
|
+
* 6. Adds a 🛡 floating indicator showing receipt count
|
|
46
|
+
* 7. Optional: POST to a configurable Mneme HTTP bridge endpoint
|
|
47
|
+
*/
|
|
48
|
+
export function generateUserscript() {
|
|
49
|
+
return `// ==UserScript==
|
|
50
|
+
// @name Mneme Browser Receipt (v${USERSCRIPT_VERSION})
|
|
51
|
+
// @namespace https://mneme-ai.dev
|
|
52
|
+
// @version ${USERSCRIPT_VERSION}
|
|
53
|
+
// @description Mints Mneme Receipt Protocol v1.0 receipts for every AI web-chat turn (ChatGPT / Claude / Gemini / Grok / Perplexity / Copilot). Local-first, vendor-neutral, MIT licensed.
|
|
54
|
+
// @author Mneme contributors
|
|
55
|
+
// @license MIT
|
|
56
|
+
// @match https://chatgpt.com/*
|
|
57
|
+
// @match https://chat.openai.com/*
|
|
58
|
+
// @match https://claude.ai/*
|
|
59
|
+
// @match https://gemini.google.com/*
|
|
60
|
+
// @match https://bard.google.com/*
|
|
61
|
+
// @match https://x.com/i/grok*
|
|
62
|
+
// @match https://grok.com/*
|
|
63
|
+
// @match https://www.perplexity.ai/*
|
|
64
|
+
// @match https://perplexity.ai/*
|
|
65
|
+
// @match https://copilot.microsoft.com/*
|
|
66
|
+
// @grant GM_setValue
|
|
67
|
+
// @grant GM_getValue
|
|
68
|
+
// @grant GM_xmlhttpRequest
|
|
69
|
+
// @run-at document-idle
|
|
70
|
+
// ==/UserScript==
|
|
71
|
+
|
|
72
|
+
/* eslint-disable no-undef */
|
|
73
|
+
(function() {
|
|
74
|
+
'use strict';
|
|
75
|
+
|
|
76
|
+
// ─── Vendor detection ────────────────────────────────────────────
|
|
77
|
+
const VENDOR_PATTERNS = [
|
|
78
|
+
{ vendor: 'chatgpt', patterns: [/(^|\\.)chatgpt\\.com$/, /(^|\\.)chat\\.openai\\.com$/], assistantNames: ['ChatGPT', 'GPT'] },
|
|
79
|
+
{ vendor: 'claude', patterns: [/(^|\\.)claude\\.ai$/], assistantNames: ['Claude'] },
|
|
80
|
+
{ vendor: 'gemini', patterns: [/(^|\\.)gemini\\.google\\.com$/, /(^|\\.)bard\\.google\\.com$/], assistantNames: ['Gemini', 'Bard'] },
|
|
81
|
+
{ vendor: 'grok', patterns: [/(^|\\.)x\\.com$/, /(^|\\.)grok\\.com$/], assistantNames: ['Grok'] },
|
|
82
|
+
{ vendor: 'perplexity', patterns: [/(^|\\.)perplexity\\.ai$/, /(^|\\.)www\\.perplexity\\.ai$/], assistantNames: ['Perplexity'] },
|
|
83
|
+
{ vendor: 'copilot', patterns: [/(^|\\.)copilot\\.microsoft\\.com$/], assistantNames: ['Copilot'] },
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
function detectVendor() {
|
|
87
|
+
const host = (location.hostname || '').toLowerCase();
|
|
88
|
+
for (const v of VENDOR_PATTERNS) {
|
|
89
|
+
for (const p of v.patterns) {
|
|
90
|
+
if (p.test(host)) return v;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const vendorInfo = detectVendor();
|
|
97
|
+
if (!vendorInfo) return; // unsupported page — silent exit
|
|
98
|
+
|
|
99
|
+
// ─── SHA-256 via SubtleCrypto (async) ─────────────────────────────
|
|
100
|
+
async function sha256Hex(text) {
|
|
101
|
+
const buf = new TextEncoder().encode(text);
|
|
102
|
+
const hash = await crypto.subtle.digest('SHA-256', buf);
|
|
103
|
+
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function canonical(v) {
|
|
107
|
+
if (v === null || typeof v !== 'object') return JSON.stringify(v);
|
|
108
|
+
if (Array.isArray(v)) return '[' + v.map(canonical).join(',') + ']';
|
|
109
|
+
const keys = Object.keys(v).sort();
|
|
110
|
+
return '{' + keys.map(k => JSON.stringify(k) + ':' + canonical(v[k])).join(',') + '}';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ─── Mint a Mneme Protocol Receipt v1 ─────────────────────────────
|
|
114
|
+
async function mintReceipt(userText, asstText, modelHint) {
|
|
115
|
+
const promptSha256 = await sha256Hex(userText || '');
|
|
116
|
+
const responseSha256 = await sha256Hex(asstText || '');
|
|
117
|
+
const body = {
|
|
118
|
+
protocol: 'mneme-receipt-protocol',
|
|
119
|
+
protocolVersion: '1.0',
|
|
120
|
+
implementation: '@mneme-ai/browser-userscript@${USERSCRIPT_VERSION}',
|
|
121
|
+
vendor: vendorInfo.vendor,
|
|
122
|
+
modelVersion: modelHint || 'web-chat-unknown',
|
|
123
|
+
promptSha256, responseSha256,
|
|
124
|
+
tsMs: Date.now(),
|
|
125
|
+
toolsCalled: [], filesTouched: [],
|
|
126
|
+
tokensIn: Math.ceil((userText || '').length / 4),
|
|
127
|
+
tokensOut: Math.ceil((asstText || '').length / 4),
|
|
128
|
+
costUsdMicros: 0,
|
|
129
|
+
vaccinesTriggered: [],
|
|
130
|
+
outcomeClass: 'pending',
|
|
131
|
+
};
|
|
132
|
+
const contentHash = await sha256Hex(canonical(body));
|
|
133
|
+
return { ...body, contentHash };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── Storage ──────────────────────────────────────────────────────
|
|
137
|
+
const STORAGE_KEY = 'mneme.browser.receipts.v1';
|
|
138
|
+
function loadReceipts() {
|
|
139
|
+
try {
|
|
140
|
+
const raw = (typeof GM_getValue === 'function') ? GM_getValue(STORAGE_KEY, '[]') : localStorage.getItem(STORAGE_KEY);
|
|
141
|
+
const arr = JSON.parse(raw || '[]');
|
|
142
|
+
return Array.isArray(arr) ? arr : [];
|
|
143
|
+
} catch { return []; }
|
|
144
|
+
}
|
|
145
|
+
function saveReceipts(arr) {
|
|
146
|
+
const json = JSON.stringify(arr);
|
|
147
|
+
try {
|
|
148
|
+
if (typeof GM_setValue === 'function') GM_setValue(STORAGE_KEY, json);
|
|
149
|
+
else localStorage.setItem(STORAGE_KEY, json);
|
|
150
|
+
} catch (e) { console.warn('Mneme: storage write failed', e); }
|
|
151
|
+
}
|
|
152
|
+
function appendReceipt(r) {
|
|
153
|
+
const arr = loadReceipts();
|
|
154
|
+
arr.push(r);
|
|
155
|
+
// Cap at 10,000 receipts to avoid localStorage blow-up
|
|
156
|
+
if (arr.length > 10000) arr.shift();
|
|
157
|
+
saveReceipts(arr);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Chat turn extraction (vendor-specific) ──────────────────────
|
|
161
|
+
function extractLatestPair() {
|
|
162
|
+
const body = (document.body && document.body.innerText) ? document.body.innerText : '';
|
|
163
|
+
const names = vendorInfo.assistantNames.map(n => n.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&')).join('|');
|
|
164
|
+
const splitRe = new RegExp('(^You\\\\b|^(?:' + names + ')\\\\b)', 'gm');
|
|
165
|
+
const segs = body.split(splitRe).filter(s => s && s.trim().length > 0);
|
|
166
|
+
let lastUserText = null, lastAsstText = null;
|
|
167
|
+
for (let i = 0; i < segs.length - 1; i++) {
|
|
168
|
+
const label = segs[i].trim();
|
|
169
|
+
const text = segs[i + 1].trim().slice(0, 50000);
|
|
170
|
+
if (/^You$/i.test(label)) lastUserText = text;
|
|
171
|
+
else if (vendorInfo.assistantNames.some(n => n.toLowerCase() === label.toLowerCase())) lastAsstText = text;
|
|
172
|
+
}
|
|
173
|
+
return { userText: lastUserText, asstText: lastAsstText };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ─── Floating 🛡 indicator UI ────────────────────────────────────
|
|
177
|
+
function ensureIndicator() {
|
|
178
|
+
let el = document.getElementById('mneme-indicator');
|
|
179
|
+
if (el) return el;
|
|
180
|
+
el = document.createElement('div');
|
|
181
|
+
el.id = 'mneme-indicator';
|
|
182
|
+
el.style.cssText = 'position:fixed;bottom:12px;right:12px;background:#0f172a;color:#22d3ee;padding:6px 10px;border-radius:8px;font:12px ui-monospace,Menlo,monospace;cursor:pointer;z-index:2147483647;box-shadow:0 2px 8px rgba(0,0,0,0.3);user-select:none;';
|
|
183
|
+
el.title = 'Mneme · click to export receipts';
|
|
184
|
+
el.onclick = function() {
|
|
185
|
+
const arr = loadReceipts();
|
|
186
|
+
const blob = new Blob([JSON.stringify({ batchVersion: 1, receipts: arr, serializedAtMs: Date.now() }, null, 2)], { type: 'application/json' });
|
|
187
|
+
const url = URL.createObjectURL(blob);
|
|
188
|
+
const a = document.createElement('a');
|
|
189
|
+
a.href = url;
|
|
190
|
+
a.download = 'mneme-receipts-' + Date.now() + '.json';
|
|
191
|
+
a.click();
|
|
192
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
193
|
+
};
|
|
194
|
+
document.body.appendChild(el);
|
|
195
|
+
return el;
|
|
196
|
+
}
|
|
197
|
+
function updateIndicator() {
|
|
198
|
+
const el = ensureIndicator();
|
|
199
|
+
if (el) el.textContent = '🛡 Mneme · ' + loadReceipts().length;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── MutationObserver: re-extract on chat updates ────────────────
|
|
203
|
+
let mintInFlight = false;
|
|
204
|
+
let lastMintHash = null;
|
|
205
|
+
async function tick() {
|
|
206
|
+
if (mintInFlight) return;
|
|
207
|
+
const { userText, asstText } = extractLatestPair();
|
|
208
|
+
if (!userText || !asstText) { updateIndicator(); return; }
|
|
209
|
+
const key = userText.slice(0, 100) + '||' + asstText.slice(0, 100);
|
|
210
|
+
if (key === lastMintHash) { updateIndicator(); return; } // dedupe
|
|
211
|
+
mintInFlight = true;
|
|
212
|
+
try {
|
|
213
|
+
const r = await mintReceipt(userText, asstText, null);
|
|
214
|
+
appendReceipt(r);
|
|
215
|
+
lastMintHash = key;
|
|
216
|
+
updateIndicator();
|
|
217
|
+
} catch (e) { console.warn('Mneme mint failed:', e); }
|
|
218
|
+
finally { mintInFlight = false; }
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function start() {
|
|
222
|
+
updateIndicator();
|
|
223
|
+
const obs = new MutationObserver(() => { setTimeout(tick, 300); });
|
|
224
|
+
obs.observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
225
|
+
setInterval(tick, 5000); // safety poll every 5s
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (document.readyState === 'loading') {
|
|
229
|
+
document.addEventListener('DOMContentLoaded', start);
|
|
230
|
+
} else {
|
|
231
|
+
start();
|
|
232
|
+
}
|
|
233
|
+
})();
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
export function generateManifestV3() {
|
|
237
|
+
return {
|
|
238
|
+
manifest_version: 3,
|
|
239
|
+
name: "Mneme Browser Receipt",
|
|
240
|
+
version: USERSCRIPT_VERSION,
|
|
241
|
+
description: "Mints Mneme Receipt Protocol v1.0 receipts for every AI web-chat turn. Local-first, vendor-neutral, MIT licensed.",
|
|
242
|
+
permissions: ["storage"],
|
|
243
|
+
host_permissions: SUPPORTED_VENDOR_DOMAINS.map((d) => `https://${d}/*`),
|
|
244
|
+
content_scripts: [{
|
|
245
|
+
matches: SUPPORTED_VENDOR_DOMAINS.map((d) => `https://${d}/*`),
|
|
246
|
+
js: ["content.js"],
|
|
247
|
+
run_at: "document_idle",
|
|
248
|
+
}],
|
|
249
|
+
action: { default_popup: "popup.html" },
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/** Content script (bundled-version of the userscript IIFE, minus Greasemonkey APIs). */
|
|
253
|
+
export function generateContentScript() {
|
|
254
|
+
// For the extension shell, just embed the userscript IIFE body without
|
|
255
|
+
// the @grant blocks — the extension uses chrome.storage instead.
|
|
256
|
+
// Keep it close to the userscript so behaviour matches; chrome.storage
|
|
257
|
+
// detection falls back to localStorage when chrome.* is unavailable.
|
|
258
|
+
return generateUserscript()
|
|
259
|
+
.replace(/^\/\/ ==UserScript==[\s\S]*?\/\/ ==\/UserScript==\s*/m, "")
|
|
260
|
+
.replace(/GM_setValue/g, "(typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local ? function(k,v){chrome.storage.local.set({[k]:v});} : function(){})")
|
|
261
|
+
.replace(/GM_getValue\(([^,]+),\s*([^)]+)\)/g, "(localStorage.getItem($1) || $2)");
|
|
262
|
+
}
|
|
263
|
+
export function generatePopupHtml() {
|
|
264
|
+
return `<!DOCTYPE html>
|
|
265
|
+
<html lang="en">
|
|
266
|
+
<head>
|
|
267
|
+
<meta charset="utf-8">
|
|
268
|
+
<title>Mneme Browser Receipt</title>
|
|
269
|
+
<style>
|
|
270
|
+
body { font: 13px ui-sans-serif, system-ui; background: #0f172a; color: #e2e8f0; padding: 12px; width: 280px; margin: 0; }
|
|
271
|
+
h1 { font-size: 14px; margin: 0 0 8px; color: #22d3ee; }
|
|
272
|
+
.stat { background: #1e293b; padding: 8px; border-radius: 6px; margin: 6px 0; }
|
|
273
|
+
.stat strong { color: #fde047; }
|
|
274
|
+
button { background: #22d3ee; color: #001821; border: none; padding: 8px 12px; border-radius: 6px; font-weight: 600; cursor: pointer; width: 100%; margin-top: 8px; }
|
|
275
|
+
button:hover { filter: brightness(1.1); }
|
|
276
|
+
a { color: #22d3ee; }
|
|
277
|
+
</style>
|
|
278
|
+
</head>
|
|
279
|
+
<body>
|
|
280
|
+
<h1>🛡 Mneme Browser Receipt</h1>
|
|
281
|
+
<div class="stat">Receipts captured: <strong id="count">…</strong></div>
|
|
282
|
+
<button id="export">📥 Export all receipts (JSON)</button>
|
|
283
|
+
<button id="clear">🗑 Clear stored receipts</button>
|
|
284
|
+
<p style="margin-top:12px;font-size:11px;color:#94a3b8;">
|
|
285
|
+
Receipts stay LOCAL. Export to share with <a href="https://mneme-ai.dev">mneme-ai.dev</a>.
|
|
286
|
+
</p>
|
|
287
|
+
<script>
|
|
288
|
+
function loadCount() {
|
|
289
|
+
try {
|
|
290
|
+
const raw = localStorage.getItem('mneme.browser.receipts.v1') || '[]';
|
|
291
|
+
const arr = JSON.parse(raw);
|
|
292
|
+
document.getElementById('count').textContent = Array.isArray(arr) ? arr.length : 0;
|
|
293
|
+
} catch { document.getElementById('count').textContent = '?'; }
|
|
294
|
+
}
|
|
295
|
+
document.getElementById('export').onclick = () => {
|
|
296
|
+
const raw = localStorage.getItem('mneme.browser.receipts.v1') || '[]';
|
|
297
|
+
const blob = new Blob([raw], { type: 'application/json' });
|
|
298
|
+
const url = URL.createObjectURL(blob);
|
|
299
|
+
const a = document.createElement('a');
|
|
300
|
+
a.href = url; a.download = 'mneme-receipts.json'; a.click();
|
|
301
|
+
};
|
|
302
|
+
document.getElementById('clear').onclick = () => {
|
|
303
|
+
if (confirm('Clear all stored receipts?')) {
|
|
304
|
+
localStorage.removeItem('mneme.browser.receipts.v1');
|
|
305
|
+
loadCount();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
loadCount();
|
|
309
|
+
</script>
|
|
310
|
+
</body>
|
|
311
|
+
</html>`;
|
|
312
|
+
}
|
|
313
|
+
// ─── README for distribution ───────────────────────────────────────
|
|
314
|
+
export function generateBrowserReadme() {
|
|
315
|
+
return `# 🛡 Mneme Browser Receipt — install once, capture forever
|
|
316
|
+
|
|
317
|
+
## Option A: Userscript (works in any browser; recommended)
|
|
318
|
+
|
|
319
|
+
1. Install [Tampermonkey](https://www.tampermonkey.net/) (Chrome / Firefox / Edge / Safari).
|
|
320
|
+
2. Click → install the latest \`mneme.user.js\` from this directory.
|
|
321
|
+
3. Visit any of: chatgpt.com, claude.ai, gemini.google.com, x.com/i/grok, perplexity.ai, copilot.microsoft.com — the 🛡 indicator appears bottom-right.
|
|
322
|
+
4. Chat normally. Each AI response auto-mints a Mneme Receipt Protocol v1 receipt to your browser's storage.
|
|
323
|
+
5. Click 🛡 to export JSON of all receipts.
|
|
324
|
+
|
|
325
|
+
## Option B: Chrome Extension (.crx)
|
|
326
|
+
|
|
327
|
+
1. Download \`mneme-browser-receipt-extension.zip\` from this directory.
|
|
328
|
+
2. Unzip.
|
|
329
|
+
3. Chrome → \`chrome://extensions\` → "Load unpacked" → select the unzipped folder.
|
|
330
|
+
4. Done. Behaves identically to userscript.
|
|
331
|
+
|
|
332
|
+
## What it does
|
|
333
|
+
|
|
334
|
+
- Detects which AI vendor's web chat you're on (6 vendors supported)
|
|
335
|
+
- Watches for new chat turns via MutationObserver
|
|
336
|
+
- Extracts (user, assistant) text pairs
|
|
337
|
+
- Mints a portable Mneme Receipt Protocol v1.0 receipt (sha256 of prompt + response, no plaintext stored)
|
|
338
|
+
- Saves to local browser storage (never leaves your device)
|
|
339
|
+
- Provides one-click export for sharing with Citizens Audit / personal archive
|
|
340
|
+
|
|
341
|
+
## What it does NOT do
|
|
342
|
+
|
|
343
|
+
- ❌ Send anything to any server without your action
|
|
344
|
+
- ❌ Read your prompts in plaintext (only sha256 hashes stored)
|
|
345
|
+
- ❌ Modify the AI vendor's page in any visible way (except the small 🛡 indicator)
|
|
346
|
+
- ❌ Inject anything into your chat
|
|
347
|
+
|
|
348
|
+
## License
|
|
349
|
+
|
|
350
|
+
MIT. Source: \`@mneme-ai/core/browser_userscript\`. Reference impl: \`@mneme-ai/core/browser_receipt\`.
|
|
351
|
+
`;
|
|
352
|
+
}
|
|
353
|
+
export function computeUserscriptStats() {
|
|
354
|
+
return {
|
|
355
|
+
userscriptBytes: generateUserscript().length,
|
|
356
|
+
manifestBytes: JSON.stringify(generateManifestV3(), null, 2).length,
|
|
357
|
+
contentScriptBytes: generateContentScript().length,
|
|
358
|
+
popupBytes: generatePopupHtml().length,
|
|
359
|
+
readmeBytes: generateBrowserReadme().length,
|
|
360
|
+
supportedDomains: SUPPORTED_VENDOR_DOMAINS.length,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
export function formatUserscriptStatsLine(s) {
|
|
364
|
+
return `🛡 USERSCRIPT · ${s.userscriptBytes}B + manifest ${s.manifestBytes}B + popup ${s.popupBytes}B · ${s.supportedDomains} domains`;
|
|
365
|
+
}
|
|
366
|
+
export const BROWSER_USERSCRIPT_TUNABLES = Object.freeze({
|
|
367
|
+
PROTOCOL_VERSION,
|
|
368
|
+
USERSCRIPT_VERSION,
|
|
369
|
+
SUPPORTED_DOMAINS: SUPPORTED_VENDOR_DOMAINS,
|
|
370
|
+
});
|
|
371
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser_userscript/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userscript.test.d.ts","sourceRoot":"","sources":["../../src/browser_userscript/userscript.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { generateUserscript, generateManifestV3, generateContentScript, generatePopupHtml, generateBrowserReadme, computeUserscriptStats, formatUserscriptStatsLine, BROWSER_USERSCRIPT_TUNABLES, } from "./index.js";
|
|
3
|
+
describe("v2.19.38 BROWSER USERSCRIPT — Tampermonkey single-file", () => {
|
|
4
|
+
it("emits valid UserScript header block", () => {
|
|
5
|
+
const us = generateUserscript();
|
|
6
|
+
expect(us).toContain("// ==UserScript==");
|
|
7
|
+
expect(us).toContain("// ==/UserScript==");
|
|
8
|
+
expect(us).toContain("@name");
|
|
9
|
+
expect(us).toContain("@version");
|
|
10
|
+
expect(us).toContain("@match");
|
|
11
|
+
expect(us).toContain("@license MIT");
|
|
12
|
+
});
|
|
13
|
+
it("@match covers 11 supported vendor URLs", () => {
|
|
14
|
+
const us = generateUserscript();
|
|
15
|
+
expect(us).toContain("https://chatgpt.com/*");
|
|
16
|
+
expect(us).toContain("https://chat.openai.com/*");
|
|
17
|
+
expect(us).toContain("https://claude.ai/*");
|
|
18
|
+
expect(us).toContain("https://gemini.google.com/*");
|
|
19
|
+
expect(us).toContain("https://bard.google.com/*");
|
|
20
|
+
expect(us).toContain("https://grok.com/*");
|
|
21
|
+
expect(us).toContain("https://x.com/i/grok*");
|
|
22
|
+
expect(us).toContain("https://perplexity.ai/*");
|
|
23
|
+
expect(us).toContain("https://copilot.microsoft.com/*");
|
|
24
|
+
});
|
|
25
|
+
it("uses SubtleCrypto for sha256 (no external dep)", () => {
|
|
26
|
+
const us = generateUserscript();
|
|
27
|
+
expect(us).toContain("crypto.subtle.digest");
|
|
28
|
+
expect(us).toContain("SHA-256");
|
|
29
|
+
});
|
|
30
|
+
it("emits Mneme Receipt Protocol v1.0 receipt shape", () => {
|
|
31
|
+
const us = generateUserscript();
|
|
32
|
+
expect(us).toContain("'mneme-receipt-protocol'");
|
|
33
|
+
expect(us).toContain("'1.0'");
|
|
34
|
+
expect(us).toContain("@mneme-ai/browser-userscript@");
|
|
35
|
+
expect(us).toContain("contentHash");
|
|
36
|
+
});
|
|
37
|
+
it("includes MutationObserver + interval safety poll", () => {
|
|
38
|
+
const us = generateUserscript();
|
|
39
|
+
expect(us).toContain("MutationObserver");
|
|
40
|
+
expect(us).toContain("setInterval(tick");
|
|
41
|
+
});
|
|
42
|
+
it("includes 🛡 floating indicator with export", () => {
|
|
43
|
+
const us = generateUserscript();
|
|
44
|
+
expect(us).toContain("mneme-indicator");
|
|
45
|
+
expect(us).toContain("🛡 Mneme");
|
|
46
|
+
expect(us).toContain("mneme-receipts-");
|
|
47
|
+
});
|
|
48
|
+
it("caps receipts at 10000 (storage safety)", () => {
|
|
49
|
+
expect(generateUserscript()).toContain("10000");
|
|
50
|
+
});
|
|
51
|
+
it("dedupes via lastMintHash (no duplicate mints per turn)", () => {
|
|
52
|
+
expect(generateUserscript()).toContain("lastMintHash");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("v2.19.38 BROWSER USERSCRIPT — Manifest V3 extension", () => {
|
|
56
|
+
it("manifest_version: 3", () => {
|
|
57
|
+
const m = generateManifestV3();
|
|
58
|
+
expect(m.manifest_version).toBe(3);
|
|
59
|
+
});
|
|
60
|
+
it("has all required manifest fields", () => {
|
|
61
|
+
const m = generateManifestV3();
|
|
62
|
+
expect(m.name).toContain("Mneme");
|
|
63
|
+
expect(m.version).toMatch(/^\d+\.\d+\.\d+$/);
|
|
64
|
+
expect(m.permissions).toContain("storage");
|
|
65
|
+
expect(m.content_scripts.length).toBe(1);
|
|
66
|
+
expect(m.content_scripts[0].js).toContain("content.js");
|
|
67
|
+
});
|
|
68
|
+
it("host_permissions covers all supported vendor domains", () => {
|
|
69
|
+
const m = generateManifestV3();
|
|
70
|
+
expect(m.host_permissions.length).toBe(BROWSER_USERSCRIPT_TUNABLES.SUPPORTED_DOMAINS.length);
|
|
71
|
+
for (const d of BROWSER_USERSCRIPT_TUNABLES.SUPPORTED_DOMAINS) {
|
|
72
|
+
expect(m.host_permissions).toContain(`https://${d}/*`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
it("manifest is JSON-serializable", () => {
|
|
76
|
+
const m = generateManifestV3();
|
|
77
|
+
expect(() => JSON.stringify(m, null, 2)).not.toThrow();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("v2.19.38 BROWSER USERSCRIPT — content.js + popup.html", () => {
|
|
81
|
+
it("content.js strips UserScript header (extension uses chrome.storage instead of GM_*)", () => {
|
|
82
|
+
const cs = generateContentScript();
|
|
83
|
+
expect(cs).not.toContain("// ==UserScript==");
|
|
84
|
+
expect(cs).not.toContain("@match");
|
|
85
|
+
});
|
|
86
|
+
it("popup.html is valid HTML5", () => {
|
|
87
|
+
const html = generatePopupHtml();
|
|
88
|
+
expect(html).toContain("<!DOCTYPE html>");
|
|
89
|
+
expect(html).toContain("Mneme Browser Receipt");
|
|
90
|
+
expect(html).toContain("Export all receipts");
|
|
91
|
+
expect(html).toContain("Clear stored receipts");
|
|
92
|
+
});
|
|
93
|
+
it("popup includes export + clear buttons", () => {
|
|
94
|
+
expect(generatePopupHtml()).toContain('id="export"');
|
|
95
|
+
expect(generatePopupHtml()).toContain('id="clear"');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe("v2.19.38 BROWSER USERSCRIPT — README + stats", () => {
|
|
99
|
+
it("README mentions both install paths", () => {
|
|
100
|
+
const md = generateBrowserReadme();
|
|
101
|
+
expect(md).toContain("Tampermonkey");
|
|
102
|
+
expect(md).toContain("Chrome Extension");
|
|
103
|
+
expect(md).toContain("MIT");
|
|
104
|
+
});
|
|
105
|
+
it("README states privacy guarantees (no plaintext leaves device)", () => {
|
|
106
|
+
const md = generateBrowserReadme();
|
|
107
|
+
expect(md).toContain("only sha256 hashes stored");
|
|
108
|
+
expect(md).toContain("never leaves your device");
|
|
109
|
+
});
|
|
110
|
+
it("computeUserscriptStats reports all 5 byte counts", () => {
|
|
111
|
+
const s = computeUserscriptStats();
|
|
112
|
+
expect(s.userscriptBytes).toBeGreaterThan(0);
|
|
113
|
+
expect(s.manifestBytes).toBeGreaterThan(0);
|
|
114
|
+
expect(s.contentScriptBytes).toBeGreaterThan(0);
|
|
115
|
+
expect(s.popupBytes).toBeGreaterThan(0);
|
|
116
|
+
expect(s.readmeBytes).toBeGreaterThan(0);
|
|
117
|
+
expect(s.supportedDomains).toBe(BROWSER_USERSCRIPT_TUNABLES.SUPPORTED_DOMAINS.length);
|
|
118
|
+
expect(formatUserscriptStatsLine(s)).toContain("USERSCRIPT");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("v2.19.38 BROWSER USERSCRIPT — A/B before vs after", () => {
|
|
122
|
+
it("A: pre-v2.19.38 = no installable artifact; B: 5 artifacts shipped (userscript + manifest + content + popup + README)", () => {
|
|
123
|
+
expect(generateUserscript().length).toBeGreaterThan(1000);
|
|
124
|
+
expect(JSON.stringify(generateManifestV3()).length).toBeGreaterThan(100);
|
|
125
|
+
expect(generateContentScript().length).toBeGreaterThan(500);
|
|
126
|
+
expect(generatePopupHtml().length).toBeGreaterThan(500);
|
|
127
|
+
expect(generateBrowserReadme().length).toBeGreaterThan(500);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=userscript.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userscript.test.js","sourceRoot":"","sources":["../../src/browser_userscript/userscript.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contribute.test.d.ts","sourceRoot":"","sources":["../../src/citizens_contribute/contribute.test.ts"],"names":[],"mappings":""}
|