memshare-mcp 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +243 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +702 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ui.d.ts +28 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +104 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +176 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/memory/project.d.ts +15 -0
- package/dist/memory/project.d.ts.map +1 -0
- package/dist/memory/project.js +77 -0
- package/dist/memory/project.js.map +1 -0
- package/dist/memory/redact.d.ts +35 -0
- package/dist/memory/redact.d.ts.map +1 -0
- package/dist/memory/redact.js +275 -0
- package/dist/memory/redact.js.map +1 -0
- package/dist/memory/store.d.ts +75 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +325 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/types.d.ts +165 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +88 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/sharing/bundle.d.ts +36 -0
- package/dist/sharing/bundle.d.ts.map +1 -0
- package/dist/sharing/bundle.js +130 -0
- package/dist/sharing/bundle.js.map +1 -0
- package/dist/sharing/export.d.ts +54 -0
- package/dist/sharing/export.d.ts.map +1 -0
- package/dist/sharing/export.js +77 -0
- package/dist/sharing/export.js.map +1 -0
- package/dist/sharing/import.d.ts +59 -0
- package/dist/sharing/import.d.ts.map +1 -0
- package/dist/sharing/import.js +101 -0
- package/dist/sharing/import.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { MemoryBundle, SCHEMA_VERSION, } from "../memory/types.js";
|
|
5
|
+
export const BUNDLE_EXTENSION = ".memshare.json";
|
|
6
|
+
/**
|
|
7
|
+
* Deterministic JSON: object keys sorted, no incidental whitespace. Two people
|
|
8
|
+
* hashing the same items must get the same digest, whatever order their
|
|
9
|
+
* JSON runtime happened to produce.
|
|
10
|
+
*/
|
|
11
|
+
export function canonicalize(value) {
|
|
12
|
+
if (value === null || typeof value !== "object")
|
|
13
|
+
return JSON.stringify(value) ?? "null";
|
|
14
|
+
if (Array.isArray(value))
|
|
15
|
+
return `[${value.map(canonicalize).join(",")}]`;
|
|
16
|
+
const entries = Object.entries(value)
|
|
17
|
+
.filter(([, v]) => v !== undefined)
|
|
18
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
19
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalize(v)}`).join(",")}}`;
|
|
20
|
+
}
|
|
21
|
+
/** SHA-256 over the canonicalised items array. */
|
|
22
|
+
export function computeContentHash(items) {
|
|
23
|
+
return createHash("sha256").update(canonicalize(items), "utf8").digest("hex");
|
|
24
|
+
}
|
|
25
|
+
export function createBundle(options) {
|
|
26
|
+
const items = options.items.map((item) => ({ ...item }));
|
|
27
|
+
const metadata = {
|
|
28
|
+
bundleId: randomUUID(),
|
|
29
|
+
exportedBy: options.exportedBy,
|
|
30
|
+
exportedAt: new Date().toISOString(),
|
|
31
|
+
...(options.description ? { description: options.description } : {}),
|
|
32
|
+
...(options.exportedFor ? { exportedFor: options.exportedFor } : {}),
|
|
33
|
+
schemaVersion: SCHEMA_VERSION,
|
|
34
|
+
contentHash: computeContentHash(items),
|
|
35
|
+
...(options.expiresAt ? { expiresAt: options.expiresAt } : {}),
|
|
36
|
+
};
|
|
37
|
+
return MemoryBundle.parse({ metadata, items });
|
|
38
|
+
}
|
|
39
|
+
/** e.g. `bundle-a3f8c2d1.memshare.json` */
|
|
40
|
+
export function bundleFileName(bundle) {
|
|
41
|
+
const short = bundle.metadata.bundleId.replace(/-/g, "").slice(0, 8);
|
|
42
|
+
return `bundle-${short}${BUNDLE_EXTENSION}`;
|
|
43
|
+
}
|
|
44
|
+
/** Schema, hash integrity, schema-version and expiry checks, in that order. */
|
|
45
|
+
export function validateBundle(raw, now = new Date()) {
|
|
46
|
+
const errors = [];
|
|
47
|
+
const warnings = [];
|
|
48
|
+
const parsed = MemoryBundle.safeParse(raw);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
for (const issue of parsed.error.issues) {
|
|
51
|
+
const where = issue.path.length > 0 ? issue.path.join(".") : "bundle";
|
|
52
|
+
errors.push(`${where}: ${issue.message}`);
|
|
53
|
+
}
|
|
54
|
+
return { ok: false, errors, warnings };
|
|
55
|
+
}
|
|
56
|
+
const bundle = parsed.data;
|
|
57
|
+
const actual = computeContentHash(bundle.items);
|
|
58
|
+
if (actual !== bundle.metadata.contentHash) {
|
|
59
|
+
errors.push(`Content hash mismatch: the bundle says ${bundle.metadata.contentHash.slice(0, 12)}... ` +
|
|
60
|
+
`but its items hash to ${actual.slice(0, 12)}... The file was modified after export.`);
|
|
61
|
+
}
|
|
62
|
+
const theirMajor = majorOf(bundle.metadata.schemaVersion);
|
|
63
|
+
const ourMajor = majorOf(SCHEMA_VERSION) ?? 0;
|
|
64
|
+
if (theirMajor === undefined) {
|
|
65
|
+
errors.push(`Unreadable schemaVersion "${bundle.metadata.schemaVersion}".`);
|
|
66
|
+
}
|
|
67
|
+
else if (theirMajor > ourMajor) {
|
|
68
|
+
errors.push(`Bundle uses schema ${bundle.metadata.schemaVersion}, this memshare understands ` +
|
|
69
|
+
`${SCHEMA_VERSION}. Upgrade with: npm install -g memshare-mcp`);
|
|
70
|
+
}
|
|
71
|
+
else if (bundle.metadata.schemaVersion !== SCHEMA_VERSION) {
|
|
72
|
+
warnings.push(`Bundle was written with schema ${bundle.metadata.schemaVersion} (this is ${SCHEMA_VERSION}).`);
|
|
73
|
+
}
|
|
74
|
+
if (bundle.metadata.expiresAt) {
|
|
75
|
+
const expires = Date.parse(bundle.metadata.expiresAt);
|
|
76
|
+
if (!Number.isNaN(expires) && expires <= now.getTime()) {
|
|
77
|
+
errors.push(`This bundle expired on ${new Date(expires).toISOString()}. ` +
|
|
78
|
+
`Ask ${bundle.metadata.exportedBy} for a fresh one.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (bundle.items.length === 0)
|
|
82
|
+
warnings.push("Bundle contains no items.");
|
|
83
|
+
return { ok: errors.length === 0, bundle, errors, warnings };
|
|
84
|
+
}
|
|
85
|
+
export async function writeBundleFile(bundle, destination) {
|
|
86
|
+
const isDirectory = await isDir(destination);
|
|
87
|
+
const file = isDirectory ? path.join(destination, bundleFileName(bundle)) : destination;
|
|
88
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
89
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
90
|
+
await fs.writeFile(tmp, `${JSON.stringify(bundle, null, 2)}\n`, "utf8");
|
|
91
|
+
await fs.rename(tmp, file);
|
|
92
|
+
return file;
|
|
93
|
+
}
|
|
94
|
+
/** Reads and validates a bundle file. Never throws on bad content. */
|
|
95
|
+
export async function readBundleFile(file, now = new Date()) {
|
|
96
|
+
let raw;
|
|
97
|
+
try {
|
|
98
|
+
raw = await fs.readFile(file, "utf8");
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
const code = err.code;
|
|
102
|
+
const message = code === "ENOENT" ? `No such file: ${file}` : `Could not read ${file}: ${String(err)}`;
|
|
103
|
+
return { ok: false, errors: [message], warnings: [] };
|
|
104
|
+
}
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = JSON.parse(raw);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
errors: [`${path.basename(file)} is not valid JSON: ${err.message}`],
|
|
113
|
+
warnings: [],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return validateBundle(parsed, now);
|
|
117
|
+
}
|
|
118
|
+
function majorOf(version) {
|
|
119
|
+
const major = Number(version.split(".")[0]);
|
|
120
|
+
return Number.isInteger(major) ? major : undefined;
|
|
121
|
+
}
|
|
122
|
+
async function isDir(p) {
|
|
123
|
+
try {
|
|
124
|
+
return (await fs.stat(p)).isDirectory();
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../src/sharing/bundle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type PiiFinding } from "../memory/redact.js";
|
|
2
|
+
import { type MemoryStore } from "../memory/store.js";
|
|
3
|
+
import type { MemoryBundle, MemoryItem } from "../memory/types.js";
|
|
4
|
+
export type SkipReason = "private" | "expired" | "no-match";
|
|
5
|
+
export interface ExportCandidate {
|
|
6
|
+
item: MemoryItem;
|
|
7
|
+
/** Empty when the item is clean. */
|
|
8
|
+
findings: PiiFinding[];
|
|
9
|
+
/** The same item with every finding replaced by a marker. */
|
|
10
|
+
redacted: MemoryItem;
|
|
11
|
+
}
|
|
12
|
+
export interface SkippedItem {
|
|
13
|
+
item: MemoryItem;
|
|
14
|
+
reason: SkipReason;
|
|
15
|
+
}
|
|
16
|
+
export interface ExportSelection {
|
|
17
|
+
/** Clean items, ready to go. */
|
|
18
|
+
included: ExportCandidate[];
|
|
19
|
+
/** Items held back because they contain PII. The user decides per item. */
|
|
20
|
+
blocked: ExportCandidate[];
|
|
21
|
+
/** Items that never entered the running, with the reason why. */
|
|
22
|
+
skipped: SkippedItem[];
|
|
23
|
+
}
|
|
24
|
+
export interface SelectOptions {
|
|
25
|
+
tags?: string[];
|
|
26
|
+
/** Only items written by this adapter (`--from chatgpt`). */
|
|
27
|
+
tool?: string;
|
|
28
|
+
query?: string;
|
|
29
|
+
/** Off by default: private means private. */
|
|
30
|
+
includePrivate?: boolean;
|
|
31
|
+
/** Defaults to the store's `autoRedactPII` config value. */
|
|
32
|
+
scanForPii?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Works out what an export would contain, without writing anything. The CLI
|
|
36
|
+
* calls this for both `--preview` and the real run, so the preview is the
|
|
37
|
+
* same computation the export is -- not a separate code path that can drift.
|
|
38
|
+
*/
|
|
39
|
+
export declare function selectForExport(store: MemoryStore, options?: SelectOptions): Promise<ExportSelection>;
|
|
40
|
+
/** Scans one item and prepares its redacted twin. */
|
|
41
|
+
export declare function inspect(item: MemoryItem, scan?: boolean): ExportCandidate;
|
|
42
|
+
export interface BuildOptions {
|
|
43
|
+
exportedBy: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
exportedFor?: string;
|
|
46
|
+
expiresAt?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Assembles the bundle from whatever the user approved. Items arrive already
|
|
50
|
+
* resolved -- redacted or not -- so consent is decided before this point.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildExportBundle(items: MemoryItem[], options: BuildOptions): MemoryBundle;
|
|
53
|
+
export declare function describeSkipReason(reason: SkipReason): string;
|
|
54
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/sharing/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGnE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,oCAAoC;IACpC,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,gCAAgC;IAChC,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,iEAAiE;IACjE,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,WAAW,EAClB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,eAAe,CAAC,CA+B1B;AAED,qDAAqD;AACrD,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,UAAO,GAAG,eAAe,CActE;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE,YAAY,GACpB,YAAY,CAQd;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAS7D"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { scanForPII, redact } from "../memory/redact.js";
|
|
2
|
+
import { isExpired } from "../memory/store.js";
|
|
3
|
+
import { createBundle } from "./bundle.js";
|
|
4
|
+
/**
|
|
5
|
+
* Works out what an export would contain, without writing anything. The CLI
|
|
6
|
+
* calls this for both `--preview` and the real run, so the preview is the
|
|
7
|
+
* same computation the export is -- not a separate code path that can drift.
|
|
8
|
+
*/
|
|
9
|
+
export async function selectForExport(store, options = {}) {
|
|
10
|
+
const config = await store.readConfig();
|
|
11
|
+
const scan = options.scanForPii ?? config.autoRedactPII;
|
|
12
|
+
const matching = await store.list({
|
|
13
|
+
...(options.tags && options.tags.length > 0 ? { tags: options.tags } : {}),
|
|
14
|
+
...(options.tool ? { tool: options.tool } : {}),
|
|
15
|
+
...(options.query ? { query: options.query } : {}),
|
|
16
|
+
includeExpired: true,
|
|
17
|
+
});
|
|
18
|
+
const included = [];
|
|
19
|
+
const blocked = [];
|
|
20
|
+
const skipped = [];
|
|
21
|
+
for (const item of matching) {
|
|
22
|
+
if (isExpired(item)) {
|
|
23
|
+
skipped.push({ item, reason: "expired" });
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (item.visibility !== "shareable" && !options.includePrivate) {
|
|
27
|
+
skipped.push({ item, reason: "private" });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const candidate = inspect(item, scan);
|
|
31
|
+
if (candidate.findings.length > 0)
|
|
32
|
+
blocked.push(candidate);
|
|
33
|
+
else
|
|
34
|
+
included.push(candidate);
|
|
35
|
+
}
|
|
36
|
+
return { included, blocked, skipped };
|
|
37
|
+
}
|
|
38
|
+
/** Scans one item and prepares its redacted twin. */
|
|
39
|
+
export function inspect(item, scan = true) {
|
|
40
|
+
if (!scan)
|
|
41
|
+
return { item, findings: [], redacted: item };
|
|
42
|
+
const inContent = scanForPII(item.content);
|
|
43
|
+
const inTags = item.tags.flatMap((tag) => scanForPII(tag));
|
|
44
|
+
const findings = [...inContent, ...inTags];
|
|
45
|
+
if (findings.length === 0)
|
|
46
|
+
return { item, findings: [], redacted: item };
|
|
47
|
+
const redacted = {
|
|
48
|
+
...item,
|
|
49
|
+
content: redact(item.content).text,
|
|
50
|
+
tags: item.tags.map((tag) => redact(tag).text),
|
|
51
|
+
};
|
|
52
|
+
return { item, findings, redacted };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Assembles the bundle from whatever the user approved. Items arrive already
|
|
56
|
+
* resolved -- redacted or not -- so consent is decided before this point.
|
|
57
|
+
*/
|
|
58
|
+
export function buildExportBundle(items, options) {
|
|
59
|
+
return createBundle({
|
|
60
|
+
items,
|
|
61
|
+
exportedBy: options.exportedBy,
|
|
62
|
+
...(options.description ? { description: options.description } : {}),
|
|
63
|
+
...(options.exportedFor ? { exportedFor: options.exportedFor } : {}),
|
|
64
|
+
...(options.expiresAt ? { expiresAt: options.expiresAt } : {}),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
export function describeSkipReason(reason) {
|
|
68
|
+
switch (reason) {
|
|
69
|
+
case "private":
|
|
70
|
+
return "marked private";
|
|
71
|
+
case "expired":
|
|
72
|
+
return "expired";
|
|
73
|
+
case "no-match":
|
|
74
|
+
return "did not match the filter";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/sharing/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAmB,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAoB,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqC3C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAkB,EAClB,OAAO,GAAkB,EAAE;IAE3B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,aAAa,CAAC;IAExD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC;QAChC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,OAAO,GAAkB,EAAE,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAC1C,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;YAC1C,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACtC,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;;YACtD,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,OAAO,CAAC,IAAgB,EAAE,IAAI,GAAG,IAAI;IACnD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAEzD,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,CAAC,GAAG,SAAS,EAAE,GAAG,MAAM,CAAC,CAAC;IAC3C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAEzE,MAAM,QAAQ,GAAe;QAC3B,GAAG,IAAI;QACP,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI;QAClC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;KAC/C,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AACtC,CAAC;AASD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAmB,EACnB,OAAqB;IAErB,OAAO,YAAY,CAAC;QAClB,KAAK;QACL,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAkB;IACnD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,gBAAgB,CAAC;QAC1B,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;QACnB,KAAK,UAAU;YACb,OAAO,0BAA0B,CAAC;IACtC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type PiiFinding } from "../memory/redact.js";
|
|
2
|
+
import { type MemoryStore } from "../memory/store.js";
|
|
3
|
+
import type { MemoryBundle, MemoryItem, Visibility } from "../memory/types.js";
|
|
4
|
+
export type EntryStatus = "new" | "duplicate" | "expired";
|
|
5
|
+
export interface ImportEntry {
|
|
6
|
+
/** The item exactly as it arrived in the bundle. */
|
|
7
|
+
item: MemoryItem;
|
|
8
|
+
status: EntryStatus;
|
|
9
|
+
/** Set when `status` is "duplicate": the local item that already says this. */
|
|
10
|
+
existingId?: string;
|
|
11
|
+
/** Anything PII-shaped the sender let through. Shown, never auto-dropped. */
|
|
12
|
+
findings: PiiFinding[];
|
|
13
|
+
}
|
|
14
|
+
export interface ImportPlan {
|
|
15
|
+
bundle: MemoryBundle;
|
|
16
|
+
entries: ImportEntry[];
|
|
17
|
+
counts: {
|
|
18
|
+
total: number;
|
|
19
|
+
new: number;
|
|
20
|
+
duplicate: number;
|
|
21
|
+
expired: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Works out what importing would do -- without touching the store. `memshare
|
|
26
|
+
* preview` shows exactly this, and `memshare import` acts on the same plan.
|
|
27
|
+
*/
|
|
28
|
+
export declare function planImport(store: MemoryStore, bundle: MemoryBundle): Promise<ImportPlan>;
|
|
29
|
+
export interface ApplyOptions {
|
|
30
|
+
/** Ids (from the bundle) the user accepted. */
|
|
31
|
+
acceptedIds: string[];
|
|
32
|
+
/**
|
|
33
|
+
* How accepted items are stored locally. Private by default: receiving
|
|
34
|
+
* something is not consent to pass it on.
|
|
35
|
+
*/
|
|
36
|
+
visibility?: Visibility;
|
|
37
|
+
/** Extra tags to attach, e.g. `from-alice`. */
|
|
38
|
+
addTags?: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface ApplyResult {
|
|
41
|
+
imported: MemoryItem[];
|
|
42
|
+
/** Accepted ids that were not in the bundle, if any. */
|
|
43
|
+
unknown: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Writes the accepted items into the local store as fresh records. Nothing is
|
|
47
|
+
* ever overwritten: an accepted duplicate becomes a second item, because the
|
|
48
|
+
* sender's phrasing is evidence in its own right.
|
|
49
|
+
*/
|
|
50
|
+
export declare function applyImport(store: MemoryStore, plan: ImportPlan, options: ApplyOptions): Promise<ApplyResult>;
|
|
51
|
+
/**
|
|
52
|
+
* The sooner of two optional deadlines. Unparseable values are ignored rather
|
|
53
|
+
* than treated as "expires now", so a malformed date cannot silently destroy
|
|
54
|
+
* a memory the moment it lands.
|
|
55
|
+
*/
|
|
56
|
+
export declare function earliestExpiry(...candidates: Array<string | undefined>): string | undefined;
|
|
57
|
+
/** A slug safe to use as a tag, e.g. `from-alice`. */
|
|
58
|
+
export declare function senderTag(exportedBy: string): string;
|
|
59
|
+
//# sourceMappingURL=import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/sharing/import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAyB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,KAAK,EAEV,YAAY,EACZ,UAAU,EACV,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,WAAW,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5E;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,UAAU,CAAC,CA+BrB;AAED,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,wDAAwD;IACxD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,WAAW,CAAC,CAmCtB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAS3F;AAED,sDAAsD;AACtD,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMpD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { scanForPII } from "../memory/redact.js";
|
|
2
|
+
import { contentKey, isExpired } from "../memory/store.js";
|
|
3
|
+
/**
|
|
4
|
+
* Works out what importing would do -- without touching the store. `memshare
|
|
5
|
+
* preview` shows exactly this, and `memshare import` acts on the same plan.
|
|
6
|
+
*/
|
|
7
|
+
export async function planImport(store, bundle) {
|
|
8
|
+
const existing = await store.all();
|
|
9
|
+
const byKey = new Map();
|
|
10
|
+
for (const item of existing) {
|
|
11
|
+
const key = contentKey(item.content);
|
|
12
|
+
if (!byKey.has(key))
|
|
13
|
+
byKey.set(key, item);
|
|
14
|
+
}
|
|
15
|
+
const entries = bundle.items.map((item) => {
|
|
16
|
+
if (isExpired(item)) {
|
|
17
|
+
return { item, status: "expired", findings: scanForPII(item.content) };
|
|
18
|
+
}
|
|
19
|
+
const match = byKey.get(contentKey(item.content));
|
|
20
|
+
return {
|
|
21
|
+
item,
|
|
22
|
+
status: match ? "duplicate" : "new",
|
|
23
|
+
...(match ? { existingId: match.id } : {}),
|
|
24
|
+
findings: scanForPII(item.content),
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
bundle,
|
|
29
|
+
entries,
|
|
30
|
+
counts: {
|
|
31
|
+
total: entries.length,
|
|
32
|
+
new: entries.filter((e) => e.status === "new").length,
|
|
33
|
+
duplicate: entries.filter((e) => e.status === "duplicate").length,
|
|
34
|
+
expired: entries.filter((e) => e.status === "expired").length,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Writes the accepted items into the local store as fresh records. Nothing is
|
|
40
|
+
* ever overwritten: an accepted duplicate becomes a second item, because the
|
|
41
|
+
* sender's phrasing is evidence in its own right.
|
|
42
|
+
*/
|
|
43
|
+
export async function applyImport(store, plan, options) {
|
|
44
|
+
const accepted = new Set(options.acceptedIds);
|
|
45
|
+
const byId = new Map(plan.entries.map((e) => [e.item.id, e]));
|
|
46
|
+
const importedAt = new Date().toISOString();
|
|
47
|
+
const imported = [];
|
|
48
|
+
for (const id of accepted) {
|
|
49
|
+
const entry = byId.get(id);
|
|
50
|
+
if (!entry)
|
|
51
|
+
continue;
|
|
52
|
+
const provenance = {
|
|
53
|
+
bundleId: plan.bundle.metadata.bundleId,
|
|
54
|
+
exportedBy: plan.bundle.metadata.exportedBy,
|
|
55
|
+
importedAt,
|
|
56
|
+
originalId: entry.item.id,
|
|
57
|
+
};
|
|
58
|
+
// A bundle that expires in 30 days should not hand over memories that
|
|
59
|
+
// live forever. The item keeps whichever deadline comes first.
|
|
60
|
+
const expiresAt = earliestExpiry(entry.item.expiresAt, plan.bundle.metadata.expiresAt);
|
|
61
|
+
const saved = await store.add({
|
|
62
|
+
content: entry.item.content,
|
|
63
|
+
tags: [...entry.item.tags, ...(options.addTags ?? [])],
|
|
64
|
+
visibility: options.visibility ?? "private",
|
|
65
|
+
confidence: "imported",
|
|
66
|
+
source: entry.item.source,
|
|
67
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
68
|
+
importedFrom: provenance,
|
|
69
|
+
});
|
|
70
|
+
imported.push(saved);
|
|
71
|
+
}
|
|
72
|
+
const unknown = options.acceptedIds.filter((id) => !byId.has(id));
|
|
73
|
+
return { imported, unknown };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The sooner of two optional deadlines. Unparseable values are ignored rather
|
|
77
|
+
* than treated as "expires now", so a malformed date cannot silently destroy
|
|
78
|
+
* a memory the moment it lands.
|
|
79
|
+
*/
|
|
80
|
+
export function earliestExpiry(...candidates) {
|
|
81
|
+
let best;
|
|
82
|
+
for (const candidate of candidates) {
|
|
83
|
+
if (!candidate)
|
|
84
|
+
continue;
|
|
85
|
+
const at = Date.parse(candidate);
|
|
86
|
+
if (Number.isNaN(at))
|
|
87
|
+
continue;
|
|
88
|
+
if (!best || at < best.at)
|
|
89
|
+
best = { iso: candidate, at };
|
|
90
|
+
}
|
|
91
|
+
return best?.iso;
|
|
92
|
+
}
|
|
93
|
+
/** A slug safe to use as a tag, e.g. `from-alice`. */
|
|
94
|
+
export function senderTag(exportedBy) {
|
|
95
|
+
const slug = exportedBy
|
|
96
|
+
.toLowerCase()
|
|
97
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
98
|
+
.replace(/^-+|-+$/g, "");
|
|
99
|
+
return slug === "" ? "from-import" : `from-${slug}`;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/sharing/import.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "memshare-mcp",
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "Peer-to-peer AI memory sharing between users — with consent. Local-first JSON memory store with an MCP server and CLI.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"ai-memory",
|
|
9
|
+
"memory",
|
|
10
|
+
"claude",
|
|
11
|
+
"llm",
|
|
12
|
+
"local-first",
|
|
13
|
+
"peer-to-peer",
|
|
14
|
+
"context"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18.17"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"memshare": "dist/cli/index.js"
|
|
23
|
+
},
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"types": "dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -p tsconfig.json",
|
|
39
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
|
+
"prepublishOnly": "npm run build && npm test && npm run check:docs",
|
|
44
|
+
"check:docs": "node scripts/check-docs.mjs"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@inquirer/prompts": "^8.7.2",
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
49
|
+
"commander": "^15.0.0",
|
|
50
|
+
"zod": "^4.6.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.20.2",
|
|
54
|
+
"typescript": "^7.0.2",
|
|
55
|
+
"vitest": "^5.0.0"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/kampana/memshare.git"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/kampana/memshare#readme",
|
|
62
|
+
"bugs": {
|
|
63
|
+
"url": "https://github.com/kampana/memshare/issues"
|
|
64
|
+
},
|
|
65
|
+
"author": "kampana",
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
}
|
|
69
|
+
}
|