@yanggao7/memlio 0.1.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/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/capture.d.ts +14 -0
- package/dist/capture.js +121 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +164 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +49 -0
- package/dist/config.js.map +1 -0
- package/dist/embedding.d.ts +16 -0
- package/dist/embedding.js +84 -0
- package/dist/embedding.js.map +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +105 -0
- package/dist/mcp.js.map +1 -0
- package/dist/setup.d.ts +14 -0
- package/dist/setup.js +62 -0
- package/dist/setup.js.map +1 -0
- package/dist/store.d.ts +166 -0
- package/dist/store.js +535 -0
- package/dist/store.js.map +1 -0
- package/docs/ARCHITECTURE.md +33 -0
- package/docs/SETUP.md +74 -0
- package/docs/VALIDATION.md +55 -0
- package/docs/WORKFLOWS.md +95 -0
- package/docs/evaluation-synthetic.json +74 -0
- package/package.json +66 -0
- package/skills/claude/memlio/SKILL.md +18 -0
- package/skills/codex/memlio/SKILL.md +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yang Gao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Memlio
|
|
2
|
+
|
|
3
|
+
A personal memory for Claude Code and Codex. Save a bookmark, note, or picture from inside the agent; find it later, from any project or session, by describing what you remember.
|
|
4
|
+
|
|
5
|
+
Everything stays on your machine: SQLite records, copies of saved files, a keyword index, and a small local embedding model for natural-language search. No accounts, no cloud, no API keys. A `memlio` command line is included for scripting and backups.
|
|
6
|
+
|
|
7
|
+
**Status:** early release, MIT-licensed. Tested on macOS with Node 24.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Requires Node.js 22.13 or newer. See [docs/SETUP.md](docs/SETUP.md) if you need to install Node first.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install -g @yanggao7/memlio
|
|
15
|
+
memlio setup claude # and/or: memlio setup codex
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`setup` registers the MCP server and the `/memlio` skill with the agent and downloads the 23 MB embedding model once. Restart the agent afterward.
|
|
19
|
+
|
|
20
|
+
To run from a checkout instead: `pnpm install --frozen-lockfile && pnpm build`, then use `node dist/cli.js` in place of `memlio`.
|
|
21
|
+
|
|
22
|
+
## Use
|
|
23
|
+
|
|
24
|
+
From Claude Code (`/memlio`) or Codex (`$memlio`):
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
/memlio store https://example.com/article why this page matters to me
|
|
28
|
+
/memlio store /absolute/path/to/dashboard.png dark dashboard with orange charts
|
|
29
|
+
/memlio store A thought I want to keep
|
|
30
|
+
/memlio find that article about background jobs
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The first URL, path, or quoted text is what gets saved; the rest becomes your note. On `find`, the agent searches the shared collection, reads the likely matches, and answers with the original link or file plus excerpts. Weak or missing matches are reported as such.
|
|
34
|
+
|
|
35
|
+
The same collection from a terminal:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
memlio store "Try weekly screenshots of competitor pricing"
|
|
39
|
+
memlio store https://example.com/article --note "For my queue project"
|
|
40
|
+
memlio find "competitor pricing"
|
|
41
|
+
memlio status
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Good to know
|
|
45
|
+
|
|
46
|
+
- **Bookmarks** are fetched once for a readable text snapshot. Login-only, JavaScript-only, and bot-protected pages cannot be captured; the bookmark is still saved and your note stays searchable.
|
|
47
|
+
- **Files** are copied, so deleting the original does not lose the memory. Text is extracted from `.txt`, `.md`, `.csv`, and `.json`. Images and PDFs rely on the description you give them; there is no OCR.
|
|
48
|
+
- **Saving files from the agent** needs permission: `memlio setup claude --allow-path ~/Screenshots`. The terminal command can save any file you name.
|
|
49
|
+
- **Saving the same thing twice** with the same note returns the existing item. A different note creates a second item.
|
|
50
|
+
- **Search** combines keyword and semantic matching. If the model is missing, search still works on keywords and says so.
|
|
51
|
+
- **Data** lives in `~/.local/share/memlio`. Back it up with `memlio export <new-dir>` and restore with `memlio import <dir>`. `memlio repair` retries failed captures and rebuilds the search index.
|
|
52
|
+
|
|
53
|
+
More detail: [setup and troubleshooting](docs/SETUP.md), [how storing and search work](docs/WORKFLOWS.md), [architecture decisions](docs/ARCHITECTURE.md), [validation results](docs/VALIDATION.md), and the [original plan](PLAN.md).
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
pnpm test # builds, then runs the suite without the model
|
|
59
|
+
MEMLIO_MODEL_CACHE=~/.local/share/memlio/models pnpm test # also runs the real-model test
|
|
60
|
+
pnpm check # types and formatting
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function isPublicAddress(address: string): boolean;
|
|
2
|
+
export declare function publicTarget(raw: string): Promise<{
|
|
3
|
+
url: URL;
|
|
4
|
+
address: import("dns").LookupAddress;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function fetchPage(raw: string, redirects?: number, deadline?: number): Promise<{
|
|
7
|
+
url: string;
|
|
8
|
+
body: string;
|
|
9
|
+
contentType: string;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function extractPage(html: string, url: string): {
|
|
12
|
+
title: string;
|
|
13
|
+
markdown: string;
|
|
14
|
+
};
|
package/dist/capture.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { lookup } from 'node:dns/promises';
|
|
2
|
+
import http from 'node:http';
|
|
3
|
+
import https from 'node:https';
|
|
4
|
+
import ipaddr from 'ipaddr.js';
|
|
5
|
+
import { parseHTML } from 'linkedom';
|
|
6
|
+
import { Readability } from '@mozilla/readability';
|
|
7
|
+
import TurndownService from 'turndown';
|
|
8
|
+
const MAX_BYTES = 5 * 1024 * 1024;
|
|
9
|
+
export function isPublicAddress(address) {
|
|
10
|
+
try {
|
|
11
|
+
return ipaddr.process(address).range() === 'unicast';
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function publicTarget(raw) {
|
|
18
|
+
const url = new URL(raw);
|
|
19
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) {
|
|
20
|
+
throw new Error('Only HTTP(S) URLs without embedded credentials can be captured.');
|
|
21
|
+
}
|
|
22
|
+
if (url.port && !['80', '443'].includes(url.port))
|
|
23
|
+
throw new Error('Only standard web ports are allowed.');
|
|
24
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, '');
|
|
25
|
+
const addresses = await lookup(hostname, { all: true });
|
|
26
|
+
if (!addresses.length || addresses.some((a) => !isPublicAddress(a.address))) {
|
|
27
|
+
throw new Error('Private, loopback, link-local, and reserved network destinations are blocked.');
|
|
28
|
+
}
|
|
29
|
+
return { url, address: addresses[0] };
|
|
30
|
+
}
|
|
31
|
+
// DNS is resolved and checked once per hop, then pinned to the actual connection.
|
|
32
|
+
export async function fetchPage(raw, redirects = 0, deadline = Date.now() + 20_000) {
|
|
33
|
+
if (process.env.MEMLIO_OFFLINE === '1')
|
|
34
|
+
throw new Error('Page capture is disabled by MEMLIO_OFFLINE=1.');
|
|
35
|
+
if (redirects > 5)
|
|
36
|
+
throw new Error('Too many redirects.');
|
|
37
|
+
const remaining = deadline - Date.now();
|
|
38
|
+
if (remaining <= 0)
|
|
39
|
+
throw new Error('Page capture timed out.');
|
|
40
|
+
let timer;
|
|
41
|
+
const target = await Promise.race([
|
|
42
|
+
publicTarget(raw),
|
|
43
|
+
new Promise((_, reject) => {
|
|
44
|
+
timer = setTimeout(() => reject(new Error('DNS lookup timed out.')), remaining);
|
|
45
|
+
}),
|
|
46
|
+
]).finally(() => clearTimeout(timer));
|
|
47
|
+
const { url, address } = target;
|
|
48
|
+
const response = await new Promise((resolve, reject) => {
|
|
49
|
+
const transport = url.protocol === 'https:' ? https : http;
|
|
50
|
+
const req = transport.get(url, {
|
|
51
|
+
headers: {
|
|
52
|
+
'User-Agent': 'memlio/0.1 (personal bookmark capture)',
|
|
53
|
+
Accept: 'text/html,text/plain,application/xhtml+xml',
|
|
54
|
+
'Accept-Encoding': 'identity',
|
|
55
|
+
},
|
|
56
|
+
lookup: ((_host, opts, callback) => opts.all ? callback(null, [address]) : callback(null, address.address, address.family)),
|
|
57
|
+
}, (res) => {
|
|
58
|
+
const status = res.statusCode ?? 500;
|
|
59
|
+
const challenge = Boolean(res.headers['cf-mitigated']) || (status === 403 && /cloudflare/i.test(String(res.headers.server ?? '')));
|
|
60
|
+
if (status >= 300 && status < 400) {
|
|
61
|
+
res.resume();
|
|
62
|
+
resolve({ status, location: res.headers.location, body: '', contentType: '', challenge });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (status < 200 || status >= 300) {
|
|
66
|
+
res.resume();
|
|
67
|
+
reject(new Error(challenge
|
|
68
|
+
? `The site blocked automated capture (HTTP ${status} bot challenge). The bookmark is saved; add a note describing the page to make it findable.`
|
|
69
|
+
: status === 401 || status === 403
|
|
70
|
+
? `The page requires a login or blocks non-browser clients (HTTP ${status}). The bookmark is saved without a snapshot.`
|
|
71
|
+
: `Page returned HTTP ${status}.`));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const contentType = res.headers['content-type'] ?? '';
|
|
75
|
+
if (!/^(text\/(html|plain)|application\/xhtml\+xml)/i.test(contentType)) {
|
|
76
|
+
res.resume();
|
|
77
|
+
reject(new Error(`Unsupported page content type: ${contentType || 'unknown'}.`));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let bytes = 0;
|
|
81
|
+
const chunks = [];
|
|
82
|
+
res.on('data', (chunk) => {
|
|
83
|
+
bytes += chunk.length;
|
|
84
|
+
if (bytes > MAX_BYTES) {
|
|
85
|
+
req.destroy(new Error('Page exceeds the 5 MB capture limit.'));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
chunks.push(chunk);
|
|
89
|
+
});
|
|
90
|
+
res.on('error', reject);
|
|
91
|
+
res.on('end', () => resolve({ status, body: Buffer.concat(chunks).toString('utf8'), contentType, challenge }));
|
|
92
|
+
});
|
|
93
|
+
const timeout = setTimeout(() => req.destroy(new Error('Page capture timed out.')), Math.max(1, deadline - Date.now()));
|
|
94
|
+
req.on('close', () => clearTimeout(timeout));
|
|
95
|
+
req.on('error', reject);
|
|
96
|
+
});
|
|
97
|
+
if (response.status >= 300 && response.status < 400) {
|
|
98
|
+
if (!response.location)
|
|
99
|
+
throw new Error('Redirect has no destination.');
|
|
100
|
+
return fetchPage(new URL(response.location, url).href, redirects + 1, deadline);
|
|
101
|
+
}
|
|
102
|
+
return { url: url.href, body: response.body, contentType: response.contentType };
|
|
103
|
+
}
|
|
104
|
+
export function extractPage(html, url) {
|
|
105
|
+
const { document } = parseHTML(html);
|
|
106
|
+
// Parse only: linkedom does not fetch resources or run page scripts.
|
|
107
|
+
const base = document.createElement('base');
|
|
108
|
+
base.href = url;
|
|
109
|
+
document.head.prepend(base);
|
|
110
|
+
const fallbackTitle = document.title || new URL(url).hostname;
|
|
111
|
+
const article = new Readability(document).parse();
|
|
112
|
+
if (!article?.content || !article.textContent?.trim()) {
|
|
113
|
+
throw new Error('No readable article text was found (the page may need JavaScript or a login). The bookmark is saved without a snapshot.');
|
|
114
|
+
}
|
|
115
|
+
const clean = parseHTML(article.content).document;
|
|
116
|
+
for (const el of clean.querySelectorAll('script,style,iframe,form,object,embed'))
|
|
117
|
+
el.remove();
|
|
118
|
+
const markdown = new TurndownService({ headingStyle: 'atx', codeBlockStyle: 'fenced' }).turndown(clean.toString());
|
|
119
|
+
return { title: article.title || fallbackTitle, markdown };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { VERSION, dataHome, loadConfig, saveConfig } from './config.js';
|
|
5
|
+
import { Memory, summarize } from './store.js';
|
|
6
|
+
import { LocalEmbedder } from './embedding.js';
|
|
7
|
+
import { setup } from './setup.js';
|
|
8
|
+
import { serve } from './mcp.js';
|
|
9
|
+
const program = new Command()
|
|
10
|
+
.name('memlio')
|
|
11
|
+
.version(VERSION)
|
|
12
|
+
.description('Save something now. Find it later from a vague description.')
|
|
13
|
+
.option('--home <directory>', 'Collection directory (also MEMLIO_HOME)')
|
|
14
|
+
.option('--json', 'Machine-readable JSON output');
|
|
15
|
+
const home = () => dataHome(program.opts().home);
|
|
16
|
+
const errorText = (e) => (e instanceof Error ? e.message : String(e));
|
|
17
|
+
function emit(value, format) {
|
|
18
|
+
console.log(program.opts().json ? JSON.stringify(value, null, 2) : format(value));
|
|
19
|
+
}
|
|
20
|
+
async function withMemory(fn, format) {
|
|
21
|
+
const memory = new Memory(home());
|
|
22
|
+
try {
|
|
23
|
+
emit(await fn(memory), format);
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
await memory.close();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const formatSaved = (s) => s.duplicate
|
|
30
|
+
? `Already saved as ${s.id}: ${s.title}`
|
|
31
|
+
: [
|
|
32
|
+
`Saved ${s.id}: ${s.title}`,
|
|
33
|
+
s.captureError ? ` Page capture failed: ${s.captureError}` : s.capture === 'pending' ? ' Page capture: pending' : '',
|
|
34
|
+
s.indexError ? ` Semantic indexing failed: ${s.indexError}` : '',
|
|
35
|
+
]
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.join('\n');
|
|
38
|
+
const formatStatus = (s) => [
|
|
39
|
+
`${s.count} item${s.count === 1 ? '' : 's'} in ${s.home}`,
|
|
40
|
+
s.capturePending || s.captureFailed ? ` Page capture: ${s.capturePending} pending, ${s.captureFailed} failed` : '',
|
|
41
|
+
s.indexPending || s.indexFailed ? ` Semantic index: ${s.indexPending} pending, ${s.indexFailed} failed` : '',
|
|
42
|
+
...s.failures.slice(0, 20).map((f) => ` ${f.id} ${f.title}: ${f.captureError ?? f.indexError}`),
|
|
43
|
+
s.next ?? 'Everything is captured and indexed.',
|
|
44
|
+
]
|
|
45
|
+
.filter(Boolean)
|
|
46
|
+
.join('\n');
|
|
47
|
+
program
|
|
48
|
+
.command('setup')
|
|
49
|
+
.description('Register the MCP server and skill with an agent, and prepare the local model')
|
|
50
|
+
.argument('<client>', 'codex or claude')
|
|
51
|
+
.option('--allow-path <directory...>', 'Let the agent save files from these folders')
|
|
52
|
+
.option('--dry-run', 'Preview paths and registration without changes')
|
|
53
|
+
.option('--target-home <directory>', 'Alternative user configuration root')
|
|
54
|
+
.action(async (client, opts) => {
|
|
55
|
+
const registration = setup(client, home(), opts);
|
|
56
|
+
const config = loadConfig(home());
|
|
57
|
+
if (opts.allowPath) {
|
|
58
|
+
config.allowedPaths = [...new Set([...config.allowedPaths, ...opts.allowPath.map((p) => resolve(p))])];
|
|
59
|
+
}
|
|
60
|
+
let model = 'ready';
|
|
61
|
+
if (!opts.dryRun) {
|
|
62
|
+
saveConfig(home(), config);
|
|
63
|
+
console.error('Preparing the local embedding model (about 23 MB, downloaded once)...');
|
|
64
|
+
const embedder = new LocalEmbedder(home());
|
|
65
|
+
try {
|
|
66
|
+
await embedder.embed(['memlio']);
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
model = `${errorText(e)} Keyword search works now; semantic search starts once the model downloads on first use.`;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await embedder.dispose();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
emit({ ...registration, allowedPaths: config.allowedPaths, model }, (r) => [
|
|
76
|
+
`${r.dryRun ? 'Would register' : 'Registered'} the memlio MCP server in ${r.configPath}`,
|
|
77
|
+
`${r.dryRun ? 'Would install' : 'Installed'} the skill at ${r.skillPath}`,
|
|
78
|
+
r.allowedPaths.length ? `Agent may save files from: ${r.allowedPaths.join(', ')}` : '',
|
|
79
|
+
`Embedding model: ${r.model}`,
|
|
80
|
+
r.dryRun ? '' : `Restart ${client} to load the server. Then try: /memlio store <url or note>`,
|
|
81
|
+
]
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join('\n'));
|
|
84
|
+
});
|
|
85
|
+
program
|
|
86
|
+
.command('store')
|
|
87
|
+
.description('Save a note, URL, or file')
|
|
88
|
+
.argument('[input...]', 'Text, URL, or local file path')
|
|
89
|
+
.option('--stdin', 'Read a note from standard input')
|
|
90
|
+
.addOption(new Option('--kind <kind>').choices(['note', 'url', 'file']))
|
|
91
|
+
.option('--title <title>')
|
|
92
|
+
.option('--note <context>', 'Why you saved it')
|
|
93
|
+
.option('--description <text>', 'A description of an image or asset')
|
|
94
|
+
.action(async (parts, opts) => {
|
|
95
|
+
let input = parts.join(' ');
|
|
96
|
+
if (opts.stdin) {
|
|
97
|
+
if (input)
|
|
98
|
+
throw new Error('Use an argument or --stdin, not both.');
|
|
99
|
+
const chunks = [];
|
|
100
|
+
let bytes = 0;
|
|
101
|
+
for await (const chunk of process.stdin) {
|
|
102
|
+
bytes += chunk.length;
|
|
103
|
+
if (bytes > 1_000_000)
|
|
104
|
+
throw new Error('Stdin exceeds 1 MB.');
|
|
105
|
+
chunks.push(Buffer.from(chunk));
|
|
106
|
+
}
|
|
107
|
+
input = Buffer.concat(chunks).toString('utf8');
|
|
108
|
+
}
|
|
109
|
+
await withMemory(async (m) => summarize(await m.store({ input, ...opts, kind: opts.kind ?? (opts.stdin ? 'note' : undefined) }, { explicit: true })), formatSaved);
|
|
110
|
+
});
|
|
111
|
+
program
|
|
112
|
+
.command('find')
|
|
113
|
+
.alias('retrieve')
|
|
114
|
+
.description('Find saved items from a description')
|
|
115
|
+
.argument('<query...>')
|
|
116
|
+
.addOption(new Option('--kind <kind>').choices(['note', 'url', 'file']))
|
|
117
|
+
.option('--limit <count>', 'Maximum results', '5')
|
|
118
|
+
.option('--after <date>', 'Saved on or after YYYY-MM-DD')
|
|
119
|
+
.option('--before <date>', 'Saved on or before YYYY-MM-DD')
|
|
120
|
+
.action(async (parts, opts) => withMemory((m) => m.search(parts.join(' '), { ...opts, limit: Number(opts.limit) }), (r) => {
|
|
121
|
+
for (const warning of r.warnings)
|
|
122
|
+
console.error(`Warning: ${warning}`);
|
|
123
|
+
if (!r.results.length)
|
|
124
|
+
return `No matches (${r.mode}).`;
|
|
125
|
+
return r.results.map((x) => `${x.id} ${x.title}\n${x.reason}\n${x.url ?? x.assetPath ?? ''}\n${x.excerpt}`).join('\n\n');
|
|
126
|
+
}));
|
|
127
|
+
program
|
|
128
|
+
.command('get')
|
|
129
|
+
.description('Print one saved record as JSON')
|
|
130
|
+
.argument('<id>')
|
|
131
|
+
.action(async (id) => withMemory((m) => m.get(id), (v) => JSON.stringify(v, null, 2)));
|
|
132
|
+
program
|
|
133
|
+
.command('status')
|
|
134
|
+
.description('Show collection health')
|
|
135
|
+
.action(async () => withMemory((m) => m.status(), formatStatus));
|
|
136
|
+
program
|
|
137
|
+
.command('repair')
|
|
138
|
+
.description('Rebuild search data, retry failed page captures, and embed anything missing')
|
|
139
|
+
.action(async () => withMemory((m) => m.repair(), formatStatus));
|
|
140
|
+
program
|
|
141
|
+
.command('delete')
|
|
142
|
+
.description('Permanently delete one item')
|
|
143
|
+
.argument('<id>')
|
|
144
|
+
.requiredOption('--yes', 'Confirm permanent deletion')
|
|
145
|
+
.action(async (id) => withMemory((m) => m.delete(id), (r) => `Deleted ${r.deleted}${r.cleanupError ? ` (asset cleanup failed: ${r.cleanupError})` : ''}`));
|
|
146
|
+
program
|
|
147
|
+
.command('export')
|
|
148
|
+
.description('Copy records and original files to a new directory')
|
|
149
|
+
.argument('<directory>')
|
|
150
|
+
.action(async (target) => withMemory((m) => m.export(target), (r) => `Exported ${r.count} items to ${r.destination}`));
|
|
151
|
+
program
|
|
152
|
+
.command('import')
|
|
153
|
+
.description('Restore records and files from a memlio export')
|
|
154
|
+
.argument('<directory>')
|
|
155
|
+
.action(async (source) => withMemory((m) => m.import(source), (r) => `Imported ${r.imported} items (${r.duplicates} already present)\n${formatStatus(r)}`));
|
|
156
|
+
program
|
|
157
|
+
.command('mcp', { hidden: true })
|
|
158
|
+
.description('Run the MCP server over stdio')
|
|
159
|
+
.action(async () => serve(home()));
|
|
160
|
+
program.parseAsync().catch((error) => {
|
|
161
|
+
console.error(`memlio: ${errorText(error)}`);
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const VERSION = "0.1.0";
|
|
3
|
+
export declare const MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
4
|
+
declare const schema: z.ZodObject<{
|
|
5
|
+
version: z.ZodLiteral<1>;
|
|
6
|
+
allowedPaths: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
version: 1;
|
|
9
|
+
allowedPaths: string[];
|
|
10
|
+
}, {
|
|
11
|
+
version: 1;
|
|
12
|
+
allowedPaths?: string[] | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
export type Config = z.infer<typeof schema>;
|
|
15
|
+
export declare function dataHome(path?: string): string;
|
|
16
|
+
/** Write a file atomically: temp file, fsync, rename, fsync the directory. */
|
|
17
|
+
export declare function atomicWrite(path: string, data: string | Buffer): void;
|
|
18
|
+
export declare function loadConfig(home: string): Config;
|
|
19
|
+
export declare function saveConfig(home: string, config: Config): void;
|
|
20
|
+
export {};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync, renameSync, existsSync, openSync, fsyncSync, closeSync, rmSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { resolve, join, dirname } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
export const VERSION = '0.1.0';
|
|
7
|
+
export const MODEL = 'Xenova/all-MiniLM-L6-v2';
|
|
8
|
+
const schema = z.object({
|
|
9
|
+
version: z.literal(1),
|
|
10
|
+
allowedPaths: z.array(z.string()).default([]),
|
|
11
|
+
});
|
|
12
|
+
export function dataHome(path) {
|
|
13
|
+
return resolve(path ?? process.env.MEMLIO_HOME ?? join(homedir(), '.local', 'share', 'memlio'));
|
|
14
|
+
}
|
|
15
|
+
/** Write a file atomically: temp file, fsync, rename, fsync the directory. */
|
|
16
|
+
export function atomicWrite(path, data) {
|
|
17
|
+
const temp = `${path}.${randomUUID()}.tmp`;
|
|
18
|
+
try {
|
|
19
|
+
const file = openSync(temp, 'wx', 0o600);
|
|
20
|
+
try {
|
|
21
|
+
writeFileSync(file, data);
|
|
22
|
+
fsyncSync(file);
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
closeSync(file);
|
|
26
|
+
}
|
|
27
|
+
renameSync(temp, path);
|
|
28
|
+
const directory = openSync(dirname(path), 'r');
|
|
29
|
+
try {
|
|
30
|
+
fsyncSync(directory);
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
closeSync(directory);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
rmSync(temp, { force: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function loadConfig(home) {
|
|
41
|
+
const path = join(home, 'config.json');
|
|
42
|
+
// Unknown keys from older versions (for example `semantic`) are dropped.
|
|
43
|
+
return existsSync(path) ? schema.parse(JSON.parse(readFileSync(path, 'utf8'))) : schema.parse({ version: 1 });
|
|
44
|
+
}
|
|
45
|
+
export function saveConfig(home, config) {
|
|
46
|
+
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
47
|
+
atomicWrite(join(home, 'config.json'), JSON.stringify(schema.parse(config), null, 2) + '\n');
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjI,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAC/B,MAAM,CAAC,MAAM,KAAK,GAAG,yBAAyB,CAAC;AAE/C,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC9C,CAAC,CAAC;AAGH,MAAM,UAAU,QAAQ,CAAC,IAAa;IACpC,OAAO,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AAClG,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAqB;IAC7D,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,MAAM,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC1B,SAAS,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvB,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,SAAS,CAAC,SAAS,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACvC,yEAAyE;IACzE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;AAChH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,MAAc;IACrD,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClD,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/F,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Embedder {
|
|
2
|
+
readonly name: string;
|
|
3
|
+
embed(texts: string[]): Promise<number[][]>;
|
|
4
|
+
dispose(): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare class LocalEmbedder implements Embedder {
|
|
7
|
+
private home;
|
|
8
|
+
readonly name = "Xenova/all-MiniLM-L6-v2:q8:mean:normalized:v1";
|
|
9
|
+
private extractor;
|
|
10
|
+
private loading?;
|
|
11
|
+
constructor(home: string);
|
|
12
|
+
embed(texts: string[]): Promise<number[][]>;
|
|
13
|
+
private load;
|
|
14
|
+
dispose(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare function cosine(a: number[], b: number[]): number;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { MODEL } from './config.js';
|
|
3
|
+
export class LocalEmbedder {
|
|
4
|
+
home;
|
|
5
|
+
name = `${MODEL}:q8:mean:normalized:v1`;
|
|
6
|
+
extractor;
|
|
7
|
+
loading;
|
|
8
|
+
constructor(home) {
|
|
9
|
+
this.home = home;
|
|
10
|
+
}
|
|
11
|
+
async embed(texts) {
|
|
12
|
+
if (!texts.length)
|
|
13
|
+
return [];
|
|
14
|
+
if (!this.extractor && !this.loading) {
|
|
15
|
+
this.loading = this.load().finally(() => {
|
|
16
|
+
this.loading = undefined;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
await this.loading;
|
|
20
|
+
const vectors = [];
|
|
21
|
+
for (let offset = 0; offset < texts.length; offset += 16) {
|
|
22
|
+
const output = await this.extractor(texts.slice(offset, offset + 16), { pooling: 'mean', normalize: true });
|
|
23
|
+
vectors.push(...output.tolist());
|
|
24
|
+
}
|
|
25
|
+
return vectors;
|
|
26
|
+
}
|
|
27
|
+
async load() {
|
|
28
|
+
const offline = process.env.MEMLIO_OFFLINE === '1';
|
|
29
|
+
try {
|
|
30
|
+
const { pipeline, env } = await import('@huggingface/transformers');
|
|
31
|
+
env.cacheDir = process.env.MEMLIO_MODEL_CACHE ?? join(this.home, 'models');
|
|
32
|
+
env.localModelPath = env.cacheDir;
|
|
33
|
+
env.allowLocalModels = true;
|
|
34
|
+
env.allowRemoteModels = !offline;
|
|
35
|
+
env.backends.onnx.logLevel = 'error';
|
|
36
|
+
this.extractor = await pipeline('feature-extraction', MODEL, { dtype: 'q8', device: 'cpu', local_files_only: offline });
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw new Error(describeModelError(error, offline));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async dispose() {
|
|
43
|
+
try {
|
|
44
|
+
await this.loading;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
/* A failed load leaves nothing to dispose. */
|
|
48
|
+
}
|
|
49
|
+
if (this.extractor) {
|
|
50
|
+
await this.extractor.dispose();
|
|
51
|
+
this.extractor = undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Translate Transformers.js/ONNX failures into one sentence a user can act on. */
|
|
56
|
+
function describeModelError(error, offline) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
if (/not found locally|allowRemoteModels|local_files_only/i.test(message)) {
|
|
59
|
+
return offline
|
|
60
|
+
? 'The local embedding model is not downloaded and MEMLIO_OFFLINE=1 prevents downloading it.'
|
|
61
|
+
: 'The local embedding model is not downloaded yet and could not be fetched.';
|
|
62
|
+
}
|
|
63
|
+
if (/fetch failed|ENOTFOUND|ECONNREFUSED|ETIMEDOUT|network/i.test(message)) {
|
|
64
|
+
return `Could not download the embedding model from Hugging Face (${message}). It will be fetched automatically once the network is available.`;
|
|
65
|
+
}
|
|
66
|
+
if (/dlopen|onnxruntime/i.test(message)) {
|
|
67
|
+
return `The ONNX runtime failed to load (${message}). Run memlio with a separately installed Node, for example from nvm.`;
|
|
68
|
+
}
|
|
69
|
+
return `Embedding model error: ${message}`;
|
|
70
|
+
}
|
|
71
|
+
export function cosine(a, b) {
|
|
72
|
+
if (a.length !== b.length || !a.length)
|
|
73
|
+
return 0;
|
|
74
|
+
let dot = 0;
|
|
75
|
+
let an = 0;
|
|
76
|
+
let bn = 0;
|
|
77
|
+
for (let i = 0; i < a.length; i++) {
|
|
78
|
+
dot += a[i] * b[i];
|
|
79
|
+
an += a[i] ** 2;
|
|
80
|
+
bn += b[i] ** 2;
|
|
81
|
+
}
|
|
82
|
+
return an && bn ? dot / Math.sqrt(an * bn) : 0;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=embedding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.js","sourceRoot":"","sources":["../src/embedding.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function serve(home: string): Promise<void>;
|