@reverbia/sdk 1.0.0-next.20260110155148 → 1.0.0-next.20260110210906
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.cjs +389 -16
- package/dist/react/index.d.mts +100 -1
- package/dist/react/index.d.ts +100 -1
- package/dist/react/index.mjs +415 -49
- package/package.json +1 -1
package/dist/react/index.mjs
CHANGED
|
@@ -2255,7 +2255,7 @@ function useEncryption(signMessage, embeddedWalletSigner) {
|
|
|
2255
2255
|
}
|
|
2256
2256
|
|
|
2257
2257
|
// src/react/useChatStorage.ts
|
|
2258
|
-
import { useCallback as useCallback2, useState as useState2, useMemo } from "react";
|
|
2258
|
+
import { useCallback as useCallback2, useState as useState2, useMemo, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
2259
2259
|
|
|
2260
2260
|
// src/lib/db/chat/schema.ts
|
|
2261
2261
|
import { appSchema, tableSchema } from "@nozbe/watermelondb";
|
|
@@ -2700,6 +2700,178 @@ async function searchMessagesOp(ctx, queryVector, options) {
|
|
|
2700
2700
|
return resultsWithSimilarity.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
2701
2701
|
}
|
|
2702
2702
|
|
|
2703
|
+
// src/lib/storage/opfs.ts
|
|
2704
|
+
var FILE_PLACEHOLDER_PREFIX = "__SDKFILE__";
|
|
2705
|
+
var FILE_PLACEHOLDER_SUFFIX = "__";
|
|
2706
|
+
var FILE_PLACEHOLDER_REGEX = /__SDKFILE__([a-f0-9-]+)__/g;
|
|
2707
|
+
function createFilePlaceholder(fileId) {
|
|
2708
|
+
return `${FILE_PLACEHOLDER_PREFIX}${fileId}${FILE_PLACEHOLDER_SUFFIX}`;
|
|
2709
|
+
}
|
|
2710
|
+
function extractFileIds(content) {
|
|
2711
|
+
const matches = content.matchAll(FILE_PLACEHOLDER_REGEX);
|
|
2712
|
+
return Array.from(matches, (m) => m[1]);
|
|
2713
|
+
}
|
|
2714
|
+
function isOPFSSupported() {
|
|
2715
|
+
return typeof navigator !== "undefined" && "storage" in navigator && "getDirectory" in navigator.storage;
|
|
2716
|
+
}
|
|
2717
|
+
async function getSDKDirectory() {
|
|
2718
|
+
const root = await navigator.storage.getDirectory();
|
|
2719
|
+
return root.getDirectoryHandle("reverbia-sdk-files", { create: true });
|
|
2720
|
+
}
|
|
2721
|
+
function bytesToHex2(bytes) {
|
|
2722
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2723
|
+
}
|
|
2724
|
+
function hexToBytes2(hex) {
|
|
2725
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
2726
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
2727
|
+
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
2728
|
+
}
|
|
2729
|
+
return bytes;
|
|
2730
|
+
}
|
|
2731
|
+
async function encryptBlob(blob, encryptionKey) {
|
|
2732
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
2733
|
+
const plaintext = new Uint8Array(arrayBuffer);
|
|
2734
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
2735
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
2736
|
+
{ name: "AES-GCM", iv },
|
|
2737
|
+
encryptionKey,
|
|
2738
|
+
plaintext
|
|
2739
|
+
);
|
|
2740
|
+
const combined = new Uint8Array(iv.length + ciphertext.byteLength);
|
|
2741
|
+
combined.set(iv, 0);
|
|
2742
|
+
combined.set(new Uint8Array(ciphertext), iv.length);
|
|
2743
|
+
return bytesToHex2(combined);
|
|
2744
|
+
}
|
|
2745
|
+
async function decryptToBytes(encryptedHex, encryptionKey) {
|
|
2746
|
+
const combined = hexToBytes2(encryptedHex);
|
|
2747
|
+
const iv = combined.slice(0, 12);
|
|
2748
|
+
const ciphertext = combined.slice(12);
|
|
2749
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
2750
|
+
{ name: "AES-GCM", iv },
|
|
2751
|
+
encryptionKey,
|
|
2752
|
+
ciphertext
|
|
2753
|
+
);
|
|
2754
|
+
return new Uint8Array(decrypted);
|
|
2755
|
+
}
|
|
2756
|
+
async function writeEncryptedFile(fileId, blob, encryptionKey, metadata) {
|
|
2757
|
+
if (!isOPFSSupported()) {
|
|
2758
|
+
throw new Error("OPFS is not supported in this browser");
|
|
2759
|
+
}
|
|
2760
|
+
const dir = await getSDKDirectory();
|
|
2761
|
+
const encryptedHex = await encryptBlob(blob, encryptionKey);
|
|
2762
|
+
const contentHandle = await dir.getFileHandle(`${fileId}.enc`, {
|
|
2763
|
+
create: true
|
|
2764
|
+
});
|
|
2765
|
+
const contentWritable = await contentHandle.createWritable();
|
|
2766
|
+
await contentWritable.write(encryptedHex);
|
|
2767
|
+
await contentWritable.close();
|
|
2768
|
+
const fileMetadata = {
|
|
2769
|
+
id: fileId,
|
|
2770
|
+
name: metadata?.name || `file-${fileId}`,
|
|
2771
|
+
type: blob.type || "application/octet-stream",
|
|
2772
|
+
size: blob.size,
|
|
2773
|
+
sourceUrl: metadata?.sourceUrl,
|
|
2774
|
+
createdAt: Date.now()
|
|
2775
|
+
};
|
|
2776
|
+
const metaHandle = await dir.getFileHandle(`${fileId}.meta.json`, {
|
|
2777
|
+
create: true
|
|
2778
|
+
});
|
|
2779
|
+
const metaWritable = await metaHandle.createWritable();
|
|
2780
|
+
await metaWritable.write(JSON.stringify(fileMetadata));
|
|
2781
|
+
await metaWritable.close();
|
|
2782
|
+
}
|
|
2783
|
+
async function readEncryptedFile(fileId, encryptionKey) {
|
|
2784
|
+
if (!isOPFSSupported()) {
|
|
2785
|
+
throw new Error("OPFS is not supported in this browser");
|
|
2786
|
+
}
|
|
2787
|
+
const dir = await getSDKDirectory();
|
|
2788
|
+
try {
|
|
2789
|
+
const contentHandle = await dir.getFileHandle(`${fileId}.enc`);
|
|
2790
|
+
const contentFile = await contentHandle.getFile();
|
|
2791
|
+
const encryptedHex = await contentFile.text();
|
|
2792
|
+
const metaHandle = await dir.getFileHandle(`${fileId}.meta.json`);
|
|
2793
|
+
const metaFile = await metaHandle.getFile();
|
|
2794
|
+
const metadata = JSON.parse(await metaFile.text());
|
|
2795
|
+
const decryptedBytes = await decryptToBytes(encryptedHex, encryptionKey);
|
|
2796
|
+
const blob = new Blob([decryptedBytes.buffer], { type: metadata.type });
|
|
2797
|
+
return { blob, metadata };
|
|
2798
|
+
} catch (error) {
|
|
2799
|
+
if (error instanceof DOMException && error.name === "NotFoundError") {
|
|
2800
|
+
return null;
|
|
2801
|
+
}
|
|
2802
|
+
throw error;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
async function deleteEncryptedFile(fileId) {
|
|
2806
|
+
if (!isOPFSSupported()) {
|
|
2807
|
+
throw new Error("OPFS is not supported in this browser");
|
|
2808
|
+
}
|
|
2809
|
+
const dir = await getSDKDirectory();
|
|
2810
|
+
try {
|
|
2811
|
+
await dir.removeEntry(`${fileId}.enc`);
|
|
2812
|
+
await dir.removeEntry(`${fileId}.meta.json`);
|
|
2813
|
+
} catch {
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
async function fileExists(fileId) {
|
|
2817
|
+
if (!isOPFSSupported()) {
|
|
2818
|
+
return false;
|
|
2819
|
+
}
|
|
2820
|
+
const dir = await getSDKDirectory();
|
|
2821
|
+
try {
|
|
2822
|
+
await dir.getFileHandle(`${fileId}.enc`);
|
|
2823
|
+
return true;
|
|
2824
|
+
} catch {
|
|
2825
|
+
return false;
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
var BlobUrlManager = class {
|
|
2829
|
+
constructor() {
|
|
2830
|
+
this.activeUrls = /* @__PURE__ */ new Map();
|
|
2831
|
+
}
|
|
2832
|
+
// fileId -> blobUrl
|
|
2833
|
+
/**
|
|
2834
|
+
* Creates a blob URL for a file and tracks it.
|
|
2835
|
+
*/
|
|
2836
|
+
createUrl(fileId, blob) {
|
|
2837
|
+
this.revokeUrl(fileId);
|
|
2838
|
+
const url = URL.createObjectURL(blob);
|
|
2839
|
+
this.activeUrls.set(fileId, url);
|
|
2840
|
+
return url;
|
|
2841
|
+
}
|
|
2842
|
+
/**
|
|
2843
|
+
* Gets the active blob URL for a file, if any.
|
|
2844
|
+
*/
|
|
2845
|
+
getUrl(fileId) {
|
|
2846
|
+
return this.activeUrls.get(fileId);
|
|
2847
|
+
}
|
|
2848
|
+
/**
|
|
2849
|
+
* Revokes a blob URL and removes it from tracking.
|
|
2850
|
+
*/
|
|
2851
|
+
revokeUrl(fileId) {
|
|
2852
|
+
const url = this.activeUrls.get(fileId);
|
|
2853
|
+
if (url) {
|
|
2854
|
+
URL.revokeObjectURL(url);
|
|
2855
|
+
this.activeUrls.delete(fileId);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
/**
|
|
2859
|
+
* Revokes all tracked blob URLs.
|
|
2860
|
+
*/
|
|
2861
|
+
revokeAll() {
|
|
2862
|
+
for (const url of this.activeUrls.values()) {
|
|
2863
|
+
URL.revokeObjectURL(url);
|
|
2864
|
+
}
|
|
2865
|
+
this.activeUrls.clear();
|
|
2866
|
+
}
|
|
2867
|
+
/**
|
|
2868
|
+
* Gets the count of active blob URLs.
|
|
2869
|
+
*/
|
|
2870
|
+
get size() {
|
|
2871
|
+
return this.activeUrls.size;
|
|
2872
|
+
}
|
|
2873
|
+
};
|
|
2874
|
+
|
|
2703
2875
|
// src/react/useChatStorage.ts
|
|
2704
2876
|
function replaceUrlWithMCPPlaceholder(content, url, fileId) {
|
|
2705
2877
|
const escapedUrl = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2721,20 +2893,69 @@ function replaceUrlWithMCPPlaceholder(content, url, fileId) {
|
|
|
2721
2893
|
function findFileIdBySourceUrl(files, sourceUrl) {
|
|
2722
2894
|
return files?.find((f) => f.sourceUrl === sourceUrl)?.id;
|
|
2723
2895
|
}
|
|
2724
|
-
function
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2896
|
+
async function isUrlValid(url, timeoutMs = 5e3) {
|
|
2897
|
+
try {
|
|
2898
|
+
const controller = new AbortController();
|
|
2899
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
2900
|
+
const response = await fetch(url, {
|
|
2901
|
+
method: "GET",
|
|
2902
|
+
headers: { Range: "bytes=0-0" },
|
|
2903
|
+
signal: controller.signal
|
|
2904
|
+
});
|
|
2905
|
+
clearTimeout(timeoutId);
|
|
2906
|
+
return response.ok || response.status === 206;
|
|
2907
|
+
} catch {
|
|
2908
|
+
return false;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
async function storedToLlmapiMessage(stored) {
|
|
2912
|
+
let textContent = stored.content;
|
|
2913
|
+
const fileUrlMap = /* @__PURE__ */ new Map();
|
|
2914
|
+
const imageParts = [];
|
|
2728
2915
|
if (stored.files?.length) {
|
|
2729
2916
|
for (const file of stored.files) {
|
|
2730
2917
|
if (file.url) {
|
|
2731
|
-
|
|
2918
|
+
imageParts.push({
|
|
2732
2919
|
type: "image_url",
|
|
2733
2920
|
image_url: { url: file.url }
|
|
2734
2921
|
});
|
|
2922
|
+
} else if (file.sourceUrl) {
|
|
2923
|
+
const isValid = await isUrlValid(file.sourceUrl);
|
|
2924
|
+
if (isValid) {
|
|
2925
|
+
imageParts.push({
|
|
2926
|
+
type: "image_url",
|
|
2927
|
+
image_url: { url: file.sourceUrl }
|
|
2928
|
+
});
|
|
2929
|
+
fileUrlMap.set(file.id, file.sourceUrl);
|
|
2930
|
+
}
|
|
2735
2931
|
}
|
|
2736
2932
|
}
|
|
2737
2933
|
}
|
|
2934
|
+
textContent = textContent.replace(
|
|
2935
|
+
/__SDKFILE__([a-f0-9-]+)__/g,
|
|
2936
|
+
(match, fileId) => {
|
|
2937
|
+
const sourceUrl = fileUrlMap.get(fileId);
|
|
2938
|
+
if (sourceUrl) {
|
|
2939
|
+
return ``;
|
|
2940
|
+
}
|
|
2941
|
+
return "";
|
|
2942
|
+
}
|
|
2943
|
+
);
|
|
2944
|
+
textContent = textContent.replace(
|
|
2945
|
+
/!\[MCP_IMAGE:([a-f0-9-]+)\]/g,
|
|
2946
|
+
(match, fileId) => {
|
|
2947
|
+
const sourceUrl = fileUrlMap.get(fileId);
|
|
2948
|
+
if (sourceUrl) {
|
|
2949
|
+
return ``;
|
|
2950
|
+
}
|
|
2951
|
+
return "";
|
|
2952
|
+
}
|
|
2953
|
+
);
|
|
2954
|
+
textContent = textContent.replace(/\n{3,}/g, "\n\n").trim();
|
|
2955
|
+
const content = [
|
|
2956
|
+
{ type: "text", text: textContent },
|
|
2957
|
+
...imageParts
|
|
2958
|
+
];
|
|
2738
2959
|
return {
|
|
2739
2960
|
role: stored.role,
|
|
2740
2961
|
content
|
|
@@ -2751,9 +2972,17 @@ function useChatStorage(options) {
|
|
|
2751
2972
|
onData,
|
|
2752
2973
|
onFinish,
|
|
2753
2974
|
onError,
|
|
2754
|
-
apiType
|
|
2975
|
+
apiType,
|
|
2976
|
+
walletAddress
|
|
2755
2977
|
} = options;
|
|
2756
2978
|
const [currentConversationId, setCurrentConversationId] = useState2(initialConversationId || null);
|
|
2979
|
+
const blobManagerRef = useRef2(new BlobUrlManager());
|
|
2980
|
+
useEffect2(() => {
|
|
2981
|
+
const manager = blobManagerRef.current;
|
|
2982
|
+
return () => {
|
|
2983
|
+
manager.revokeAll();
|
|
2984
|
+
};
|
|
2985
|
+
}, []);
|
|
2757
2986
|
const messagesCollection = useMemo(
|
|
2758
2987
|
() => database.get("history"),
|
|
2759
2988
|
[database]
|
|
@@ -2821,9 +3050,49 @@ function useChatStorage(options) {
|
|
|
2821
3050
|
);
|
|
2822
3051
|
const getMessages = useCallback2(
|
|
2823
3052
|
async (convId) => {
|
|
2824
|
-
|
|
3053
|
+
const messages = await getMessagesOp(storageCtx, convId);
|
|
3054
|
+
if (walletAddress && hasEncryptionKey(walletAddress) && isOPFSSupported()) {
|
|
3055
|
+
try {
|
|
3056
|
+
const encryptionKey = await getEncryptionKey(walletAddress);
|
|
3057
|
+
const blobManager = blobManagerRef.current;
|
|
3058
|
+
const resolvedMessages = await Promise.all(
|
|
3059
|
+
messages.map(async (msg) => {
|
|
3060
|
+
const fileIds = extractFileIds(msg.content);
|
|
3061
|
+
if (fileIds.length === 0) {
|
|
3062
|
+
return msg;
|
|
3063
|
+
}
|
|
3064
|
+
let resolvedContent = msg.content;
|
|
3065
|
+
for (const fileId of fileIds) {
|
|
3066
|
+
let url = blobManager.getUrl(fileId);
|
|
3067
|
+
if (!url) {
|
|
3068
|
+
const result = await readEncryptedFile(fileId, encryptionKey);
|
|
3069
|
+
if (result) {
|
|
3070
|
+
url = blobManager.createUrl(fileId, result.blob);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
if (url) {
|
|
3074
|
+
const placeholder = createFilePlaceholder(fileId);
|
|
3075
|
+
resolvedContent = resolvedContent.replace(
|
|
3076
|
+
new RegExp(
|
|
3077
|
+
placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"),
|
|
3078
|
+
"g"
|
|
3079
|
+
),
|
|
3080
|
+
``
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
return { ...msg, content: resolvedContent };
|
|
3085
|
+
})
|
|
3086
|
+
);
|
|
3087
|
+
return resolvedMessages;
|
|
3088
|
+
} catch (error) {
|
|
3089
|
+
console.error("[useChatStorage] Failed to resolve file placeholders:", error);
|
|
3090
|
+
return messages;
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
return messages;
|
|
2825
3094
|
},
|
|
2826
|
-
[storageCtx]
|
|
3095
|
+
[storageCtx, walletAddress]
|
|
2827
3096
|
);
|
|
2828
3097
|
const getMessageCount = useCallback2(
|
|
2829
3098
|
async (convId) => {
|
|
@@ -3047,6 +3316,89 @@ function useChatStorage(options) {
|
|
|
3047
3316
|
},
|
|
3048
3317
|
[]
|
|
3049
3318
|
);
|
|
3319
|
+
const extractAndStoreEncryptedMCPImages = useCallback2(
|
|
3320
|
+
async (content, address) => {
|
|
3321
|
+
try {
|
|
3322
|
+
if (!isOPFSSupported()) {
|
|
3323
|
+
console.warn("[extractAndStoreEncryptedMCPImages] OPFS not supported");
|
|
3324
|
+
return { processedFiles: [], cleanedContent: content };
|
|
3325
|
+
}
|
|
3326
|
+
if (!hasEncryptionKey(address)) {
|
|
3327
|
+
console.warn("[extractAndStoreEncryptedMCPImages] Encryption key not available");
|
|
3328
|
+
return { processedFiles: [], cleanedContent: content };
|
|
3329
|
+
}
|
|
3330
|
+
const MCP_IMAGE_URL_PATTERN = new RegExp(
|
|
3331
|
+
`https://${MCP_R2_DOMAIN.replace(/\./g, "\\.")}[^\\s)]*`,
|
|
3332
|
+
"g"
|
|
3333
|
+
);
|
|
3334
|
+
const urlMatches = content.match(MCP_IMAGE_URL_PATTERN);
|
|
3335
|
+
if (!urlMatches || urlMatches.length === 0) {
|
|
3336
|
+
return { processedFiles: [], cleanedContent: content };
|
|
3337
|
+
}
|
|
3338
|
+
const uniqueUrls = [...new Set(urlMatches)];
|
|
3339
|
+
const encryptionKey = await getEncryptionKey(address);
|
|
3340
|
+
const processedFiles = [];
|
|
3341
|
+
let cleanedContent = content;
|
|
3342
|
+
const results = await Promise.allSettled(
|
|
3343
|
+
uniqueUrls.map(async (imageUrl) => {
|
|
3344
|
+
const controller = new AbortController();
|
|
3345
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
3346
|
+
try {
|
|
3347
|
+
const response = await fetch(imageUrl, {
|
|
3348
|
+
signal: controller.signal,
|
|
3349
|
+
cache: "no-store"
|
|
3350
|
+
});
|
|
3351
|
+
if (!response.ok) {
|
|
3352
|
+
throw new Error(`Failed to fetch image: ${response.status}`);
|
|
3353
|
+
}
|
|
3354
|
+
const blob = await response.blob();
|
|
3355
|
+
const fileId = crypto.randomUUID();
|
|
3356
|
+
const urlPath = imageUrl.split("?")[0] ?? imageUrl;
|
|
3357
|
+
const extension = urlPath.match(/\.([a-zA-Z0-9]+)$/)?.[1] || "png";
|
|
3358
|
+
const mimeType = blob.type || `image/${extension}`;
|
|
3359
|
+
const fileName = `mcp-image-${Date.now()}-${fileId.slice(0, 8)}.${extension}`;
|
|
3360
|
+
await writeEncryptedFile(fileId, blob, encryptionKey, {
|
|
3361
|
+
name: fileName,
|
|
3362
|
+
sourceUrl: imageUrl
|
|
3363
|
+
});
|
|
3364
|
+
return { fileId, fileName, mimeType, size: blob.size, imageUrl };
|
|
3365
|
+
} finally {
|
|
3366
|
+
clearTimeout(timeoutId);
|
|
3367
|
+
}
|
|
3368
|
+
})
|
|
3369
|
+
);
|
|
3370
|
+
results.forEach((result, i) => {
|
|
3371
|
+
const imageUrl = uniqueUrls[i];
|
|
3372
|
+
if (result.status === "fulfilled") {
|
|
3373
|
+
const { fileId, fileName, mimeType, size } = result.value;
|
|
3374
|
+
processedFiles.push({
|
|
3375
|
+
id: fileId,
|
|
3376
|
+
name: fileName,
|
|
3377
|
+
type: mimeType,
|
|
3378
|
+
size,
|
|
3379
|
+
sourceUrl: imageUrl
|
|
3380
|
+
});
|
|
3381
|
+
const placeholder = createFilePlaceholder(fileId);
|
|
3382
|
+
const escapedUrl = imageUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3383
|
+
const markdownImagePattern = new RegExp(
|
|
3384
|
+
`!\\[[^\\]]*\\]\\([\\s]*${escapedUrl}[\\s]*\\)`,
|
|
3385
|
+
"g"
|
|
3386
|
+
);
|
|
3387
|
+
cleanedContent = cleanedContent.replace(markdownImagePattern, placeholder);
|
|
3388
|
+
cleanedContent = cleanedContent.replace(new RegExp(escapedUrl, "g"), placeholder);
|
|
3389
|
+
} else {
|
|
3390
|
+
console.error("[extractAndStoreEncryptedMCPImages] Failed:", result.reason);
|
|
3391
|
+
}
|
|
3392
|
+
});
|
|
3393
|
+
cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n").trim();
|
|
3394
|
+
return { processedFiles, cleanedContent };
|
|
3395
|
+
} catch (err) {
|
|
3396
|
+
console.error("[extractAndStoreEncryptedMCPImages] Unexpected error:", err);
|
|
3397
|
+
return { processedFiles: [], cleanedContent: content };
|
|
3398
|
+
}
|
|
3399
|
+
},
|
|
3400
|
+
[]
|
|
3401
|
+
);
|
|
3050
3402
|
const sendMessage = useCallback2(
|
|
3051
3403
|
async (args) => {
|
|
3052
3404
|
const {
|
|
@@ -3098,10 +3450,10 @@ function useChatStorage(options) {
|
|
|
3098
3450
|
const storedMessages = await getMessages(convId);
|
|
3099
3451
|
const validMessages = storedMessages.filter((msg) => !msg.error);
|
|
3100
3452
|
const limitedMessages = validMessages.slice(-maxHistoryMessages);
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
];
|
|
3453
|
+
const historyMessages = await Promise.all(
|
|
3454
|
+
limitedMessages.map(storedToLlmapiMessage)
|
|
3455
|
+
);
|
|
3456
|
+
messagesToSend = [...historyMessages, ...messages];
|
|
3105
3457
|
} else {
|
|
3106
3458
|
messagesToSend = [...messages];
|
|
3107
3459
|
}
|
|
@@ -3239,7 +3591,14 @@ function useChatStorage(options) {
|
|
|
3239
3591
|
let cleanedContent = assistantContent.replace(jsonSourcesBlockRegex, "").trim();
|
|
3240
3592
|
cleanedContent = cleanedContent.replace(/\n{3,}/g, "\n\n");
|
|
3241
3593
|
let processedFiles = [];
|
|
3242
|
-
if (
|
|
3594
|
+
if (walletAddress) {
|
|
3595
|
+
const result2 = await extractAndStoreEncryptedMCPImages(
|
|
3596
|
+
cleanedContent,
|
|
3597
|
+
walletAddress
|
|
3598
|
+
);
|
|
3599
|
+
processedFiles = result2.processedFiles;
|
|
3600
|
+
cleanedContent = result2.cleanedContent;
|
|
3601
|
+
} else if (writeFile) {
|
|
3243
3602
|
const result2 = await extractAndStoreMCPImages(
|
|
3244
3603
|
cleanedContent,
|
|
3245
3604
|
writeFile
|
|
@@ -3275,7 +3634,7 @@ function useChatStorage(options) {
|
|
|
3275
3634
|
assistantMessage: storedAssistantMessage
|
|
3276
3635
|
};
|
|
3277
3636
|
},
|
|
3278
|
-
[ensureConversation, getMessages, storageCtx, baseSendMessage]
|
|
3637
|
+
[ensureConversation, getMessages, storageCtx, baseSendMessage, walletAddress, extractAndStoreEncryptedMCPImages]
|
|
3279
3638
|
);
|
|
3280
3639
|
const searchMessages = useCallback2(
|
|
3281
3640
|
async (queryVector, options2) => {
|
|
@@ -3598,7 +3957,7 @@ var sdkModelClasses = [
|
|
|
3598
3957
|
];
|
|
3599
3958
|
|
|
3600
3959
|
// src/react/useMemoryStorage.ts
|
|
3601
|
-
import { useCallback as useCallback3, useState as useState3, useMemo as useMemo2, useRef as
|
|
3960
|
+
import { useCallback as useCallback3, useState as useState3, useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
3602
3961
|
import { postApiV1ChatCompletions } from "@reverbia/sdk";
|
|
3603
3962
|
|
|
3604
3963
|
// src/lib/db/memory/schema.ts
|
|
@@ -4390,7 +4749,7 @@ function useMemoryStorage(options) {
|
|
|
4390
4749
|
} = options;
|
|
4391
4750
|
const embeddingModel = userEmbeddingModel === void 0 ? DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
|
|
4392
4751
|
const [memories, setMemories] = useState3([]);
|
|
4393
|
-
const extractionInProgressRef =
|
|
4752
|
+
const extractionInProgressRef = useRef3(false);
|
|
4394
4753
|
const memoriesCollection = useMemo2(
|
|
4395
4754
|
() => database.get("memories"),
|
|
4396
4755
|
[database]
|
|
@@ -4914,7 +5273,7 @@ function useMemoryStorage(options) {
|
|
|
4914
5273
|
|
|
4915
5274
|
// src/react/useSettings.ts
|
|
4916
5275
|
import { Q as Q5 } from "@nozbe/watermelondb";
|
|
4917
|
-
import { useCallback as useCallback4, useState as useState4, useMemo as useMemo3, useEffect as
|
|
5276
|
+
import { useCallback as useCallback4, useState as useState4, useMemo as useMemo3, useEffect as useEffect3 } from "react";
|
|
4918
5277
|
|
|
4919
5278
|
// src/lib/db/settings/schema.ts
|
|
4920
5279
|
import { appSchema as appSchema4, tableSchema as tableSchema4 } from "@nozbe/watermelondb";
|
|
@@ -5391,7 +5750,7 @@ function useSettings(options) {
|
|
|
5391
5750
|
},
|
|
5392
5751
|
[storageCtx, walletAddress]
|
|
5393
5752
|
);
|
|
5394
|
-
|
|
5753
|
+
useEffect3(() => {
|
|
5395
5754
|
if (!walletAddress) {
|
|
5396
5755
|
setModelPreferenceState(null);
|
|
5397
5756
|
setUserPreferenceState(null);
|
|
@@ -5429,7 +5788,7 @@ function useSettings(options) {
|
|
|
5429
5788
|
cancelled = true;
|
|
5430
5789
|
};
|
|
5431
5790
|
}, [walletAddress, storageCtx, legacyStorageCtx]);
|
|
5432
|
-
|
|
5791
|
+
useEffect3(() => {
|
|
5433
5792
|
if (!walletAddress) return;
|
|
5434
5793
|
const subscription = userPreferencesCollection.query(Q5.where("wallet_address", walletAddress)).observeWithColumns([
|
|
5435
5794
|
"nickname",
|
|
@@ -5670,22 +6029,22 @@ ${text5}`;
|
|
|
5670
6029
|
}
|
|
5671
6030
|
|
|
5672
6031
|
// src/react/useModels.ts
|
|
5673
|
-
import { useCallback as useCallback7, useEffect as
|
|
6032
|
+
import { useCallback as useCallback7, useEffect as useEffect4, useRef as useRef4, useState as useState7 } from "react";
|
|
5674
6033
|
function useModels(options = {}) {
|
|
5675
6034
|
const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
|
|
5676
6035
|
const [models, setModels] = useState7([]);
|
|
5677
6036
|
const [isLoading, setIsLoading] = useState7(false);
|
|
5678
6037
|
const [error, setError] = useState7(null);
|
|
5679
|
-
const getTokenRef =
|
|
5680
|
-
const baseUrlRef =
|
|
5681
|
-
const providerRef =
|
|
5682
|
-
const abortControllerRef =
|
|
5683
|
-
|
|
6038
|
+
const getTokenRef = useRef4(getToken);
|
|
6039
|
+
const baseUrlRef = useRef4(baseUrl);
|
|
6040
|
+
const providerRef = useRef4(provider);
|
|
6041
|
+
const abortControllerRef = useRef4(null);
|
|
6042
|
+
useEffect4(() => {
|
|
5684
6043
|
getTokenRef.current = getToken;
|
|
5685
6044
|
baseUrlRef.current = baseUrl;
|
|
5686
6045
|
providerRef.current = provider;
|
|
5687
6046
|
});
|
|
5688
|
-
|
|
6047
|
+
useEffect4(() => {
|
|
5689
6048
|
return () => {
|
|
5690
6049
|
if (abortControllerRef.current) {
|
|
5691
6050
|
abortControllerRef.current.abort();
|
|
@@ -5755,8 +6114,8 @@ function useModels(options = {}) {
|
|
|
5755
6114
|
setModels([]);
|
|
5756
6115
|
await fetchModels();
|
|
5757
6116
|
}, [fetchModels]);
|
|
5758
|
-
const hasFetchedRef =
|
|
5759
|
-
|
|
6117
|
+
const hasFetchedRef = useRef4(false);
|
|
6118
|
+
useEffect4(() => {
|
|
5760
6119
|
if (autoFetch && !hasFetchedRef.current) {
|
|
5761
6120
|
hasFetchedRef.current = true;
|
|
5762
6121
|
fetchModels();
|
|
@@ -5774,15 +6133,15 @@ function useModels(options = {}) {
|
|
|
5774
6133
|
}
|
|
5775
6134
|
|
|
5776
6135
|
// src/react/useSearch.ts
|
|
5777
|
-
import { useCallback as useCallback8, useEffect as
|
|
6136
|
+
import { useCallback as useCallback8, useEffect as useEffect5, useRef as useRef5, useState as useState8 } from "react";
|
|
5778
6137
|
function useSearch(options = {}) {
|
|
5779
6138
|
const { getToken, baseUrl = BASE_URL, onError } = options;
|
|
5780
6139
|
const [isLoading, setIsLoading] = useState8(false);
|
|
5781
6140
|
const [results, setResults] = useState8(null);
|
|
5782
6141
|
const [response, setResponse] = useState8(null);
|
|
5783
6142
|
const [error, setError] = useState8(null);
|
|
5784
|
-
const abortControllerRef =
|
|
5785
|
-
|
|
6143
|
+
const abortControllerRef = useRef5(null);
|
|
6144
|
+
useEffect5(() => {
|
|
5786
6145
|
return () => {
|
|
5787
6146
|
if (abortControllerRef.current) {
|
|
5788
6147
|
abortControllerRef.current.abort();
|
|
@@ -5858,12 +6217,12 @@ function useSearch(options = {}) {
|
|
|
5858
6217
|
}
|
|
5859
6218
|
|
|
5860
6219
|
// src/react/useImageGeneration.ts
|
|
5861
|
-
import { useCallback as useCallback9, useEffect as
|
|
6220
|
+
import { useCallback as useCallback9, useEffect as useEffect6, useRef as useRef6, useState as useState9 } from "react";
|
|
5862
6221
|
function useImageGeneration(options = {}) {
|
|
5863
6222
|
const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
|
|
5864
6223
|
const [isLoading, setIsLoading] = useState9(false);
|
|
5865
|
-
const abortControllerRef =
|
|
5866
|
-
|
|
6224
|
+
const abortControllerRef = useRef6(null);
|
|
6225
|
+
useEffect6(() => {
|
|
5867
6226
|
return () => {
|
|
5868
6227
|
if (abortControllerRef.current) {
|
|
5869
6228
|
abortControllerRef.current.abort();
|
|
@@ -6245,7 +6604,7 @@ import {
|
|
|
6245
6604
|
createElement,
|
|
6246
6605
|
useCallback as useCallback10,
|
|
6247
6606
|
useContext,
|
|
6248
|
-
useEffect as
|
|
6607
|
+
useEffect as useEffect7,
|
|
6249
6608
|
useState as useState10
|
|
6250
6609
|
} from "react";
|
|
6251
6610
|
|
|
@@ -6576,7 +6935,7 @@ function DropboxAuthProvider({
|
|
|
6576
6935
|
}) {
|
|
6577
6936
|
const [accessToken, setAccessToken] = useState10(null);
|
|
6578
6937
|
const isConfigured = !!appKey;
|
|
6579
|
-
|
|
6938
|
+
useEffect7(() => {
|
|
6580
6939
|
const checkStoredToken = async () => {
|
|
6581
6940
|
if (walletAddress) {
|
|
6582
6941
|
await migrateUnencryptedTokens("dropbox", walletAddress);
|
|
@@ -6590,7 +6949,7 @@ function DropboxAuthProvider({
|
|
|
6590
6949
|
};
|
|
6591
6950
|
checkStoredToken();
|
|
6592
6951
|
}, [apiClient, walletAddress]);
|
|
6593
|
-
|
|
6952
|
+
useEffect7(() => {
|
|
6594
6953
|
if (!isConfigured) return;
|
|
6595
6954
|
const handleCallback = async () => {
|
|
6596
6955
|
if (isDropboxCallback()) {
|
|
@@ -6762,7 +7121,7 @@ import {
|
|
|
6762
7121
|
createElement as createElement2,
|
|
6763
7122
|
useCallback as useCallback12,
|
|
6764
7123
|
useContext as useContext2,
|
|
6765
|
-
useEffect as
|
|
7124
|
+
useEffect as useEffect8,
|
|
6766
7125
|
useState as useState11
|
|
6767
7126
|
} from "react";
|
|
6768
7127
|
|
|
@@ -6996,7 +7355,7 @@ function GoogleDriveAuthProvider({
|
|
|
6996
7355
|
}) {
|
|
6997
7356
|
const [accessToken, setAccessToken] = useState11(null);
|
|
6998
7357
|
const isConfigured = !!clientId;
|
|
6999
|
-
|
|
7358
|
+
useEffect8(() => {
|
|
7000
7359
|
const checkStoredToken = async () => {
|
|
7001
7360
|
if (walletAddress) {
|
|
7002
7361
|
await migrateUnencryptedTokens("google-drive", walletAddress);
|
|
@@ -7010,7 +7369,7 @@ function GoogleDriveAuthProvider({
|
|
|
7010
7369
|
};
|
|
7011
7370
|
checkStoredToken();
|
|
7012
7371
|
}, [apiClient, walletAddress]);
|
|
7013
|
-
|
|
7372
|
+
useEffect8(() => {
|
|
7014
7373
|
if (!isConfigured) return;
|
|
7015
7374
|
const handleCallback = async () => {
|
|
7016
7375
|
if (isGoogleDriveCallback()) {
|
|
@@ -7493,7 +7852,7 @@ import {
|
|
|
7493
7852
|
createElement as createElement3,
|
|
7494
7853
|
useCallback as useCallback14,
|
|
7495
7854
|
useContext as useContext3,
|
|
7496
|
-
useEffect as
|
|
7855
|
+
useEffect as useEffect9,
|
|
7497
7856
|
useState as useState12
|
|
7498
7857
|
} from "react";
|
|
7499
7858
|
|
|
@@ -7743,7 +8102,7 @@ function ICloudAuthProvider({
|
|
|
7743
8102
|
const [isAvailable, setIsAvailable] = useState12(false);
|
|
7744
8103
|
const [isConfigured, setIsConfigured] = useState12(false);
|
|
7745
8104
|
const [isLoading, setIsLoading] = useState12(false);
|
|
7746
|
-
|
|
8105
|
+
useEffect9(() => {
|
|
7747
8106
|
if (!apiToken || typeof window === "undefined") {
|
|
7748
8107
|
return;
|
|
7749
8108
|
}
|
|
@@ -8059,7 +8418,7 @@ import {
|
|
|
8059
8418
|
createElement as createElement4,
|
|
8060
8419
|
useCallback as useCallback16,
|
|
8061
8420
|
useContext as useContext4,
|
|
8062
|
-
useEffect as
|
|
8421
|
+
useEffect as useEffect10,
|
|
8063
8422
|
useState as useState13
|
|
8064
8423
|
} from "react";
|
|
8065
8424
|
var BackupAuthContext = createContext4(null);
|
|
@@ -8083,7 +8442,7 @@ function BackupAuthProvider({
|
|
|
8083
8442
|
const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
|
|
8084
8443
|
const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
|
|
8085
8444
|
const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
|
|
8086
|
-
|
|
8445
|
+
useEffect10(() => {
|
|
8087
8446
|
const checkStoredTokens = async () => {
|
|
8088
8447
|
if (walletAddress) {
|
|
8089
8448
|
await Promise.all([
|
|
@@ -8106,7 +8465,7 @@ function BackupAuthProvider({
|
|
|
8106
8465
|
};
|
|
8107
8466
|
checkStoredTokens();
|
|
8108
8467
|
}, [apiClient, walletAddress]);
|
|
8109
|
-
|
|
8468
|
+
useEffect10(() => {
|
|
8110
8469
|
if (!icloudApiToken || typeof window === "undefined") {
|
|
8111
8470
|
return;
|
|
8112
8471
|
}
|
|
@@ -8134,7 +8493,7 @@ function BackupAuthProvider({
|
|
|
8134
8493
|
};
|
|
8135
8494
|
initCloudKit();
|
|
8136
8495
|
}, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
|
|
8137
|
-
|
|
8496
|
+
useEffect10(() => {
|
|
8138
8497
|
if (!isDropboxConfigured) return;
|
|
8139
8498
|
const handleCallback = async () => {
|
|
8140
8499
|
if (isDropboxCallback()) {
|
|
@@ -8154,7 +8513,7 @@ function BackupAuthProvider({
|
|
|
8154
8513
|
};
|
|
8155
8514
|
handleCallback();
|
|
8156
8515
|
}, [dropboxCallbackPath, isDropboxConfigured, apiClient, walletAddress]);
|
|
8157
|
-
|
|
8516
|
+
useEffect10(() => {
|
|
8158
8517
|
if (!isGoogleConfigured) return;
|
|
8159
8518
|
const handleCallback = async () => {
|
|
8160
8519
|
if (isGoogleDriveCallback()) {
|
|
@@ -9082,6 +9441,7 @@ export {
|
|
|
9082
9441
|
DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
|
|
9083
9442
|
DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
|
|
9084
9443
|
BackupAuthProvider,
|
|
9444
|
+
BlobUrlManager,
|
|
9085
9445
|
Conversation as ChatConversation,
|
|
9086
9446
|
Message as ChatMessage,
|
|
9087
9447
|
DEFAULT_BACKUP_FOLDER,
|
|
@@ -9111,9 +9471,11 @@ export {
|
|
|
9111
9471
|
createMemoryContextSystemMessage,
|
|
9112
9472
|
decryptData,
|
|
9113
9473
|
decryptDataBytes,
|
|
9474
|
+
deleteEncryptedFile,
|
|
9114
9475
|
encryptData,
|
|
9115
9476
|
exportPublicKey,
|
|
9116
9477
|
extractConversationContext,
|
|
9478
|
+
fileExists,
|
|
9117
9479
|
findFileIdBySourceUrl,
|
|
9118
9480
|
formatMemoriesForChat,
|
|
9119
9481
|
generateCompositeKey,
|
|
@@ -9125,6 +9487,7 @@ export {
|
|
|
9125
9487
|
getAndClearDriveReturnUrl,
|
|
9126
9488
|
getCalendarAccessToken,
|
|
9127
9489
|
getDriveAccessToken,
|
|
9490
|
+
getEncryptionKey,
|
|
9128
9491
|
getGoogleDriveStoredToken,
|
|
9129
9492
|
getValidCalendarToken,
|
|
9130
9493
|
getValidDriveToken,
|
|
@@ -9139,7 +9502,9 @@ export {
|
|
|
9139
9502
|
hasKeyPair,
|
|
9140
9503
|
isCalendarCallback,
|
|
9141
9504
|
isDriveCallback,
|
|
9505
|
+
isOPFSSupported,
|
|
9142
9506
|
memoryStorageSchema,
|
|
9507
|
+
readEncryptedFile,
|
|
9143
9508
|
refreshCalendarToken,
|
|
9144
9509
|
refreshDriveToken,
|
|
9145
9510
|
replaceUrlWithMCPPlaceholder,
|
|
@@ -9177,5 +9542,6 @@ export {
|
|
|
9177
9542
|
usePdf,
|
|
9178
9543
|
useSearch,
|
|
9179
9544
|
useSettings,
|
|
9180
|
-
userPreferencesStorageSchema
|
|
9545
|
+
userPreferencesStorageSchema,
|
|
9546
|
+
writeEncryptedFile
|
|
9181
9547
|
};
|