recall-mcp-v3 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +65 -33
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +187 -14
- package/dist/api.js.map +1 -1
- package/dist/crypto.d.ts +36 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +104 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.js +0 -0
- package/package.json +1 -1
package/dist/api.d.ts
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recall API Client
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Reads token from RECALL_API_TOKEN environment variable
|
|
4
|
+
* HTTP client for the Recall v3 API with encryption support.
|
|
5
|
+
* - Reads token from RECALL_API_TOKEN environment variable
|
|
6
|
+
* - Fetches team encryption key from API
|
|
7
|
+
* - Encrypts content before sending, decrypts after receiving
|
|
6
8
|
*/
|
|
7
9
|
declare class RecallApiError extends Error {
|
|
8
10
|
code: string;
|
|
9
11
|
status: number;
|
|
10
12
|
constructor(message: string, code: string, status: number);
|
|
11
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Fetch the team encryption key from API.
|
|
16
|
+
* Caches the key in memory for the duration of the process.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getTeamKey(): Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Get cached team tier (must call getTeamKey first).
|
|
21
|
+
*/
|
|
22
|
+
export declare function getCachedTier(): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Clear cached team key (useful for testing or key rotation).
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearKeyCache(): void;
|
|
12
27
|
interface ResolveRepoResponse {
|
|
13
28
|
repoId: string;
|
|
14
29
|
teamId: string;
|
|
15
30
|
}
|
|
31
|
+
interface SessionUser {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string | null;
|
|
34
|
+
github_username: string | null;
|
|
35
|
+
avatar_url?: string | null;
|
|
36
|
+
}
|
|
16
37
|
interface Session {
|
|
17
38
|
id: string;
|
|
18
39
|
summary: string;
|
|
@@ -25,36 +46,19 @@ interface Session {
|
|
|
25
46
|
files_changed?: string[];
|
|
26
47
|
blockers?: string | null;
|
|
27
48
|
next_steps?: string | null;
|
|
28
|
-
user:
|
|
29
|
-
id: string;
|
|
30
|
-
name: string | null;
|
|
31
|
-
github_username: string | null;
|
|
32
|
-
};
|
|
49
|
+
user: SessionUser;
|
|
33
50
|
}
|
|
34
|
-
interface
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
interface HistoryDecision {
|
|
52
|
+
id: string;
|
|
53
|
+
decision: string;
|
|
54
|
+
reasoning: string;
|
|
55
|
+
created_at: string;
|
|
56
|
+
user: SessionUser;
|
|
39
57
|
}
|
|
40
|
-
interface
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
decision: string;
|
|
45
|
-
reasoning: string;
|
|
46
|
-
created_at: string;
|
|
47
|
-
user: {
|
|
48
|
-
id: string;
|
|
49
|
-
name: string | null;
|
|
50
|
-
github_username: string | null;
|
|
51
|
-
};
|
|
52
|
-
}>;
|
|
53
|
-
mistakes: Array<{
|
|
54
|
-
id: string;
|
|
55
|
-
content: string;
|
|
56
|
-
created_at: string;
|
|
57
|
-
}>;
|
|
58
|
+
interface HistoryMistake {
|
|
59
|
+
id: string;
|
|
60
|
+
content: string;
|
|
61
|
+
created_at: string;
|
|
58
62
|
}
|
|
59
63
|
interface SaveSessionResponse {
|
|
60
64
|
id: string;
|
|
@@ -65,9 +69,34 @@ interface LogDecisionResponse {
|
|
|
65
69
|
created_at: string;
|
|
66
70
|
}
|
|
67
71
|
export declare function resolveRepo(fullName: string, defaultBranch?: string): Promise<ResolveRepoResponse>;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Get context for a repo. Decrypts session content.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getContext(repoId: string): Promise<{
|
|
76
|
+
sessions: Session[];
|
|
77
|
+
cached_context: string;
|
|
78
|
+
session_count: number;
|
|
79
|
+
tier: string;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Get history for a repo. Decrypts session, decision, and mistake content.
|
|
83
|
+
*/
|
|
84
|
+
export declare function getHistory(repoId: string, days?: number): Promise<{
|
|
85
|
+
sessions: Session[];
|
|
86
|
+
decisions: HistoryDecision[];
|
|
87
|
+
mistakes: HistoryMistake[];
|
|
88
|
+
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Get transcripts for a repo. Same as history but for Pro tier with full content.
|
|
91
|
+
*/
|
|
92
|
+
export declare function getTranscripts(repoId: string): Promise<{
|
|
93
|
+
sessions: Session[];
|
|
94
|
+
decisions: HistoryDecision[];
|
|
95
|
+
mistakes: HistoryMistake[];
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Save a session. Encrypts content before sending.
|
|
99
|
+
*/
|
|
71
100
|
export declare function saveSession(repoId: string, data: {
|
|
72
101
|
summary: string;
|
|
73
102
|
status?: 'complete' | 'in-progress' | 'blocked';
|
|
@@ -80,6 +109,9 @@ export declare function saveSession(repoId: string, data: {
|
|
|
80
109
|
next_steps?: string;
|
|
81
110
|
blockers?: string;
|
|
82
111
|
}): Promise<SaveSessionResponse>;
|
|
112
|
+
/**
|
|
113
|
+
* Log a decision. Encrypts content before sending.
|
|
114
|
+
*/
|
|
83
115
|
export declare function logDecision(repoId: string, decision: string, reasoning: string): Promise<LogDecisionResponse>;
|
|
84
116
|
export { RecallApiError };
|
|
85
117
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Recall API Client
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Reads token from RECALL_API_TOKEN environment variable
|
|
4
|
+
* HTTP client for the Recall v3 API with encryption support.
|
|
5
|
+
* - Reads token from RECALL_API_TOKEN environment variable
|
|
6
|
+
* - Fetches team encryption key from API
|
|
7
|
+
* - Encrypts content before sending, decrypts after receiving
|
|
6
8
|
*/
|
|
9
|
+
import { encrypt, decrypt, isEncrypted, safeDecrypt } from './crypto.js';
|
|
7
10
|
const API_URL = process.env.RECALL_API_URL || 'https://api-v3.recall.team';
|
|
11
|
+
// Cache team key in memory (per process)
|
|
12
|
+
let cachedTeamKey = null;
|
|
13
|
+
let cachedTeamId = null;
|
|
14
|
+
let cachedTier = null;
|
|
8
15
|
class RecallApiError extends Error {
|
|
9
16
|
code;
|
|
10
17
|
status;
|
|
@@ -29,46 +36,212 @@ async function request(method, path, body) {
|
|
|
29
36
|
method,
|
|
30
37
|
headers: {
|
|
31
38
|
'Content-Type': 'application/json',
|
|
32
|
-
|
|
39
|
+
Authorization: `Bearer ${token}`,
|
|
33
40
|
},
|
|
34
41
|
body: body ? JSON.stringify(body) : undefined,
|
|
35
42
|
});
|
|
36
43
|
if (!response.ok) {
|
|
37
|
-
const errorBody = await response.json().catch(() => ({}));
|
|
44
|
+
const errorBody = (await response.json().catch(() => ({})));
|
|
38
45
|
throw new RecallApiError(errorBody.error || `HTTP ${response.status}`, errorBody.code || 'API_ERROR', response.status);
|
|
39
46
|
}
|
|
40
47
|
return response.json();
|
|
41
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Fetch the team encryption key from API.
|
|
51
|
+
* Caches the key in memory for the duration of the process.
|
|
52
|
+
*/
|
|
53
|
+
export async function getTeamKey() {
|
|
54
|
+
if (cachedTeamKey) {
|
|
55
|
+
return cachedTeamKey;
|
|
56
|
+
}
|
|
57
|
+
const response = await request('GET', '/v1/keys/team');
|
|
58
|
+
if (!response.hasAccess) {
|
|
59
|
+
throw new RecallApiError('No access to team encryption key', 'NO_ACCESS', 403);
|
|
60
|
+
}
|
|
61
|
+
cachedTeamKey = response.encryptionKey;
|
|
62
|
+
cachedTeamId = response.teamId;
|
|
63
|
+
cachedTier = response.tier;
|
|
64
|
+
return cachedTeamKey;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get cached team tier (must call getTeamKey first).
|
|
68
|
+
*/
|
|
69
|
+
export function getCachedTier() {
|
|
70
|
+
return cachedTier;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Clear cached team key (useful for testing or key rotation).
|
|
74
|
+
*/
|
|
75
|
+
export function clearKeyCache() {
|
|
76
|
+
cachedTeamKey = null;
|
|
77
|
+
cachedTeamId = null;
|
|
78
|
+
cachedTier = null;
|
|
79
|
+
}
|
|
80
|
+
// ============================================================================
|
|
42
81
|
// API Methods
|
|
82
|
+
// ============================================================================
|
|
43
83
|
export async function resolveRepo(fullName, defaultBranch) {
|
|
44
|
-
return request('POST', '/v1/repos/resolve', {
|
|
84
|
+
return request('POST', '/v1/repos/resolve', {
|
|
85
|
+
fullName,
|
|
86
|
+
defaultBranch,
|
|
87
|
+
});
|
|
45
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Get context for a repo. Decrypts session content.
|
|
91
|
+
*/
|
|
46
92
|
export async function getContext(repoId) {
|
|
47
|
-
|
|
93
|
+
// Ensure we have the team key for decryption
|
|
94
|
+
const teamKey = await getTeamKey();
|
|
95
|
+
const response = await request('GET', `/v1/repos/${repoId}/context`);
|
|
96
|
+
// Decrypt session content
|
|
97
|
+
const sessions = response.sessions.map((raw) => {
|
|
98
|
+
// Try to decrypt encrypted_content, use tldr_summary as fallback
|
|
99
|
+
let summary = raw.tldr_summary || '';
|
|
100
|
+
if (raw.encrypted_content && isEncrypted(raw.encrypted_content)) {
|
|
101
|
+
try {
|
|
102
|
+
summary = decrypt(raw.encrypted_content, teamKey);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// If decryption fails, use tldr_summary
|
|
106
|
+
summary = raw.tldr_summary || '';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (raw.encrypted_content) {
|
|
110
|
+
// Not encrypted (legacy or plaintext) - use as-is
|
|
111
|
+
summary = raw.encrypted_content;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
id: raw.id,
|
|
115
|
+
summary,
|
|
116
|
+
status: raw.status,
|
|
117
|
+
started_at: raw.started_at,
|
|
118
|
+
ended_at: raw.ended_at,
|
|
119
|
+
user: raw.user,
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
sessions,
|
|
124
|
+
cached_context: response.cached_context,
|
|
125
|
+
session_count: response.session_count,
|
|
126
|
+
tier: response.tier,
|
|
127
|
+
};
|
|
48
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Get history for a repo. Decrypts session, decision, and mistake content.
|
|
131
|
+
*/
|
|
49
132
|
export async function getHistory(repoId, days = 30) {
|
|
50
|
-
|
|
133
|
+
// Ensure we have the team key for decryption
|
|
134
|
+
const teamKey = await getTeamKey();
|
|
135
|
+
const response = await request('GET', `/v1/repos/${repoId}/history?days=${days}`);
|
|
136
|
+
// Decrypt sessions
|
|
137
|
+
const sessions = response.sessions.map((raw) => {
|
|
138
|
+
let summary = raw.tldr_summary || '';
|
|
139
|
+
if (raw.encrypted_content && isEncrypted(raw.encrypted_content)) {
|
|
140
|
+
summary = safeDecrypt(raw.encrypted_content, teamKey);
|
|
141
|
+
}
|
|
142
|
+
else if (raw.encrypted_content) {
|
|
143
|
+
summary = raw.encrypted_content;
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
id: raw.id,
|
|
147
|
+
summary,
|
|
148
|
+
status: raw.status,
|
|
149
|
+
started_at: raw.started_at,
|
|
150
|
+
ended_at: raw.ended_at,
|
|
151
|
+
user: raw.user,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
// Decrypt decisions
|
|
155
|
+
const decisions = response.decisions.map((raw) => {
|
|
156
|
+
let reasoning = '';
|
|
157
|
+
if (raw.encrypted_content && isEncrypted(raw.encrypted_content)) {
|
|
158
|
+
reasoning = safeDecrypt(raw.encrypted_content, teamKey);
|
|
159
|
+
}
|
|
160
|
+
else if (raw.encrypted_content) {
|
|
161
|
+
reasoning = raw.encrypted_content;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
id: raw.id,
|
|
165
|
+
decision: raw.title,
|
|
166
|
+
reasoning,
|
|
167
|
+
created_at: raw.created_at,
|
|
168
|
+
user: raw.user,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
// Decrypt mistakes
|
|
172
|
+
const mistakes = response.mistakes.map((raw) => {
|
|
173
|
+
let content = raw.title;
|
|
174
|
+
if (raw.encrypted_content && isEncrypted(raw.encrypted_content)) {
|
|
175
|
+
content = safeDecrypt(raw.encrypted_content, teamKey);
|
|
176
|
+
}
|
|
177
|
+
else if (raw.encrypted_content) {
|
|
178
|
+
content = raw.encrypted_content;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
id: raw.id,
|
|
182
|
+
content,
|
|
183
|
+
created_at: raw.created_at,
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
return { sessions, decisions, mistakes };
|
|
51
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Get transcripts for a repo. Same as history but for Pro tier with full content.
|
|
190
|
+
*/
|
|
52
191
|
export async function getTranscripts(repoId) {
|
|
53
|
-
|
|
192
|
+
// Use the history endpoint with longer timeframe for transcripts
|
|
193
|
+
return getHistory(repoId, 90);
|
|
54
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Save a session. Encrypts content before sending.
|
|
197
|
+
*/
|
|
55
198
|
export async function saveSession(repoId, data) {
|
|
56
|
-
|
|
199
|
+
// Ensure we have the team key for encryption
|
|
200
|
+
const teamKey = await getTeamKey();
|
|
201
|
+
// Build the content to encrypt (full session summary)
|
|
202
|
+
const contentToEncrypt = JSON.stringify({
|
|
57
203
|
summary: data.summary,
|
|
58
|
-
status: data.status || 'complete',
|
|
59
204
|
decisions: data.decisions,
|
|
60
205
|
mistakes: data.mistakes,
|
|
61
206
|
files_changed: data.files_changed,
|
|
62
207
|
next_steps: data.next_steps,
|
|
63
208
|
blockers: data.blockers,
|
|
64
|
-
|
|
65
|
-
|
|
209
|
+
});
|
|
210
|
+
// Encrypt the content
|
|
211
|
+
const encryptedContent = encrypt(contentToEncrypt, teamKey);
|
|
212
|
+
// Build TLDR for plaintext metadata
|
|
213
|
+
const tldr = {
|
|
214
|
+
summary: data.summary.substring(0, 1000), // First 1000 chars for search
|
|
215
|
+
status: data.status || 'complete',
|
|
216
|
+
decisions: data.decisions?.map((d) => d.what) || [],
|
|
217
|
+
mistakes: data.mistakes || [],
|
|
218
|
+
tags: [], // Could extract tags from content
|
|
219
|
+
files_changed: data.files_changed || [],
|
|
220
|
+
blockers: data.blockers || null,
|
|
221
|
+
next_steps: data.next_steps || null,
|
|
222
|
+
};
|
|
223
|
+
const now = new Date().toISOString();
|
|
224
|
+
// Send to API with encrypted content + plaintext TLDR
|
|
225
|
+
return request('POST', `/v1/repos/${repoId}/sessions`, {
|
|
226
|
+
encrypted_content: encryptedContent,
|
|
227
|
+
tldr,
|
|
228
|
+
started_at: now,
|
|
229
|
+
ended_at: now,
|
|
230
|
+
tool: 'claude-code',
|
|
66
231
|
});
|
|
67
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Log a decision. Encrypts content before sending.
|
|
235
|
+
*/
|
|
68
236
|
export async function logDecision(repoId, decision, reasoning) {
|
|
237
|
+
// Ensure we have the team key for encryption
|
|
238
|
+
const teamKey = await getTeamKey();
|
|
239
|
+
// Encrypt the reasoning (the full decision context)
|
|
240
|
+
const encryptedContent = encrypt(reasoning, teamKey);
|
|
241
|
+
// API expects: title (plaintext for search) + encrypted_content
|
|
69
242
|
return request('POST', `/v1/repos/${repoId}/decisions`, {
|
|
70
|
-
decision,
|
|
71
|
-
reasoning
|
|
243
|
+
title: decision, // Plaintext title for search/display
|
|
244
|
+
encrypted_content: encryptedContent, // Encrypted reasoning
|
|
72
245
|
});
|
|
73
246
|
}
|
|
74
247
|
export { RecallApiError };
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recall Encryption Module
|
|
3
|
+
*
|
|
4
|
+
* Client-side AES-256-GCM encryption/decryption for zero-knowledge architecture.
|
|
5
|
+
* Team key is fetched from API and used to encrypt content before sending,
|
|
6
|
+
* and decrypt content after receiving.
|
|
7
|
+
*
|
|
8
|
+
* Format: RECALL_ENCRYPTED:v1:[base64_iv]:[base64_ciphertext]:[base64_tag]
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Encrypt content using AES-256-GCM.
|
|
12
|
+
*
|
|
13
|
+
* @param plaintext - The content to encrypt
|
|
14
|
+
* @param teamKeyBase64 - The team encryption key (base64)
|
|
15
|
+
* @returns Encrypted string in format: RECALL_ENCRYPTED:v1:[iv]:[ciphertext]:[tag]
|
|
16
|
+
*/
|
|
17
|
+
export declare function encrypt(plaintext: string, teamKeyBase64: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Decrypt content that was encrypted with AES-256-GCM.
|
|
20
|
+
*
|
|
21
|
+
* @param encryptedString - The encrypted content (RECALL_ENCRYPTED:v1:...)
|
|
22
|
+
* @param teamKeyBase64 - The team encryption key (base64)
|
|
23
|
+
* @returns Decrypted plaintext
|
|
24
|
+
* @throws Error if decryption fails or format is invalid
|
|
25
|
+
*/
|
|
26
|
+
export declare function decrypt(encryptedString: string, teamKeyBase64: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a string is encrypted content.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isEncrypted(content: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Safely decrypt content - returns original if not encrypted or if decryption fails.
|
|
33
|
+
* This allows gradual migration and handling of mixed content.
|
|
34
|
+
*/
|
|
35
|
+
export declare function safeDecrypt(content: string, teamKeyBase64: string): string;
|
|
36
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyBH;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAYxE;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,eAAe,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAwC9E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAW1E"}
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recall Encryption Module
|
|
3
|
+
*
|
|
4
|
+
* Client-side AES-256-GCM encryption/decryption for zero-knowledge architecture.
|
|
5
|
+
* Team key is fetched from API and used to encrypt content before sending,
|
|
6
|
+
* and decrypt content after receiving.
|
|
7
|
+
*
|
|
8
|
+
* Format: RECALL_ENCRYPTED:v1:[base64_iv]:[base64_ciphertext]:[base64_tag]
|
|
9
|
+
*/
|
|
10
|
+
import * as crypto from 'crypto';
|
|
11
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
12
|
+
const IV_LENGTH = 16; // 128 bits
|
|
13
|
+
const TAG_LENGTH = 16; // 128 bits
|
|
14
|
+
const ENCRYPTED_PREFIX = 'RECALL_ENCRYPTED:v1:';
|
|
15
|
+
/**
|
|
16
|
+
* Derive a 256-bit key from the base64 team key.
|
|
17
|
+
* The team key from API is already a raw 256-bit key in base64.
|
|
18
|
+
*/
|
|
19
|
+
function deriveKey(teamKeyBase64) {
|
|
20
|
+
// Decode the base64 key - it's already 256 bits (32 bytes)
|
|
21
|
+
const keyBuffer = Buffer.from(teamKeyBase64, 'base64');
|
|
22
|
+
// If the key is not 32 bytes, hash it to get 32 bytes
|
|
23
|
+
if (keyBuffer.length !== 32) {
|
|
24
|
+
return crypto.createHash('sha256').update(teamKeyBase64).digest();
|
|
25
|
+
}
|
|
26
|
+
return keyBuffer;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Encrypt content using AES-256-GCM.
|
|
30
|
+
*
|
|
31
|
+
* @param plaintext - The content to encrypt
|
|
32
|
+
* @param teamKeyBase64 - The team encryption key (base64)
|
|
33
|
+
* @returns Encrypted string in format: RECALL_ENCRYPTED:v1:[iv]:[ciphertext]:[tag]
|
|
34
|
+
*/
|
|
35
|
+
export function encrypt(plaintext, teamKeyBase64) {
|
|
36
|
+
const key = deriveKey(teamKeyBase64);
|
|
37
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
38
|
+
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
39
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
|
|
40
|
+
encrypted += cipher.final('base64');
|
|
41
|
+
const authTag = cipher.getAuthTag();
|
|
42
|
+
return `${ENCRYPTED_PREFIX}${iv.toString('base64')}:${encrypted}:${authTag.toString('base64')}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Decrypt content that was encrypted with AES-256-GCM.
|
|
46
|
+
*
|
|
47
|
+
* @param encryptedString - The encrypted content (RECALL_ENCRYPTED:v1:...)
|
|
48
|
+
* @param teamKeyBase64 - The team encryption key (base64)
|
|
49
|
+
* @returns Decrypted plaintext
|
|
50
|
+
* @throws Error if decryption fails or format is invalid
|
|
51
|
+
*/
|
|
52
|
+
export function decrypt(encryptedString, teamKeyBase64) {
|
|
53
|
+
// Check format
|
|
54
|
+
if (!encryptedString.startsWith(ENCRYPTED_PREFIX)) {
|
|
55
|
+
throw new Error('Invalid encrypted content format: missing RECALL_ENCRYPTED prefix');
|
|
56
|
+
}
|
|
57
|
+
// Parse components
|
|
58
|
+
const withoutPrefix = encryptedString.substring(ENCRYPTED_PREFIX.length);
|
|
59
|
+
const parts = withoutPrefix.split(':');
|
|
60
|
+
if (parts.length !== 3) {
|
|
61
|
+
throw new Error(`Invalid encrypted content format: expected 3 parts (iv:ciphertext:tag), got ${parts.length}`);
|
|
62
|
+
}
|
|
63
|
+
const [ivBase64, ciphertextBase64, tagBase64] = parts;
|
|
64
|
+
const key = deriveKey(teamKeyBase64);
|
|
65
|
+
const iv = Buffer.from(ivBase64, 'base64');
|
|
66
|
+
const ciphertext = Buffer.from(ciphertextBase64, 'base64');
|
|
67
|
+
const authTag = Buffer.from(tagBase64, 'base64');
|
|
68
|
+
// Validate IV length
|
|
69
|
+
if (iv.length !== IV_LENGTH) {
|
|
70
|
+
throw new Error(`Invalid IV length: expected ${IV_LENGTH}, got ${iv.length}`);
|
|
71
|
+
}
|
|
72
|
+
// Validate auth tag length
|
|
73
|
+
if (authTag.length !== TAG_LENGTH) {
|
|
74
|
+
throw new Error(`Invalid auth tag length: expected ${TAG_LENGTH}, got ${authTag.length}`);
|
|
75
|
+
}
|
|
76
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
77
|
+
decipher.setAuthTag(authTag);
|
|
78
|
+
let decrypted = decipher.update(ciphertext, undefined, 'utf8');
|
|
79
|
+
decrypted += decipher.final('utf8');
|
|
80
|
+
return decrypted;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if a string is encrypted content.
|
|
84
|
+
*/
|
|
85
|
+
export function isEncrypted(content) {
|
|
86
|
+
return content.startsWith(ENCRYPTED_PREFIX);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Safely decrypt content - returns original if not encrypted or if decryption fails.
|
|
90
|
+
* This allows gradual migration and handling of mixed content.
|
|
91
|
+
*/
|
|
92
|
+
export function safeDecrypt(content, teamKeyBase64) {
|
|
93
|
+
if (!isEncrypted(content)) {
|
|
94
|
+
return content;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return decrypt(content, teamKeyBase64);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
console.error('Failed to decrypt content:', error);
|
|
101
|
+
return content; // Return as-is if decryption fails
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
File without changes
|