@batalabs/virlow-mcp-core 3.11.4
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 +15 -0
- package/dist/api.d.ts +118 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +109 -0
- package/dist/api.js.map +1 -0
- package/dist/embedding-cache.d.ts +34 -0
- package/dist/embedding-cache.d.ts.map +1 -0
- package/dist/embedding-cache.js +108 -0
- package/dist/embedding-cache.js.map +1 -0
- package/dist/exposure.d.ts +42 -0
- package/dist/exposure.d.ts.map +1 -0
- package/dist/exposure.js +74 -0
- package/dist/exposure.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/memories-enabled.d.ts +16 -0
- package/dist/memories-enabled.d.ts.map +1 -0
- package/dist/memories-enabled.js +29 -0
- package/dist/memories-enabled.js.map +1 -0
- package/dist/memories.d.ts +86 -0
- package/dist/memories.d.ts.map +1 -0
- package/dist/memories.js +350 -0
- package/dist/memories.js.map +1 -0
- package/dist/memory-note.d.ts +45 -0
- package/dist/memory-note.d.ts.map +1 -0
- package/dist/memory-note.js +144 -0
- package/dist/memory-note.js.map +1 -0
- package/dist/memory-tree.d.ts +28 -0
- package/dist/memory-tree.d.ts.map +1 -0
- package/dist/memory-tree.js +68 -0
- package/dist/memory-tree.js.map +1 -0
- package/dist/notes.d.ts +109 -0
- package/dist/notes.d.ts.map +1 -0
- package/dist/notes.js +234 -0
- package/dist/notes.js.map +1 -0
- package/dist/vault.d.ts +35 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +73 -0
- package/dist/vault.js.map +1 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Typelets Secure Notes
|
|
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,15 @@
|
|
|
1
|
+
# @batalabs/virlow-mcp-core
|
|
2
|
+
|
|
3
|
+
The connector core behind [Virlow](https://virlow.com)'s MCP integration:
|
|
4
|
+
encrypted note and memory access, folder exposure, and the memories opt-in.
|
|
5
|
+
|
|
6
|
+
Shared deliberately. The local `virlow-mcp` CLI and the hosted connector both
|
|
7
|
+
decide what an AI tool may read, and those rules — `folders.mcpEnabled` and the
|
|
8
|
+
account's memories setting — must have one implementation. Two copies of the
|
|
9
|
+
code that decides who can read what would drift, and drift there is a security
|
|
10
|
+
bug rather than an inconsistency.
|
|
11
|
+
|
|
12
|
+
What it does *not* contain: transports, the CLI, and the unlock user interface.
|
|
13
|
+
Those differ between local and hosted and belong to their hosts.
|
|
14
|
+
|
|
15
|
+
Published from [batalabs/virlow-app](https://github.com/batalabs/virlow-app).
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export declare class ApiError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
constructor(status: number, message: string);
|
|
4
|
+
}
|
|
5
|
+
export interface AuthTokens {
|
|
6
|
+
accessToken: string;
|
|
7
|
+
refreshToken: string;
|
|
8
|
+
}
|
|
9
|
+
export type LoginResult = {
|
|
10
|
+
mfaRequired: true;
|
|
11
|
+
mfaToken: string;
|
|
12
|
+
} | {
|
|
13
|
+
mfaRequired?: false;
|
|
14
|
+
user: {
|
|
15
|
+
id: string;
|
|
16
|
+
email: string;
|
|
17
|
+
};
|
|
18
|
+
accessToken: string;
|
|
19
|
+
refreshToken: string;
|
|
20
|
+
};
|
|
21
|
+
export interface ApiNote {
|
|
22
|
+
id: string;
|
|
23
|
+
folderId: string | null;
|
|
24
|
+
type: string;
|
|
25
|
+
/** Plaintext columns: the real content for legacy (unencrypted) notes,
|
|
26
|
+
* or the literal "[ENCRYPTED]" placeholder once a note carries ciphertext. */
|
|
27
|
+
title: string;
|
|
28
|
+
content: string;
|
|
29
|
+
encryptedTitle: string | null;
|
|
30
|
+
encryptedContent: string | null;
|
|
31
|
+
iv: string | null;
|
|
32
|
+
salt: string | null;
|
|
33
|
+
starred: boolean | null;
|
|
34
|
+
archived: boolean | null;
|
|
35
|
+
deleted: boolean | null;
|
|
36
|
+
/** The web app segregates hidden notes client-side; virlow-mcp filters
|
|
37
|
+
* them out of listNotes/searchNotes for the same reason (see notes.ts). */
|
|
38
|
+
hidden: boolean | null;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
}
|
|
42
|
+
export interface NoteWrite {
|
|
43
|
+
title: string;
|
|
44
|
+
content: string;
|
|
45
|
+
encryptedTitle: string;
|
|
46
|
+
encryptedContent: string;
|
|
47
|
+
iv: string;
|
|
48
|
+
salt: string;
|
|
49
|
+
folderId?: string | null;
|
|
50
|
+
type?: string;
|
|
51
|
+
tags?: string[];
|
|
52
|
+
starred?: boolean;
|
|
53
|
+
archived?: boolean;
|
|
54
|
+
deleted?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export interface ApiFolder {
|
|
57
|
+
id: string;
|
|
58
|
+
name: string;
|
|
59
|
+
parentId: string | null;
|
|
60
|
+
/** Marks the single root folder whose notes are AI memories; null for every
|
|
61
|
+
* ordinary folder, including the namespace subfolders under that root. */
|
|
62
|
+
kind: 'memories' | null;
|
|
63
|
+
/** The user's "Expose to MCP" switch. False by default — nothing in this
|
|
64
|
+
* folder is shown to AI tools until they turn it on. */
|
|
65
|
+
mcpEnabled: boolean;
|
|
66
|
+
}
|
|
67
|
+
export declare class VirlowApi {
|
|
68
|
+
private readonly baseUrl;
|
|
69
|
+
private tokens;
|
|
70
|
+
private refreshInFlight;
|
|
71
|
+
onTokensRotated?: (tokens: AuthTokens) => void;
|
|
72
|
+
constructor(baseUrl: string);
|
|
73
|
+
setTokens(tokens: AuthTokens | null): void;
|
|
74
|
+
private request;
|
|
75
|
+
private refresh;
|
|
76
|
+
private doRefresh;
|
|
77
|
+
login(email: string, password: string): Promise<LoginResult>;
|
|
78
|
+
verify2fa(mfaToken: string, codeOrRecovery: {
|
|
79
|
+
code?: string;
|
|
80
|
+
recoveryCode?: string;
|
|
81
|
+
}): Promise<AuthTokens>;
|
|
82
|
+
me(): Promise<{
|
|
83
|
+
id: string;
|
|
84
|
+
email: string;
|
|
85
|
+
memoriesEnabled?: boolean;
|
|
86
|
+
}>;
|
|
87
|
+
encryptionStatus(): Promise<{
|
|
88
|
+
verifier: {
|
|
89
|
+
ciphertext: string;
|
|
90
|
+
iv: string;
|
|
91
|
+
salt: string;
|
|
92
|
+
} | null;
|
|
93
|
+
}>;
|
|
94
|
+
listNotes(params: {
|
|
95
|
+
page?: number;
|
|
96
|
+
limit?: number;
|
|
97
|
+
folderId?: string;
|
|
98
|
+
starred?: boolean;
|
|
99
|
+
archived?: boolean;
|
|
100
|
+
deleted?: boolean;
|
|
101
|
+
}): Promise<{
|
|
102
|
+
notes: ApiNote[];
|
|
103
|
+
pagination: {
|
|
104
|
+
page: number;
|
|
105
|
+
limit: number;
|
|
106
|
+
total: number;
|
|
107
|
+
pages: number;
|
|
108
|
+
};
|
|
109
|
+
}>;
|
|
110
|
+
getNote(id: string): Promise<ApiNote>;
|
|
111
|
+
createNote(body: NoteWrite): Promise<ApiNote>;
|
|
112
|
+
updateNote(id: string, body: Partial<NoteWrite>): Promise<ApiNote>;
|
|
113
|
+
listFolders(): Promise<{
|
|
114
|
+
folders: ApiFolder[];
|
|
115
|
+
}>;
|
|
116
|
+
createFolder(name: string, parentId?: string | null, kind?: 'memories'): Promise<ApiFolder>;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export class ApiError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = 'ApiError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class VirlowApi {
|
|
10
|
+
baseUrl;
|
|
11
|
+
tokens = null;
|
|
12
|
+
refreshInFlight = null;
|
|
13
|
+
onTokensRotated;
|
|
14
|
+
constructor(baseUrl) {
|
|
15
|
+
this.baseUrl = baseUrl;
|
|
16
|
+
}
|
|
17
|
+
setTokens(tokens) {
|
|
18
|
+
this.tokens = tokens;
|
|
19
|
+
}
|
|
20
|
+
async request(method, path, body, opts = {}) {
|
|
21
|
+
const { auth = true, retryOn401 = true } = opts;
|
|
22
|
+
const headers = { 'content-type': 'application/json' };
|
|
23
|
+
if (auth && this.tokens)
|
|
24
|
+
headers.authorization = `Bearer ${this.tokens.accessToken}`;
|
|
25
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
26
|
+
method,
|
|
27
|
+
headers,
|
|
28
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
29
|
+
});
|
|
30
|
+
if (res.status === 401 && auth && retryOn401 && this.tokens) {
|
|
31
|
+
await this.refresh();
|
|
32
|
+
return this.request(method, path, body, { auth, retryOn401: false });
|
|
33
|
+
}
|
|
34
|
+
if (res.status === 204)
|
|
35
|
+
return undefined;
|
|
36
|
+
const data = await res.json().catch(() => ({}));
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
const d = data;
|
|
39
|
+
throw new ApiError(res.status, d.error ?? d.message ?? res.statusText);
|
|
40
|
+
}
|
|
41
|
+
return data;
|
|
42
|
+
}
|
|
43
|
+
refresh() {
|
|
44
|
+
this.refreshInFlight ??= this.doRefresh().finally(() => {
|
|
45
|
+
this.refreshInFlight = null;
|
|
46
|
+
});
|
|
47
|
+
return this.refreshInFlight;
|
|
48
|
+
}
|
|
49
|
+
async doRefresh() {
|
|
50
|
+
if (!this.tokens)
|
|
51
|
+
throw new ApiError(401, 'Not authenticated');
|
|
52
|
+
const rotated = await this.request('POST', '/api/auth/refresh', { refreshToken: this.tokens.refreshToken }, { auth: false, retryOn401: false });
|
|
53
|
+
this.tokens = { accessToken: rotated.accessToken, refreshToken: rotated.refreshToken };
|
|
54
|
+
this.onTokensRotated?.(this.tokens);
|
|
55
|
+
}
|
|
56
|
+
login(email, password) {
|
|
57
|
+
return this.request('POST', '/api/auth/login', { email, password }, { auth: false });
|
|
58
|
+
}
|
|
59
|
+
verify2fa(mfaToken, codeOrRecovery) {
|
|
60
|
+
return this.request('POST', '/api/auth/2fa/verify', { mfaToken, ...codeOrRecovery }, { auth: false });
|
|
61
|
+
}
|
|
62
|
+
me() {
|
|
63
|
+
return this.request('GET', '/api/users/me');
|
|
64
|
+
}
|
|
65
|
+
encryptionStatus() {
|
|
66
|
+
return this.request('GET', '/api/users/me/encryption');
|
|
67
|
+
}
|
|
68
|
+
listNotes(params) {
|
|
69
|
+
const query = new URLSearchParams();
|
|
70
|
+
if (params.page !== undefined)
|
|
71
|
+
query.set('page', String(params.page));
|
|
72
|
+
if (params.limit !== undefined)
|
|
73
|
+
query.set('limit', String(params.limit));
|
|
74
|
+
if (params.folderId !== undefined)
|
|
75
|
+
query.set('folderId', params.folderId);
|
|
76
|
+
if (params.starred !== undefined)
|
|
77
|
+
query.set('starred', String(params.starred));
|
|
78
|
+
if (params.archived !== undefined)
|
|
79
|
+
query.set('archived', String(params.archived));
|
|
80
|
+
if (params.deleted !== undefined)
|
|
81
|
+
query.set('deleted', String(params.deleted));
|
|
82
|
+
const qs = query.toString();
|
|
83
|
+
return this.request('GET', `/api/notes${qs ? `?${qs}` : ''}`);
|
|
84
|
+
}
|
|
85
|
+
getNote(id) {
|
|
86
|
+
return this.request('GET', `/api/notes/${id}`);
|
|
87
|
+
}
|
|
88
|
+
createNote(body) {
|
|
89
|
+
return this.request('POST', '/api/notes', body);
|
|
90
|
+
}
|
|
91
|
+
updateNote(id, body) {
|
|
92
|
+
return this.request('PUT', `/api/notes/${id}`, body);
|
|
93
|
+
}
|
|
94
|
+
listFolders() {
|
|
95
|
+
return this.request('GET', '/api/folders');
|
|
96
|
+
}
|
|
97
|
+
createFolder(name, parentId, kind) {
|
|
98
|
+
// `kind` is omitted rather than sent as null: the API rejects unknown kinds
|
|
99
|
+
// and a namespace subfolder is an ordinary folder.
|
|
100
|
+
const body = {
|
|
101
|
+
name,
|
|
102
|
+
parentId,
|
|
103
|
+
};
|
|
104
|
+
if (kind !== undefined)
|
|
105
|
+
body.kind = kind;
|
|
106
|
+
return this.request('POST', '/api/folders', body);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IADlB,YACkB,MAAc,EAC9B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QAI9B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AA4DD,MAAM,OAAO,SAAS;IAKS;IAJrB,MAAM,GAAsB,IAAI,CAAC;IACjC,eAAe,GAAyB,IAAI,CAAC;IACrD,eAAe,CAAgC;IAE/C,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAEhD,SAAS,CAAC,MAAyB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,MAAc,EACd,IAAY,EACZ,IAAc,EACd,OAAiD,EAAE;QAEnD,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;QAChD,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;QAC/E,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,aAAa,GAAG,UAAU,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAErF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YAChD,MAAM;YACN,OAAO;YACP,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,IAAI,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5D,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,SAAc,CAAC;QAC9C,MAAM,IAAI,GAAY,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,CAAC,GAAG,IAA4C,CAAC;YACvD,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,IAAS,CAAC;IACnB,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACrD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,SAAS;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAChC,MAAM,EACN,mBAAmB,EACnB,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,EAC1C,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CACnC,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;QACvF,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,KAAa,EAAE,QAAgB;QACnC,OAAO,IAAI,CAAC,OAAO,CAAc,MAAM,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG,CAAC;IAED,SAAS,CAAC,QAAgB,EAAE,cAAwD;QAClF,OAAO,IAAI,CAAC,OAAO,CACjB,MAAM,EACN,sBAAsB,EACtB,EAAE,QAAQ,EAAE,GAAG,cAAc,EAAE,EAC/B,EAAE,IAAI,EAAE,KAAK,EAAE,CAChB,CAAC;IACJ,CAAC;IAED,EAAE;QACA,OAAO,IAAI,CAAC,OAAO,CACjB,KAAK,EACL,eAAe,CAChB,CAAC;IACJ,CAAC;IAED,gBAAgB;QACd,OAAO,IAAI,CAAC,OAAO,CACjB,KAAK,EACL,0BAA0B,CAC3B,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,MAOT;QACC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACtE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzE,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1E,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/E,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClF,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/E,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC,OAAO,CAGhB,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,CAAC,EAAU;QAChB,OAAO,IAAI,CAAC,OAAO,CAAU,KAAK,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,UAAU,CAAC,IAAe;QACxB,OAAO,IAAI,CAAC,OAAO,CAAU,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;IAC3D,CAAC;IAED,UAAU,CAAC,EAAU,EAAE,IAAwB;QAC7C,OAAO,IAAI,CAAC,OAAO,CAAU,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IAChE,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,CAA2B,KAAK,EAAE,cAAc,CAAC,CAAC;IACvE,CAAC;IAED,YAAY,CACV,IAAY,EACZ,QAAwB,EACxB,IAAiB;QAEjB,4EAA4E;QAC5E,mDAAmD;QACnD,MAAM,IAAI,GAAkE;YAC1E,IAAI;YACJ,QAAQ;SACT,CAAC;QACF,IAAI,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACzC,OAAO,IAAI,CAAC,OAAO,CAAY,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;IAC/D,CAAC;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding vectors cached on disk, encrypted under the master key.
|
|
3
|
+
*
|
|
4
|
+
* Embedding a few hundred memories takes long enough to be felt at the start
|
|
5
|
+
* of every session, so the vectors are kept between runs. They are derived
|
|
6
|
+
* from plaintext memories, so the file is AES-GCM ciphertext and nothing —
|
|
7
|
+
* not the note ids, not the timestamps — is readable without the key.
|
|
8
|
+
*
|
|
9
|
+
* Every failure mode is a silent miss: a missing file, a file written under a
|
|
10
|
+
* different master password, a different embedding model, or corrupt bytes all
|
|
11
|
+
* yield an empty cache that gets rebuilt. The cache is an optimisation, never
|
|
12
|
+
* a source of truth.
|
|
13
|
+
*/
|
|
14
|
+
export declare class EmbeddingCache {
|
|
15
|
+
private readonly file;
|
|
16
|
+
private readonly embeddingModel;
|
|
17
|
+
private readonly dir;
|
|
18
|
+
private records;
|
|
19
|
+
private dirty;
|
|
20
|
+
constructor(opts: {
|
|
21
|
+
dir: string;
|
|
22
|
+
userId: string;
|
|
23
|
+
embeddingModel: string;
|
|
24
|
+
});
|
|
25
|
+
load(key: CryptoKey): Promise<void>;
|
|
26
|
+
/** The cached vector, but only if the note has not been edited since. */
|
|
27
|
+
get(noteId: string, updatedAt: string): Float32Array | undefined;
|
|
28
|
+
set(noteId: string, updatedAt: string, vector: Float32Array): void;
|
|
29
|
+
delete(noteId: string): void;
|
|
30
|
+
clear(): void;
|
|
31
|
+
save(key: CryptoKey): Promise<void>;
|
|
32
|
+
get size(): number;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=embedding-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-cache.d.ts","sourceRoot":"","sources":["../src/embedding-cache.ts"],"names":[],"mappings":"AA0BA;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAkE;IACjF,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE;IAQnE,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BzC,yEAAyE;IACzE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAKhE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,IAAI;IAKlE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI5B,KAAK,IAAI,IAAI;IAKP,IAAI,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BzC,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { bytesToBase64, base64ToBytes, decryptBytesBlob, encryptBytesBlob, } from '@batalabs/virlow-crypto';
|
|
5
|
+
/** Bumped if the on-disk record shape ever changes; a mismatch is a cache miss. */
|
|
6
|
+
const CACHE_VERSION = 1;
|
|
7
|
+
/**
|
|
8
|
+
* Embedding vectors cached on disk, encrypted under the master key.
|
|
9
|
+
*
|
|
10
|
+
* Embedding a few hundred memories takes long enough to be felt at the start
|
|
11
|
+
* of every session, so the vectors are kept between runs. They are derived
|
|
12
|
+
* from plaintext memories, so the file is AES-GCM ciphertext and nothing —
|
|
13
|
+
* not the note ids, not the timestamps — is readable without the key.
|
|
14
|
+
*
|
|
15
|
+
* Every failure mode is a silent miss: a missing file, a file written under a
|
|
16
|
+
* different master password, a different embedding model, or corrupt bytes all
|
|
17
|
+
* yield an empty cache that gets rebuilt. The cache is an optimisation, never
|
|
18
|
+
* a source of truth.
|
|
19
|
+
*/
|
|
20
|
+
export class EmbeddingCache {
|
|
21
|
+
file;
|
|
22
|
+
embeddingModel;
|
|
23
|
+
dir;
|
|
24
|
+
records = new Map();
|
|
25
|
+
dirty = false;
|
|
26
|
+
constructor(opts) {
|
|
27
|
+
this.dir = opts.dir;
|
|
28
|
+
this.embeddingModel = opts.embeddingModel;
|
|
29
|
+
// The user id is hashed so the filename itself identifies nobody.
|
|
30
|
+
const name = createHash('sha256').update(opts.userId).digest('hex');
|
|
31
|
+
this.file = path.join(opts.dir, `${name}.bin`);
|
|
32
|
+
}
|
|
33
|
+
async load(key) {
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
const blob = await readFile(this.file, 'utf8');
|
|
37
|
+
const bytes = await decryptBytesBlob(key, blob);
|
|
38
|
+
parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Missing, undecryptable, or malformed — all just a cold cache.
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (parsed.version !== CACHE_VERSION)
|
|
45
|
+
return;
|
|
46
|
+
// Vectors from another model are meaningless against current ones: mixing
|
|
47
|
+
// them would produce similarity scores that silently misrank.
|
|
48
|
+
if (parsed.embeddingModel !== this.embeddingModel)
|
|
49
|
+
return;
|
|
50
|
+
if (!parsed.records || typeof parsed.records !== 'object')
|
|
51
|
+
return;
|
|
52
|
+
for (const [noteId, record] of Object.entries(parsed.records)) {
|
|
53
|
+
if (!record || typeof record.v !== 'string' || typeof record.u !== 'string') {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const bytes = base64ToBytes(record.v);
|
|
57
|
+
if (bytes.byteLength % 4 !== 0)
|
|
58
|
+
continue;
|
|
59
|
+
const vector = new Float32Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
|
|
60
|
+
this.records.set(noteId, { vector, updatedAt: record.u });
|
|
61
|
+
}
|
|
62
|
+
this.dirty = false;
|
|
63
|
+
}
|
|
64
|
+
/** The cached vector, but only if the note has not been edited since. */
|
|
65
|
+
get(noteId, updatedAt) {
|
|
66
|
+
const record = this.records.get(noteId);
|
|
67
|
+
return record && record.updatedAt === updatedAt ? record.vector : undefined;
|
|
68
|
+
}
|
|
69
|
+
set(noteId, updatedAt, vector) {
|
|
70
|
+
this.records.set(noteId, { vector, updatedAt });
|
|
71
|
+
this.dirty = true;
|
|
72
|
+
}
|
|
73
|
+
delete(noteId) {
|
|
74
|
+
if (this.records.delete(noteId))
|
|
75
|
+
this.dirty = true;
|
|
76
|
+
}
|
|
77
|
+
clear() {
|
|
78
|
+
if (this.records.size > 0)
|
|
79
|
+
this.dirty = true;
|
|
80
|
+
this.records.clear();
|
|
81
|
+
}
|
|
82
|
+
async save(key) {
|
|
83
|
+
if (!this.dirty)
|
|
84
|
+
return;
|
|
85
|
+
const records = {};
|
|
86
|
+
for (const [noteId, { vector, updatedAt }] of this.records) {
|
|
87
|
+
const bytes = new Uint8Array(vector.buffer.slice(vector.byteOffset, vector.byteOffset + vector.byteLength));
|
|
88
|
+
records[noteId] = { v: bytesToBase64(bytes), u: updatedAt };
|
|
89
|
+
}
|
|
90
|
+
const payload = {
|
|
91
|
+
version: CACHE_VERSION,
|
|
92
|
+
embeddingModel: this.embeddingModel,
|
|
93
|
+
records,
|
|
94
|
+
};
|
|
95
|
+
const blob = await encryptBytesBlob(key, new TextEncoder().encode(JSON.stringify(payload)));
|
|
96
|
+
await mkdir(this.dir, { recursive: true, mode: 0o700 });
|
|
97
|
+
// Write-then-rename so an interrupted save cannot leave a half-written file
|
|
98
|
+
// where the next load expects ciphertext.
|
|
99
|
+
const tmp = `${this.file}.${randomUUID()}.tmp`;
|
|
100
|
+
await writeFile(tmp, blob, { mode: 0o600 });
|
|
101
|
+
await rename(tmp, this.file);
|
|
102
|
+
this.dirty = false;
|
|
103
|
+
}
|
|
104
|
+
get size() {
|
|
105
|
+
return this.records.size;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=embedding-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-cache.js","sourceRoot":"","sources":["../src/embedding-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ApiFolder } from './api';
|
|
2
|
+
/**
|
|
3
|
+
* Which folders the user has actually opened to AI tools.
|
|
4
|
+
*
|
|
5
|
+
* Every folder carries an "Expose to MCP" switch — *"Let the local AI
|
|
6
|
+
* assistant read & edit this folder and its subfolders. Off by default."* —
|
|
7
|
+
* and until now nothing read it, so the app was telling users something untrue
|
|
8
|
+
* about who could see their notes. This is the thing that makes it true.
|
|
9
|
+
*
|
|
10
|
+
* The rule follows the switch's own wording: a folder is exposed when it, or
|
|
11
|
+
* any ancestor, is switched on. Turning a parent on therefore opens everything
|
|
12
|
+
* beneath it, and a child cannot opt back out — matching what the label
|
|
13
|
+
* promises rather than inventing a precedence rule the UI never mentions.
|
|
14
|
+
*/
|
|
15
|
+
export interface Exposure {
|
|
16
|
+
/** Whether notes in this folder may be shown. */
|
|
17
|
+
allows(folderId: string | null): boolean;
|
|
18
|
+
/** Folders the user has not opened, for reporting how much is hidden. */
|
|
19
|
+
hiddenFolderCount: number;
|
|
20
|
+
/** True when nothing at all is exposed — worth saying differently, since
|
|
21
|
+
* "no results" and "you have not opened anything yet" look identical
|
|
22
|
+
* otherwise. */
|
|
23
|
+
allHidden: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface ExposureOptions {
|
|
26
|
+
/** The memories root, if one exists. It and its namespace subfolders are
|
|
27
|
+
* exempt: the MCP server creates that tree and it exists solely for AI
|
|
28
|
+
* tools, so requiring the user to opt it in would be asking them to
|
|
29
|
+
* authorise a folder they never made for a purpose it already has. */
|
|
30
|
+
memoriesRootId?: string | null;
|
|
31
|
+
}
|
|
32
|
+
export declare function computeExposure(folders: ReadonlyArray<ApiFolder>, opts?: ExposureOptions): Exposure;
|
|
33
|
+
/**
|
|
34
|
+
* The sentence appended to any result that was filtered. Never omitted when
|
|
35
|
+
* something was withheld: a quietly shortened list is indistinguishable from
|
|
36
|
+
* an empty vault, and the user would have no reason to suspect a setting.
|
|
37
|
+
*/
|
|
38
|
+
export declare function filteredNotice(hidden: {
|
|
39
|
+
inClosedFolders: number;
|
|
40
|
+
unfiled: number;
|
|
41
|
+
}, exposure: Exposure): string;
|
|
42
|
+
//# sourceMappingURL=exposure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exposure.d.ts","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,QAAQ;IACvB,iDAAiD;IACjD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IACzC,yEAAyE;IACzE,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;oBAEgB;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B;;;0EAGsE;IACtE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EACjC,IAAI,GAAE,eAAoB,GACzB,QAAQ,CAgDV;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACpD,QAAQ,EAAE,QAAQ,GACjB,MAAM,CA6BR"}
|
package/dist/exposure.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export function computeExposure(folders, opts = {}) {
|
|
2
|
+
const byId = new Map(folders.map((f) => [f.id, f]));
|
|
3
|
+
const memoriesRootId = opts.memoriesRootId ?? folders.find((f) => f.kind === 'memories')?.id ?? null;
|
|
4
|
+
const resolved = new Map();
|
|
5
|
+
const isExposed = (folderId) => {
|
|
6
|
+
const cached = resolved.get(folderId);
|
|
7
|
+
if (cached !== undefined)
|
|
8
|
+
return cached;
|
|
9
|
+
// Walk to the root, stopping at the first switched-on ancestor. The seen
|
|
10
|
+
// set keeps a corrupt parent cycle from hanging the server rather than
|
|
11
|
+
// trusting the data to be a tree.
|
|
12
|
+
const chain = [];
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
let current = folderId;
|
|
15
|
+
let exposed = false;
|
|
16
|
+
while (current !== null && !seen.has(current)) {
|
|
17
|
+
seen.add(current);
|
|
18
|
+
const folder = byId.get(current);
|
|
19
|
+
if (folder === undefined)
|
|
20
|
+
break;
|
|
21
|
+
chain.push(folder.id);
|
|
22
|
+
if (folder.mcpEnabled === true || folder.id === memoriesRootId) {
|
|
23
|
+
exposed = true;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
current = folder.parentId;
|
|
27
|
+
}
|
|
28
|
+
for (const id of chain)
|
|
29
|
+
resolved.set(id, exposed);
|
|
30
|
+
return exposed;
|
|
31
|
+
};
|
|
32
|
+
let hiddenFolderCount = 0;
|
|
33
|
+
for (const folder of folders) {
|
|
34
|
+
if (!isExposed(folder.id))
|
|
35
|
+
hiddenFolderCount++;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
// A note in no folder is in nothing the user has opened, so it stays
|
|
39
|
+
// hidden. That is consistent with off-by-default, and the tools say so
|
|
40
|
+
// rather than letting it look like the note is gone.
|
|
41
|
+
allows: (folderId) => (folderId === null ? false : isExposed(folderId)),
|
|
42
|
+
hiddenFolderCount,
|
|
43
|
+
allHidden: folders.length > 0 && hiddenFolderCount === folders.length,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The sentence appended to any result that was filtered. Never omitted when
|
|
48
|
+
* something was withheld: a quietly shortened list is indistinguishable from
|
|
49
|
+
* an empty vault, and the user would have no reason to suspect a setting.
|
|
50
|
+
*/
|
|
51
|
+
export function filteredNotice(hidden, exposure) {
|
|
52
|
+
const parts = [];
|
|
53
|
+
if (hidden.inClosedFolders > 0) {
|
|
54
|
+
const n = hidden.inClosedFolders;
|
|
55
|
+
const notes = `${n} note${n === 1 ? '' : 's'}`;
|
|
56
|
+
parts.push(exposure.allHidden
|
|
57
|
+
? `${notes} hidden: no folder is exposed to MCP yet. Open a folder in the ` +
|
|
58
|
+
'Virlow app, edit it, and turn on "Expose to MCP" to let AI tools read it.'
|
|
59
|
+
: `${notes} hidden in folders that are not exposed to MCP. Turn on ` +
|
|
60
|
+
'"Expose to MCP" in a folder\'s settings in the Virlow app to include it.');
|
|
61
|
+
}
|
|
62
|
+
// Worth saying separately: the remedy is different. There is no switch on a
|
|
63
|
+
// note that lives in no folder, so "turn on the folder" is useless advice —
|
|
64
|
+
// it has to be filed into an exposed folder first.
|
|
65
|
+
if (hidden.unfiled > 0) {
|
|
66
|
+
const n = hidden.unfiled;
|
|
67
|
+
parts.push(`${n} note${n === 1 ? '' : 's'} hidden because ${n === 1 ? 'it is' : 'they are'} ` +
|
|
68
|
+
'not in any folder, and exposure is granted per folder. Move ' +
|
|
69
|
+
`${n === 1 ? 'it' : 'them'} into a folder that is exposed to MCP to include ` +
|
|
70
|
+
`${n === 1 ? 'it' : 'them'}.`);
|
|
71
|
+
}
|
|
72
|
+
return parts.length === 0 ? '' : `\n\n(${parts.join(' ')})`;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=exposure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exposure.js","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { ApiError, VirlowApi } from './api';
|
|
2
|
+
export type { ApiFolder, ApiNote, AuthTokens, LoginResult, NoteWrite } from './api';
|
|
3
|
+
export { LockedError, Vault } from './vault';
|
|
4
|
+
export type { UnlockedSession } from './vault';
|
|
5
|
+
export { NotesService, SEARCH_SCAN_CAP } from './notes';
|
|
6
|
+
export { MemoryStore, SYNC_PAGE_LIMIT } from './memories';
|
|
7
|
+
export type { CachedMemory, SimilarMemory, SyncResult } from './memories';
|
|
8
|
+
export { mergeMeta, parseMemoryNote, serializeMemoryBody, toCodeEnvelope, } from './memory-note';
|
|
9
|
+
export type { MemoryMeta, ParsedMemoryNote } from './memory-note';
|
|
10
|
+
export { MEMORIES_ROOT_NAME, ensureNamespace, resolveMemoryTree, } from './memory-tree';
|
|
11
|
+
export type { MemoryTree } from './memory-tree';
|
|
12
|
+
export { computeExposure, filteredNotice } from './exposure';
|
|
13
|
+
export type { Exposure } from './exposure';
|
|
14
|
+
export { EmbeddingCache } from './embedding-cache';
|
|
15
|
+
export { MEMORIES_DISABLED_MESSAGE, memoriesAllowed } from './memories-enabled';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EACL,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ApiError, VirlowApi } from './api';
|
|
2
|
+
export { LockedError, Vault } from './vault';
|
|
3
|
+
export { NotesService, SEARCH_SCAN_CAP } from './notes';
|
|
4
|
+
export { MemoryStore, SYNC_PAGE_LIMIT } from './memories';
|
|
5
|
+
export { mergeMeta, parseMemoryNote, serializeMemoryBody, toCodeEnvelope, } from './memory-note';
|
|
6
|
+
export { MEMORIES_ROOT_NAME, ensureNamespace, resolveMemoryTree, } from './memory-tree';
|
|
7
|
+
export { computeExposure, filteredNotice } from './exposure';
|
|
8
|
+
export { EmbeddingCache } from './embedding-cache';
|
|
9
|
+
export { MEMORIES_DISABLED_MESSAGE, memoriesAllowed } from './memories-enabled';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,EACL,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { VirlowApi } from './api';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the user has switched AI memories on, read fresh from the account.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately not cached for the session. Turning the switch *on* being slow
|
|
6
|
+
* is an inconvenience; turning it *off* being slow is a privacy control that
|
|
7
|
+
* does not work. A user who switches memories off in the app expects the next
|
|
8
|
+
* tool call to respect it, not the next time they happen to restart their
|
|
9
|
+
* editor — and they would have no way to discover the difference.
|
|
10
|
+
*
|
|
11
|
+
* The cost is one GET alongside operations that already list folders, page
|
|
12
|
+
* through notes, decrypt, and embed.
|
|
13
|
+
*/
|
|
14
|
+
export declare function memoriesAllowed(api: VirlowApi): Promise<boolean>;
|
|
15
|
+
export declare const MEMORIES_DISABLED_MESSAGE: string;
|
|
16
|
+
//# sourceMappingURL=memories-enabled.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memories-enabled.d.ts","sourceRoot":"","sources":["../src/memories-enabled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAYtE;AAED,eAAO,MAAM,yBAAyB,QAEqB,CAAC"}
|