@reverbia/sdk 1.0.0-next.20251204090034 → 1.0.0-next.20251205130522
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/react/index.d.mts
CHANGED
|
@@ -585,20 +585,43 @@ declare function useChat(options?: UseChatOptions): UseChatResult;
|
|
|
585
585
|
* @param plaintext - The data to encrypt (string or Uint8Array)
|
|
586
586
|
* @returns Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
587
587
|
*/
|
|
588
|
-
declare function encryptData(plaintext: string | Uint8Array): Promise<string>;
|
|
588
|
+
declare function encryptData(plaintext: string | Uint8Array, address: string): Promise<string>;
|
|
589
589
|
/**
|
|
590
590
|
* Decrypts data using AES-GCM with the stored encryption key
|
|
591
591
|
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
592
592
|
* @returns Decrypted data as string
|
|
593
593
|
*/
|
|
594
|
-
declare function decryptData(encryptedHex: string): Promise<string>;
|
|
594
|
+
declare function decryptData(encryptedHex: string, address: string): Promise<string>;
|
|
595
595
|
/**
|
|
596
596
|
* Decrypts data and returns as Uint8Array (for binary data)
|
|
597
597
|
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
598
598
|
* @returns Decrypted data as Uint8Array
|
|
599
599
|
*/
|
|
600
|
-
declare function decryptDataBytes(encryptedHex: string): Promise<Uint8Array>;
|
|
601
|
-
|
|
600
|
+
declare function decryptDataBytes(encryptedHex: string, address: string): Promise<Uint8Array>;
|
|
601
|
+
/**
|
|
602
|
+
* Checks if an encryption key exists for the given wallet address
|
|
603
|
+
*/
|
|
604
|
+
declare function hasEncryptionKey(address: string): boolean;
|
|
605
|
+
/**
|
|
606
|
+
* Type for the signMessage function that client must provide
|
|
607
|
+
*/
|
|
608
|
+
type SignMessageFn = (message: string) => Promise<string>;
|
|
609
|
+
/**
|
|
610
|
+
* Requests the user to sign a message to generate an encryption key.
|
|
611
|
+
* If a key already exists for the given wallet, resolves immediately.
|
|
612
|
+
* @param walletAddress - The wallet address to generate the key for
|
|
613
|
+
* @param signMessage - Function to sign a message (returns signature hex string)
|
|
614
|
+
* @returns Promise that resolves when the key is available
|
|
615
|
+
*/
|
|
616
|
+
declare function requestEncryptionKey(walletAddress: string, signMessage: SignMessageFn): Promise<void>;
|
|
617
|
+
/**
|
|
618
|
+
* Hook that provides on-demand encryption key management.
|
|
619
|
+
* @param signMessage - Function to sign a message (from Privy's useSignMessage)
|
|
620
|
+
* @returns Functions to request encryption keys
|
|
621
|
+
*/
|
|
622
|
+
declare function useEncryption(signMessage: SignMessageFn): {
|
|
623
|
+
requestEncryptionKey: (walletAddress: string) => Promise<void>;
|
|
624
|
+
};
|
|
602
625
|
|
|
603
626
|
interface MemoryItem {
|
|
604
627
|
type: "identity" | "preference" | "project" | "skill" | "constraint";
|
|
@@ -857,4 +880,4 @@ declare function executeTool(tool: ClientTool, params: Record<string, unknown>):
|
|
|
857
880
|
error?: string;
|
|
858
881
|
}>;
|
|
859
882
|
|
|
860
|
-
export { type ClientTool, DEFAULT_TOOL_SELECTOR_MODEL, type PdfFile, type ToolExecutionResult, type ToolParameter, type ToolSelectionResult, createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, executeTool, extractConversationContext, formatMemoriesForChat, selectTool, useChat, useEncryption, useImageGeneration, useMemory, useModels, usePdf, useSearch };
|
|
883
|
+
export { type ClientTool, DEFAULT_TOOL_SELECTOR_MODEL, type PdfFile, type SignMessageFn, type ToolExecutionResult, type ToolParameter, type ToolSelectionResult, createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, executeTool, extractConversationContext, formatMemoriesForChat, hasEncryptionKey, requestEncryptionKey, selectTool, useChat, useEncryption, useImageGeneration, useMemory, useModels, usePdf, useSearch };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -585,20 +585,43 @@ declare function useChat(options?: UseChatOptions): UseChatResult;
|
|
|
585
585
|
* @param plaintext - The data to encrypt (string or Uint8Array)
|
|
586
586
|
* @returns Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
587
587
|
*/
|
|
588
|
-
declare function encryptData(plaintext: string | Uint8Array): Promise<string>;
|
|
588
|
+
declare function encryptData(plaintext: string | Uint8Array, address: string): Promise<string>;
|
|
589
589
|
/**
|
|
590
590
|
* Decrypts data using AES-GCM with the stored encryption key
|
|
591
591
|
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
592
592
|
* @returns Decrypted data as string
|
|
593
593
|
*/
|
|
594
|
-
declare function decryptData(encryptedHex: string): Promise<string>;
|
|
594
|
+
declare function decryptData(encryptedHex: string, address: string): Promise<string>;
|
|
595
595
|
/**
|
|
596
596
|
* Decrypts data and returns as Uint8Array (for binary data)
|
|
597
597
|
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
598
598
|
* @returns Decrypted data as Uint8Array
|
|
599
599
|
*/
|
|
600
|
-
declare function decryptDataBytes(encryptedHex: string): Promise<Uint8Array>;
|
|
601
|
-
|
|
600
|
+
declare function decryptDataBytes(encryptedHex: string, address: string): Promise<Uint8Array>;
|
|
601
|
+
/**
|
|
602
|
+
* Checks if an encryption key exists for the given wallet address
|
|
603
|
+
*/
|
|
604
|
+
declare function hasEncryptionKey(address: string): boolean;
|
|
605
|
+
/**
|
|
606
|
+
* Type for the signMessage function that client must provide
|
|
607
|
+
*/
|
|
608
|
+
type SignMessageFn = (message: string) => Promise<string>;
|
|
609
|
+
/**
|
|
610
|
+
* Requests the user to sign a message to generate an encryption key.
|
|
611
|
+
* If a key already exists for the given wallet, resolves immediately.
|
|
612
|
+
* @param walletAddress - The wallet address to generate the key for
|
|
613
|
+
* @param signMessage - Function to sign a message (returns signature hex string)
|
|
614
|
+
* @returns Promise that resolves when the key is available
|
|
615
|
+
*/
|
|
616
|
+
declare function requestEncryptionKey(walletAddress: string, signMessage: SignMessageFn): Promise<void>;
|
|
617
|
+
/**
|
|
618
|
+
* Hook that provides on-demand encryption key management.
|
|
619
|
+
* @param signMessage - Function to sign a message (from Privy's useSignMessage)
|
|
620
|
+
* @returns Functions to request encryption keys
|
|
621
|
+
*/
|
|
622
|
+
declare function useEncryption(signMessage: SignMessageFn): {
|
|
623
|
+
requestEncryptionKey: (walletAddress: string) => Promise<void>;
|
|
624
|
+
};
|
|
602
625
|
|
|
603
626
|
interface MemoryItem {
|
|
604
627
|
type: "identity" | "preference" | "project" | "skill" | "constraint";
|
|
@@ -857,4 +880,4 @@ declare function executeTool(tool: ClientTool, params: Record<string, unknown>):
|
|
|
857
880
|
error?: string;
|
|
858
881
|
}>;
|
|
859
882
|
|
|
860
|
-
export { type ClientTool, DEFAULT_TOOL_SELECTOR_MODEL, type PdfFile, type ToolExecutionResult, type ToolParameter, type ToolSelectionResult, createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, executeTool, extractConversationContext, formatMemoriesForChat, selectTool, useChat, useEncryption, useImageGeneration, useMemory, useModels, usePdf, useSearch };
|
|
883
|
+
export { type ClientTool, DEFAULT_TOOL_SELECTOR_MODEL, type PdfFile, type SignMessageFn, type ToolExecutionResult, type ToolParameter, type ToolSelectionResult, createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, executeTool, extractConversationContext, formatMemoriesForChat, hasEncryptionKey, requestEncryptionKey, selectTool, useChat, useEncryption, useImageGeneration, useMemory, useModels, usePdf, useSearch };
|
package/dist/react/index.mjs
CHANGED
|
@@ -830,7 +830,7 @@ async function getTextGenerationPipeline(options) {
|
|
|
830
830
|
if (sharedPipeline && currentModel === model && currentDevice === device) {
|
|
831
831
|
return sharedPipeline;
|
|
832
832
|
}
|
|
833
|
-
const { pipeline, env } = await import("./transformers.node-
|
|
833
|
+
const { pipeline, env } = await import("./transformers.node-LUTOZWVQ.mjs");
|
|
834
834
|
env.allowLocalModels = false;
|
|
835
835
|
if (env.backends?.onnx) {
|
|
836
836
|
env.backends.onnx.logLevel = "fatal";
|
|
@@ -856,7 +856,7 @@ async function generateLocalChatCompletion(messages, options = {}) {
|
|
|
856
856
|
onToken,
|
|
857
857
|
signal
|
|
858
858
|
} = options;
|
|
859
|
-
const { TextStreamer } = await import("./transformers.node-
|
|
859
|
+
const { TextStreamer } = await import("./transformers.node-LUTOZWVQ.mjs");
|
|
860
860
|
const chatPipeline = await getTextGenerationPipeline({
|
|
861
861
|
model,
|
|
862
862
|
device: "wasm",
|
|
@@ -1359,10 +1359,11 @@ Please inform the user about this issue and try to help them alternatively.`
|
|
|
1359
1359
|
}
|
|
1360
1360
|
|
|
1361
1361
|
// src/react/useEncryption.ts
|
|
1362
|
-
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1363
|
-
import { useSignMessage, useWallets } from "@privy-io/react-auth";
|
|
1364
1362
|
var SIGN_MESSAGE = "The app is asking you to sign this message to generate a key, which will be used to encrypt data.";
|
|
1365
|
-
var
|
|
1363
|
+
var BASE_SIGNATURE_STORAGE_KEY = "privy_encryption_key";
|
|
1364
|
+
function getStorageKey(address) {
|
|
1365
|
+
return `${BASE_SIGNATURE_STORAGE_KEY}_${address}`;
|
|
1366
|
+
}
|
|
1366
1367
|
function getStorageItem(key) {
|
|
1367
1368
|
if (typeof window === "undefined" || !window.localStorage) {
|
|
1368
1369
|
return null;
|
|
@@ -1404,8 +1405,9 @@ async function deriveKeyFromSignature(signature) {
|
|
|
1404
1405
|
const hashBytes = new Uint8Array(hashBuffer);
|
|
1405
1406
|
return bytesToHex(hashBytes);
|
|
1406
1407
|
}
|
|
1407
|
-
async function getEncryptionKey() {
|
|
1408
|
-
const
|
|
1408
|
+
async function getEncryptionKey(address) {
|
|
1409
|
+
const storageKey = getStorageKey(address);
|
|
1410
|
+
const keyHex = getStorageItem(storageKey);
|
|
1409
1411
|
if (!keyHex) {
|
|
1410
1412
|
throw new Error("Encryption key not found. Please sign in first.");
|
|
1411
1413
|
}
|
|
@@ -1418,8 +1420,8 @@ async function getEncryptionKey() {
|
|
|
1418
1420
|
["encrypt", "decrypt"]
|
|
1419
1421
|
);
|
|
1420
1422
|
}
|
|
1421
|
-
async function encryptData(plaintext) {
|
|
1422
|
-
const key = await getEncryptionKey();
|
|
1423
|
+
async function encryptData(plaintext, address) {
|
|
1424
|
+
const key = await getEncryptionKey(address);
|
|
1423
1425
|
const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
|
|
1424
1426
|
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1425
1427
|
const encryptedData = await crypto.subtle.encrypt(
|
|
@@ -1436,8 +1438,8 @@ async function encryptData(plaintext) {
|
|
|
1436
1438
|
combined.set(encryptedBytes, iv.length);
|
|
1437
1439
|
return bytesToHex(combined);
|
|
1438
1440
|
}
|
|
1439
|
-
async function decryptData(encryptedHex) {
|
|
1440
|
-
const key = await getEncryptionKey();
|
|
1441
|
+
async function decryptData(encryptedHex, address) {
|
|
1442
|
+
const key = await getEncryptionKey(address);
|
|
1441
1443
|
const combined = hexToBytes(encryptedHex);
|
|
1442
1444
|
const iv = combined.slice(0, 12);
|
|
1443
1445
|
const encryptedData = combined.slice(12);
|
|
@@ -1451,8 +1453,8 @@ async function decryptData(encryptedHex) {
|
|
|
1451
1453
|
);
|
|
1452
1454
|
return new TextDecoder().decode(decryptedData);
|
|
1453
1455
|
}
|
|
1454
|
-
async function decryptDataBytes(encryptedHex) {
|
|
1455
|
-
const key = await getEncryptionKey();
|
|
1456
|
+
async function decryptDataBytes(encryptedHex, address) {
|
|
1457
|
+
const key = await getEncryptionKey(address);
|
|
1456
1458
|
const combined = hexToBytes(encryptedHex);
|
|
1457
1459
|
const iv = combined.slice(0, 12);
|
|
1458
1460
|
const encryptedData = combined.slice(12);
|
|
@@ -1466,58 +1468,31 @@ async function decryptDataBytes(encryptedHex) {
|
|
|
1466
1468
|
);
|
|
1467
1469
|
return new Uint8Array(decryptedData);
|
|
1468
1470
|
}
|
|
1469
|
-
function
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
{ message: SIGN_MESSAGE },
|
|
1491
|
-
{
|
|
1492
|
-
address: wallets[0].address
|
|
1493
|
-
}
|
|
1494
|
-
);
|
|
1495
|
-
const encryptionKey = await deriveKeyFromSignature(signature);
|
|
1496
|
-
const stored = setStorageItem(SIGNATURE_STORAGE_KEY, encryptionKey);
|
|
1497
|
-
if (!stored) {
|
|
1498
|
-
throw new Error("Failed to store encryption key in localStorage");
|
|
1499
|
-
}
|
|
1500
|
-
} catch (error) {
|
|
1501
|
-
hasRequestedSignature.current = false;
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
};
|
|
1505
|
-
requestSignature();
|
|
1506
|
-
}, [
|
|
1507
|
-
authenticated,
|
|
1508
|
-
wallets.length > 0 ? wallets[0]?.address : null,
|
|
1509
|
-
signMessage
|
|
1510
|
-
]);
|
|
1511
|
-
useEffect2(() => {
|
|
1512
|
-
if (!authenticated) {
|
|
1513
|
-
hasRequestedSignature.current = false;
|
|
1514
|
-
hasCheckedStorage.current = false;
|
|
1515
|
-
}
|
|
1516
|
-
}, [authenticated]);
|
|
1471
|
+
function hasEncryptionKey(address) {
|
|
1472
|
+
const storageKey = getStorageKey(address);
|
|
1473
|
+
return getStorageItem(storageKey) !== null;
|
|
1474
|
+
}
|
|
1475
|
+
async function requestEncryptionKey(walletAddress, signMessage) {
|
|
1476
|
+
const storageKey = getStorageKey(walletAddress);
|
|
1477
|
+
const existingKey = getStorageItem(storageKey);
|
|
1478
|
+
if (existingKey) {
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
const signature = await signMessage(SIGN_MESSAGE);
|
|
1482
|
+
const encryptionKey = await deriveKeyFromSignature(signature);
|
|
1483
|
+
const stored = setStorageItem(storageKey, encryptionKey);
|
|
1484
|
+
if (!stored) {
|
|
1485
|
+
throw new Error("Failed to store encryption key in localStorage");
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
function useEncryption(signMessage) {
|
|
1489
|
+
return {
|
|
1490
|
+
requestEncryptionKey: (walletAddress) => requestEncryptionKey(walletAddress, signMessage)
|
|
1491
|
+
};
|
|
1517
1492
|
}
|
|
1518
1493
|
|
|
1519
1494
|
// src/react/useMemory.ts
|
|
1520
|
-
import { useCallback as useCallback2, useRef as
|
|
1495
|
+
import { useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
1521
1496
|
import { postApiV1ChatCompletions } from "@reverbia/sdk";
|
|
1522
1497
|
|
|
1523
1498
|
// src/lib/memory/service.ts
|
|
@@ -1816,7 +1791,7 @@ var generateEmbeddingForText = async (text, options = {}) => {
|
|
|
1816
1791
|
}
|
|
1817
1792
|
try {
|
|
1818
1793
|
if (!embeddingPipeline) {
|
|
1819
|
-
const { pipeline } = await import("./transformers.node-
|
|
1794
|
+
const { pipeline } = await import("./transformers.node-LUTOZWVQ.mjs");
|
|
1820
1795
|
embeddingPipeline = await pipeline("feature-extraction", model);
|
|
1821
1796
|
}
|
|
1822
1797
|
const output = await embeddingPipeline(text, {
|
|
@@ -1915,7 +1890,7 @@ function useMemory(options = {}) {
|
|
|
1915
1890
|
baseUrl = BASE_URL
|
|
1916
1891
|
} = options;
|
|
1917
1892
|
const embeddingModel = userEmbeddingModel === void 0 ? embeddingProvider === "local" ? DEFAULT_LOCAL_EMBEDDING_MODEL : DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
|
|
1918
|
-
const extractionInProgressRef =
|
|
1893
|
+
const extractionInProgressRef = useRef2(false);
|
|
1919
1894
|
const extractMemoriesFromMessage = useCallback2(
|
|
1920
1895
|
async (options2) => {
|
|
1921
1896
|
const { messages, model } = options2;
|
|
@@ -2222,22 +2197,22 @@ ${text}`;
|
|
|
2222
2197
|
}
|
|
2223
2198
|
|
|
2224
2199
|
// src/react/useModels.ts
|
|
2225
|
-
import { useCallback as useCallback4, useEffect as
|
|
2200
|
+
import { useCallback as useCallback4, useEffect as useEffect2, useRef as useRef3, useState as useState3 } from "react";
|
|
2226
2201
|
function useModels(options = {}) {
|
|
2227
2202
|
const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
|
|
2228
2203
|
const [models, setModels] = useState3([]);
|
|
2229
2204
|
const [isLoading, setIsLoading] = useState3(false);
|
|
2230
2205
|
const [error, setError] = useState3(null);
|
|
2231
|
-
const getTokenRef =
|
|
2232
|
-
const baseUrlRef =
|
|
2233
|
-
const providerRef =
|
|
2234
|
-
const abortControllerRef =
|
|
2235
|
-
|
|
2206
|
+
const getTokenRef = useRef3(getToken);
|
|
2207
|
+
const baseUrlRef = useRef3(baseUrl);
|
|
2208
|
+
const providerRef = useRef3(provider);
|
|
2209
|
+
const abortControllerRef = useRef3(null);
|
|
2210
|
+
useEffect2(() => {
|
|
2236
2211
|
getTokenRef.current = getToken;
|
|
2237
2212
|
baseUrlRef.current = baseUrl;
|
|
2238
2213
|
providerRef.current = provider;
|
|
2239
2214
|
});
|
|
2240
|
-
|
|
2215
|
+
useEffect2(() => {
|
|
2241
2216
|
return () => {
|
|
2242
2217
|
if (abortControllerRef.current) {
|
|
2243
2218
|
abortControllerRef.current.abort();
|
|
@@ -2307,8 +2282,8 @@ function useModels(options = {}) {
|
|
|
2307
2282
|
setModels([]);
|
|
2308
2283
|
await fetchModels();
|
|
2309
2284
|
}, [fetchModels]);
|
|
2310
|
-
const hasFetchedRef =
|
|
2311
|
-
|
|
2285
|
+
const hasFetchedRef = useRef3(false);
|
|
2286
|
+
useEffect2(() => {
|
|
2312
2287
|
if (autoFetch && !hasFetchedRef.current) {
|
|
2313
2288
|
hasFetchedRef.current = true;
|
|
2314
2289
|
fetchModels();
|
|
@@ -2326,15 +2301,15 @@ function useModels(options = {}) {
|
|
|
2326
2301
|
}
|
|
2327
2302
|
|
|
2328
2303
|
// src/react/useSearch.ts
|
|
2329
|
-
import { useCallback as useCallback5, useEffect as
|
|
2304
|
+
import { useCallback as useCallback5, useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
|
|
2330
2305
|
function useSearch(options = {}) {
|
|
2331
2306
|
const { getToken, baseUrl = BASE_URL, onError } = options;
|
|
2332
2307
|
const [isLoading, setIsLoading] = useState4(false);
|
|
2333
2308
|
const [results, setResults] = useState4(null);
|
|
2334
2309
|
const [response, setResponse] = useState4(null);
|
|
2335
2310
|
const [error, setError] = useState4(null);
|
|
2336
|
-
const abortControllerRef =
|
|
2337
|
-
|
|
2311
|
+
const abortControllerRef = useRef4(null);
|
|
2312
|
+
useEffect3(() => {
|
|
2338
2313
|
return () => {
|
|
2339
2314
|
if (abortControllerRef.current) {
|
|
2340
2315
|
abortControllerRef.current.abort();
|
|
@@ -2410,12 +2385,12 @@ function useSearch(options = {}) {
|
|
|
2410
2385
|
}
|
|
2411
2386
|
|
|
2412
2387
|
// src/react/useImageGeneration.ts
|
|
2413
|
-
import { useCallback as useCallback6, useEffect as
|
|
2388
|
+
import { useCallback as useCallback6, useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
|
|
2414
2389
|
function useImageGeneration(options = {}) {
|
|
2415
2390
|
const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
|
|
2416
2391
|
const [isLoading, setIsLoading] = useState5(false);
|
|
2417
|
-
const abortControllerRef =
|
|
2418
|
-
|
|
2392
|
+
const abortControllerRef = useRef5(null);
|
|
2393
|
+
useEffect4(() => {
|
|
2419
2394
|
return () => {
|
|
2420
2395
|
if (abortControllerRef.current) {
|
|
2421
2396
|
abortControllerRef.current.abort();
|
|
@@ -2554,6 +2529,8 @@ export {
|
|
|
2554
2529
|
executeTool,
|
|
2555
2530
|
extractConversationContext,
|
|
2556
2531
|
formatMemoriesForChat,
|
|
2532
|
+
hasEncryptionKey,
|
|
2533
|
+
requestEncryptionKey,
|
|
2557
2534
|
selectTool,
|
|
2558
2535
|
useChat,
|
|
2559
2536
|
useEncryption,
|