@frontmcp/plugin-remember 1.9.1 → 1.9.3
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/esm/index.mjs +179 -82
- package/esm/package.json +4 -4
- package/index.js +178 -75
- package/package.json +4 -4
- package/providers/remember-accessor.provider.d.ts.map +1 -1
- package/providers/remember-key-prefix.d.ts +20 -0
- package/providers/remember-key-prefix.d.ts.map +1 -0
- package/providers/remember-redis.provider.d.ts +12 -1
- package/providers/remember-redis.provider.d.ts.map +1 -1
- package/providers/remember-vercel-kv.provider.d.ts +18 -4
- package/providers/remember-vercel-kv.provider.d.ts.map +1 -1
- package/remember.crypto.d.ts +10 -5
- package/remember.crypto.d.ts.map +1 -1
- package/remember.plugin.d.ts.map +1 -1
- package/remember.types.d.ts +4 -1
- package/remember.types.d.ts.map +1 -1
package/esm/index.mjs
CHANGED
|
@@ -4,12 +4,6 @@ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.ur
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
6
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
-
}) : x)(function(x) {
|
|
10
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
-
});
|
|
13
7
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
14
8
|
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
15
9
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
@@ -188,6 +182,9 @@ async function getBaseSecret() {
|
|
|
188
182
|
}
|
|
189
183
|
return getOrCreatePersistedSecret();
|
|
190
184
|
}
|
|
185
|
+
async function secretFor(source) {
|
|
186
|
+
return source.secret ? source.secret : getBaseSecret();
|
|
187
|
+
}
|
|
191
188
|
var textEncoder = new TextEncoder();
|
|
192
189
|
async function deriveEncryptionKey(source) {
|
|
193
190
|
const salt = textEncoder.encode("remember-plugin-v1");
|
|
@@ -195,19 +192,19 @@ async function deriveEncryptionKey(source) {
|
|
|
195
192
|
let context;
|
|
196
193
|
switch (source.type) {
|
|
197
194
|
case "session":
|
|
198
|
-
ikm = await
|
|
195
|
+
ikm = await secretFor(source) + source.sessionId;
|
|
199
196
|
context = `remember:session:${source.sessionId}`;
|
|
200
197
|
break;
|
|
201
198
|
case "tool":
|
|
202
|
-
ikm = await
|
|
199
|
+
ikm = await secretFor(source) + source.sessionId;
|
|
203
200
|
context = `remember:tool:${source.toolName}:${source.sessionId}`;
|
|
204
201
|
break;
|
|
205
202
|
case "user":
|
|
206
|
-
ikm = await
|
|
203
|
+
ikm = await secretFor(source) + source.userId;
|
|
207
204
|
context = `remember:user:${source.userId}`;
|
|
208
205
|
break;
|
|
209
206
|
case "global":
|
|
210
|
-
ikm = await
|
|
207
|
+
ikm = await secretFor(source);
|
|
211
208
|
context = "remember:global";
|
|
212
209
|
break;
|
|
213
210
|
case "custom":
|
|
@@ -219,20 +216,21 @@ async function deriveEncryptionKey(source) {
|
|
|
219
216
|
const info = textEncoder.encode(context);
|
|
220
217
|
return hkdfSha256(ikmBytes, salt, info, 32);
|
|
221
218
|
}
|
|
222
|
-
function getKeySourceForScope(scope, context) {
|
|
219
|
+
function getKeySourceForScope(scope, context, secret) {
|
|
223
220
|
switch (scope) {
|
|
224
221
|
case "session":
|
|
225
|
-
return { type: "session", sessionId: context.sessionId };
|
|
222
|
+
return { type: "session", sessionId: context.sessionId, secret };
|
|
226
223
|
case "user":
|
|
227
|
-
return { type: "user", userId: context.userId ?? "anonymous" };
|
|
224
|
+
return { type: "user", userId: context.userId ?? "anonymous", secret };
|
|
228
225
|
case "tool":
|
|
229
226
|
return {
|
|
230
227
|
type: "tool",
|
|
231
228
|
toolName: context.toolName ?? "unknown",
|
|
232
|
-
sessionId: context.sessionId
|
|
229
|
+
sessionId: context.sessionId,
|
|
230
|
+
secret
|
|
233
231
|
};
|
|
234
232
|
case "global":
|
|
235
|
-
return { type: "global" };
|
|
233
|
+
return { type: "global", secret };
|
|
236
234
|
}
|
|
237
235
|
}
|
|
238
236
|
var textDecoder = new TextDecoder();
|
|
@@ -635,7 +633,7 @@ var RememberAccessor = class {
|
|
|
635
633
|
* `'unknown'`.
|
|
636
634
|
*/
|
|
637
635
|
get toolName() {
|
|
638
|
-
return getRunningTool()?.fullName ??
|
|
636
|
+
return getRunningTool()?.fullName ?? "unknown";
|
|
639
637
|
}
|
|
640
638
|
/**
|
|
641
639
|
* Build the scope-specific prefix.
|
|
@@ -669,11 +667,15 @@ var RememberAccessor = class {
|
|
|
669
667
|
*/
|
|
670
668
|
getKeySource(scope) {
|
|
671
669
|
const needsSessionIdentity = scope === "session" || scope === "tool";
|
|
672
|
-
return getKeySourceForScope(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
670
|
+
return getKeySourceForScope(
|
|
671
|
+
scope,
|
|
672
|
+
{
|
|
673
|
+
sessionId: needsSessionIdentity ? this.resolveSessionIdentity() : this.ctx.sessionId,
|
|
674
|
+
userId: this.userId,
|
|
675
|
+
toolName: scope === "tool" ? this.toolName : void 0
|
|
676
|
+
},
|
|
677
|
+
this.config.encryption?.customKey
|
|
678
|
+
);
|
|
677
679
|
}
|
|
678
680
|
/** The entry stored under `storageKey`, if readable and unexpired; an expired one is deleted. */
|
|
679
681
|
async readEntry(storageKey, scope) {
|
|
@@ -885,10 +887,28 @@ RememberMemoryProvider = __decorateClass([
|
|
|
885
887
|
// plugins/plugin-remember/src/providers/remember-redis.provider.ts
|
|
886
888
|
import Redis from "ioredis";
|
|
887
889
|
import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmcp/sdk";
|
|
890
|
+
|
|
891
|
+
// plugins/plugin-remember/src/providers/remember-key-prefix.ts
|
|
892
|
+
function prefixedStoreKey(keyPrefix, key) {
|
|
893
|
+
return keyPrefix && key.startsWith(keyPrefix) ? key : keyPrefix + key;
|
|
894
|
+
}
|
|
895
|
+
function doubledPrefixKey(keyPrefix, key) {
|
|
896
|
+
return keyPrefix && key.startsWith(keyPrefix) ? keyPrefix + key : void 0;
|
|
897
|
+
}
|
|
898
|
+
function callerKeyOf(keyPrefix, pattern, backendKey) {
|
|
899
|
+
return keyPrefix && pattern.startsWith(keyPrefix) ? backendKey : backendKey.slice(keyPrefix.length);
|
|
900
|
+
}
|
|
901
|
+
var SET_IF_NEITHER_KEY_EXISTS_SCRIPT = `
|
|
902
|
+
if redis.call('EXISTS', KEYS[1], KEYS[2]) > 0 then return 0 end
|
|
903
|
+
if ARGV[2] ~= '' then redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2]) else redis.call('SET', KEYS[1], ARGV[1]) end
|
|
904
|
+
return 1
|
|
905
|
+
`;
|
|
906
|
+
|
|
907
|
+
// plugins/plugin-remember/src/providers/remember-redis.provider.ts
|
|
888
908
|
var RememberRedisProvider = class {
|
|
889
909
|
client;
|
|
890
910
|
/**
|
|
891
|
-
* Prefix prepended to
|
|
911
|
+
* Prefix prepended to Redis keys that don't already start with it.
|
|
892
912
|
* Include any separator (e.g., "myapp:" or "user:123:") as part of the prefix.
|
|
893
913
|
*/
|
|
894
914
|
keyPrefix;
|
|
@@ -942,7 +962,7 @@ var RememberRedisProvider = class {
|
|
|
942
962
|
throw new Error(`Invalid TTL: must be an integer, got ${ttlSeconds}`);
|
|
943
963
|
}
|
|
944
964
|
}
|
|
945
|
-
const fullKey = this.keyPrefix
|
|
965
|
+
const fullKey = prefixedStoreKey(this.keyPrefix, key);
|
|
946
966
|
const strValue = JSON.stringify(value);
|
|
947
967
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
948
968
|
if (ttl !== void 0 && ttl > 0) {
|
|
@@ -950,18 +970,35 @@ var RememberRedisProvider = class {
|
|
|
950
970
|
} else {
|
|
951
971
|
await this.client.set(fullKey, strValue);
|
|
952
972
|
}
|
|
973
|
+
await this.deleteDoubledPrefixKey(key);
|
|
953
974
|
}
|
|
954
975
|
/**
|
|
955
976
|
* Store a value only if the key is absent, via Redis `SET ... NX`.
|
|
956
977
|
*
|
|
957
978
|
* `set` resolves to `'OK'` when it created the key and `null` when the key already existed,
|
|
958
979
|
* so the whole check-and-write is one round trip and two callers racing cannot both win.
|
|
980
|
+
* A key that releases up to 1.9.1 may hold under the doubled prefix counts as present too: one
|
|
981
|
+
* script checks both keys and writes, so an entry still there is never shadowed.
|
|
959
982
|
*/
|
|
960
983
|
async setIfAbsent(key, value, ttlSeconds) {
|
|
961
|
-
const fullKey = this.keyPrefix
|
|
984
|
+
const fullKey = prefixedStoreKey(this.keyPrefix, key);
|
|
962
985
|
const strValue = JSON.stringify(value);
|
|
963
986
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
964
|
-
const
|
|
987
|
+
const hasTtl = ttl !== void 0 && ttl > 0;
|
|
988
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
989
|
+
if (doubledKey) {
|
|
990
|
+
const ttlArgument = hasTtl ? String(ttl) : "";
|
|
991
|
+
const created = await this.client.eval(
|
|
992
|
+
SET_IF_NEITHER_KEY_EXISTS_SCRIPT,
|
|
993
|
+
2,
|
|
994
|
+
fullKey,
|
|
995
|
+
doubledKey,
|
|
996
|
+
strValue,
|
|
997
|
+
ttlArgument
|
|
998
|
+
);
|
|
999
|
+
return created === 1;
|
|
1000
|
+
}
|
|
1001
|
+
const result = hasTtl ? await this.client.set(fullKey, strValue, "EX", ttl, "NX") : await this.client.set(fullKey, strValue, "NX");
|
|
965
1002
|
return result === "OK";
|
|
966
1003
|
}
|
|
967
1004
|
/**
|
|
@@ -976,8 +1013,7 @@ var RememberRedisProvider = class {
|
|
|
976
1013
|
* @returns The parsed value as T, or defaultValue/undefined
|
|
977
1014
|
*/
|
|
978
1015
|
async getValue(key, defaultValue) {
|
|
979
|
-
const
|
|
980
|
-
const raw = await this.client.get(fullKey);
|
|
1016
|
+
const raw = await this.client.get(prefixedStoreKey(this.keyPrefix, key)) ?? await this.readDoubledPrefixValue(key);
|
|
981
1017
|
if (raw === null) return defaultValue;
|
|
982
1018
|
try {
|
|
983
1019
|
return JSON.parse(raw);
|
|
@@ -989,32 +1025,55 @@ var RememberRedisProvider = class {
|
|
|
989
1025
|
* Delete a key.
|
|
990
1026
|
*/
|
|
991
1027
|
async delete(key) {
|
|
992
|
-
|
|
993
|
-
await this.
|
|
1028
|
+
await this.client.del(prefixedStoreKey(this.keyPrefix, key));
|
|
1029
|
+
await this.deleteDoubledPrefixKey(key);
|
|
994
1030
|
}
|
|
995
1031
|
/**
|
|
996
1032
|
* Check if a key exists.
|
|
997
1033
|
*/
|
|
998
1034
|
async exists(key) {
|
|
999
|
-
|
|
1000
|
-
|
|
1035
|
+
if (await this.client.exists(prefixedStoreKey(this.keyPrefix, key)) === 1) return true;
|
|
1036
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1037
|
+
return doubledKey !== void 0 && await this.client.exists(doubledKey) === 1;
|
|
1001
1038
|
}
|
|
1002
1039
|
/**
|
|
1003
1040
|
* List keys matching a pattern.
|
|
1004
1041
|
* Uses Redis SCAN for efficient iteration.
|
|
1005
1042
|
*/
|
|
1006
|
-
async keys(pattern) {
|
|
1007
|
-
const
|
|
1008
|
-
const
|
|
1043
|
+
async keys(pattern = "*") {
|
|
1044
|
+
const result = /* @__PURE__ */ new Set();
|
|
1045
|
+
for (const key of await this.scan(prefixedStoreKey(this.keyPrefix, pattern))) {
|
|
1046
|
+
result.add(callerKeyOf(this.keyPrefix, pattern, key));
|
|
1047
|
+
}
|
|
1048
|
+
const doubledPattern = doubledPrefixKey(this.keyPrefix, pattern);
|
|
1049
|
+
if (doubledPattern) {
|
|
1050
|
+
for (const key of await this.scan(doubledPattern)) result.add(key.slice(this.keyPrefix.length));
|
|
1051
|
+
}
|
|
1052
|
+
return [...result];
|
|
1053
|
+
}
|
|
1054
|
+
async scan(match) {
|
|
1055
|
+
const found = [];
|
|
1009
1056
|
let cursor = "0";
|
|
1010
1057
|
do {
|
|
1011
|
-
const [nextCursor, keys] = await this.client.scan(cursor, "MATCH",
|
|
1058
|
+
const [nextCursor, keys] = await this.client.scan(cursor, "MATCH", match, "COUNT", 100);
|
|
1012
1059
|
cursor = nextCursor;
|
|
1013
|
-
|
|
1014
|
-
result.push(key.slice(this.keyPrefix.length));
|
|
1015
|
-
}
|
|
1060
|
+
found.push(...keys);
|
|
1016
1061
|
} while (cursor !== "0");
|
|
1017
|
-
return
|
|
1062
|
+
return found;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* The stored text of an entry a release up to 1.9.1 wrote under the doubled key, or null. It is read
|
|
1066
|
+
* in place, not copied: the entry keeps its TTL, a rolled-back release still finds it, and a
|
|
1067
|
+
* concurrent `delete()` cannot be undone by a copy. The next write or delete of the key removes it.
|
|
1068
|
+
*/
|
|
1069
|
+
async readDoubledPrefixValue(key) {
|
|
1070
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1071
|
+
return doubledKey ? this.client.get(doubledKey) : null;
|
|
1072
|
+
}
|
|
1073
|
+
/** Drops what releases up to 1.9.1 left under the doubled key, so it cannot come back once this key is gone. */
|
|
1074
|
+
async deleteDoubledPrefixKey(key) {
|
|
1075
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1076
|
+
if (doubledKey) await this.client.del(doubledKey);
|
|
1018
1077
|
}
|
|
1019
1078
|
/**
|
|
1020
1079
|
* Gracefully close the Redis connection.
|
|
@@ -1037,15 +1096,13 @@ RememberRedisProvider = __decorateClass([
|
|
|
1037
1096
|
|
|
1038
1097
|
// plugins/plugin-remember/src/providers/remember-vercel-kv.provider.ts
|
|
1039
1098
|
import { Provider as Provider4, ProviderScope as ProviderScope4 } from "@frontmcp/sdk";
|
|
1040
|
-
import {
|
|
1099
|
+
import { createVercelKvClient } from "@frontmcp/utils";
|
|
1041
1100
|
var RememberVercelKvProvider = class {
|
|
1042
|
-
createClient;
|
|
1043
1101
|
connection;
|
|
1044
1102
|
client;
|
|
1045
1103
|
keyPrefix;
|
|
1046
1104
|
defaultTTL;
|
|
1047
1105
|
constructor(options = {}) {
|
|
1048
|
-
const vercelKv = __require("@vercel/kv");
|
|
1049
1106
|
const hasUrl = options.url !== void 0;
|
|
1050
1107
|
const hasToken = options.token !== void 0;
|
|
1051
1108
|
if (hasUrl !== hasToken) {
|
|
@@ -1053,25 +1110,24 @@ var RememberVercelKvProvider = class {
|
|
|
1053
1110
|
`RememberVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. Received: url=${hasUrl ? "provided" : "missing"}, token=${hasToken ? "provided" : "missing"}`
|
|
1054
1111
|
);
|
|
1055
1112
|
}
|
|
1056
|
-
this.createClient = vercelKv.createClient;
|
|
1057
1113
|
this.connection = { url: options.url, token: options.token };
|
|
1058
1114
|
this.keyPrefix = options.keyPrefix ?? "remember:";
|
|
1059
1115
|
this.defaultTTL = options.defaultTTL;
|
|
1060
1116
|
}
|
|
1061
|
-
/**
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
}
|
|
1117
|
+
/**
|
|
1118
|
+
* The client, built on first use from the url and token (or KV_REST_API_URL / KV_REST_API_TOKEN)
|
|
1119
|
+
* by the shared loader, which a Cloudflare Worker can bundle (#711). Values are read back as
|
|
1120
|
+
* stored. A failed attempt is retried.
|
|
1121
|
+
*/
|
|
1122
|
+
kv() {
|
|
1123
|
+
this.client ??= createVercelKvClient(this.connection).catch((error) => {
|
|
1124
|
+
this.client = void 0;
|
|
1125
|
+
throw error;
|
|
1126
|
+
});
|
|
1071
1127
|
return this.client;
|
|
1072
1128
|
}
|
|
1073
1129
|
prefixKey(key) {
|
|
1074
|
-
return
|
|
1130
|
+
return prefixedStoreKey(this.keyPrefix, key);
|
|
1075
1131
|
}
|
|
1076
1132
|
/**
|
|
1077
1133
|
* Store a value with optional TTL.
|
|
@@ -1080,31 +1136,42 @@ var RememberVercelKvProvider = class {
|
|
|
1080
1136
|
const fullKey = this.prefixKey(key);
|
|
1081
1137
|
const strValue = JSON.stringify(value);
|
|
1082
1138
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
1139
|
+
const kv = await this.kv();
|
|
1083
1140
|
if (ttl && ttl > 0) {
|
|
1084
|
-
await
|
|
1141
|
+
await kv.set(fullKey, strValue, { ex: ttl });
|
|
1085
1142
|
} else {
|
|
1086
|
-
await
|
|
1143
|
+
await kv.set(fullKey, strValue);
|
|
1087
1144
|
}
|
|
1145
|
+
await this.deleteDoubledPrefixKey(key);
|
|
1088
1146
|
}
|
|
1089
1147
|
/**
|
|
1090
1148
|
* Store a value only if the key is absent, via the `nx` option.
|
|
1091
1149
|
*
|
|
1092
1150
|
* Resolves to `'OK'` when it created the key and `null` when the key already existed, so two
|
|
1093
|
-
* callers racing cannot both win.
|
|
1151
|
+
* callers racing cannot both win. A key that releases up to 1.9.1 may hold under the doubled
|
|
1152
|
+
* prefix counts as present too: one script checks both keys and writes, so an entry still there
|
|
1153
|
+
* is never shadowed.
|
|
1094
1154
|
*/
|
|
1095
1155
|
async setIfAbsent(key, value, ttlSeconds) {
|
|
1096
1156
|
const fullKey = this.prefixKey(key);
|
|
1097
1157
|
const strValue = JSON.stringify(value);
|
|
1098
1158
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
1099
|
-
const
|
|
1159
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1160
|
+
const kv = await this.kv();
|
|
1161
|
+
if (doubledKey) {
|
|
1162
|
+
const ttlArgument = ttl && ttl > 0 ? String(ttl) : "";
|
|
1163
|
+
const created = await kv.eval(SET_IF_NEITHER_KEY_EXISTS_SCRIPT, [fullKey, doubledKey], [strValue, ttlArgument]);
|
|
1164
|
+
return Number(created) === 1;
|
|
1165
|
+
}
|
|
1166
|
+
const result = ttl && ttl > 0 ? await kv.set(fullKey, strValue, { nx: true, ex: ttl }) : await kv.set(fullKey, strValue, { nx: true });
|
|
1100
1167
|
return result === "OK";
|
|
1101
1168
|
}
|
|
1102
1169
|
/**
|
|
1103
1170
|
* Retrieve a value by key.
|
|
1104
1171
|
*/
|
|
1105
1172
|
async getValue(key, defaultValue) {
|
|
1106
|
-
const
|
|
1107
|
-
const raw = await
|
|
1173
|
+
const kv = await this.kv();
|
|
1174
|
+
const raw = await kv.get(this.prefixKey(key)) ?? await this.readDoubledPrefixValue(key);
|
|
1108
1175
|
if (raw === null) return defaultValue;
|
|
1109
1176
|
try {
|
|
1110
1177
|
return JSON.parse(raw);
|
|
@@ -1116,46 +1183,70 @@ var RememberVercelKvProvider = class {
|
|
|
1116
1183
|
* Delete a key.
|
|
1117
1184
|
*/
|
|
1118
1185
|
async delete(key) {
|
|
1119
|
-
const
|
|
1120
|
-
await
|
|
1186
|
+
const kv = await this.kv();
|
|
1187
|
+
await kv.del(this.prefixKey(key));
|
|
1188
|
+
await this.deleteDoubledPrefixKey(key);
|
|
1121
1189
|
}
|
|
1122
1190
|
/**
|
|
1123
1191
|
* Check if a key exists.
|
|
1124
1192
|
*/
|
|
1125
1193
|
async exists(key) {
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1194
|
+
const kv = await this.kv();
|
|
1195
|
+
if (await kv.exists(this.prefixKey(key)) === 1) return true;
|
|
1196
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1197
|
+
return doubledKey !== void 0 && await kv.exists(doubledKey) === 1;
|
|
1128
1198
|
}
|
|
1129
1199
|
/**
|
|
1130
1200
|
* List keys matching a pattern.
|
|
1131
1201
|
* Uses SCAN for efficient iteration.
|
|
1132
1202
|
*/
|
|
1133
|
-
async keys(pattern) {
|
|
1134
|
-
const
|
|
1135
|
-
const
|
|
1203
|
+
async keys(pattern = "*") {
|
|
1204
|
+
const result = /* @__PURE__ */ new Set();
|
|
1205
|
+
for (const key of await this.scan(this.prefixKey(pattern))) {
|
|
1206
|
+
result.add(callerKeyOf(this.keyPrefix, pattern, key));
|
|
1207
|
+
}
|
|
1208
|
+
const doubledPattern = doubledPrefixKey(this.keyPrefix, pattern);
|
|
1209
|
+
if (doubledPattern) {
|
|
1210
|
+
for (const key of await this.scan(doubledPattern)) result.add(key.slice(this.keyPrefix.length));
|
|
1211
|
+
}
|
|
1212
|
+
return [...result];
|
|
1213
|
+
}
|
|
1214
|
+
async scan(match) {
|
|
1215
|
+
const found = [];
|
|
1216
|
+
const kv = await this.kv();
|
|
1136
1217
|
try {
|
|
1137
1218
|
let cursor = 0;
|
|
1138
1219
|
do {
|
|
1139
|
-
const [nextCursor, keys] = await
|
|
1140
|
-
match: searchPattern,
|
|
1141
|
-
count: 100
|
|
1142
|
-
});
|
|
1220
|
+
const [nextCursor, keys] = await kv.scan(cursor, { match, count: 100 });
|
|
1143
1221
|
cursor = nextCursor;
|
|
1144
|
-
|
|
1145
|
-
result.push(key.slice(this.keyPrefix.length));
|
|
1146
|
-
}
|
|
1222
|
+
found.push(...keys);
|
|
1147
1223
|
} while (String(cursor) !== "0");
|
|
1148
1224
|
} catch {
|
|
1149
1225
|
try {
|
|
1150
|
-
|
|
1151
|
-
for (const key of keys) {
|
|
1152
|
-
result.push(key.slice(this.keyPrefix.length));
|
|
1153
|
-
}
|
|
1226
|
+
found.push(...await kv.keys(match));
|
|
1154
1227
|
} catch {
|
|
1155
1228
|
console.warn("[RememberPlugin:VercelKV] keys() operation not supported");
|
|
1156
1229
|
}
|
|
1157
1230
|
}
|
|
1158
|
-
return
|
|
1231
|
+
return found;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* The stored text of an entry a release up to 1.9.1 wrote under the doubled key, or null. It is read
|
|
1235
|
+
* in place, not copied: the entry keeps its TTL, a rolled-back release still finds it, and a
|
|
1236
|
+
* concurrent `delete()` cannot be undone by a copy. The next write or delete of the key removes it.
|
|
1237
|
+
*/
|
|
1238
|
+
async readDoubledPrefixValue(key) {
|
|
1239
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1240
|
+
if (!doubledKey) return null;
|
|
1241
|
+
const kv = await this.kv();
|
|
1242
|
+
return kv.get(doubledKey);
|
|
1243
|
+
}
|
|
1244
|
+
/** Drops what releases up to 1.9.1 left under the doubled key, so it cannot come back once this key is gone. */
|
|
1245
|
+
async deleteDoubledPrefixKey(key) {
|
|
1246
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1247
|
+
if (!doubledKey) return;
|
|
1248
|
+
const kv = await this.kv();
|
|
1249
|
+
await kv.del(doubledKey);
|
|
1159
1250
|
}
|
|
1160
1251
|
/**
|
|
1161
1252
|
* Gracefully close the provider.
|
|
@@ -1442,7 +1533,13 @@ function createRememberTools(prefix = "") {
|
|
|
1442
1533
|
}
|
|
1443
1534
|
|
|
1444
1535
|
// plugins/plugin-remember/src/remember.plugin.ts
|
|
1445
|
-
function
|
|
1536
|
+
function withCheckedOptions(options) {
|
|
1537
|
+
const customKey = options.encryption?.customKey;
|
|
1538
|
+
if (customKey !== void 0 && customKey.trim() === "") {
|
|
1539
|
+
throw new RememberConfigurationError(
|
|
1540
|
+
"RememberPlugin encryption.customKey must not be empty; omit it to derive keys from the server secret"
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1446
1543
|
const { defaultTTL } = options;
|
|
1447
1544
|
if (defaultTTL === void 0) return options;
|
|
1448
1545
|
if (defaultTTL === 0) return { ...options, defaultTTL: void 0 };
|
|
@@ -1459,7 +1556,7 @@ var RememberPlugin = class extends DynamicPlugin {
|
|
|
1459
1556
|
super();
|
|
1460
1557
|
this.options = {
|
|
1461
1558
|
...RememberPlugin.defaultOptions,
|
|
1462
|
-
...
|
|
1559
|
+
...withCheckedOptions(options)
|
|
1463
1560
|
};
|
|
1464
1561
|
}
|
|
1465
1562
|
};
|
|
@@ -1480,7 +1577,7 @@ __publicField(RememberPlugin, "dynamicProviders", (options) => {
|
|
|
1480
1577
|
const providers = [];
|
|
1481
1578
|
const config = {
|
|
1482
1579
|
...RememberPlugin.defaultOptions,
|
|
1483
|
-
...
|
|
1580
|
+
...withCheckedOptions(options)
|
|
1484
1581
|
};
|
|
1485
1582
|
switch (config.type) {
|
|
1486
1583
|
case "global-store":
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-remember",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "Remember plugin for FrontMCP - encrypted session memory with approval system for secure tool authorization",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@frontmcp/sdk": "1.9.
|
|
53
|
-
"@frontmcp/utils": "1.9.
|
|
52
|
+
"@frontmcp/sdk": "1.9.3",
|
|
53
|
+
"@frontmcp/utils": "1.9.3",
|
|
54
54
|
"ioredis": "^5.8.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@frontmcp/lazy-zod": "1.9.
|
|
57
|
+
"@frontmcp/lazy-zod": "1.9.3",
|
|
58
58
|
"@vercel/kv": "^2.0.0 || ^3.0.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependenciesMeta": {
|
package/index.js
CHANGED
|
@@ -208,6 +208,9 @@ async function getBaseSecret() {
|
|
|
208
208
|
}
|
|
209
209
|
return getOrCreatePersistedSecret();
|
|
210
210
|
}
|
|
211
|
+
async function secretFor(source) {
|
|
212
|
+
return source.secret ? source.secret : getBaseSecret();
|
|
213
|
+
}
|
|
211
214
|
var textEncoder = new TextEncoder();
|
|
212
215
|
async function deriveEncryptionKey(source) {
|
|
213
216
|
const salt = textEncoder.encode("remember-plugin-v1");
|
|
@@ -215,19 +218,19 @@ async function deriveEncryptionKey(source) {
|
|
|
215
218
|
let context;
|
|
216
219
|
switch (source.type) {
|
|
217
220
|
case "session":
|
|
218
|
-
ikm = await
|
|
221
|
+
ikm = await secretFor(source) + source.sessionId;
|
|
219
222
|
context = `remember:session:${source.sessionId}`;
|
|
220
223
|
break;
|
|
221
224
|
case "tool":
|
|
222
|
-
ikm = await
|
|
225
|
+
ikm = await secretFor(source) + source.sessionId;
|
|
223
226
|
context = `remember:tool:${source.toolName}:${source.sessionId}`;
|
|
224
227
|
break;
|
|
225
228
|
case "user":
|
|
226
|
-
ikm = await
|
|
229
|
+
ikm = await secretFor(source) + source.userId;
|
|
227
230
|
context = `remember:user:${source.userId}`;
|
|
228
231
|
break;
|
|
229
232
|
case "global":
|
|
230
|
-
ikm = await
|
|
233
|
+
ikm = await secretFor(source);
|
|
231
234
|
context = "remember:global";
|
|
232
235
|
break;
|
|
233
236
|
case "custom":
|
|
@@ -239,20 +242,21 @@ async function deriveEncryptionKey(source) {
|
|
|
239
242
|
const info = textEncoder.encode(context);
|
|
240
243
|
return (0, import_utils2.hkdfSha256)(ikmBytes, salt, info, 32);
|
|
241
244
|
}
|
|
242
|
-
function getKeySourceForScope(scope, context) {
|
|
245
|
+
function getKeySourceForScope(scope, context, secret) {
|
|
243
246
|
switch (scope) {
|
|
244
247
|
case "session":
|
|
245
|
-
return { type: "session", sessionId: context.sessionId };
|
|
248
|
+
return { type: "session", sessionId: context.sessionId, secret };
|
|
246
249
|
case "user":
|
|
247
|
-
return { type: "user", userId: context.userId ?? "anonymous" };
|
|
250
|
+
return { type: "user", userId: context.userId ?? "anonymous", secret };
|
|
248
251
|
case "tool":
|
|
249
252
|
return {
|
|
250
253
|
type: "tool",
|
|
251
254
|
toolName: context.toolName ?? "unknown",
|
|
252
|
-
sessionId: context.sessionId
|
|
255
|
+
sessionId: context.sessionId,
|
|
256
|
+
secret
|
|
253
257
|
};
|
|
254
258
|
case "global":
|
|
255
|
-
return { type: "global" };
|
|
259
|
+
return { type: "global", secret };
|
|
256
260
|
}
|
|
257
261
|
}
|
|
258
262
|
var textDecoder = new TextDecoder();
|
|
@@ -655,7 +659,7 @@ var RememberAccessor = class {
|
|
|
655
659
|
* `'unknown'`.
|
|
656
660
|
*/
|
|
657
661
|
get toolName() {
|
|
658
|
-
return (0, import_sdk2.getRunningTool)()?.fullName ??
|
|
662
|
+
return (0, import_sdk2.getRunningTool)()?.fullName ?? "unknown";
|
|
659
663
|
}
|
|
660
664
|
/**
|
|
661
665
|
* Build the scope-specific prefix.
|
|
@@ -689,11 +693,15 @@ var RememberAccessor = class {
|
|
|
689
693
|
*/
|
|
690
694
|
getKeySource(scope) {
|
|
691
695
|
const needsSessionIdentity = scope === "session" || scope === "tool";
|
|
692
|
-
return getKeySourceForScope(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
696
|
+
return getKeySourceForScope(
|
|
697
|
+
scope,
|
|
698
|
+
{
|
|
699
|
+
sessionId: needsSessionIdentity ? this.resolveSessionIdentity() : this.ctx.sessionId,
|
|
700
|
+
userId: this.userId,
|
|
701
|
+
toolName: scope === "tool" ? this.toolName : void 0
|
|
702
|
+
},
|
|
703
|
+
this.config.encryption?.customKey
|
|
704
|
+
);
|
|
697
705
|
}
|
|
698
706
|
/** The entry stored under `storageKey`, if readable and unexpired; an expired one is deleted. */
|
|
699
707
|
async readEntry(storageKey, scope) {
|
|
@@ -905,10 +913,28 @@ RememberMemoryProvider = __decorateClass([
|
|
|
905
913
|
// plugins/plugin-remember/src/providers/remember-redis.provider.ts
|
|
906
914
|
var import_ioredis = __toESM(require("ioredis"));
|
|
907
915
|
var import_sdk4 = require("@frontmcp/sdk");
|
|
916
|
+
|
|
917
|
+
// plugins/plugin-remember/src/providers/remember-key-prefix.ts
|
|
918
|
+
function prefixedStoreKey(keyPrefix, key) {
|
|
919
|
+
return keyPrefix && key.startsWith(keyPrefix) ? key : keyPrefix + key;
|
|
920
|
+
}
|
|
921
|
+
function doubledPrefixKey(keyPrefix, key) {
|
|
922
|
+
return keyPrefix && key.startsWith(keyPrefix) ? keyPrefix + key : void 0;
|
|
923
|
+
}
|
|
924
|
+
function callerKeyOf(keyPrefix, pattern, backendKey) {
|
|
925
|
+
return keyPrefix && pattern.startsWith(keyPrefix) ? backendKey : backendKey.slice(keyPrefix.length);
|
|
926
|
+
}
|
|
927
|
+
var SET_IF_NEITHER_KEY_EXISTS_SCRIPT = `
|
|
928
|
+
if redis.call('EXISTS', KEYS[1], KEYS[2]) > 0 then return 0 end
|
|
929
|
+
if ARGV[2] ~= '' then redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2]) else redis.call('SET', KEYS[1], ARGV[1]) end
|
|
930
|
+
return 1
|
|
931
|
+
`;
|
|
932
|
+
|
|
933
|
+
// plugins/plugin-remember/src/providers/remember-redis.provider.ts
|
|
908
934
|
var RememberRedisProvider = class {
|
|
909
935
|
client;
|
|
910
936
|
/**
|
|
911
|
-
* Prefix prepended to
|
|
937
|
+
* Prefix prepended to Redis keys that don't already start with it.
|
|
912
938
|
* Include any separator (e.g., "myapp:" or "user:123:") as part of the prefix.
|
|
913
939
|
*/
|
|
914
940
|
keyPrefix;
|
|
@@ -962,7 +988,7 @@ var RememberRedisProvider = class {
|
|
|
962
988
|
throw new Error(`Invalid TTL: must be an integer, got ${ttlSeconds}`);
|
|
963
989
|
}
|
|
964
990
|
}
|
|
965
|
-
const fullKey = this.keyPrefix
|
|
991
|
+
const fullKey = prefixedStoreKey(this.keyPrefix, key);
|
|
966
992
|
const strValue = JSON.stringify(value);
|
|
967
993
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
968
994
|
if (ttl !== void 0 && ttl > 0) {
|
|
@@ -970,18 +996,35 @@ var RememberRedisProvider = class {
|
|
|
970
996
|
} else {
|
|
971
997
|
await this.client.set(fullKey, strValue);
|
|
972
998
|
}
|
|
999
|
+
await this.deleteDoubledPrefixKey(key);
|
|
973
1000
|
}
|
|
974
1001
|
/**
|
|
975
1002
|
* Store a value only if the key is absent, via Redis `SET ... NX`.
|
|
976
1003
|
*
|
|
977
1004
|
* `set` resolves to `'OK'` when it created the key and `null` when the key already existed,
|
|
978
1005
|
* so the whole check-and-write is one round trip and two callers racing cannot both win.
|
|
1006
|
+
* A key that releases up to 1.9.1 may hold under the doubled prefix counts as present too: one
|
|
1007
|
+
* script checks both keys and writes, so an entry still there is never shadowed.
|
|
979
1008
|
*/
|
|
980
1009
|
async setIfAbsent(key, value, ttlSeconds) {
|
|
981
|
-
const fullKey = this.keyPrefix
|
|
1010
|
+
const fullKey = prefixedStoreKey(this.keyPrefix, key);
|
|
982
1011
|
const strValue = JSON.stringify(value);
|
|
983
1012
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
984
|
-
const
|
|
1013
|
+
const hasTtl = ttl !== void 0 && ttl > 0;
|
|
1014
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1015
|
+
if (doubledKey) {
|
|
1016
|
+
const ttlArgument = hasTtl ? String(ttl) : "";
|
|
1017
|
+
const created = await this.client.eval(
|
|
1018
|
+
SET_IF_NEITHER_KEY_EXISTS_SCRIPT,
|
|
1019
|
+
2,
|
|
1020
|
+
fullKey,
|
|
1021
|
+
doubledKey,
|
|
1022
|
+
strValue,
|
|
1023
|
+
ttlArgument
|
|
1024
|
+
);
|
|
1025
|
+
return created === 1;
|
|
1026
|
+
}
|
|
1027
|
+
const result = hasTtl ? await this.client.set(fullKey, strValue, "EX", ttl, "NX") : await this.client.set(fullKey, strValue, "NX");
|
|
985
1028
|
return result === "OK";
|
|
986
1029
|
}
|
|
987
1030
|
/**
|
|
@@ -996,8 +1039,7 @@ var RememberRedisProvider = class {
|
|
|
996
1039
|
* @returns The parsed value as T, or defaultValue/undefined
|
|
997
1040
|
*/
|
|
998
1041
|
async getValue(key, defaultValue) {
|
|
999
|
-
const
|
|
1000
|
-
const raw = await this.client.get(fullKey);
|
|
1042
|
+
const raw = await this.client.get(prefixedStoreKey(this.keyPrefix, key)) ?? await this.readDoubledPrefixValue(key);
|
|
1001
1043
|
if (raw === null) return defaultValue;
|
|
1002
1044
|
try {
|
|
1003
1045
|
return JSON.parse(raw);
|
|
@@ -1009,32 +1051,55 @@ var RememberRedisProvider = class {
|
|
|
1009
1051
|
* Delete a key.
|
|
1010
1052
|
*/
|
|
1011
1053
|
async delete(key) {
|
|
1012
|
-
|
|
1013
|
-
await this.
|
|
1054
|
+
await this.client.del(prefixedStoreKey(this.keyPrefix, key));
|
|
1055
|
+
await this.deleteDoubledPrefixKey(key);
|
|
1014
1056
|
}
|
|
1015
1057
|
/**
|
|
1016
1058
|
* Check if a key exists.
|
|
1017
1059
|
*/
|
|
1018
1060
|
async exists(key) {
|
|
1019
|
-
|
|
1020
|
-
|
|
1061
|
+
if (await this.client.exists(prefixedStoreKey(this.keyPrefix, key)) === 1) return true;
|
|
1062
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1063
|
+
return doubledKey !== void 0 && await this.client.exists(doubledKey) === 1;
|
|
1021
1064
|
}
|
|
1022
1065
|
/**
|
|
1023
1066
|
* List keys matching a pattern.
|
|
1024
1067
|
* Uses Redis SCAN for efficient iteration.
|
|
1025
1068
|
*/
|
|
1026
|
-
async keys(pattern) {
|
|
1027
|
-
const
|
|
1028
|
-
const
|
|
1069
|
+
async keys(pattern = "*") {
|
|
1070
|
+
const result = /* @__PURE__ */ new Set();
|
|
1071
|
+
for (const key of await this.scan(prefixedStoreKey(this.keyPrefix, pattern))) {
|
|
1072
|
+
result.add(callerKeyOf(this.keyPrefix, pattern, key));
|
|
1073
|
+
}
|
|
1074
|
+
const doubledPattern = doubledPrefixKey(this.keyPrefix, pattern);
|
|
1075
|
+
if (doubledPattern) {
|
|
1076
|
+
for (const key of await this.scan(doubledPattern)) result.add(key.slice(this.keyPrefix.length));
|
|
1077
|
+
}
|
|
1078
|
+
return [...result];
|
|
1079
|
+
}
|
|
1080
|
+
async scan(match) {
|
|
1081
|
+
const found = [];
|
|
1029
1082
|
let cursor = "0";
|
|
1030
1083
|
do {
|
|
1031
|
-
const [nextCursor, keys] = await this.client.scan(cursor, "MATCH",
|
|
1084
|
+
const [nextCursor, keys] = await this.client.scan(cursor, "MATCH", match, "COUNT", 100);
|
|
1032
1085
|
cursor = nextCursor;
|
|
1033
|
-
|
|
1034
|
-
result.push(key.slice(this.keyPrefix.length));
|
|
1035
|
-
}
|
|
1086
|
+
found.push(...keys);
|
|
1036
1087
|
} while (cursor !== "0");
|
|
1037
|
-
return
|
|
1088
|
+
return found;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* The stored text of an entry a release up to 1.9.1 wrote under the doubled key, or null. It is read
|
|
1092
|
+
* in place, not copied: the entry keeps its TTL, a rolled-back release still finds it, and a
|
|
1093
|
+
* concurrent `delete()` cannot be undone by a copy. The next write or delete of the key removes it.
|
|
1094
|
+
*/
|
|
1095
|
+
async readDoubledPrefixValue(key) {
|
|
1096
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1097
|
+
return doubledKey ? this.client.get(doubledKey) : null;
|
|
1098
|
+
}
|
|
1099
|
+
/** Drops what releases up to 1.9.1 left under the doubled key, so it cannot come back once this key is gone. */
|
|
1100
|
+
async deleteDoubledPrefixKey(key) {
|
|
1101
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1102
|
+
if (doubledKey) await this.client.del(doubledKey);
|
|
1038
1103
|
}
|
|
1039
1104
|
/**
|
|
1040
1105
|
* Gracefully close the Redis connection.
|
|
@@ -1059,13 +1124,11 @@ RememberRedisProvider = __decorateClass([
|
|
|
1059
1124
|
var import_sdk5 = require("@frontmcp/sdk");
|
|
1060
1125
|
var import_utils3 = require("@frontmcp/utils");
|
|
1061
1126
|
var RememberVercelKvProvider = class {
|
|
1062
|
-
createClient;
|
|
1063
1127
|
connection;
|
|
1064
1128
|
client;
|
|
1065
1129
|
keyPrefix;
|
|
1066
1130
|
defaultTTL;
|
|
1067
1131
|
constructor(options = {}) {
|
|
1068
|
-
const vercelKv = require("@vercel/kv");
|
|
1069
1132
|
const hasUrl = options.url !== void 0;
|
|
1070
1133
|
const hasToken = options.token !== void 0;
|
|
1071
1134
|
if (hasUrl !== hasToken) {
|
|
@@ -1073,25 +1136,24 @@ var RememberVercelKvProvider = class {
|
|
|
1073
1136
|
`RememberVercelKvProvider: Both 'url' and 'token' must be provided together, or neither. Received: url=${hasUrl ? "provided" : "missing"}, token=${hasToken ? "provided" : "missing"}`
|
|
1074
1137
|
);
|
|
1075
1138
|
}
|
|
1076
|
-
this.createClient = vercelKv.createClient;
|
|
1077
1139
|
this.connection = { url: options.url, token: options.token };
|
|
1078
1140
|
this.keyPrefix = options.keyPrefix ?? "remember:";
|
|
1079
1141
|
this.defaultTTL = options.defaultTTL;
|
|
1080
1142
|
}
|
|
1081
|
-
/**
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
}
|
|
1143
|
+
/**
|
|
1144
|
+
* The client, built on first use from the url and token (or KV_REST_API_URL / KV_REST_API_TOKEN)
|
|
1145
|
+
* by the shared loader, which a Cloudflare Worker can bundle (#711). Values are read back as
|
|
1146
|
+
* stored. A failed attempt is retried.
|
|
1147
|
+
*/
|
|
1148
|
+
kv() {
|
|
1149
|
+
this.client ??= (0, import_utils3.createVercelKvClient)(this.connection).catch((error) => {
|
|
1150
|
+
this.client = void 0;
|
|
1151
|
+
throw error;
|
|
1152
|
+
});
|
|
1091
1153
|
return this.client;
|
|
1092
1154
|
}
|
|
1093
1155
|
prefixKey(key) {
|
|
1094
|
-
return
|
|
1156
|
+
return prefixedStoreKey(this.keyPrefix, key);
|
|
1095
1157
|
}
|
|
1096
1158
|
/**
|
|
1097
1159
|
* Store a value with optional TTL.
|
|
@@ -1100,31 +1162,42 @@ var RememberVercelKvProvider = class {
|
|
|
1100
1162
|
const fullKey = this.prefixKey(key);
|
|
1101
1163
|
const strValue = JSON.stringify(value);
|
|
1102
1164
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
1165
|
+
const kv = await this.kv();
|
|
1103
1166
|
if (ttl && ttl > 0) {
|
|
1104
|
-
await
|
|
1167
|
+
await kv.set(fullKey, strValue, { ex: ttl });
|
|
1105
1168
|
} else {
|
|
1106
|
-
await
|
|
1169
|
+
await kv.set(fullKey, strValue);
|
|
1107
1170
|
}
|
|
1171
|
+
await this.deleteDoubledPrefixKey(key);
|
|
1108
1172
|
}
|
|
1109
1173
|
/**
|
|
1110
1174
|
* Store a value only if the key is absent, via the `nx` option.
|
|
1111
1175
|
*
|
|
1112
1176
|
* Resolves to `'OK'` when it created the key and `null` when the key already existed, so two
|
|
1113
|
-
* callers racing cannot both win.
|
|
1177
|
+
* callers racing cannot both win. A key that releases up to 1.9.1 may hold under the doubled
|
|
1178
|
+
* prefix counts as present too: one script checks both keys and writes, so an entry still there
|
|
1179
|
+
* is never shadowed.
|
|
1114
1180
|
*/
|
|
1115
1181
|
async setIfAbsent(key, value, ttlSeconds) {
|
|
1116
1182
|
const fullKey = this.prefixKey(key);
|
|
1117
1183
|
const strValue = JSON.stringify(value);
|
|
1118
1184
|
const ttl = ttlSeconds ?? this.defaultTTL;
|
|
1119
|
-
const
|
|
1185
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1186
|
+
const kv = await this.kv();
|
|
1187
|
+
if (doubledKey) {
|
|
1188
|
+
const ttlArgument = ttl && ttl > 0 ? String(ttl) : "";
|
|
1189
|
+
const created = await kv.eval(SET_IF_NEITHER_KEY_EXISTS_SCRIPT, [fullKey, doubledKey], [strValue, ttlArgument]);
|
|
1190
|
+
return Number(created) === 1;
|
|
1191
|
+
}
|
|
1192
|
+
const result = ttl && ttl > 0 ? await kv.set(fullKey, strValue, { nx: true, ex: ttl }) : await kv.set(fullKey, strValue, { nx: true });
|
|
1120
1193
|
return result === "OK";
|
|
1121
1194
|
}
|
|
1122
1195
|
/**
|
|
1123
1196
|
* Retrieve a value by key.
|
|
1124
1197
|
*/
|
|
1125
1198
|
async getValue(key, defaultValue) {
|
|
1126
|
-
const
|
|
1127
|
-
const raw = await
|
|
1199
|
+
const kv = await this.kv();
|
|
1200
|
+
const raw = await kv.get(this.prefixKey(key)) ?? await this.readDoubledPrefixValue(key);
|
|
1128
1201
|
if (raw === null) return defaultValue;
|
|
1129
1202
|
try {
|
|
1130
1203
|
return JSON.parse(raw);
|
|
@@ -1136,46 +1209,70 @@ var RememberVercelKvProvider = class {
|
|
|
1136
1209
|
* Delete a key.
|
|
1137
1210
|
*/
|
|
1138
1211
|
async delete(key) {
|
|
1139
|
-
const
|
|
1140
|
-
await
|
|
1212
|
+
const kv = await this.kv();
|
|
1213
|
+
await kv.del(this.prefixKey(key));
|
|
1214
|
+
await this.deleteDoubledPrefixKey(key);
|
|
1141
1215
|
}
|
|
1142
1216
|
/**
|
|
1143
1217
|
* Check if a key exists.
|
|
1144
1218
|
*/
|
|
1145
1219
|
async exists(key) {
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1220
|
+
const kv = await this.kv();
|
|
1221
|
+
if (await kv.exists(this.prefixKey(key)) === 1) return true;
|
|
1222
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1223
|
+
return doubledKey !== void 0 && await kv.exists(doubledKey) === 1;
|
|
1148
1224
|
}
|
|
1149
1225
|
/**
|
|
1150
1226
|
* List keys matching a pattern.
|
|
1151
1227
|
* Uses SCAN for efficient iteration.
|
|
1152
1228
|
*/
|
|
1153
|
-
async keys(pattern) {
|
|
1154
|
-
const
|
|
1155
|
-
const
|
|
1229
|
+
async keys(pattern = "*") {
|
|
1230
|
+
const result = /* @__PURE__ */ new Set();
|
|
1231
|
+
for (const key of await this.scan(this.prefixKey(pattern))) {
|
|
1232
|
+
result.add(callerKeyOf(this.keyPrefix, pattern, key));
|
|
1233
|
+
}
|
|
1234
|
+
const doubledPattern = doubledPrefixKey(this.keyPrefix, pattern);
|
|
1235
|
+
if (doubledPattern) {
|
|
1236
|
+
for (const key of await this.scan(doubledPattern)) result.add(key.slice(this.keyPrefix.length));
|
|
1237
|
+
}
|
|
1238
|
+
return [...result];
|
|
1239
|
+
}
|
|
1240
|
+
async scan(match) {
|
|
1241
|
+
const found = [];
|
|
1242
|
+
const kv = await this.kv();
|
|
1156
1243
|
try {
|
|
1157
1244
|
let cursor = 0;
|
|
1158
1245
|
do {
|
|
1159
|
-
const [nextCursor, keys] = await
|
|
1160
|
-
match: searchPattern,
|
|
1161
|
-
count: 100
|
|
1162
|
-
});
|
|
1246
|
+
const [nextCursor, keys] = await kv.scan(cursor, { match, count: 100 });
|
|
1163
1247
|
cursor = nextCursor;
|
|
1164
|
-
|
|
1165
|
-
result.push(key.slice(this.keyPrefix.length));
|
|
1166
|
-
}
|
|
1248
|
+
found.push(...keys);
|
|
1167
1249
|
} while (String(cursor) !== "0");
|
|
1168
1250
|
} catch {
|
|
1169
1251
|
try {
|
|
1170
|
-
|
|
1171
|
-
for (const key of keys) {
|
|
1172
|
-
result.push(key.slice(this.keyPrefix.length));
|
|
1173
|
-
}
|
|
1252
|
+
found.push(...await kv.keys(match));
|
|
1174
1253
|
} catch {
|
|
1175
1254
|
console.warn("[RememberPlugin:VercelKV] keys() operation not supported");
|
|
1176
1255
|
}
|
|
1177
1256
|
}
|
|
1178
|
-
return
|
|
1257
|
+
return found;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* The stored text of an entry a release up to 1.9.1 wrote under the doubled key, or null. It is read
|
|
1261
|
+
* in place, not copied: the entry keeps its TTL, a rolled-back release still finds it, and a
|
|
1262
|
+
* concurrent `delete()` cannot be undone by a copy. The next write or delete of the key removes it.
|
|
1263
|
+
*/
|
|
1264
|
+
async readDoubledPrefixValue(key) {
|
|
1265
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1266
|
+
if (!doubledKey) return null;
|
|
1267
|
+
const kv = await this.kv();
|
|
1268
|
+
return kv.get(doubledKey);
|
|
1269
|
+
}
|
|
1270
|
+
/** Drops what releases up to 1.9.1 left under the doubled key, so it cannot come back once this key is gone. */
|
|
1271
|
+
async deleteDoubledPrefixKey(key) {
|
|
1272
|
+
const doubledKey = doubledPrefixKey(this.keyPrefix, key);
|
|
1273
|
+
if (!doubledKey) return;
|
|
1274
|
+
const kv = await this.kv();
|
|
1275
|
+
await kv.del(doubledKey);
|
|
1179
1276
|
}
|
|
1180
1277
|
/**
|
|
1181
1278
|
* Gracefully close the provider.
|
|
@@ -1462,7 +1559,13 @@ function createRememberTools(prefix = "") {
|
|
|
1462
1559
|
}
|
|
1463
1560
|
|
|
1464
1561
|
// plugins/plugin-remember/src/remember.plugin.ts
|
|
1465
|
-
function
|
|
1562
|
+
function withCheckedOptions(options) {
|
|
1563
|
+
const customKey = options.encryption?.customKey;
|
|
1564
|
+
if (customKey !== void 0 && customKey.trim() === "") {
|
|
1565
|
+
throw new RememberConfigurationError(
|
|
1566
|
+
"RememberPlugin encryption.customKey must not be empty; omit it to derive keys from the server secret"
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1466
1569
|
const { defaultTTL } = options;
|
|
1467
1570
|
if (defaultTTL === void 0) return options;
|
|
1468
1571
|
if (defaultTTL === 0) return { ...options, defaultTTL: void 0 };
|
|
@@ -1479,7 +1582,7 @@ var RememberPlugin = class extends import_sdk11.DynamicPlugin {
|
|
|
1479
1582
|
super();
|
|
1480
1583
|
this.options = {
|
|
1481
1584
|
...RememberPlugin.defaultOptions,
|
|
1482
|
-
...
|
|
1585
|
+
...withCheckedOptions(options)
|
|
1483
1586
|
};
|
|
1484
1587
|
}
|
|
1485
1588
|
};
|
|
@@ -1500,7 +1603,7 @@ __publicField(RememberPlugin, "dynamicProviders", (options) => {
|
|
|
1500
1603
|
const providers = [];
|
|
1501
1604
|
const config = {
|
|
1502
1605
|
...RememberPlugin.defaultOptions,
|
|
1503
|
-
...
|
|
1606
|
+
...withCheckedOptions(options)
|
|
1504
1607
|
};
|
|
1505
1608
|
switch (config.type) {
|
|
1506
1609
|
case "global-store":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-remember",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
4
|
"description": "Remember plugin for FrontMCP - encrypted session memory with approval system for secure tool authorization",
|
|
5
5
|
"author": "AgentFront <info@agentfront.dev>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@frontmcp/sdk": "1.9.
|
|
53
|
-
"@frontmcp/utils": "1.9.
|
|
52
|
+
"@frontmcp/sdk": "1.9.3",
|
|
53
|
+
"@frontmcp/utils": "1.9.3",
|
|
54
54
|
"ioredis": "^5.8.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@frontmcp/lazy-zod": "1.9.
|
|
57
|
+
"@frontmcp/lazy-zod": "1.9.3",
|
|
58
58
|
"@vercel/kv": "^2.0.0 || ^3.0.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependenciesMeta": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remember-accessor.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-accessor.provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAKvB,OAAO,KAAK,EACV,aAAa,EACb,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAmCzE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAKa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;gBAG1C,KAAK,EAAE,sBAAsB,EAC7B,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,qBAAqB,EAC7B,MAAM,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;IAwBjD;;;;;;;;;OASG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAuBhG;;;;;;OAMG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAkB,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAMtF;;;;;;OAMG;IACG,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,aAAa,CAAA;KAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAK9G;;;;;OAKG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7E;;;;;;OAMG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC;IAK9E;;;;;OAKG;IACG,IAAI,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAWhE;;;;;;;;;;OAUG;IACG,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,aAAa,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAiC/G;;;;;OAKG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAS/B;IAMD;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;;;;;;;OAQG;IACH,OAAO,KAAK,iBAAiB,GAQ5B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sBAAsB;IAmB9B;;;;;OAKG;IACH,OAAO,KAAK,QAAQ,GAEnB;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IA4BxB;;OAEG;IACH,OAAO,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"remember-accessor.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-accessor.provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key prefixing for the stores that take a `keyPrefix` (Redis, Vercel KV).
|
|
3
|
+
*
|
|
4
|
+
* The Remember accessor hands a store keys that already start with the plugin's `keyPrefix`
|
|
5
|
+
* (`remember:v2:user:…`), and the plugin gives the store the same prefix. Up to 1.9.1 the store
|
|
6
|
+
* added it again, so entries lived under `remember:remember:v2:user:…` (#767). A key that already
|
|
7
|
+
* carries the prefix is now stored as is, and the doubled key is where such an entry may still be.
|
|
8
|
+
*/
|
|
9
|
+
/** The backend key for a store key. */
|
|
10
|
+
export declare function prefixedStoreKey(keyPrefix: string, key: string): string;
|
|
11
|
+
/** Where releases up to 1.9.1 stored a key that carries the prefix: with the prefix twice. */
|
|
12
|
+
export declare function doubledPrefixKey(keyPrefix: string, key: string): string | undefined;
|
|
13
|
+
/** A backend key found by `keys(pattern)`, as the caller names it. */
|
|
14
|
+
export declare function callerKeyOf(keyPrefix: string, pattern: string, backendKey: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Sets KEYS[1] to ARGV[1], with ARGV[2] seconds of TTL when it is not empty, unless KEYS[1] or KEYS[2]
|
|
17
|
+
* exists. Run as one script, a value still under the doubled key keeps a conditional write out.
|
|
18
|
+
*/
|
|
19
|
+
export declare const SET_IF_NEITHER_KEY_EXISTS_SCRIPT = "\nif redis.call('EXISTS', KEYS[1], KEYS[2]) > 0 then return 0 end\nif ARGV[2] ~= '' then redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2]) else redis.call('SET', KEYS[1], ARGV[1]) end\nreturn 1\n";
|
|
20
|
+
//# sourceMappingURL=remember-key-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remember-key-prefix.d.ts","sourceRoot":"","sources":["../../src/providers/remember-key-prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uCAAuC;AACvC,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,8FAA8F;AAC9F,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1F;AAED;;;GAGG;AACH,eAAO,MAAM,gCAAgC,yMAI5C,CAAC"}
|
|
@@ -11,7 +11,7 @@ export type RedisRememberOptions = RedisRememberPluginOptions | RedisClientRemem
|
|
|
11
11
|
export default class RememberRedisProvider implements RememberStoreInterface {
|
|
12
12
|
private readonly client;
|
|
13
13
|
/**
|
|
14
|
-
* Prefix prepended to
|
|
14
|
+
* Prefix prepended to Redis keys that don't already start with it.
|
|
15
15
|
* Include any separator (e.g., "myapp:" or "user:123:") as part of the prefix.
|
|
16
16
|
*/
|
|
17
17
|
private readonly keyPrefix;
|
|
@@ -34,6 +34,8 @@ export default class RememberRedisProvider implements RememberStoreInterface {
|
|
|
34
34
|
*
|
|
35
35
|
* `set` resolves to `'OK'` when it created the key and `null` when the key already existed,
|
|
36
36
|
* so the whole check-and-write is one round trip and two callers racing cannot both win.
|
|
37
|
+
* A key that releases up to 1.9.1 may hold under the doubled prefix counts as present too: one
|
|
38
|
+
* script checks both keys and writes, so an entry still there is never shadowed.
|
|
37
39
|
*/
|
|
38
40
|
setIfAbsent(key: string, value: unknown, ttlSeconds?: number): Promise<boolean>;
|
|
39
41
|
/**
|
|
@@ -61,6 +63,15 @@ export default class RememberRedisProvider implements RememberStoreInterface {
|
|
|
61
63
|
* Uses Redis SCAN for efficient iteration.
|
|
62
64
|
*/
|
|
63
65
|
keys(pattern?: string): Promise<string[]>;
|
|
66
|
+
private scan;
|
|
67
|
+
/**
|
|
68
|
+
* The stored text of an entry a release up to 1.9.1 wrote under the doubled key, or null. It is read
|
|
69
|
+
* in place, not copied: the entry keeps its TTL, a rolled-back release still finds it, and a
|
|
70
|
+
* concurrent `delete()` cannot be undone by a copy. The next write or delete of the key removes it.
|
|
71
|
+
*/
|
|
72
|
+
private readDoubledPrefixValue;
|
|
73
|
+
/** Drops what releases up to 1.9.1 left under the doubled key, so it cannot come back once this key is gone. */
|
|
74
|
+
private deleteDoubledPrefixKey;
|
|
64
75
|
/**
|
|
65
76
|
* Gracefully close the Redis connection.
|
|
66
77
|
* Only closes if this provider owns the client (created it internally).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remember-redis.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-redis.provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gCAAgC,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"remember-redis.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-redis.provider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gCAAgC,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAOtG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,GAAG,gCAAgC,CAAC;AAEjG;;;GAGG;AAMH,MAAM,CAAC,OAAO,OAAO,qBAAsB,YAAW,sBAAsB;IAC1E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;gBAEzB,OAAO,EAAE,oBAAoB;IA8BzC;;;;;;;OAOG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B/E;;;;;;;OAOG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BrF;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAclF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM3C;;;OAGG;IACG,IAAI,CAAC,OAAO,SAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAY9B,IAAI;IAWlB;;;;OAIG;YACW,sBAAsB;IAKpC,gHAAgH;YAClG,sBAAsB;IAKpC;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
|
|
@@ -17,14 +17,17 @@ export interface RememberVercelKvProviderOptions {
|
|
|
17
17
|
* Provides serverless-compatible, edge-ready storage.
|
|
18
18
|
*/
|
|
19
19
|
export default class RememberVercelKvProvider implements RememberStoreInterface {
|
|
20
|
-
private readonly createClient;
|
|
21
20
|
private readonly connection;
|
|
22
21
|
private client?;
|
|
23
22
|
private readonly keyPrefix;
|
|
24
23
|
private readonly defaultTTL?;
|
|
25
24
|
constructor(options?: RememberVercelKvProviderOptions);
|
|
26
|
-
/**
|
|
27
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The client, built on first use from the url and token (or KV_REST_API_URL / KV_REST_API_TOKEN)
|
|
27
|
+
* by the shared loader, which a Cloudflare Worker can bundle (#711). Values are read back as
|
|
28
|
+
* stored. A failed attempt is retried.
|
|
29
|
+
*/
|
|
30
|
+
private kv;
|
|
28
31
|
private prefixKey;
|
|
29
32
|
/**
|
|
30
33
|
* Store a value with optional TTL.
|
|
@@ -34,7 +37,9 @@ export default class RememberVercelKvProvider implements RememberStoreInterface
|
|
|
34
37
|
* Store a value only if the key is absent, via the `nx` option.
|
|
35
38
|
*
|
|
36
39
|
* Resolves to `'OK'` when it created the key and `null` when the key already existed, so two
|
|
37
|
-
* callers racing cannot both win.
|
|
40
|
+
* callers racing cannot both win. A key that releases up to 1.9.1 may hold under the doubled
|
|
41
|
+
* prefix counts as present too: one script checks both keys and writes, so an entry still there
|
|
42
|
+
* is never shadowed.
|
|
38
43
|
*/
|
|
39
44
|
setIfAbsent(key: string, value: unknown, ttlSeconds?: number): Promise<boolean>;
|
|
40
45
|
/**
|
|
@@ -54,6 +59,15 @@ export default class RememberVercelKvProvider implements RememberStoreInterface
|
|
|
54
59
|
* Uses SCAN for efficient iteration.
|
|
55
60
|
*/
|
|
56
61
|
keys(pattern?: string): Promise<string[]>;
|
|
62
|
+
private scan;
|
|
63
|
+
/**
|
|
64
|
+
* The stored text of an entry a release up to 1.9.1 wrote under the doubled key, or null. It is read
|
|
65
|
+
* in place, not copied: the entry keeps its TTL, a rolled-back release still finds it, and a
|
|
66
|
+
* concurrent `delete()` cannot be undone by a copy. The next write or delete of the key removes it.
|
|
67
|
+
*/
|
|
68
|
+
private readDoubledPrefixValue;
|
|
69
|
+
/** Drops what releases up to 1.9.1 left under the doubled key, so it cannot come back once this key is gone. */
|
|
70
|
+
private deleteDoubledPrefixKey;
|
|
57
71
|
/**
|
|
58
72
|
* Gracefully close the provider.
|
|
59
73
|
* No-op for Vercel KV as it uses stateless REST API.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remember-vercel-kv.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-vercel-kv.provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remember-vercel-kv.provider.d.ts","sourceRoot":"","sources":["../../src/providers/remember-vercel-kv.provider.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAgBzE;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AAMH,MAAM,CAAC,OAAO,OAAO,wBAAyB,YAAW,sBAAsB;IAC7E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,MAAM,CAAC,CAA0B;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;gBAEzB,OAAO,GAAE,+BAAoC;IAgBzD;;;;OAIG;IACH,OAAO,CAAC,EAAE;IAQV,OAAO,CAAC,SAAS;IAIjB;;OAEG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/E;;;;;;;OAOG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBrF;;OAEG;IACG,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAalF;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxC;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO3C;;;OAGG;IACG,IAAI,CAAC,OAAO,SAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAY9B,IAAI;IAuBlB;;;;OAIG;YACW,sBAAsB;IAOpC,gHAAgH;YAClG,sBAAsB;IAOpC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
package/remember.crypto.d.ts
CHANGED
|
@@ -8,7 +8,14 @@ export type EncryptedBlob = EncBlob;
|
|
|
8
8
|
/**
|
|
9
9
|
* Key derivation source configuration.
|
|
10
10
|
*/
|
|
11
|
-
export type EncryptionKeySource = {
|
|
11
|
+
export type EncryptionKeySource = (ScopeKeySource & {
|
|
12
|
+
/** The secret keys are derived from, in place of the server secret (`encryption.customKey`). */
|
|
13
|
+
secret?: string;
|
|
14
|
+
}) | {
|
|
15
|
+
type: 'custom';
|
|
16
|
+
key: string;
|
|
17
|
+
};
|
|
18
|
+
type ScopeKeySource = {
|
|
12
19
|
type: 'session';
|
|
13
20
|
sessionId: string;
|
|
14
21
|
} | {
|
|
@@ -20,9 +27,6 @@ export type EncryptionKeySource = {
|
|
|
20
27
|
sessionId: string;
|
|
21
28
|
} | {
|
|
22
29
|
type: 'global';
|
|
23
|
-
} | {
|
|
24
|
-
type: 'custom';
|
|
25
|
-
key: string;
|
|
26
30
|
};
|
|
27
31
|
/**
|
|
28
32
|
* Derive a 256-bit encryption key based on the source.
|
|
@@ -54,7 +58,7 @@ export declare function getKeySourceForScope(scope: RememberScope, context: {
|
|
|
54
58
|
sessionId: string;
|
|
55
59
|
userId?: string;
|
|
56
60
|
toolName?: string;
|
|
57
|
-
}): EncryptionKeySource;
|
|
61
|
+
}, secret?: string): EncryptionKeySource;
|
|
58
62
|
/**
|
|
59
63
|
* Encrypt a value using AES-256-GCM.
|
|
60
64
|
* Uses @frontmcp/utils crypto for cross-platform support.
|
|
@@ -89,4 +93,5 @@ export declare function encryptAndSerialize(data: unknown, keySource: Encryption
|
|
|
89
93
|
* Convenience function to deserialize and decrypt in one step.
|
|
90
94
|
*/
|
|
91
95
|
export declare function deserializeAndDecrypt<T = unknown>(str: string, keySource: EncryptionKeySource): Promise<T | null>;
|
|
96
|
+
export {};
|
|
92
97
|
//# sourceMappingURL=remember.crypto.d.ts.map
|
package/remember.crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remember.crypto.d.ts","sourceRoot":"","sources":["../src/remember.crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,OAAO,EACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMtD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"remember.crypto.d.ts","sourceRoot":"","sources":["../src/remember.crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,OAAO,EACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAMtD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,CAAC,cAAc,GAAG;IAChB,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,GACF;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC,KAAK,cAAc,GACf;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAiCvB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAwC1F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE;IACP,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,EACD,MAAM,CAAC,EAAE,MAAM,GACd,mBAAmB,CAgBrB;AASD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAaxG;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAC5C,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,mBAAmB,GAC7B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAmBnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAiBjE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxG;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,GAAG,OAAO,EACrD,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,mBAAmB,GAC7B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAInB"}
|
package/remember.plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remember.plugin.d.ts","sourceRoot":"","sources":["../src/remember.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EASb,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAQvB,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"remember.plugin.d.ts","sourceRoot":"","sources":["../src/remember.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EASb,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAQvB,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAyB1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAwBH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,aAAa,CAAC,qBAAqB,EAAE,0BAA0B,CAAC;IAC1G,MAAM,CAAC,cAAc,EAAE,qBAAqB,CAI1C;IAEF,OAAO,EAAE,qBAAqB,CAAC;gBAEnB,OAAO,GAAE,0BAA+B;IAQpD;;;OAGG;IACH,OAAgB,YAAY,GAAI,SAAS,0BAA0B,KAAG,SAAS,QAAQ,EAAE,CACf;IAE1E;;OAEG;IACH,OAAgB,gBAAgB,GAAI,SAAS,0BAA0B,KAAG,YAAY,EAAE,CA2HtF;CACH"}
|
package/remember.types.d.ts
CHANGED
|
@@ -116,7 +116,10 @@ export interface BaseRememberPluginOptions {
|
|
|
116
116
|
encryption?: {
|
|
117
117
|
/** Whether encryption is enabled (default: true) */
|
|
118
118
|
enabled?: boolean;
|
|
119
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Secret every encryption key is derived from, in place of `REMEMBER_SECRET` (or `MCP_MEMORY_SECRET` /
|
|
121
|
+
* `MCP_SESSION_SECRET`). Each scope, session and user still gets its own key.
|
|
122
|
+
*/
|
|
120
123
|
customKey?: string;
|
|
121
124
|
};
|
|
122
125
|
/** LLM tools configuration */
|
package/remember.types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remember.types.d.ts","sourceRoot":"","sources":["../src/remember.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AAQpD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnE;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;EAAgD,CAAC;AAMjF;;GAEG;AACH,OAAO,CAAC,MAAM,YAAY,EAAE,OAAO,MAAM,CAAC;AAE1C;;;GAGG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,KAAK,SAAS,MAAM,IAAI,CAAC,GAAG;IACxD,QAAQ,CAAC,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC;CAChC,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAEzE,4CAA4C;AAC5C,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAE7E,kCAAkC;AAClC,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAEnE,wCAAwC;AACxC,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AAEnE,0DAA0D;AAC1D,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;AAEjF,8CAA8C;AAC9C,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI,cAAc,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErE;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,YAAY,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,GAAG,QAAQ,CAAC;AAMzG;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,uBAAuB;IACvB,KAAK,EAAE,CAAC,CAAC;IAET,4CAA4C;IAC5C,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAEzB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAElB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAElB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;gCAO9B,CAAC;AAMH;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,+BAA+B;IAC/B,UAAU,CAAC,EAAE;QACX,oDAAoD;QACpD,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB
|
|
1
|
+
{"version":3,"file":"remember.types.d.ts","sourceRoot":"","sources":["../src/remember.types.ts"],"names":[],"mappings":"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"}
|