@reverbia/sdk 1.0.0-next.20251219092050 → 1.0.0-next.20251219154503
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 +760 -27
- package/dist/react/index.d.mts +347 -8
- package/dist/react/index.d.ts +347 -8
- package/dist/react/index.mjs +760 -27
- package/package.json +1 -1
package/dist/react/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ var index_exports = {};
|
|
|
40
40
|
__export(index_exports, {
|
|
41
41
|
BACKUP_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
|
|
42
42
|
BACKUP_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
|
|
43
|
+
BACKUP_ICLOUD_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
|
|
43
44
|
BackupAuthProvider: () => BackupAuthProvider,
|
|
44
45
|
ChatConversation: () => Conversation,
|
|
45
46
|
ChatMessage: () => Message,
|
|
@@ -47,15 +48,18 @@ __export(index_exports, {
|
|
|
47
48
|
DEFAULT_DRIVE_CONVERSATIONS_FOLDER: () => DEFAULT_CONVERSATIONS_FOLDER,
|
|
48
49
|
DEFAULT_DRIVE_ROOT_FOLDER: () => DEFAULT_ROOT_FOLDER,
|
|
49
50
|
DEFAULT_DROPBOX_FOLDER: () => DEFAULT_BACKUP_FOLDER,
|
|
51
|
+
DEFAULT_ICLOUD_BACKUP_FOLDER: () => DEFAULT_BACKUP_FOLDER2,
|
|
50
52
|
DEFAULT_TOOL_SELECTOR_MODEL: () => DEFAULT_TOOL_SELECTOR_MODEL,
|
|
51
53
|
DropboxAuthProvider: () => DropboxAuthProvider,
|
|
52
54
|
GoogleDriveAuthProvider: () => GoogleDriveAuthProvider,
|
|
55
|
+
ICloudAuthProvider: () => ICloudAuthProvider,
|
|
53
56
|
StoredMemoryModel: () => Memory,
|
|
54
57
|
StoredModelPreferenceModel: () => ModelPreference,
|
|
55
58
|
chatStorageMigrations: () => chatStorageMigrations,
|
|
56
59
|
chatStorageSchema: () => chatStorageSchema,
|
|
57
60
|
clearDropboxToken: () => clearToken,
|
|
58
61
|
clearGoogleDriveToken: () => clearGoogleDriveToken,
|
|
62
|
+
clearICloudAuth: () => clearICloudAuth,
|
|
59
63
|
createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
|
|
60
64
|
decryptData: () => decryptData,
|
|
61
65
|
decryptDataBytes: () => decryptDataBytes,
|
|
@@ -70,6 +74,7 @@ __export(index_exports, {
|
|
|
70
74
|
hasDropboxCredentials: () => hasDropboxCredentials,
|
|
71
75
|
hasEncryptionKey: () => hasEncryptionKey,
|
|
72
76
|
hasGoogleDriveCredentials: () => hasGoogleDriveCredentials,
|
|
77
|
+
hasICloudCredentials: () => hasICloudCredentials,
|
|
73
78
|
memoryStorageSchema: () => memoryStorageSchema,
|
|
74
79
|
requestEncryptionKey: () => requestEncryptionKey,
|
|
75
80
|
sdkMigrations: () => sdkMigrations,
|
|
@@ -86,6 +91,8 @@ __export(index_exports, {
|
|
|
86
91
|
useEncryption: () => useEncryption,
|
|
87
92
|
useGoogleDriveAuth: () => useGoogleDriveAuth,
|
|
88
93
|
useGoogleDriveBackup: () => useGoogleDriveBackup,
|
|
94
|
+
useICloudAuth: () => useICloudAuth,
|
|
95
|
+
useICloudBackup: () => useICloudBackup,
|
|
89
96
|
useImageGeneration: () => useImageGeneration,
|
|
90
97
|
useMemoryStorage: () => useMemoryStorage,
|
|
91
98
|
useModels: () => useModels,
|
|
@@ -5641,22 +5648,588 @@ function useGoogleDriveBackup(options) {
|
|
|
5641
5648
|
};
|
|
5642
5649
|
}
|
|
5643
5650
|
|
|
5644
|
-
// src/react/
|
|
5651
|
+
// src/react/useICloudAuth.ts
|
|
5645
5652
|
var import_react14 = require("react");
|
|
5646
|
-
|
|
5653
|
+
|
|
5654
|
+
// src/lib/backup/icloud/api.ts
|
|
5655
|
+
var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
|
|
5656
|
+
var DEFAULT_BACKUP_FOLDER2 = "conversations";
|
|
5657
|
+
var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
|
|
5658
|
+
var RECORD_TYPE = "ConversationBackup";
|
|
5659
|
+
var cloudKitLoadPromise = null;
|
|
5660
|
+
function isCloudKitAvailable() {
|
|
5661
|
+
return typeof window !== "undefined" && !!window.CloudKit;
|
|
5662
|
+
}
|
|
5663
|
+
async function loadCloudKit() {
|
|
5664
|
+
if (typeof window === "undefined") {
|
|
5665
|
+
throw new Error("CloudKit JS can only be loaded in browser environment");
|
|
5666
|
+
}
|
|
5667
|
+
if (window.CloudKit) {
|
|
5668
|
+
return;
|
|
5669
|
+
}
|
|
5670
|
+
if (cloudKitLoadPromise) {
|
|
5671
|
+
return cloudKitLoadPromise;
|
|
5672
|
+
}
|
|
5673
|
+
cloudKitLoadPromise = new Promise((resolve, reject) => {
|
|
5674
|
+
const script = document.createElement("script");
|
|
5675
|
+
script.src = CLOUDKIT_JS_URL;
|
|
5676
|
+
script.async = true;
|
|
5677
|
+
script.onload = () => {
|
|
5678
|
+
if (window.CloudKit) {
|
|
5679
|
+
resolve();
|
|
5680
|
+
} else {
|
|
5681
|
+
reject(new Error("CloudKit JS loaded but CloudKit object not found"));
|
|
5682
|
+
}
|
|
5683
|
+
};
|
|
5684
|
+
script.onerror = () => {
|
|
5685
|
+
cloudKitLoadPromise = null;
|
|
5686
|
+
reject(new Error("Failed to load CloudKit JS"));
|
|
5687
|
+
};
|
|
5688
|
+
document.head.appendChild(script);
|
|
5689
|
+
});
|
|
5690
|
+
return cloudKitLoadPromise;
|
|
5691
|
+
}
|
|
5692
|
+
async function ensureCloudKitLoaded() {
|
|
5693
|
+
if (!isCloudKitAvailable()) {
|
|
5694
|
+
await loadCloudKit();
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
async function configureCloudKit(config) {
|
|
5698
|
+
await ensureCloudKitLoaded();
|
|
5699
|
+
ensureAuthElements();
|
|
5700
|
+
window.CloudKit.configure({
|
|
5701
|
+
containers: [
|
|
5702
|
+
{
|
|
5703
|
+
containerIdentifier: config.containerIdentifier,
|
|
5704
|
+
apiTokenAuth: {
|
|
5705
|
+
apiToken: config.apiToken,
|
|
5706
|
+
persist: true,
|
|
5707
|
+
signInButton: {
|
|
5708
|
+
id: "apple-sign-in-button",
|
|
5709
|
+
theme: "black"
|
|
5710
|
+
},
|
|
5711
|
+
signOutButton: {
|
|
5712
|
+
id: "apple-sign-out-button",
|
|
5713
|
+
theme: "black"
|
|
5714
|
+
}
|
|
5715
|
+
},
|
|
5716
|
+
environment: config.environment
|
|
5717
|
+
}
|
|
5718
|
+
]
|
|
5719
|
+
});
|
|
5720
|
+
}
|
|
5721
|
+
async function getContainer() {
|
|
5722
|
+
await ensureCloudKitLoaded();
|
|
5723
|
+
return window.CloudKit.getDefaultContainer();
|
|
5724
|
+
}
|
|
5725
|
+
function ensureAuthElements() {
|
|
5726
|
+
let signInButton = document.getElementById("apple-sign-in-button");
|
|
5727
|
+
let signOutButton = document.getElementById("apple-sign-out-button");
|
|
5728
|
+
if (!signInButton) {
|
|
5729
|
+
signInButton = document.createElement("div");
|
|
5730
|
+
signInButton.id = "apple-sign-in-button";
|
|
5731
|
+
signInButton.style.position = "fixed";
|
|
5732
|
+
signInButton.style.top = "-9999px";
|
|
5733
|
+
signInButton.style.left = "-9999px";
|
|
5734
|
+
document.body.appendChild(signInButton);
|
|
5735
|
+
}
|
|
5736
|
+
if (!signOutButton) {
|
|
5737
|
+
signOutButton = document.createElement("div");
|
|
5738
|
+
signOutButton.id = "apple-sign-out-button";
|
|
5739
|
+
signOutButton.style.position = "fixed";
|
|
5740
|
+
signOutButton.style.top = "-9999px";
|
|
5741
|
+
signOutButton.style.left = "-9999px";
|
|
5742
|
+
document.body.appendChild(signOutButton);
|
|
5743
|
+
}
|
|
5744
|
+
return { signIn: signInButton, signOut: signOutButton };
|
|
5745
|
+
}
|
|
5746
|
+
async function authenticateICloud() {
|
|
5747
|
+
const container = await getContainer();
|
|
5748
|
+
ensureAuthElements();
|
|
5749
|
+
return container.setUpAuth();
|
|
5750
|
+
}
|
|
5751
|
+
async function requestICloudSignIn() {
|
|
5752
|
+
const container = await getContainer();
|
|
5753
|
+
const { signIn } = ensureAuthElements();
|
|
5754
|
+
const existingUser = await container.setUpAuth();
|
|
5755
|
+
if (existingUser) {
|
|
5756
|
+
return existingUser;
|
|
5757
|
+
}
|
|
5758
|
+
console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
|
|
5759
|
+
console.log("[CloudKit] Sign-in container children:", signIn.children.length);
|
|
5760
|
+
const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
|
|
5761
|
+
console.log("[CloudKit] Found button element:", appleButton);
|
|
5762
|
+
if (appleButton) {
|
|
5763
|
+
console.log("[CloudKit] Clicking button...");
|
|
5764
|
+
appleButton.click();
|
|
5765
|
+
} else {
|
|
5766
|
+
const anyClickable = signIn.firstElementChild;
|
|
5767
|
+
if (anyClickable) {
|
|
5768
|
+
console.log("[CloudKit] Clicking first child element:", anyClickable);
|
|
5769
|
+
anyClickable.click();
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
return container.whenUserSignsIn();
|
|
5773
|
+
}
|
|
5774
|
+
async function uploadFileToICloud(filename, content) {
|
|
5775
|
+
const container = await getContainer();
|
|
5776
|
+
const database = container.privateCloudDatabase;
|
|
5777
|
+
const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
5778
|
+
const arrayBuffer = await content.arrayBuffer();
|
|
5779
|
+
const base64Data = btoa(
|
|
5780
|
+
String.fromCharCode(...new Uint8Array(arrayBuffer))
|
|
5781
|
+
);
|
|
5782
|
+
const record = {
|
|
5783
|
+
recordType: RECORD_TYPE,
|
|
5784
|
+
recordName,
|
|
5785
|
+
fields: {
|
|
5786
|
+
filename: { value: filename },
|
|
5787
|
+
data: { value: base64Data },
|
|
5788
|
+
size: { value: content.size },
|
|
5789
|
+
contentType: { value: content.type || "application/json" }
|
|
5790
|
+
}
|
|
5791
|
+
};
|
|
5792
|
+
const response = await database.saveRecords(record);
|
|
5793
|
+
if (!response.records || response.records.length === 0) {
|
|
5794
|
+
throw new Error("Failed to upload file to iCloud");
|
|
5795
|
+
}
|
|
5796
|
+
const savedRecord = response.records[0];
|
|
5797
|
+
return {
|
|
5798
|
+
recordName: savedRecord.recordName,
|
|
5799
|
+
filename,
|
|
5800
|
+
modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
|
|
5801
|
+
size: content.size
|
|
5802
|
+
};
|
|
5803
|
+
}
|
|
5804
|
+
async function listICloudFiles() {
|
|
5805
|
+
const container = await getContainer();
|
|
5806
|
+
const database = container.privateCloudDatabase;
|
|
5807
|
+
const query = {
|
|
5808
|
+
recordType: RECORD_TYPE
|
|
5809
|
+
// Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
|
|
5810
|
+
// For now, we skip sorting and sort client-side after fetching
|
|
5811
|
+
};
|
|
5812
|
+
const allRecords = [];
|
|
5813
|
+
let response = await database.performQuery(query);
|
|
5814
|
+
if (response.records) {
|
|
5815
|
+
allRecords.push(...response.records);
|
|
5816
|
+
}
|
|
5817
|
+
while (response.continuationMarker) {
|
|
5818
|
+
break;
|
|
5819
|
+
}
|
|
5820
|
+
const files = allRecords.map((record) => ({
|
|
5821
|
+
recordName: record.recordName,
|
|
5822
|
+
filename: record.fields.filename?.value ?? "",
|
|
5823
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5824
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5825
|
+
}));
|
|
5826
|
+
return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
5827
|
+
}
|
|
5828
|
+
async function downloadICloudFile(recordName) {
|
|
5829
|
+
const container = await getContainer();
|
|
5830
|
+
const database = container.privateCloudDatabase;
|
|
5831
|
+
const response = await database.fetchRecords([{ recordName }], {
|
|
5832
|
+
desiredKeys: ["filename", "data", "contentType"]
|
|
5833
|
+
});
|
|
5834
|
+
if (!response.records || response.records.length === 0) {
|
|
5835
|
+
throw new Error(`File not found: ${recordName}`);
|
|
5836
|
+
}
|
|
5837
|
+
const record = response.records[0];
|
|
5838
|
+
const dataField = record.fields.data?.value;
|
|
5839
|
+
if (!dataField) {
|
|
5840
|
+
throw new Error("No data in record");
|
|
5841
|
+
}
|
|
5842
|
+
if (typeof dataField === "string") {
|
|
5843
|
+
const binaryString = atob(dataField);
|
|
5844
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
5845
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
5846
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
5847
|
+
}
|
|
5848
|
+
return new Blob([bytes], { type: "application/json" });
|
|
5849
|
+
}
|
|
5850
|
+
if (typeof dataField === "object" && "downloadURL" in dataField) {
|
|
5851
|
+
const fetchResponse = await fetch(
|
|
5852
|
+
dataField.downloadURL
|
|
5853
|
+
);
|
|
5854
|
+
if (!fetchResponse.ok) {
|
|
5855
|
+
throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
|
|
5856
|
+
}
|
|
5857
|
+
return fetchResponse.blob();
|
|
5858
|
+
}
|
|
5859
|
+
throw new Error("Unknown data format in iCloud record");
|
|
5860
|
+
}
|
|
5861
|
+
async function findICloudFile(filename) {
|
|
5862
|
+
const container = await getContainer();
|
|
5863
|
+
const database = container.privateCloudDatabase;
|
|
5864
|
+
const query = {
|
|
5865
|
+
recordType: RECORD_TYPE,
|
|
5866
|
+
filterBy: [
|
|
5867
|
+
{
|
|
5868
|
+
fieldName: "filename",
|
|
5869
|
+
comparator: "EQUALS",
|
|
5870
|
+
fieldValue: { value: filename }
|
|
5871
|
+
}
|
|
5872
|
+
]
|
|
5873
|
+
};
|
|
5874
|
+
const response = await database.performQuery(query);
|
|
5875
|
+
if (!response.records || response.records.length === 0) {
|
|
5876
|
+
return null;
|
|
5877
|
+
}
|
|
5878
|
+
const record = response.records[0];
|
|
5879
|
+
return {
|
|
5880
|
+
recordName: record.recordName,
|
|
5881
|
+
filename: record.fields.filename?.value ?? "",
|
|
5882
|
+
modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
|
|
5883
|
+
size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
|
|
5884
|
+
};
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5887
|
+
// src/react/useICloudAuth.ts
|
|
5888
|
+
var ICloudAuthContext = (0, import_react14.createContext)(null);
|
|
5889
|
+
function ICloudAuthProvider({
|
|
5890
|
+
apiToken,
|
|
5891
|
+
containerIdentifier = DEFAULT_CONTAINER_ID,
|
|
5892
|
+
environment = "production",
|
|
5893
|
+
children
|
|
5894
|
+
}) {
|
|
5895
|
+
const [isAuthenticated, setIsAuthenticated] = (0, import_react14.useState)(false);
|
|
5896
|
+
const [userRecordName, setUserRecordName] = (0, import_react14.useState)(null);
|
|
5897
|
+
const [isAvailable, setIsAvailable] = (0, import_react14.useState)(false);
|
|
5898
|
+
const [isConfigured, setIsConfigured] = (0, import_react14.useState)(false);
|
|
5899
|
+
const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
|
|
5900
|
+
(0, import_react14.useEffect)(() => {
|
|
5901
|
+
if (!apiToken || typeof window === "undefined") {
|
|
5902
|
+
return;
|
|
5903
|
+
}
|
|
5904
|
+
const initCloudKit = async () => {
|
|
5905
|
+
setIsLoading(true);
|
|
5906
|
+
try {
|
|
5907
|
+
await loadCloudKit();
|
|
5908
|
+
setIsAvailable(true);
|
|
5909
|
+
const config = {
|
|
5910
|
+
containerIdentifier,
|
|
5911
|
+
apiToken,
|
|
5912
|
+
environment
|
|
5913
|
+
};
|
|
5914
|
+
await configureCloudKit(config);
|
|
5915
|
+
setIsConfigured(true);
|
|
5916
|
+
try {
|
|
5917
|
+
const userIdentity = await authenticateICloud();
|
|
5918
|
+
if (userIdentity) {
|
|
5919
|
+
setIsAuthenticated(true);
|
|
5920
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5921
|
+
}
|
|
5922
|
+
} catch {
|
|
5923
|
+
}
|
|
5924
|
+
} catch {
|
|
5925
|
+
setIsAvailable(false);
|
|
5926
|
+
setIsConfigured(false);
|
|
5927
|
+
} finally {
|
|
5928
|
+
setIsLoading(false);
|
|
5929
|
+
}
|
|
5930
|
+
};
|
|
5931
|
+
initCloudKit();
|
|
5932
|
+
}, [apiToken, containerIdentifier, environment]);
|
|
5933
|
+
const requestAccess = (0, import_react14.useCallback)(async () => {
|
|
5934
|
+
if (!isConfigured) {
|
|
5935
|
+
throw new Error("iCloud is not configured");
|
|
5936
|
+
}
|
|
5937
|
+
if (isAuthenticated) {
|
|
5938
|
+
return;
|
|
5939
|
+
}
|
|
5940
|
+
try {
|
|
5941
|
+
const userIdentity = await requestICloudSignIn();
|
|
5942
|
+
setIsAuthenticated(true);
|
|
5943
|
+
setUserRecordName(userIdentity.userRecordName);
|
|
5944
|
+
} catch (err) {
|
|
5945
|
+
throw new Error(
|
|
5946
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
5947
|
+
);
|
|
5948
|
+
}
|
|
5949
|
+
}, [isAuthenticated, isConfigured]);
|
|
5950
|
+
const logout = (0, import_react14.useCallback)(() => {
|
|
5951
|
+
setIsAuthenticated(false);
|
|
5952
|
+
setUserRecordName(null);
|
|
5953
|
+
}, []);
|
|
5954
|
+
return (0, import_react14.createElement)(
|
|
5955
|
+
ICloudAuthContext.Provider,
|
|
5956
|
+
{
|
|
5957
|
+
value: {
|
|
5958
|
+
isAuthenticated,
|
|
5959
|
+
isConfigured,
|
|
5960
|
+
isAvailable,
|
|
5961
|
+
userRecordName,
|
|
5962
|
+
requestAccess,
|
|
5963
|
+
logout
|
|
5964
|
+
}
|
|
5965
|
+
},
|
|
5966
|
+
children
|
|
5967
|
+
);
|
|
5968
|
+
}
|
|
5969
|
+
function useICloudAuth() {
|
|
5970
|
+
const context = (0, import_react14.useContext)(ICloudAuthContext);
|
|
5971
|
+
if (!context) {
|
|
5972
|
+
throw new Error("useICloudAuth must be used within ICloudAuthProvider");
|
|
5973
|
+
}
|
|
5974
|
+
return context;
|
|
5975
|
+
}
|
|
5976
|
+
function hasICloudCredentials() {
|
|
5977
|
+
return isCloudKitAvailable();
|
|
5978
|
+
}
|
|
5979
|
+
function clearICloudAuth() {
|
|
5980
|
+
}
|
|
5981
|
+
|
|
5982
|
+
// src/react/useICloudBackup.ts
|
|
5983
|
+
var import_react15 = require("react");
|
|
5984
|
+
|
|
5985
|
+
// src/lib/backup/icloud/backup.ts
|
|
5986
|
+
var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
|
|
5987
|
+
async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
|
|
5988
|
+
try {
|
|
5989
|
+
await deps.requestEncryptionKey(userAddress);
|
|
5990
|
+
const filename = `${conversationId}.json`;
|
|
5991
|
+
const existingFile = await findICloudFile(filename);
|
|
5992
|
+
if (existingFile) {
|
|
5993
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
5994
|
+
const conversationsCollection = database.get("conversations");
|
|
5995
|
+
const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
|
|
5996
|
+
if (records.length > 0) {
|
|
5997
|
+
const conversation = conversationToStored(records[0]);
|
|
5998
|
+
const localUpdated = conversation.updatedAt.getTime();
|
|
5999
|
+
const remoteModified = existingFile.modifiedAt.getTime();
|
|
6000
|
+
if (localUpdated <= remoteModified) {
|
|
6001
|
+
return "skipped";
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
const exportResult = await deps.exportConversation(
|
|
6006
|
+
conversationId,
|
|
6007
|
+
userAddress
|
|
6008
|
+
);
|
|
6009
|
+
if (!exportResult.success || !exportResult.blob) {
|
|
6010
|
+
return "failed";
|
|
6011
|
+
}
|
|
6012
|
+
await uploadFileToICloud(filename, exportResult.blob);
|
|
6013
|
+
return "uploaded";
|
|
6014
|
+
} catch (err) {
|
|
6015
|
+
if (isAuthError3(err) && !_retried) {
|
|
6016
|
+
try {
|
|
6017
|
+
await deps.requestICloudAccess();
|
|
6018
|
+
return pushConversationToICloud(
|
|
6019
|
+
database,
|
|
6020
|
+
conversationId,
|
|
6021
|
+
userAddress,
|
|
6022
|
+
deps,
|
|
6023
|
+
true
|
|
6024
|
+
);
|
|
6025
|
+
} catch {
|
|
6026
|
+
return "failed";
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
return "failed";
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
async function performICloudExport(database, userAddress, deps, onProgress) {
|
|
6033
|
+
await deps.requestEncryptionKey(userAddress);
|
|
6034
|
+
const { Q: Q4 } = await import("@nozbe/watermelondb");
|
|
6035
|
+
const conversationsCollection = database.get("conversations");
|
|
6036
|
+
const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
|
|
6037
|
+
const conversations = records.map(conversationToStored);
|
|
6038
|
+
const total = conversations.length;
|
|
6039
|
+
if (total === 0) {
|
|
6040
|
+
return { success: true, uploaded: 0, skipped: 0, total: 0 };
|
|
6041
|
+
}
|
|
6042
|
+
let uploaded = 0;
|
|
6043
|
+
let skipped = 0;
|
|
6044
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
6045
|
+
const conv = conversations[i];
|
|
6046
|
+
onProgress?.(i + 1, total);
|
|
6047
|
+
const result = await pushConversationToICloud(
|
|
6048
|
+
database,
|
|
6049
|
+
conv.conversationId,
|
|
6050
|
+
userAddress,
|
|
6051
|
+
deps
|
|
6052
|
+
);
|
|
6053
|
+
if (result === "uploaded") uploaded++;
|
|
6054
|
+
if (result === "skipped") skipped++;
|
|
6055
|
+
}
|
|
6056
|
+
return { success: true, uploaded, skipped, total };
|
|
6057
|
+
}
|
|
6058
|
+
async function performICloudImport(userAddress, deps, onProgress) {
|
|
6059
|
+
await deps.requestEncryptionKey(userAddress);
|
|
6060
|
+
const remoteFiles = await listICloudFiles();
|
|
6061
|
+
if (remoteFiles.length === 0) {
|
|
6062
|
+
return {
|
|
6063
|
+
success: false,
|
|
6064
|
+
restored: 0,
|
|
6065
|
+
failed: 0,
|
|
6066
|
+
total: 0,
|
|
6067
|
+
noBackupsFound: true
|
|
6068
|
+
};
|
|
6069
|
+
}
|
|
6070
|
+
const jsonFiles = remoteFiles.filter(
|
|
6071
|
+
(file) => file.filename.endsWith(".json")
|
|
6072
|
+
);
|
|
6073
|
+
const total = jsonFiles.length;
|
|
6074
|
+
let restored = 0;
|
|
6075
|
+
let failed = 0;
|
|
6076
|
+
for (let i = 0; i < jsonFiles.length; i++) {
|
|
6077
|
+
const file = jsonFiles[i];
|
|
6078
|
+
onProgress?.(i + 1, total);
|
|
6079
|
+
try {
|
|
6080
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6081
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6082
|
+
if (result.success) {
|
|
6083
|
+
restored++;
|
|
6084
|
+
} else {
|
|
6085
|
+
failed++;
|
|
6086
|
+
}
|
|
6087
|
+
} catch (err) {
|
|
6088
|
+
if (isAuthError3(err)) {
|
|
6089
|
+
try {
|
|
6090
|
+
await deps.requestICloudAccess();
|
|
6091
|
+
const blob = await downloadICloudFile(file.recordName);
|
|
6092
|
+
const result = await deps.importConversation(blob, userAddress);
|
|
6093
|
+
if (result.success) {
|
|
6094
|
+
restored++;
|
|
6095
|
+
} else {
|
|
6096
|
+
failed++;
|
|
6097
|
+
}
|
|
6098
|
+
} catch {
|
|
6099
|
+
failed++;
|
|
6100
|
+
}
|
|
6101
|
+
} else {
|
|
6102
|
+
failed++;
|
|
6103
|
+
}
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
return { success: true, restored, failed, total };
|
|
6107
|
+
}
|
|
6108
|
+
|
|
6109
|
+
// src/react/useICloudBackup.ts
|
|
6110
|
+
function useICloudBackup(options) {
|
|
6111
|
+
const {
|
|
6112
|
+
database,
|
|
6113
|
+
userAddress,
|
|
6114
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6115
|
+
exportConversation,
|
|
6116
|
+
importConversation
|
|
6117
|
+
} = options;
|
|
6118
|
+
const {
|
|
6119
|
+
isAuthenticated,
|
|
6120
|
+
isConfigured,
|
|
6121
|
+
isAvailable,
|
|
6122
|
+
requestAccess
|
|
6123
|
+
} = useICloudAuth();
|
|
6124
|
+
const deps = (0, import_react15.useMemo)(
|
|
6125
|
+
() => ({
|
|
6126
|
+
requestICloudAccess: requestAccess,
|
|
6127
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6128
|
+
exportConversation,
|
|
6129
|
+
importConversation
|
|
6130
|
+
}),
|
|
6131
|
+
[requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6132
|
+
);
|
|
6133
|
+
const ensureAuthenticated = (0, import_react15.useCallback)(async () => {
|
|
6134
|
+
if (isAuthenticated) return true;
|
|
6135
|
+
try {
|
|
6136
|
+
await requestAccess();
|
|
6137
|
+
return true;
|
|
6138
|
+
} catch {
|
|
6139
|
+
return false;
|
|
6140
|
+
}
|
|
6141
|
+
}, [isAuthenticated, requestAccess]);
|
|
6142
|
+
const backup = (0, import_react15.useCallback)(
|
|
6143
|
+
async (backupOptions) => {
|
|
6144
|
+
if (!userAddress) {
|
|
6145
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6146
|
+
}
|
|
6147
|
+
if (!isAvailable) {
|
|
6148
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6149
|
+
}
|
|
6150
|
+
if (!isConfigured) {
|
|
6151
|
+
return { error: "iCloud is not configured" };
|
|
6152
|
+
}
|
|
6153
|
+
const authenticated = await ensureAuthenticated();
|
|
6154
|
+
if (!authenticated) {
|
|
6155
|
+
return { error: "iCloud access denied" };
|
|
6156
|
+
}
|
|
6157
|
+
try {
|
|
6158
|
+
return await performICloudExport(
|
|
6159
|
+
database,
|
|
6160
|
+
userAddress,
|
|
6161
|
+
deps,
|
|
6162
|
+
backupOptions?.onProgress
|
|
6163
|
+
);
|
|
6164
|
+
} catch (err) {
|
|
6165
|
+
return {
|
|
6166
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6167
|
+
};
|
|
6168
|
+
}
|
|
6169
|
+
},
|
|
6170
|
+
[database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6171
|
+
);
|
|
6172
|
+
const restore = (0, import_react15.useCallback)(
|
|
6173
|
+
async (restoreOptions) => {
|
|
6174
|
+
if (!userAddress) {
|
|
6175
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6176
|
+
}
|
|
6177
|
+
if (!isAvailable) {
|
|
6178
|
+
return { error: "CloudKit JS is not loaded" };
|
|
6179
|
+
}
|
|
6180
|
+
if (!isConfigured) {
|
|
6181
|
+
return { error: "iCloud is not configured" };
|
|
6182
|
+
}
|
|
6183
|
+
const authenticated = await ensureAuthenticated();
|
|
6184
|
+
if (!authenticated) {
|
|
6185
|
+
return { error: "iCloud access denied" };
|
|
6186
|
+
}
|
|
6187
|
+
try {
|
|
6188
|
+
return await performICloudImport(
|
|
6189
|
+
userAddress,
|
|
6190
|
+
deps,
|
|
6191
|
+
restoreOptions?.onProgress
|
|
6192
|
+
);
|
|
6193
|
+
} catch (err) {
|
|
6194
|
+
return {
|
|
6195
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6196
|
+
};
|
|
6197
|
+
}
|
|
6198
|
+
},
|
|
6199
|
+
[userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
|
|
6200
|
+
);
|
|
6201
|
+
return {
|
|
6202
|
+
backup,
|
|
6203
|
+
restore,
|
|
6204
|
+
isConfigured,
|
|
6205
|
+
isAuthenticated,
|
|
6206
|
+
isAvailable
|
|
6207
|
+
};
|
|
6208
|
+
}
|
|
6209
|
+
|
|
6210
|
+
// src/react/useBackupAuth.ts
|
|
6211
|
+
var import_react16 = require("react");
|
|
6212
|
+
var BackupAuthContext = (0, import_react16.createContext)(null);
|
|
5647
6213
|
function BackupAuthProvider({
|
|
5648
6214
|
dropboxAppKey,
|
|
5649
6215
|
dropboxCallbackPath = "/auth/dropbox/callback",
|
|
5650
6216
|
googleClientId,
|
|
5651
6217
|
googleCallbackPath = "/auth/google/callback",
|
|
6218
|
+
icloudApiToken,
|
|
6219
|
+
icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
|
|
6220
|
+
icloudEnvironment = "production",
|
|
5652
6221
|
apiClient,
|
|
5653
6222
|
children
|
|
5654
6223
|
}) {
|
|
5655
|
-
const [dropboxToken, setDropboxToken] = (0,
|
|
6224
|
+
const [dropboxToken, setDropboxToken] = (0, import_react16.useState)(null);
|
|
5656
6225
|
const isDropboxConfigured = !!dropboxAppKey;
|
|
5657
|
-
const [googleToken, setGoogleToken] = (0,
|
|
6226
|
+
const [googleToken, setGoogleToken] = (0, import_react16.useState)(null);
|
|
5658
6227
|
const isGoogleConfigured = !!googleClientId;
|
|
5659
|
-
(0,
|
|
6228
|
+
const [icloudAuthenticated, setIcloudAuthenticated] = (0, import_react16.useState)(false);
|
|
6229
|
+
const [icloudUserRecordName, setIcloudUserRecordName] = (0, import_react16.useState)(null);
|
|
6230
|
+
const [isIcloudAvailable, setIsIcloudAvailable] = (0, import_react16.useState)(false);
|
|
6231
|
+
const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
|
|
6232
|
+
(0, import_react16.useEffect)(() => {
|
|
5660
6233
|
const checkStoredTokens = async () => {
|
|
5661
6234
|
if (hasDropboxCredentials()) {
|
|
5662
6235
|
const token = await getDropboxAccessToken(apiClient);
|
|
@@ -5673,7 +6246,35 @@ function BackupAuthProvider({
|
|
|
5673
6246
|
};
|
|
5674
6247
|
checkStoredTokens();
|
|
5675
6248
|
}, [apiClient]);
|
|
5676
|
-
(0,
|
|
6249
|
+
(0, import_react16.useEffect)(() => {
|
|
6250
|
+
if (!icloudApiToken || typeof window === "undefined") {
|
|
6251
|
+
return;
|
|
6252
|
+
}
|
|
6253
|
+
const initCloudKit = async () => {
|
|
6254
|
+
try {
|
|
6255
|
+
await loadCloudKit();
|
|
6256
|
+
setIsIcloudAvailable(true);
|
|
6257
|
+
const config = {
|
|
6258
|
+
containerIdentifier: icloudContainerIdentifier,
|
|
6259
|
+
apiToken: icloudApiToken,
|
|
6260
|
+
environment: icloudEnvironment
|
|
6261
|
+
};
|
|
6262
|
+
await configureCloudKit(config);
|
|
6263
|
+
try {
|
|
6264
|
+
const userIdentity = await authenticateICloud();
|
|
6265
|
+
if (userIdentity) {
|
|
6266
|
+
setIcloudAuthenticated(true);
|
|
6267
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6268
|
+
}
|
|
6269
|
+
} catch {
|
|
6270
|
+
}
|
|
6271
|
+
} catch {
|
|
6272
|
+
setIsIcloudAvailable(false);
|
|
6273
|
+
}
|
|
6274
|
+
};
|
|
6275
|
+
initCloudKit();
|
|
6276
|
+
}, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
|
|
6277
|
+
(0, import_react16.useEffect)(() => {
|
|
5677
6278
|
if (!isDropboxConfigured) return;
|
|
5678
6279
|
const handleCallback = async () => {
|
|
5679
6280
|
if (isDropboxCallback()) {
|
|
@@ -5688,7 +6289,7 @@ function BackupAuthProvider({
|
|
|
5688
6289
|
};
|
|
5689
6290
|
handleCallback();
|
|
5690
6291
|
}, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
|
|
5691
|
-
(0,
|
|
6292
|
+
(0, import_react16.useEffect)(() => {
|
|
5692
6293
|
if (!isGoogleConfigured) return;
|
|
5693
6294
|
const handleCallback = async () => {
|
|
5694
6295
|
if (isGoogleDriveCallback()) {
|
|
@@ -5703,14 +6304,14 @@ function BackupAuthProvider({
|
|
|
5703
6304
|
};
|
|
5704
6305
|
handleCallback();
|
|
5705
6306
|
}, [googleCallbackPath, isGoogleConfigured, apiClient]);
|
|
5706
|
-
const refreshDropboxTokenFn = (0,
|
|
6307
|
+
const refreshDropboxTokenFn = (0, import_react16.useCallback)(async () => {
|
|
5707
6308
|
const token = await getDropboxAccessToken(apiClient);
|
|
5708
6309
|
if (token) {
|
|
5709
6310
|
setDropboxToken(token);
|
|
5710
6311
|
}
|
|
5711
6312
|
return token;
|
|
5712
6313
|
}, [apiClient]);
|
|
5713
|
-
const requestDropboxAccess = (0,
|
|
6314
|
+
const requestDropboxAccess = (0, import_react16.useCallback)(async () => {
|
|
5714
6315
|
if (!isDropboxConfigured || !dropboxAppKey) {
|
|
5715
6316
|
throw new Error("Dropbox is not configured");
|
|
5716
6317
|
}
|
|
@@ -5730,18 +6331,18 @@ function BackupAuthProvider({
|
|
|
5730
6331
|
isDropboxConfigured,
|
|
5731
6332
|
apiClient
|
|
5732
6333
|
]);
|
|
5733
|
-
const logoutDropbox = (0,
|
|
6334
|
+
const logoutDropbox = (0, import_react16.useCallback)(async () => {
|
|
5734
6335
|
await revokeDropboxToken(apiClient);
|
|
5735
6336
|
setDropboxToken(null);
|
|
5736
6337
|
}, [apiClient]);
|
|
5737
|
-
const refreshGoogleTokenFn = (0,
|
|
6338
|
+
const refreshGoogleTokenFn = (0, import_react16.useCallback)(async () => {
|
|
5738
6339
|
const token = await getGoogleDriveAccessToken(apiClient);
|
|
5739
6340
|
if (token) {
|
|
5740
6341
|
setGoogleToken(token);
|
|
5741
6342
|
}
|
|
5742
6343
|
return token;
|
|
5743
6344
|
}, [apiClient]);
|
|
5744
|
-
const requestGoogleAccess = (0,
|
|
6345
|
+
const requestGoogleAccess = (0, import_react16.useCallback)(async () => {
|
|
5745
6346
|
if (!isGoogleConfigured || !googleClientId) {
|
|
5746
6347
|
throw new Error("Google Drive is not configured");
|
|
5747
6348
|
}
|
|
@@ -5761,16 +6362,51 @@ function BackupAuthProvider({
|
|
|
5761
6362
|
isGoogleConfigured,
|
|
5762
6363
|
apiClient
|
|
5763
6364
|
]);
|
|
5764
|
-
const logoutGoogle = (0,
|
|
6365
|
+
const logoutGoogle = (0, import_react16.useCallback)(async () => {
|
|
5765
6366
|
await revokeGoogleDriveToken(apiClient);
|
|
5766
6367
|
setGoogleToken(null);
|
|
5767
6368
|
}, [apiClient]);
|
|
5768
|
-
const
|
|
6369
|
+
const refreshIcloudTokenFn = (0, import_react16.useCallback)(async () => {
|
|
6370
|
+
try {
|
|
6371
|
+
const userIdentity = await authenticateICloud();
|
|
6372
|
+
if (userIdentity) {
|
|
6373
|
+
setIcloudAuthenticated(true);
|
|
6374
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6375
|
+
return userIdentity.userRecordName;
|
|
6376
|
+
}
|
|
6377
|
+
} catch {
|
|
6378
|
+
}
|
|
6379
|
+
return null;
|
|
6380
|
+
}, []);
|
|
6381
|
+
const requestIcloudAccess = (0, import_react16.useCallback)(async () => {
|
|
6382
|
+
if (!isIcloudConfigured) {
|
|
6383
|
+
throw new Error("iCloud is not configured");
|
|
6384
|
+
}
|
|
6385
|
+
if (icloudAuthenticated && icloudUserRecordName) {
|
|
6386
|
+
return icloudUserRecordName;
|
|
6387
|
+
}
|
|
6388
|
+
try {
|
|
6389
|
+
const userIdentity = await requestICloudSignIn();
|
|
6390
|
+
setIcloudAuthenticated(true);
|
|
6391
|
+
setIcloudUserRecordName(userIdentity.userRecordName);
|
|
6392
|
+
return userIdentity.userRecordName;
|
|
6393
|
+
} catch (err) {
|
|
6394
|
+
throw new Error(
|
|
6395
|
+
err instanceof Error ? err.message : "Failed to sign in to iCloud"
|
|
6396
|
+
);
|
|
6397
|
+
}
|
|
6398
|
+
}, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
|
|
6399
|
+
const logoutIcloud = (0, import_react16.useCallback)(async () => {
|
|
6400
|
+
setIcloudAuthenticated(false);
|
|
6401
|
+
setIcloudUserRecordName(null);
|
|
6402
|
+
}, []);
|
|
6403
|
+
const logoutAll = (0, import_react16.useCallback)(async () => {
|
|
5769
6404
|
await Promise.all([
|
|
5770
6405
|
isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
|
|
5771
|
-
isGoogleConfigured ? logoutGoogle() : Promise.resolve()
|
|
6406
|
+
isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
|
|
6407
|
+
isIcloudConfigured ? logoutIcloud() : Promise.resolve()
|
|
5772
6408
|
]);
|
|
5773
|
-
}, [isDropboxConfigured, isGoogleConfigured, logoutDropbox, logoutGoogle]);
|
|
6409
|
+
}, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
|
|
5774
6410
|
const dropboxState = {
|
|
5775
6411
|
accessToken: dropboxToken,
|
|
5776
6412
|
isAuthenticated: !!dropboxToken,
|
|
@@ -5787,14 +6423,24 @@ function BackupAuthProvider({
|
|
|
5787
6423
|
logout: logoutGoogle,
|
|
5788
6424
|
refreshToken: refreshGoogleTokenFn
|
|
5789
6425
|
};
|
|
5790
|
-
|
|
6426
|
+
const icloudState = {
|
|
6427
|
+
accessToken: icloudUserRecordName,
|
|
6428
|
+
// Use userRecordName as the "token" for iCloud
|
|
6429
|
+
isAuthenticated: icloudAuthenticated,
|
|
6430
|
+
isConfigured: isIcloudConfigured,
|
|
6431
|
+
requestAccess: requestIcloudAccess,
|
|
6432
|
+
logout: logoutIcloud,
|
|
6433
|
+
refreshToken: refreshIcloudTokenFn
|
|
6434
|
+
};
|
|
6435
|
+
return (0, import_react16.createElement)(
|
|
5791
6436
|
BackupAuthContext.Provider,
|
|
5792
6437
|
{
|
|
5793
6438
|
value: {
|
|
5794
6439
|
dropbox: dropboxState,
|
|
5795
6440
|
googleDrive: googleDriveState,
|
|
5796
|
-
|
|
5797
|
-
|
|
6441
|
+
icloud: icloudState,
|
|
6442
|
+
hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
|
|
6443
|
+
hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
|
|
5798
6444
|
logoutAll
|
|
5799
6445
|
}
|
|
5800
6446
|
},
|
|
@@ -5802,7 +6448,7 @@ function BackupAuthProvider({
|
|
|
5802
6448
|
);
|
|
5803
6449
|
}
|
|
5804
6450
|
function useBackupAuth() {
|
|
5805
|
-
const context = (0,
|
|
6451
|
+
const context = (0, import_react16.useContext)(BackupAuthContext);
|
|
5806
6452
|
if (!context) {
|
|
5807
6453
|
throw new Error("useBackupAuth must be used within BackupAuthProvider");
|
|
5808
6454
|
}
|
|
@@ -5810,7 +6456,7 @@ function useBackupAuth() {
|
|
|
5810
6456
|
}
|
|
5811
6457
|
|
|
5812
6458
|
// src/react/useBackup.ts
|
|
5813
|
-
var
|
|
6459
|
+
var import_react17 = require("react");
|
|
5814
6460
|
function useBackup(options) {
|
|
5815
6461
|
const {
|
|
5816
6462
|
database,
|
|
@@ -5825,11 +6471,12 @@ function useBackup(options) {
|
|
|
5825
6471
|
const {
|
|
5826
6472
|
dropbox: dropboxAuth,
|
|
5827
6473
|
googleDrive: googleDriveAuth,
|
|
6474
|
+
icloud: icloudAuth,
|
|
5828
6475
|
hasAnyProvider,
|
|
5829
6476
|
hasAnyAuthentication,
|
|
5830
6477
|
logoutAll
|
|
5831
6478
|
} = useBackupAuth();
|
|
5832
|
-
const dropboxDeps = (0,
|
|
6479
|
+
const dropboxDeps = (0, import_react17.useMemo)(
|
|
5833
6480
|
() => ({
|
|
5834
6481
|
requestDropboxAccess: dropboxAuth.requestAccess,
|
|
5835
6482
|
requestEncryptionKey: requestEncryptionKey2,
|
|
@@ -5838,7 +6485,7 @@ function useBackup(options) {
|
|
|
5838
6485
|
}),
|
|
5839
6486
|
[dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
5840
6487
|
);
|
|
5841
|
-
const googleDriveDeps = (0,
|
|
6488
|
+
const googleDriveDeps = (0, import_react17.useMemo)(
|
|
5842
6489
|
() => ({
|
|
5843
6490
|
requestDriveAccess: googleDriveAuth.requestAccess,
|
|
5844
6491
|
requestEncryptionKey: requestEncryptionKey2,
|
|
@@ -5847,7 +6494,18 @@ function useBackup(options) {
|
|
|
5847
6494
|
}),
|
|
5848
6495
|
[googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
5849
6496
|
);
|
|
5850
|
-
const
|
|
6497
|
+
const icloudDeps = (0, import_react17.useMemo)(
|
|
6498
|
+
() => ({
|
|
6499
|
+
requestICloudAccess: async () => {
|
|
6500
|
+
await icloudAuth.requestAccess();
|
|
6501
|
+
},
|
|
6502
|
+
requestEncryptionKey: requestEncryptionKey2,
|
|
6503
|
+
exportConversation,
|
|
6504
|
+
importConversation
|
|
6505
|
+
}),
|
|
6506
|
+
[icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
|
|
6507
|
+
);
|
|
6508
|
+
const dropboxBackup = (0, import_react17.useCallback)(
|
|
5851
6509
|
async (backupOptions) => {
|
|
5852
6510
|
if (!userAddress) {
|
|
5853
6511
|
return { error: "Please sign in to backup to Dropbox" };
|
|
@@ -5877,7 +6535,7 @@ function useBackup(options) {
|
|
|
5877
6535
|
},
|
|
5878
6536
|
[database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
5879
6537
|
);
|
|
5880
|
-
const dropboxRestore = (0,
|
|
6538
|
+
const dropboxRestore = (0, import_react17.useCallback)(
|
|
5881
6539
|
async (restoreOptions) => {
|
|
5882
6540
|
if (!userAddress) {
|
|
5883
6541
|
return { error: "Please sign in to restore from Dropbox" };
|
|
@@ -5906,7 +6564,7 @@ function useBackup(options) {
|
|
|
5906
6564
|
},
|
|
5907
6565
|
[userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
|
|
5908
6566
|
);
|
|
5909
|
-
const googleDriveBackup = (0,
|
|
6567
|
+
const googleDriveBackup = (0, import_react17.useCallback)(
|
|
5910
6568
|
async (backupOptions) => {
|
|
5911
6569
|
if (!userAddress) {
|
|
5912
6570
|
return { error: "Please sign in to backup to Google Drive" };
|
|
@@ -5944,7 +6602,7 @@ function useBackup(options) {
|
|
|
5944
6602
|
googleConversationsFolder
|
|
5945
6603
|
]
|
|
5946
6604
|
);
|
|
5947
|
-
const googleDriveRestore = (0,
|
|
6605
|
+
const googleDriveRestore = (0, import_react17.useCallback)(
|
|
5948
6606
|
async (restoreOptions) => {
|
|
5949
6607
|
if (!userAddress) {
|
|
5950
6608
|
return { error: "Please sign in to restore from Google Drive" };
|
|
@@ -5996,9 +6654,77 @@ function useBackup(options) {
|
|
|
5996
6654
|
connect: googleDriveAuth.requestAccess,
|
|
5997
6655
|
disconnect: googleDriveAuth.logout
|
|
5998
6656
|
};
|
|
6657
|
+
const icloudBackup = (0, import_react17.useCallback)(
|
|
6658
|
+
async (backupOptions) => {
|
|
6659
|
+
if (!userAddress) {
|
|
6660
|
+
return { error: "Please sign in to backup to iCloud" };
|
|
6661
|
+
}
|
|
6662
|
+
if (!icloudAuth.isConfigured) {
|
|
6663
|
+
return { error: "iCloud is not configured" };
|
|
6664
|
+
}
|
|
6665
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6666
|
+
try {
|
|
6667
|
+
await icloudAuth.requestAccess();
|
|
6668
|
+
} catch {
|
|
6669
|
+
return { error: "iCloud access denied" };
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6672
|
+
try {
|
|
6673
|
+
return await performICloudExport(
|
|
6674
|
+
database,
|
|
6675
|
+
userAddress,
|
|
6676
|
+
icloudDeps,
|
|
6677
|
+
backupOptions?.onProgress
|
|
6678
|
+
);
|
|
6679
|
+
} catch (err) {
|
|
6680
|
+
return {
|
|
6681
|
+
error: err instanceof Error ? err.message : "Failed to backup to iCloud"
|
|
6682
|
+
};
|
|
6683
|
+
}
|
|
6684
|
+
},
|
|
6685
|
+
[database, userAddress, icloudAuth, icloudDeps]
|
|
6686
|
+
);
|
|
6687
|
+
const icloudRestore = (0, import_react17.useCallback)(
|
|
6688
|
+
async (restoreOptions) => {
|
|
6689
|
+
if (!userAddress) {
|
|
6690
|
+
return { error: "Please sign in to restore from iCloud" };
|
|
6691
|
+
}
|
|
6692
|
+
if (!icloudAuth.isConfigured) {
|
|
6693
|
+
return { error: "iCloud is not configured" };
|
|
6694
|
+
}
|
|
6695
|
+
if (!icloudAuth.isAuthenticated) {
|
|
6696
|
+
try {
|
|
6697
|
+
await icloudAuth.requestAccess();
|
|
6698
|
+
} catch {
|
|
6699
|
+
return { error: "iCloud access denied" };
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
try {
|
|
6703
|
+
return await performICloudImport(
|
|
6704
|
+
userAddress,
|
|
6705
|
+
icloudDeps,
|
|
6706
|
+
restoreOptions?.onProgress
|
|
6707
|
+
);
|
|
6708
|
+
} catch (err) {
|
|
6709
|
+
return {
|
|
6710
|
+
error: err instanceof Error ? err.message : "Failed to restore from iCloud"
|
|
6711
|
+
};
|
|
6712
|
+
}
|
|
6713
|
+
},
|
|
6714
|
+
[userAddress, icloudAuth, icloudDeps]
|
|
6715
|
+
);
|
|
6716
|
+
const icloudState = {
|
|
6717
|
+
isConfigured: icloudAuth.isConfigured,
|
|
6718
|
+
isAuthenticated: icloudAuth.isAuthenticated,
|
|
6719
|
+
backup: icloudBackup,
|
|
6720
|
+
restore: icloudRestore,
|
|
6721
|
+
connect: icloudAuth.requestAccess,
|
|
6722
|
+
disconnect: icloudAuth.logout
|
|
6723
|
+
};
|
|
5999
6724
|
return {
|
|
6000
6725
|
dropbox: dropboxState,
|
|
6001
6726
|
googleDrive: googleDriveState,
|
|
6727
|
+
icloud: icloudState,
|
|
6002
6728
|
hasAnyProvider,
|
|
6003
6729
|
hasAnyAuthentication,
|
|
6004
6730
|
disconnectAll: logoutAll
|
|
@@ -6008,6 +6734,7 @@ function useBackup(options) {
|
|
|
6008
6734
|
0 && (module.exports = {
|
|
6009
6735
|
BACKUP_DRIVE_CONVERSATIONS_FOLDER,
|
|
6010
6736
|
BACKUP_DRIVE_ROOT_FOLDER,
|
|
6737
|
+
BACKUP_ICLOUD_FOLDER,
|
|
6011
6738
|
BackupAuthProvider,
|
|
6012
6739
|
ChatConversation,
|
|
6013
6740
|
ChatMessage,
|
|
@@ -6015,15 +6742,18 @@ function useBackup(options) {
|
|
|
6015
6742
|
DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
|
|
6016
6743
|
DEFAULT_DRIVE_ROOT_FOLDER,
|
|
6017
6744
|
DEFAULT_DROPBOX_FOLDER,
|
|
6745
|
+
DEFAULT_ICLOUD_BACKUP_FOLDER,
|
|
6018
6746
|
DEFAULT_TOOL_SELECTOR_MODEL,
|
|
6019
6747
|
DropboxAuthProvider,
|
|
6020
6748
|
GoogleDriveAuthProvider,
|
|
6749
|
+
ICloudAuthProvider,
|
|
6021
6750
|
StoredMemoryModel,
|
|
6022
6751
|
StoredModelPreferenceModel,
|
|
6023
6752
|
chatStorageMigrations,
|
|
6024
6753
|
chatStorageSchema,
|
|
6025
6754
|
clearDropboxToken,
|
|
6026
6755
|
clearGoogleDriveToken,
|
|
6756
|
+
clearICloudAuth,
|
|
6027
6757
|
createMemoryContextSystemMessage,
|
|
6028
6758
|
decryptData,
|
|
6029
6759
|
decryptDataBytes,
|
|
@@ -6038,6 +6768,7 @@ function useBackup(options) {
|
|
|
6038
6768
|
hasDropboxCredentials,
|
|
6039
6769
|
hasEncryptionKey,
|
|
6040
6770
|
hasGoogleDriveCredentials,
|
|
6771
|
+
hasICloudCredentials,
|
|
6041
6772
|
memoryStorageSchema,
|
|
6042
6773
|
requestEncryptionKey,
|
|
6043
6774
|
sdkMigrations,
|
|
@@ -6054,6 +6785,8 @@ function useBackup(options) {
|
|
|
6054
6785
|
useEncryption,
|
|
6055
6786
|
useGoogleDriveAuth,
|
|
6056
6787
|
useGoogleDriveBackup,
|
|
6788
|
+
useICloudAuth,
|
|
6789
|
+
useICloudBackup,
|
|
6057
6790
|
useImageGeneration,
|
|
6058
6791
|
useMemoryStorage,
|
|
6059
6792
|
useModels,
|