@batalabs/virlow-mcp-core 3.116.0 → 3.117.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/README.md +4 -0
- package/dist/api.d.ts +93 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +23 -1
- package/dist/api.js.map +1 -1
- package/dist/attachments.d.ts +20 -2
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +136 -21
- package/dist/attachments.js.map +1 -1
- package/dist/cjs/api.d.ts +93 -1
- package/dist/cjs/api.js +23 -1
- package/dist/cjs/attachments.d.ts +20 -2
- package/dist/cjs/attachments.js +134 -19
- package/dist/cjs/exposure.d.ts +35 -1
- package/dist/cjs/exposure.js +59 -2
- package/dist/cjs/index.d.ts +4 -1
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/keyring.d.ts +155 -0
- package/dist/cjs/keyring.js +319 -0
- package/dist/cjs/memories.d.ts +2 -2
- package/dist/cjs/notes.d.ts +53 -1
- package/dist/cjs/notes.js +304 -58
- package/dist/cjs/signer-file.d.ts +21 -0
- package/dist/cjs/signer-file.js +117 -0
- package/dist/exposure.d.ts +35 -1
- package/dist/exposure.d.ts.map +1 -1
- package/dist/exposure.js +56 -2
- package/dist/exposure.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/keyring.d.ts +156 -0
- package/dist/keyring.d.ts.map +1 -0
- package/dist/keyring.js +308 -0
- package/dist/keyring.js.map +1 -0
- package/dist/memories.d.ts +2 -2
- package/dist/notes.d.ts +53 -1
- package/dist/notes.d.ts.map +1 -1
- package/dist/notes.js +306 -60
- package/dist/notes.js.map +1 -1
- package/dist/signer-file.d.ts +22 -0
- package/dist/signer-file.d.ts.map +1 -0
- package/dist/signer-file.js +112 -0
- package/dist/signer-file.js.map +1 -0
- package/dist/tools.d.ts +10 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +6 -2
- package/dist/tools.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fileSignerMemory = fileSignerMemory;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const keyring_js_1 = require("./keyring.js");
|
|
11
|
+
const FILE_VERSION = 1;
|
|
12
|
+
/** How long to wait for another process holding the pin file's lock. */
|
|
13
|
+
const LOCK_WAIT_MS = 2_000;
|
|
14
|
+
/** A lock older than this was left by a process that died holding it. */
|
|
15
|
+
const LOCK_STALE_MS = 10_000;
|
|
16
|
+
function readSigners(file) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
|
|
19
|
+
if (parsed.version === FILE_VERSION && parsed.signers && typeof parsed.signers === 'object') {
|
|
20
|
+
return Object.fromEntries(Object.entries(parsed.signers).filter((entry) => typeof entry[1] === 'string'));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// First run, or a file this version cannot read: nothing remembered yet.
|
|
25
|
+
}
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
function sleepSync(ms) {
|
|
29
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
30
|
+
}
|
|
31
|
+
/** Runs `fn` holding an exclusive lock file next to `file`, or throws if it cannot get one. */
|
|
32
|
+
function withLock(file, fn) {
|
|
33
|
+
const lock = `${file}.lock`;
|
|
34
|
+
const deadline = Date.now() + LOCK_WAIT_MS;
|
|
35
|
+
for (;;) {
|
|
36
|
+
try {
|
|
37
|
+
(0, node_fs_1.closeSync)((0, node_fs_1.openSync)(lock, 'wx', 0o600));
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err.code !== 'EEXIST')
|
|
42
|
+
throw err;
|
|
43
|
+
try {
|
|
44
|
+
if (Date.now() - (0, node_fs_1.statSync)(lock).mtimeMs > LOCK_STALE_MS)
|
|
45
|
+
(0, node_fs_1.unlinkSync)(lock);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Gone already: try again.
|
|
49
|
+
}
|
|
50
|
+
if (Date.now() > deadline)
|
|
51
|
+
throw new Error('The signing-key pin file is locked by another process.');
|
|
52
|
+
sleepSync(20);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return fn();
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
try {
|
|
60
|
+
(0, node_fs_1.unlinkSync)(lock);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Already removed as stale by another process.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Remembered signing keys in a small JSON file per account, for the local
|
|
69
|
+
* CLI: the connector's counterpart of the web app's `virlow.knownSigners`
|
|
70
|
+
* localStorage entry. The keys are public, so the file is not encrypted; what
|
|
71
|
+
* matters is that it outlives the process, so a granter's key that changes
|
|
72
|
+
* between runs is refused rather than trusted afresh. The file name is a hash
|
|
73
|
+
* of the account id, not the id itself.
|
|
74
|
+
*
|
|
75
|
+
* `get` reads the file each time, so a pin another process (a second CLI)
|
|
76
|
+
* wrote is seen. `remember` is a compare-and-set under a lock file: it reads
|
|
77
|
+
* the file again inside the lock and throws KeyChangedError if a different
|
|
78
|
+
* key is already pinned, never overwriting it.
|
|
79
|
+
*
|
|
80
|
+
* A missing or unreadable file starts empty (first use), and a failed write
|
|
81
|
+
* keeps the key in memory for this run, as the web does in private mode.
|
|
82
|
+
*/
|
|
83
|
+
function fileSignerMemory(opts) {
|
|
84
|
+
const name = (0, node_crypto_1.createHash)('sha256').update(opts.userId).digest('hex').slice(0, 32);
|
|
85
|
+
const file = node_path_1.default.join(opts.dir, `known-signers-${name}.json`);
|
|
86
|
+
/** Pins this process made but could not write; checked like the file's. */
|
|
87
|
+
const unsaved = new Map();
|
|
88
|
+
const current = (id) => readSigners(file)[id] ?? unsaved.get(id) ?? null;
|
|
89
|
+
return {
|
|
90
|
+
get: current,
|
|
91
|
+
remember: (id, sign) => {
|
|
92
|
+
const check = (known) => {
|
|
93
|
+
if (known !== null && known !== sign)
|
|
94
|
+
throw new keyring_js_1.KeyChangedError(id);
|
|
95
|
+
};
|
|
96
|
+
check(unsaved.get(id) ?? null);
|
|
97
|
+
try {
|
|
98
|
+
(0, node_fs_1.mkdirSync)(opts.dir, { recursive: true, mode: 0o700 });
|
|
99
|
+
withLock(file, () => {
|
|
100
|
+
const signers = readSigners(file);
|
|
101
|
+
check(signers[id] ?? null);
|
|
102
|
+
if (signers[id] === sign)
|
|
103
|
+
return;
|
|
104
|
+
const tmp = `${file}.${(0, node_crypto_1.randomUUID)()}.tmp`;
|
|
105
|
+
(0, node_fs_1.writeFileSync)(tmp, JSON.stringify({ version: FILE_VERSION, signers: { ...signers, [id]: sign } }), { mode: 0o600 });
|
|
106
|
+
(0, node_fs_1.renameSync)(tmp, file);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
if (err instanceof keyring_js_1.KeyChangedError)
|
|
111
|
+
throw err;
|
|
112
|
+
// Remembered for this run only.
|
|
113
|
+
unsaved.set(id, sign);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
package/dist/exposure.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import type { ApiFolder } from './api.js';
|
|
1
|
+
import type { ApiFolder, ApiNote } from './api.js';
|
|
2
|
+
/**
|
|
3
|
+
* A note in a shared workspace is encrypted under that workspace's key at the
|
|
4
|
+
* note's version, not the account key. The keyring (keyring.ts) opens that key
|
|
5
|
+
* from this account's grant; when it cannot (no grant yet, a grant that fails
|
|
6
|
+
* its checks, a granter whose keys changed) the note is skipped in every scan
|
|
7
|
+
* and refused by id, never opened or written with the account key.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isSharedNote(row: Pick<ApiNote, 'keyVersion'>): boolean;
|
|
10
|
+
/** The reason is one of a fixed set (keyring.ts, describeKeyFailure), never
|
|
11
|
+
* the cause's own message, which can be server text or decrypted bytes. */
|
|
12
|
+
export declare function sharedNoteError(id: string, cause?: unknown): Error;
|
|
2
13
|
/**
|
|
3
14
|
* Which folders the user has actually opened to AI tools.
|
|
4
15
|
*
|
|
@@ -28,7 +39,29 @@ export interface ExposureOptions {
|
|
|
28
39
|
* tools, so requiring the user to opt it in would be asking them to
|
|
29
40
|
* authorise a folder they never made for a purpose it already has. */
|
|
30
41
|
memoriesRootId?: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* The unlocked account. When given, a folder that belongs to someone else's
|
|
44
|
+
* shared workspace (its row is not this account's, or its workspace's owner
|
|
45
|
+
* is someone else) is judged by `mcpEnabledForMember` alone: the row's own
|
|
46
|
+
* `mcpEnabled` is one value every editor of the workspace can set, so it is
|
|
47
|
+
* not this member's consent. See `isForeignFolder`.
|
|
48
|
+
*/
|
|
49
|
+
viewerId?: string;
|
|
50
|
+
/** Workspace id to its owner, for the folders that carry a workspaceId.
|
|
51
|
+
* A workspace missing here counts as someone else's. */
|
|
52
|
+
workspaceOwners?: ReadonlyMap<string, string>;
|
|
31
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Whether a folder is in a shared workspace this account does not own.
|
|
56
|
+
*
|
|
57
|
+
* Such a folder is closed to this account's AI tools unless the server reports
|
|
58
|
+
* it open for this member (`mcpEnabledForMember`). The hosted connector's
|
|
59
|
+
* folder list carries that per-member value. The local CLI has no per-member
|
|
60
|
+
* switch yet, so there a workspace shared with this account stays closed to
|
|
61
|
+
* AI tools; the workspaces this account owns follow their own switch as
|
|
62
|
+
* before.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isForeignFolder(folder: Pick<ApiFolder, 'userId' | 'workspaceId'>, viewerId: string, workspaceOwners?: ReadonlyMap<string, string>): boolean;
|
|
32
65
|
export declare function computeExposure(folders: ReadonlyArray<ApiFolder>, opts?: ExposureOptions): Exposure;
|
|
33
66
|
/**
|
|
34
67
|
* The sentence appended to any result that was filtered. Never omitted when
|
|
@@ -38,5 +71,6 @@ export declare function computeExposure(folders: ReadonlyArray<ApiFolder>, opts?
|
|
|
38
71
|
export declare function filteredNotice(hidden: {
|
|
39
72
|
inClosedFolders: number;
|
|
40
73
|
unfiled: number;
|
|
74
|
+
shared?: number;
|
|
41
75
|
}, exposure: Exposure): string;
|
|
42
76
|
//# sourceMappingURL=exposure.d.ts.map
|
package/dist/exposure.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exposure.d.ts","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"exposure.d.ts","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGnD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,GAAG,OAAO,CAEtE;AAED;2EAC2E;AAC3E,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAKlE;AAED;;;;;;;;;;;;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;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;4DACwD;IACxD,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,GAAG,aAAa,CAAC,EACjD,QAAQ,EAAE,MAAM,EAChB,eAAe,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GACvD,OAAO,CAIT;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,EACjC,IAAI,GAAE,eAAoB,GACzB,QAAQ,CA+DV;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EACrE,QAAQ,EAAE,QAAQ,GACjB,MAAM,CAqCR"}
|
package/dist/exposure.js
CHANGED
|
@@ -1,6 +1,46 @@
|
|
|
1
|
+
import { describeKeyFailure } from './keyring.js';
|
|
2
|
+
/**
|
|
3
|
+
* A note in a shared workspace is encrypted under that workspace's key at the
|
|
4
|
+
* note's version, not the account key. The keyring (keyring.ts) opens that key
|
|
5
|
+
* from this account's grant; when it cannot (no grant yet, a grant that fails
|
|
6
|
+
* its checks, a granter whose keys changed) the note is skipped in every scan
|
|
7
|
+
* and refused by id, never opened or written with the account key.
|
|
8
|
+
*/
|
|
9
|
+
export function isSharedNote(row) {
|
|
10
|
+
return row.keyVersion !== undefined && row.keyVersion !== null;
|
|
11
|
+
}
|
|
12
|
+
/** The reason is one of a fixed set (keyring.ts, describeKeyFailure), never
|
|
13
|
+
* the cause's own message, which can be server text or decrypted bytes. */
|
|
14
|
+
export function sharedNoteError(id, cause) {
|
|
15
|
+
return new Error(`Note ${id} is in a shared workspace whose key could not be opened here (${describeKeyFailure(cause)}). ` +
|
|
16
|
+
'Open it in the Virlow app instead.');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Whether a folder is in a shared workspace this account does not own.
|
|
20
|
+
*
|
|
21
|
+
* Such a folder is closed to this account's AI tools unless the server reports
|
|
22
|
+
* it open for this member (`mcpEnabledForMember`). The hosted connector's
|
|
23
|
+
* folder list carries that per-member value. The local CLI has no per-member
|
|
24
|
+
* switch yet, so there a workspace shared with this account stays closed to
|
|
25
|
+
* AI tools; the workspaces this account owns follow their own switch as
|
|
26
|
+
* before.
|
|
27
|
+
*/
|
|
28
|
+
export function isForeignFolder(folder, viewerId, workspaceOwners = new Map()) {
|
|
29
|
+
if (folder.userId !== undefined && folder.userId !== null && folder.userId !== viewerId)
|
|
30
|
+
return true;
|
|
31
|
+
if (folder.workspaceId)
|
|
32
|
+
return workspaceOwners.get(folder.workspaceId) !== viewerId;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
1
35
|
export function computeExposure(folders, opts = {}) {
|
|
2
36
|
const byId = new Map(folders.map((f) => [f.id, f]));
|
|
3
|
-
const
|
|
37
|
+
const viewerId = opts.viewerId;
|
|
38
|
+
const foreign = (folder) => viewerId !== undefined && isForeignFolder(folder, viewerId, opts.workspaceOwners);
|
|
39
|
+
// Memories are never shared: a "memories" folder in someone else's
|
|
40
|
+
// workspace is not this account's memories root, whatever its kind says.
|
|
41
|
+
const candidateRootId = opts.memoriesRootId ?? folders.find((f) => f.kind === 'memories' && !foreign(f))?.id ?? null;
|
|
42
|
+
const candidateRoot = candidateRootId === null ? undefined : byId.get(candidateRootId);
|
|
43
|
+
const memoriesRootId = candidateRoot !== undefined && foreign(candidateRoot) ? null : candidateRootId;
|
|
4
44
|
const resolved = new Map();
|
|
5
45
|
const isExposed = (folderId) => {
|
|
6
46
|
const cached = resolved.get(folderId);
|
|
@@ -13,13 +53,22 @@ export function computeExposure(folders, opts = {}) {
|
|
|
13
53
|
const seen = new Set();
|
|
14
54
|
let current = folderId;
|
|
15
55
|
let exposed = false;
|
|
56
|
+
const start = byId.get(folderId);
|
|
57
|
+
const startForeign = start !== undefined && foreign(start);
|
|
16
58
|
while (current !== null && !seen.has(current)) {
|
|
17
59
|
seen.add(current);
|
|
18
60
|
const folder = byId.get(current);
|
|
19
61
|
if (folder === undefined)
|
|
20
62
|
break;
|
|
63
|
+
// A shared workspace is a top-level tree, so a walk never legitimately
|
|
64
|
+
// crosses between this account's folders and someone else's. One that
|
|
65
|
+
// would (a parent the server put there) stops, closed, so neither side's
|
|
66
|
+
// switch can open the other.
|
|
67
|
+
if (foreign(folder) !== startForeign)
|
|
68
|
+
break;
|
|
21
69
|
chain.push(folder.id);
|
|
22
|
-
|
|
70
|
+
const switchedOn = startForeign ? folder.mcpEnabledForMember === true : folder.mcpEnabled === true;
|
|
71
|
+
if (switchedOn || folder.id === memoriesRootId) {
|
|
23
72
|
exposed = true;
|
|
24
73
|
break;
|
|
25
74
|
}
|
|
@@ -69,6 +118,11 @@ export function filteredNotice(hidden, exposure) {
|
|
|
69
118
|
`${n === 1 ? 'it' : 'them'} into a folder that is exposed to MCP to include ` +
|
|
70
119
|
`${n === 1 ? 'it' : 'them'}.`);
|
|
71
120
|
}
|
|
121
|
+
if (hidden.shared && hidden.shared > 0) {
|
|
122
|
+
const n = hidden.shared;
|
|
123
|
+
parts.push(`${n} note${n === 1 ? '' : 's'} in shared workspaces left out: the workspace key did not ` +
|
|
124
|
+
'open or did not check out here. Open the workspace in the Virlow app to see them.');
|
|
125
|
+
}
|
|
72
126
|
return parts.length === 0 ? '' : `\n\n(${parts.join(' ')})`;
|
|
73
127
|
}
|
|
74
128
|
//# sourceMappingURL=exposure.js.map
|
package/dist/exposure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exposure.js","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"exposure.js","sourceRoot":"","sources":["../src/exposure.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export { ApiError, VirlowApi } from './api.js';
|
|
2
|
-
export type { ApiFile, ApiFileWithData, ApiFolder, ApiNote, FileUpload, AuthTokens, LoginResult, NoteWrite, RefreshBody } from './api.js';
|
|
2
|
+
export type { ApiFile, ApiFileWithData, ApiFolder, ApiIdentity, ApiNote, ApiWorkspaceDetail, ApiWorkspaceGrant, ApiWorkspaceSummary, FileUpload, AuthTokens, LoginResult, NoteWrite, RefreshBody, } from './api.js';
|
|
3
3
|
export { LockedError, Vault } from './vault.js';
|
|
4
4
|
export type { UnlockedSession } from './vault.js';
|
|
5
5
|
export { NotesService, SEARCH_SCAN_CAP } from './notes.js';
|
|
6
6
|
export { AttachmentsService } from './attachments.js';
|
|
7
|
+
export { KeyChangedError, NoWorkspaceKeyError, WorkspaceKeyring, memorySignerMemory } from './keyring.js';
|
|
8
|
+
export type { KeyChoice, SignerMemory } from './keyring.js';
|
|
9
|
+
export { fileSignerMemory } from './signer-file.js';
|
|
7
10
|
export type { AttachmentInfo } from './attachments.js';
|
|
8
11
|
export { MemoryStore, SYNC_PAGE_LIMIT } from './memories.js';
|
|
9
12
|
export type { CachedMemory, SimilarMemory, SyncResult } from './memories.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EACV,OAAO,EACP,eAAe,EACf,SAAS,EACT,WAAW,EACX,OAAO,EACP,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC1G,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChE,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC1H,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACnF,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjG;;;;;;;;;;GAUG"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ export { ApiError, VirlowApi } from './api.js';
|
|
|
2
2
|
export { LockedError, Vault } from './vault.js';
|
|
3
3
|
export { NotesService, SEARCH_SCAN_CAP } from './notes.js';
|
|
4
4
|
export { AttachmentsService } from './attachments.js';
|
|
5
|
+
export { KeyChangedError, NoWorkspaceKeyError, WorkspaceKeyring, memorySignerMemory } from './keyring.js';
|
|
6
|
+
export { fileSignerMemory } from './signer-file.js';
|
|
5
7
|
export { MemoryStore, SYNC_PAGE_LIMIT } from './memories.js';
|
|
6
8
|
export { MEMORY_NOTE_TYPE, mergeMeta, parseMemoryNote, serializeMemoryBody, toCodeEnvelope, } from './memory-note.js';
|
|
7
9
|
export { ENTITY_REGISTRY_NOTE_TYPE, ENTITY_REGISTRY_TITLE, ENTITY_REGISTRY_VERSION, canWriteRegistry, describeInRegistry, describedEntities, emptyRegistry, mergeRegistries, parseRegistry, registryKeyFor, serializeRegistry, } from './entity-registry.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAgB/C,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE1G,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACL,eAAe,EACf,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { ApiFolder, ApiWorkspaceSummary, VirlowApi } from './api.js';
|
|
2
|
+
import { type Vault } from './vault.js';
|
|
3
|
+
/**
|
|
4
|
+
* Which key a row in a shared workspace is encrypted under, and the key
|
|
5
|
+
* itself: the connector's side of the web app's keyring
|
|
6
|
+
* (src/lib/sharing/keyring.ts in the app, KeyBook.kt on Android;
|
|
7
|
+
* docs/superpowers/specs/2026-10-14-shared-workspaces-design.md).
|
|
8
|
+
*
|
|
9
|
+
* A personal row (no key version) uses the account key. A row in a shared
|
|
10
|
+
* workspace uses that workspace's key at the row's version, opened from this
|
|
11
|
+
* account's grant. Before a key is used:
|
|
12
|
+
*
|
|
13
|
+
* - the grant must open with this account's identity (whose private halves
|
|
14
|
+
* are sealed under the account key), bound to this workspace, version and
|
|
15
|
+
* person;
|
|
16
|
+
* - the granter must be allowed to grant: the workspace's owner, or an owner
|
|
17
|
+
* or editor in its member list; a grant this account made itself must carry
|
|
18
|
+
* this account's own signing key;
|
|
19
|
+
* - the signature must check out against the signing key this connector
|
|
20
|
+
* first saw for the granter. A key that changed since is refused, because
|
|
21
|
+
* that is what a server swapping keys would look like.
|
|
22
|
+
*
|
|
23
|
+
* Writes never go under a key version older than one this account has
|
|
24
|
+
* already seen for the workspace, so a server cannot roll a workspace back to
|
|
25
|
+
* a key a removed member still holds.
|
|
26
|
+
*
|
|
27
|
+
* Everything opened is held for one unlock and dropped when the vault locks,
|
|
28
|
+
* with the same epoch guard NotesService uses, so a fetch still in flight at
|
|
29
|
+
* the lock never seeds the next session. The remembered signing keys are
|
|
30
|
+
* public and are kept across locks (they are what makes trust-on-first-use
|
|
31
|
+
* mean anything); a host can persist them with `signers`.
|
|
32
|
+
*
|
|
33
|
+
* Where the pins live: the local CLI keeps them in a file (signer-file.ts),
|
|
34
|
+
* so they survive restarts. The hosted connector keeps them in memory, per
|
|
35
|
+
* process: a restart or another machine starts trusting afresh. That host
|
|
36
|
+
* already holds the account key in memory, so its pins defend against
|
|
37
|
+
* nothing it could not do anyway; they are kept for parity, not as a
|
|
38
|
+
* boundary.
|
|
39
|
+
*/
|
|
40
|
+
/** Signing keys seen for each person, first one wins: the web's `virlow.knownSigners`. */
|
|
41
|
+
export interface SignerMemory {
|
|
42
|
+
get(userId: string): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Remember `sign` for `userId` unless a key is already remembered. Must be
|
|
45
|
+
* a compare-and-set: throws KeyChangedError when a different key got there
|
|
46
|
+
* first, never overwrites it.
|
|
47
|
+
*/
|
|
48
|
+
remember(userId: string, sign: string): void;
|
|
49
|
+
}
|
|
50
|
+
/** Remembered for as long as the process lives. The default, and what the
|
|
51
|
+
* hosted connector uses: pins there are per process (see above). */
|
|
52
|
+
export declare function memorySignerMemory(): SignerMemory;
|
|
53
|
+
/**
|
|
54
|
+
* Why a workspace key did not open, as one of a fixed set. The text an AI
|
|
55
|
+
* tool sees comes only from here, never from an error's own message: those
|
|
56
|
+
* can carry server-chosen text, or (a JSON parse error) decrypted bytes.
|
|
57
|
+
*/
|
|
58
|
+
export type KeyFailureReason = 'no-grant' | 'key-changed' | 'signature-rejected' | 'not-authorized' | 'locked';
|
|
59
|
+
export declare class SharedKeyError extends Error {
|
|
60
|
+
readonly reason: KeyFailureReason;
|
|
61
|
+
constructor(reason: KeyFailureReason);
|
|
62
|
+
}
|
|
63
|
+
/** Someone's signing key is not the one first seen for them. */
|
|
64
|
+
export declare class KeyChangedError extends SharedKeyError {
|
|
65
|
+
readonly userId: string;
|
|
66
|
+
constructor(userId: string);
|
|
67
|
+
}
|
|
68
|
+
/** No key for this workspace and version opens here. */
|
|
69
|
+
export declare class NoWorkspaceKeyError extends SharedKeyError {
|
|
70
|
+
constructor();
|
|
71
|
+
}
|
|
72
|
+
/** The grant came from someone the workspace does not let grant. */
|
|
73
|
+
export declare class GranterNotAuthorizedError extends SharedKeyError {
|
|
74
|
+
constructor();
|
|
75
|
+
}
|
|
76
|
+
/** A write refused for a reason this module states itself (a viewer, a
|
|
77
|
+
* rollback). Its message is fixed text, safe to show as it is. */
|
|
78
|
+
export declare class WriteRefusedError extends Error {
|
|
79
|
+
constructor(message: string);
|
|
80
|
+
}
|
|
81
|
+
export declare function keyFailureReason(err: unknown): KeyFailureReason;
|
|
82
|
+
/** The fixed sentence for a failure; see KeyFailureReason. */
|
|
83
|
+
export declare function describeKeyFailure(err: unknown): string;
|
|
84
|
+
export interface KeyChoice {
|
|
85
|
+
key: CryptoKey;
|
|
86
|
+
/** The version sent with the ciphertext; null = the account key. */
|
|
87
|
+
keyVersion: number | null;
|
|
88
|
+
/** The workspace's owner; null for the account key. */
|
|
89
|
+
ownerId: string | null;
|
|
90
|
+
}
|
|
91
|
+
export declare class WorkspaceKeyring {
|
|
92
|
+
private readonly api;
|
|
93
|
+
private readonly vault;
|
|
94
|
+
/** Where the remembered signing keys live, per account. In memory by default. */
|
|
95
|
+
private readonly signers;
|
|
96
|
+
private epoch;
|
|
97
|
+
private identityPending;
|
|
98
|
+
private readonly details;
|
|
99
|
+
private readonly keys;
|
|
100
|
+
private readonly signerMemories;
|
|
101
|
+
/** `userId:workspaceId` to the highest key version this account has seen
|
|
102
|
+
* granted there. Kept across locks: versions only ever go up. */
|
|
103
|
+
private readonly highestSeen;
|
|
104
|
+
constructor(api: VirlowApi, vault: Vault,
|
|
105
|
+
/** Where the remembered signing keys live, per account. In memory by default. */
|
|
106
|
+
signers?: (userId: string) => SignerMemory);
|
|
107
|
+
private handleLock;
|
|
108
|
+
/** The key to read a row with, from the version it was written under. */
|
|
109
|
+
forRead(accountKey: CryptoKey, keyVersion: number | null | undefined, workspaceId: string | null | undefined): Promise<CryptoKey>;
|
|
110
|
+
/**
|
|
111
|
+
* The key to write into a workspace with: its current version, as the API
|
|
112
|
+
* requires of every new ciphertext there. Never the account key: a null
|
|
113
|
+
* workspace is the only way to get that, and it is the caller's to ask.
|
|
114
|
+
* Asks the server for the current version every time, since a removal
|
|
115
|
+
* rotates the key and the connector hears no change signal.
|
|
116
|
+
*
|
|
117
|
+
* Refuses a version older than `atLeast` (the version the row being
|
|
118
|
+
* rewritten is already under) or than any this account has been granted
|
|
119
|
+
* for the workspace: writing under an older key would hand the new text to
|
|
120
|
+
* whoever a rotation meant to shut out.
|
|
121
|
+
*/
|
|
122
|
+
forWrite(accountKey: CryptoKey, workspaceId: string | null | undefined, opts?: {
|
|
123
|
+
atLeast?: number | null;
|
|
124
|
+
}): Promise<KeyChoice>;
|
|
125
|
+
/** This account's role and the current key version of one workspace. Not cached. */
|
|
126
|
+
summary(workspaceId: string): Promise<ApiWorkspaceSummary>;
|
|
127
|
+
/**
|
|
128
|
+
* Who owns each workspace these folders belong to, for the exposure rule
|
|
129
|
+
* (exposure.ts, isForeignFolder). No request when none of them is shared.
|
|
130
|
+
*/
|
|
131
|
+
workspaceOwners(folders: ReadonlyArray<ApiFolder>): Promise<Map<string, string>>;
|
|
132
|
+
/** A workspace key version, opened and checked. Kept until the vault locks. */
|
|
133
|
+
workspaceKey(workspaceId: string, keyVersion: number): Promise<CryptoKey>;
|
|
134
|
+
private versionKey;
|
|
135
|
+
private highestVersion;
|
|
136
|
+
private noteVersion;
|
|
137
|
+
private openKey;
|
|
138
|
+
/**
|
|
139
|
+
* The granter must be allowed to grant (the owner, or an owner or editor in
|
|
140
|
+
* the member list), and a grant this account made must carry its own
|
|
141
|
+
* signing key: one the server substituted would otherwise be pinned for
|
|
142
|
+
* this account itself on first use.
|
|
143
|
+
*/
|
|
144
|
+
private requireAuthorizedGranter;
|
|
145
|
+
private detail;
|
|
146
|
+
/**
|
|
147
|
+
* This account's identity, opened with the unlocked account key. Never made
|
|
148
|
+
* here: an account without one has been sealed no workspace keys, and the
|
|
149
|
+
* app makes it on the next unlock there. Any failure to open it is reported
|
|
150
|
+
* as "no grant", without its cause: a parse error would quote the bytes it
|
|
151
|
+
* choked on, and those are decrypted.
|
|
152
|
+
*/
|
|
153
|
+
private identity;
|
|
154
|
+
private signerMemory;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=keyring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyring.d.ts","sourceRoot":"","sources":["../src/keyring.ts"],"names":[],"mappings":"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"}
|