@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.
@@ -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/useBackupAuth.ts
5651
+ // src/react/useICloudAuth.ts
5645
5652
  var import_react14 = require("react");
5646
- var BackupAuthContext = (0, import_react14.createContext)(null);
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, import_react14.useState)(null);
6224
+ const [dropboxToken, setDropboxToken] = (0, import_react16.useState)(null);
5656
6225
  const isDropboxConfigured = !!dropboxAppKey;
5657
- const [googleToken, setGoogleToken] = (0, import_react14.useState)(null);
6226
+ const [googleToken, setGoogleToken] = (0, import_react16.useState)(null);
5658
6227
  const isGoogleConfigured = !!googleClientId;
5659
- (0, import_react14.useEffect)(() => {
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, import_react14.useEffect)(() => {
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, import_react14.useEffect)(() => {
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, import_react14.useCallback)(async () => {
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, import_react14.useCallback)(async () => {
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, import_react14.useCallback)(async () => {
6334
+ const logoutDropbox = (0, import_react16.useCallback)(async () => {
5734
6335
  await revokeDropboxToken(apiClient);
5735
6336
  setDropboxToken(null);
5736
6337
  }, [apiClient]);
5737
- const refreshGoogleTokenFn = (0, import_react14.useCallback)(async () => {
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, import_react14.useCallback)(async () => {
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, import_react14.useCallback)(async () => {
6365
+ const logoutGoogle = (0, import_react16.useCallback)(async () => {
5765
6366
  await revokeGoogleDriveToken(apiClient);
5766
6367
  setGoogleToken(null);
5767
6368
  }, [apiClient]);
5768
- const logoutAll = (0, import_react14.useCallback)(async () => {
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
- return (0, import_react14.createElement)(
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
- hasAnyProvider: isDropboxConfigured || isGoogleConfigured,
5797
- hasAnyAuthentication: !!dropboxToken || !!googleToken,
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, import_react14.useContext)(BackupAuthContext);
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 import_react15 = require("react");
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, import_react15.useMemo)(
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, import_react15.useMemo)(
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 dropboxBackup = (0, import_react15.useCallback)(
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, import_react15.useCallback)(
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, import_react15.useCallback)(
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, import_react15.useCallback)(
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,