@reverbia/sdk 1.0.0-next.20251219092050 → 1.0.0-next.20251219162520

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.
@@ -1704,7 +1704,7 @@ import {
1704
1704
  addColumns
1705
1705
  } from "@nozbe/watermelondb/Schema/migrations";
1706
1706
  var chatStorageSchema = appSchema({
1707
- version: 2,
1707
+ version: 3,
1708
1708
  tables: [
1709
1709
  tableSchema({
1710
1710
  name: "history",
@@ -1722,7 +1722,8 @@ var chatStorageSchema = appSchema({
1722
1722
  { name: "usage", type: "string", isOptional: true },
1723
1723
  { name: "sources", type: "string", isOptional: true },
1724
1724
  { name: "response_duration", type: "number", isOptional: true },
1725
- { name: "was_stopped", type: "boolean", isOptional: true }
1725
+ { name: "was_stopped", type: "boolean", isOptional: true },
1726
+ { name: "error", type: "string", isOptional: true }
1726
1727
  ]
1727
1728
  }),
1728
1729
  tableSchema({
@@ -1747,6 +1748,15 @@ var chatStorageMigrations = schemaMigrations({
1747
1748
  columns: [{ name: "was_stopped", type: "boolean", isOptional: true }]
1748
1749
  })
1749
1750
  ]
1751
+ },
1752
+ {
1753
+ toVersion: 3,
1754
+ steps: [
1755
+ addColumns({
1756
+ table: "history",
1757
+ columns: [{ name: "error", type: "string", isOptional: true }]
1758
+ })
1759
+ ]
1750
1760
  }
1751
1761
  ]
1752
1762
  });
@@ -1802,6 +1812,9 @@ __decorateClass([
1802
1812
  __decorateClass([
1803
1813
  field("was_stopped")
1804
1814
  ], Message.prototype, "wasStopped", 2);
1815
+ __decorateClass([
1816
+ text("error")
1817
+ ], Message.prototype, "error", 2);
1805
1818
  var Conversation = class extends Model {
1806
1819
  };
1807
1820
  Conversation.table = "conversations";
@@ -1856,7 +1869,8 @@ function messageToStored(message) {
1856
1869
  usage: message.usage,
1857
1870
  sources: message.sources,
1858
1871
  responseDuration: message.responseDuration,
1859
- wasStopped: message.wasStopped
1872
+ wasStopped: message.wasStopped,
1873
+ error: message.error
1860
1874
  };
1861
1875
  }
1862
1876
  function conversationToStored(conversation) {
@@ -1946,6 +1960,7 @@ async function createMessageOp(ctx, opts) {
1946
1960
  if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
1947
1961
  if (opts.embeddingModel) msg._setRaw("embedding_model", opts.embeddingModel);
1948
1962
  if (opts.wasStopped) msg._setRaw("was_stopped", opts.wasStopped);
1963
+ if (opts.error) msg._setRaw("error", opts.error);
1949
1964
  });
1950
1965
  });
1951
1966
  return messageToStored(created);
@@ -1965,6 +1980,20 @@ async function updateMessageEmbeddingOp(ctx, uniqueId, vector, embeddingModel) {
1965
1980
  });
1966
1981
  return messageToStored(message);
1967
1982
  }
1983
+ async function updateMessageErrorOp(ctx, uniqueId, error) {
1984
+ let message;
1985
+ try {
1986
+ message = await ctx.messagesCollection.find(uniqueId);
1987
+ } catch {
1988
+ return null;
1989
+ }
1990
+ await ctx.database.write(async () => {
1991
+ await message.update((msg) => {
1992
+ msg._setRaw("error", error);
1993
+ });
1994
+ });
1995
+ return messageToStored(message);
1996
+ }
1968
1997
  function cosineSimilarity(a, b) {
1969
1998
  if (a.length !== b.length) return 0;
1970
1999
  let dotProduct = 0;
@@ -2182,7 +2211,8 @@ function useChatStorage(options) {
2182
2211
  let messagesToSend = [];
2183
2212
  if (includeHistory && !providedMessages) {
2184
2213
  const storedMessages = await getMessages(convId);
2185
- const limitedMessages = storedMessages.slice(-maxHistoryMessages);
2214
+ const validMessages = storedMessages.filter((msg) => !msg.error);
2215
+ const limitedMessages = validMessages.slice(-maxHistoryMessages);
2186
2216
  messagesToSend = limitedMessages.map(storedToLlmapiMessage);
2187
2217
  } else if (providedMessages) {
2188
2218
  messagesToSend = providedMessages;
@@ -2281,14 +2311,37 @@ function useChatStorage(options) {
2281
2311
  userMessage: storedUserMessage,
2282
2312
  assistantMessage: storedAssistantMessage2
2283
2313
  };
2284
- } catch (err) {
2314
+ } catch {
2315
+ return {
2316
+ data: null,
2317
+ error: "Request aborted",
2318
+ toolExecution: abortedResult.toolExecution,
2319
+ userMessage: storedUserMessage
2320
+ };
2285
2321
  }
2286
2322
  }
2323
+ const errorMessage = result.error || "No response data received";
2324
+ try {
2325
+ await updateMessageErrorOp(
2326
+ storageCtx,
2327
+ storedUserMessage.uniqueId,
2328
+ errorMessage
2329
+ );
2330
+ await createMessageOp(storageCtx, {
2331
+ conversationId: convId,
2332
+ role: "assistant",
2333
+ content: "",
2334
+ model: model || "",
2335
+ responseDuration,
2336
+ error: errorMessage
2337
+ });
2338
+ } catch {
2339
+ }
2287
2340
  return {
2288
2341
  data: null,
2289
- error: result.error || "No response data received",
2342
+ error: errorMessage,
2290
2343
  toolExecution: result.toolExecution,
2291
- userMessage: storedUserMessage
2344
+ userMessage: { ...storedUserMessage, error: errorMessage }
2292
2345
  };
2293
2346
  }
2294
2347
  const responseData = result.data;
@@ -2430,7 +2483,7 @@ __decorateClass([
2430
2483
  ], ModelPreference.prototype, "models", 2);
2431
2484
 
2432
2485
  // src/lib/db/schema.ts
2433
- var SDK_SCHEMA_VERSION = 4;
2486
+ var SDK_SCHEMA_VERSION = 5;
2434
2487
  var sdkSchema = appSchema2({
2435
2488
  version: SDK_SCHEMA_VERSION,
2436
2489
  tables: [
@@ -2451,7 +2504,8 @@ var sdkSchema = appSchema2({
2451
2504
  { name: "usage", type: "string", isOptional: true },
2452
2505
  { name: "sources", type: "string", isOptional: true },
2453
2506
  { name: "response_duration", type: "number", isOptional: true },
2454
- { name: "was_stopped", type: "boolean", isOptional: true }
2507
+ { name: "was_stopped", type: "boolean", isOptional: true },
2508
+ { name: "error", type: "string", isOptional: true }
2455
2509
  ]
2456
2510
  }),
2457
2511
  tableSchema2({
@@ -2518,6 +2572,16 @@ var sdkMigrations = schemaMigrations2({
2518
2572
  ]
2519
2573
  })
2520
2574
  ]
2575
+ },
2576
+ // v4 -> v5: Added error column to history for error persistence
2577
+ {
2578
+ toVersion: 5,
2579
+ steps: [
2580
+ addColumns2({
2581
+ table: "history",
2582
+ columns: [{ name: "error", type: "string", isOptional: true }]
2583
+ })
2584
+ ]
2521
2585
  }
2522
2586
  ]
2523
2587
  });
@@ -5575,7 +5639,7 @@ function useGoogleDriveBackup(options) {
5575
5639
  };
5576
5640
  }
5577
5641
 
5578
- // src/react/useBackupAuth.ts
5642
+ // src/react/useICloudAuth.ts
5579
5643
  import {
5580
5644
  createContext as createContext3,
5581
5645
  createElement as createElement3,
@@ -5584,20 +5648,593 @@ import {
5584
5648
  useEffect as useEffect8,
5585
5649
  useState as useState12
5586
5650
  } from "react";
5587
- var BackupAuthContext = createContext3(null);
5651
+
5652
+ // src/lib/backup/icloud/api.ts
5653
+ var CLOUDKIT_JS_URL = "https://cdn.apple-cloudkit.com/ck/2/cloudkit.js";
5654
+ var DEFAULT_BACKUP_FOLDER2 = "conversations";
5655
+ var DEFAULT_CONTAINER_ID = "iCloud.Memoryless";
5656
+ var RECORD_TYPE = "ConversationBackup";
5657
+ var cloudKitLoadPromise = null;
5658
+ function isCloudKitAvailable() {
5659
+ return typeof window !== "undefined" && !!window.CloudKit;
5660
+ }
5661
+ async function loadCloudKit() {
5662
+ if (typeof window === "undefined") {
5663
+ throw new Error("CloudKit JS can only be loaded in browser environment");
5664
+ }
5665
+ if (window.CloudKit) {
5666
+ return;
5667
+ }
5668
+ if (cloudKitLoadPromise) {
5669
+ return cloudKitLoadPromise;
5670
+ }
5671
+ cloudKitLoadPromise = new Promise((resolve, reject) => {
5672
+ const script = document.createElement("script");
5673
+ script.src = CLOUDKIT_JS_URL;
5674
+ script.async = true;
5675
+ script.onload = () => {
5676
+ if (window.CloudKit) {
5677
+ resolve();
5678
+ } else {
5679
+ reject(new Error("CloudKit JS loaded but CloudKit object not found"));
5680
+ }
5681
+ };
5682
+ script.onerror = () => {
5683
+ cloudKitLoadPromise = null;
5684
+ reject(new Error("Failed to load CloudKit JS"));
5685
+ };
5686
+ document.head.appendChild(script);
5687
+ });
5688
+ return cloudKitLoadPromise;
5689
+ }
5690
+ async function ensureCloudKitLoaded() {
5691
+ if (!isCloudKitAvailable()) {
5692
+ await loadCloudKit();
5693
+ }
5694
+ }
5695
+ async function configureCloudKit(config) {
5696
+ await ensureCloudKitLoaded();
5697
+ ensureAuthElements();
5698
+ window.CloudKit.configure({
5699
+ containers: [
5700
+ {
5701
+ containerIdentifier: config.containerIdentifier,
5702
+ apiTokenAuth: {
5703
+ apiToken: config.apiToken,
5704
+ persist: true,
5705
+ signInButton: {
5706
+ id: "apple-sign-in-button",
5707
+ theme: "black"
5708
+ },
5709
+ signOutButton: {
5710
+ id: "apple-sign-out-button",
5711
+ theme: "black"
5712
+ }
5713
+ },
5714
+ environment: config.environment
5715
+ }
5716
+ ]
5717
+ });
5718
+ }
5719
+ async function getContainer() {
5720
+ await ensureCloudKitLoaded();
5721
+ return window.CloudKit.getDefaultContainer();
5722
+ }
5723
+ function ensureAuthElements() {
5724
+ let signInButton = document.getElementById("apple-sign-in-button");
5725
+ let signOutButton = document.getElementById("apple-sign-out-button");
5726
+ if (!signInButton) {
5727
+ signInButton = document.createElement("div");
5728
+ signInButton.id = "apple-sign-in-button";
5729
+ signInButton.style.position = "fixed";
5730
+ signInButton.style.top = "-9999px";
5731
+ signInButton.style.left = "-9999px";
5732
+ document.body.appendChild(signInButton);
5733
+ }
5734
+ if (!signOutButton) {
5735
+ signOutButton = document.createElement("div");
5736
+ signOutButton.id = "apple-sign-out-button";
5737
+ signOutButton.style.position = "fixed";
5738
+ signOutButton.style.top = "-9999px";
5739
+ signOutButton.style.left = "-9999px";
5740
+ document.body.appendChild(signOutButton);
5741
+ }
5742
+ return { signIn: signInButton, signOut: signOutButton };
5743
+ }
5744
+ async function authenticateICloud() {
5745
+ const container = await getContainer();
5746
+ ensureAuthElements();
5747
+ return container.setUpAuth();
5748
+ }
5749
+ async function requestICloudSignIn() {
5750
+ const container = await getContainer();
5751
+ const { signIn } = ensureAuthElements();
5752
+ const existingUser = await container.setUpAuth();
5753
+ if (existingUser) {
5754
+ return existingUser;
5755
+ }
5756
+ console.log("[CloudKit] Sign-in container innerHTML:", signIn.innerHTML);
5757
+ console.log("[CloudKit] Sign-in container children:", signIn.children.length);
5758
+ const appleButton = signIn.querySelector("a, button, [role='button'], div[id*='apple']");
5759
+ console.log("[CloudKit] Found button element:", appleButton);
5760
+ if (appleButton) {
5761
+ console.log("[CloudKit] Clicking button...");
5762
+ appleButton.click();
5763
+ } else {
5764
+ const anyClickable = signIn.firstElementChild;
5765
+ if (anyClickable) {
5766
+ console.log("[CloudKit] Clicking first child element:", anyClickable);
5767
+ anyClickable.click();
5768
+ }
5769
+ }
5770
+ return container.whenUserSignsIn();
5771
+ }
5772
+ async function uploadFileToICloud(filename, content) {
5773
+ const container = await getContainer();
5774
+ const database = container.privateCloudDatabase;
5775
+ const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
5776
+ const arrayBuffer = await content.arrayBuffer();
5777
+ const base64Data = btoa(
5778
+ String.fromCharCode(...new Uint8Array(arrayBuffer))
5779
+ );
5780
+ const record = {
5781
+ recordType: RECORD_TYPE,
5782
+ recordName,
5783
+ fields: {
5784
+ filename: { value: filename },
5785
+ data: { value: base64Data },
5786
+ size: { value: content.size },
5787
+ contentType: { value: content.type || "application/json" }
5788
+ }
5789
+ };
5790
+ const response = await database.saveRecords(record);
5791
+ if (!response.records || response.records.length === 0) {
5792
+ throw new Error("Failed to upload file to iCloud");
5793
+ }
5794
+ const savedRecord = response.records[0];
5795
+ return {
5796
+ recordName: savedRecord.recordName,
5797
+ filename,
5798
+ modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
5799
+ size: content.size
5800
+ };
5801
+ }
5802
+ async function listICloudFiles() {
5803
+ const container = await getContainer();
5804
+ const database = container.privateCloudDatabase;
5805
+ const query = {
5806
+ recordType: RECORD_TYPE
5807
+ // Note: Sorting requires SORTABLE index on the field in CloudKit Dashboard
5808
+ // For now, we skip sorting and sort client-side after fetching
5809
+ };
5810
+ const allRecords = [];
5811
+ let response = await database.performQuery(query);
5812
+ if (response.records) {
5813
+ allRecords.push(...response.records);
5814
+ }
5815
+ while (response.continuationMarker) {
5816
+ break;
5817
+ }
5818
+ const files = allRecords.map((record) => ({
5819
+ recordName: record.recordName,
5820
+ filename: record.fields.filename?.value ?? "",
5821
+ modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
5822
+ size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
5823
+ }));
5824
+ return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
5825
+ }
5826
+ async function downloadICloudFile(recordName) {
5827
+ const container = await getContainer();
5828
+ const database = container.privateCloudDatabase;
5829
+ const response = await database.fetchRecords([{ recordName }], {
5830
+ desiredKeys: ["filename", "data", "contentType"]
5831
+ });
5832
+ if (!response.records || response.records.length === 0) {
5833
+ throw new Error(`File not found: ${recordName}`);
5834
+ }
5835
+ const record = response.records[0];
5836
+ const dataField = record.fields.data?.value;
5837
+ if (!dataField) {
5838
+ throw new Error("No data in record");
5839
+ }
5840
+ if (typeof dataField === "string") {
5841
+ const binaryString = atob(dataField);
5842
+ const bytes = new Uint8Array(binaryString.length);
5843
+ for (let i = 0; i < binaryString.length; i++) {
5844
+ bytes[i] = binaryString.charCodeAt(i);
5845
+ }
5846
+ return new Blob([bytes], { type: "application/json" });
5847
+ }
5848
+ if (typeof dataField === "object" && "downloadURL" in dataField) {
5849
+ const fetchResponse = await fetch(
5850
+ dataField.downloadURL
5851
+ );
5852
+ if (!fetchResponse.ok) {
5853
+ throw new Error(`Failed to download from iCloud: ${fetchResponse.status}`);
5854
+ }
5855
+ return fetchResponse.blob();
5856
+ }
5857
+ throw new Error("Unknown data format in iCloud record");
5858
+ }
5859
+ async function findICloudFile(filename) {
5860
+ const container = await getContainer();
5861
+ const database = container.privateCloudDatabase;
5862
+ const query = {
5863
+ recordType: RECORD_TYPE,
5864
+ filterBy: [
5865
+ {
5866
+ fieldName: "filename",
5867
+ comparator: "EQUALS",
5868
+ fieldValue: { value: filename }
5869
+ }
5870
+ ]
5871
+ };
5872
+ const response = await database.performQuery(query);
5873
+ if (!response.records || response.records.length === 0) {
5874
+ return null;
5875
+ }
5876
+ const record = response.records[0];
5877
+ return {
5878
+ recordName: record.recordName,
5879
+ filename: record.fields.filename?.value ?? "",
5880
+ modifiedAt: new Date(record.modified?.timestamp ?? Date.now()),
5881
+ size: typeof record.fields.data?.value === "object" && record.fields.data?.value !== null ? record.fields.data.value.size : 0
5882
+ };
5883
+ }
5884
+
5885
+ // src/react/useICloudAuth.ts
5886
+ var ICloudAuthContext = createContext3(null);
5887
+ function ICloudAuthProvider({
5888
+ apiToken,
5889
+ containerIdentifier = DEFAULT_CONTAINER_ID,
5890
+ environment = "production",
5891
+ children
5892
+ }) {
5893
+ const [isAuthenticated, setIsAuthenticated] = useState12(false);
5894
+ const [userRecordName, setUserRecordName] = useState12(null);
5895
+ const [isAvailable, setIsAvailable] = useState12(false);
5896
+ const [isConfigured, setIsConfigured] = useState12(false);
5897
+ const [isLoading, setIsLoading] = useState12(false);
5898
+ useEffect8(() => {
5899
+ if (!apiToken || typeof window === "undefined") {
5900
+ return;
5901
+ }
5902
+ const initCloudKit = async () => {
5903
+ setIsLoading(true);
5904
+ try {
5905
+ await loadCloudKit();
5906
+ setIsAvailable(true);
5907
+ const config = {
5908
+ containerIdentifier,
5909
+ apiToken,
5910
+ environment
5911
+ };
5912
+ await configureCloudKit(config);
5913
+ setIsConfigured(true);
5914
+ try {
5915
+ const userIdentity = await authenticateICloud();
5916
+ if (userIdentity) {
5917
+ setIsAuthenticated(true);
5918
+ setUserRecordName(userIdentity.userRecordName);
5919
+ }
5920
+ } catch {
5921
+ }
5922
+ } catch {
5923
+ setIsAvailable(false);
5924
+ setIsConfigured(false);
5925
+ } finally {
5926
+ setIsLoading(false);
5927
+ }
5928
+ };
5929
+ initCloudKit();
5930
+ }, [apiToken, containerIdentifier, environment]);
5931
+ const requestAccess = useCallback14(async () => {
5932
+ if (!isConfigured) {
5933
+ throw new Error("iCloud is not configured");
5934
+ }
5935
+ if (isAuthenticated) {
5936
+ return;
5937
+ }
5938
+ try {
5939
+ const userIdentity = await requestICloudSignIn();
5940
+ setIsAuthenticated(true);
5941
+ setUserRecordName(userIdentity.userRecordName);
5942
+ } catch (err) {
5943
+ throw new Error(
5944
+ err instanceof Error ? err.message : "Failed to sign in to iCloud"
5945
+ );
5946
+ }
5947
+ }, [isAuthenticated, isConfigured]);
5948
+ const logout = useCallback14(() => {
5949
+ setIsAuthenticated(false);
5950
+ setUserRecordName(null);
5951
+ }, []);
5952
+ return createElement3(
5953
+ ICloudAuthContext.Provider,
5954
+ {
5955
+ value: {
5956
+ isAuthenticated,
5957
+ isConfigured,
5958
+ isAvailable,
5959
+ userRecordName,
5960
+ requestAccess,
5961
+ logout
5962
+ }
5963
+ },
5964
+ children
5965
+ );
5966
+ }
5967
+ function useICloudAuth() {
5968
+ const context = useContext3(ICloudAuthContext);
5969
+ if (!context) {
5970
+ throw new Error("useICloudAuth must be used within ICloudAuthProvider");
5971
+ }
5972
+ return context;
5973
+ }
5974
+ function hasICloudCredentials() {
5975
+ return isCloudKitAvailable();
5976
+ }
5977
+ function clearICloudAuth() {
5978
+ }
5979
+
5980
+ // src/react/useICloudBackup.ts
5981
+ import { useCallback as useCallback15, useMemo as useMemo6 } from "react";
5982
+
5983
+ // src/lib/backup/icloud/backup.ts
5984
+ var isAuthError3 = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
5985
+ async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
5986
+ try {
5987
+ await deps.requestEncryptionKey(userAddress);
5988
+ const filename = `${conversationId}.json`;
5989
+ const existingFile = await findICloudFile(filename);
5990
+ if (existingFile) {
5991
+ const { Q: Q4 } = await import("@nozbe/watermelondb");
5992
+ const conversationsCollection = database.get("conversations");
5993
+ const records = await conversationsCollection.query(Q4.where("conversation_id", conversationId)).fetch();
5994
+ if (records.length > 0) {
5995
+ const conversation = conversationToStored(records[0]);
5996
+ const localUpdated = conversation.updatedAt.getTime();
5997
+ const remoteModified = existingFile.modifiedAt.getTime();
5998
+ if (localUpdated <= remoteModified) {
5999
+ return "skipped";
6000
+ }
6001
+ }
6002
+ }
6003
+ const exportResult = await deps.exportConversation(
6004
+ conversationId,
6005
+ userAddress
6006
+ );
6007
+ if (!exportResult.success || !exportResult.blob) {
6008
+ return "failed";
6009
+ }
6010
+ await uploadFileToICloud(filename, exportResult.blob);
6011
+ return "uploaded";
6012
+ } catch (err) {
6013
+ if (isAuthError3(err) && !_retried) {
6014
+ try {
6015
+ await deps.requestICloudAccess();
6016
+ return pushConversationToICloud(
6017
+ database,
6018
+ conversationId,
6019
+ userAddress,
6020
+ deps,
6021
+ true
6022
+ );
6023
+ } catch {
6024
+ return "failed";
6025
+ }
6026
+ }
6027
+ return "failed";
6028
+ }
6029
+ }
6030
+ async function performICloudExport(database, userAddress, deps, onProgress) {
6031
+ await deps.requestEncryptionKey(userAddress);
6032
+ const { Q: Q4 } = await import("@nozbe/watermelondb");
6033
+ const conversationsCollection = database.get("conversations");
6034
+ const records = await conversationsCollection.query(Q4.where("is_deleted", false)).fetch();
6035
+ const conversations = records.map(conversationToStored);
6036
+ const total = conversations.length;
6037
+ if (total === 0) {
6038
+ return { success: true, uploaded: 0, skipped: 0, total: 0 };
6039
+ }
6040
+ let uploaded = 0;
6041
+ let skipped = 0;
6042
+ for (let i = 0; i < conversations.length; i++) {
6043
+ const conv = conversations[i];
6044
+ onProgress?.(i + 1, total);
6045
+ const result = await pushConversationToICloud(
6046
+ database,
6047
+ conv.conversationId,
6048
+ userAddress,
6049
+ deps
6050
+ );
6051
+ if (result === "uploaded") uploaded++;
6052
+ if (result === "skipped") skipped++;
6053
+ }
6054
+ return { success: true, uploaded, skipped, total };
6055
+ }
6056
+ async function performICloudImport(userAddress, deps, onProgress) {
6057
+ await deps.requestEncryptionKey(userAddress);
6058
+ const remoteFiles = await listICloudFiles();
6059
+ if (remoteFiles.length === 0) {
6060
+ return {
6061
+ success: false,
6062
+ restored: 0,
6063
+ failed: 0,
6064
+ total: 0,
6065
+ noBackupsFound: true
6066
+ };
6067
+ }
6068
+ const jsonFiles = remoteFiles.filter(
6069
+ (file) => file.filename.endsWith(".json")
6070
+ );
6071
+ const total = jsonFiles.length;
6072
+ let restored = 0;
6073
+ let failed = 0;
6074
+ for (let i = 0; i < jsonFiles.length; i++) {
6075
+ const file = jsonFiles[i];
6076
+ onProgress?.(i + 1, total);
6077
+ try {
6078
+ const blob = await downloadICloudFile(file.recordName);
6079
+ const result = await deps.importConversation(blob, userAddress);
6080
+ if (result.success) {
6081
+ restored++;
6082
+ } else {
6083
+ failed++;
6084
+ }
6085
+ } catch (err) {
6086
+ if (isAuthError3(err)) {
6087
+ try {
6088
+ await deps.requestICloudAccess();
6089
+ const blob = await downloadICloudFile(file.recordName);
6090
+ const result = await deps.importConversation(blob, userAddress);
6091
+ if (result.success) {
6092
+ restored++;
6093
+ } else {
6094
+ failed++;
6095
+ }
6096
+ } catch {
6097
+ failed++;
6098
+ }
6099
+ } else {
6100
+ failed++;
6101
+ }
6102
+ }
6103
+ }
6104
+ return { success: true, restored, failed, total };
6105
+ }
6106
+
6107
+ // src/react/useICloudBackup.ts
6108
+ function useICloudBackup(options) {
6109
+ const {
6110
+ database,
6111
+ userAddress,
6112
+ requestEncryptionKey: requestEncryptionKey2,
6113
+ exportConversation,
6114
+ importConversation
6115
+ } = options;
6116
+ const {
6117
+ isAuthenticated,
6118
+ isConfigured,
6119
+ isAvailable,
6120
+ requestAccess
6121
+ } = useICloudAuth();
6122
+ const deps = useMemo6(
6123
+ () => ({
6124
+ requestICloudAccess: requestAccess,
6125
+ requestEncryptionKey: requestEncryptionKey2,
6126
+ exportConversation,
6127
+ importConversation
6128
+ }),
6129
+ [requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6130
+ );
6131
+ const ensureAuthenticated = useCallback15(async () => {
6132
+ if (isAuthenticated) return true;
6133
+ try {
6134
+ await requestAccess();
6135
+ return true;
6136
+ } catch {
6137
+ return false;
6138
+ }
6139
+ }, [isAuthenticated, requestAccess]);
6140
+ const backup = useCallback15(
6141
+ async (backupOptions) => {
6142
+ if (!userAddress) {
6143
+ return { error: "Please sign in to backup to iCloud" };
6144
+ }
6145
+ if (!isAvailable) {
6146
+ return { error: "CloudKit JS is not loaded" };
6147
+ }
6148
+ if (!isConfigured) {
6149
+ return { error: "iCloud is not configured" };
6150
+ }
6151
+ const authenticated = await ensureAuthenticated();
6152
+ if (!authenticated) {
6153
+ return { error: "iCloud access denied" };
6154
+ }
6155
+ try {
6156
+ return await performICloudExport(
6157
+ database,
6158
+ userAddress,
6159
+ deps,
6160
+ backupOptions?.onProgress
6161
+ );
6162
+ } catch (err) {
6163
+ return {
6164
+ error: err instanceof Error ? err.message : "Failed to backup to iCloud"
6165
+ };
6166
+ }
6167
+ },
6168
+ [database, userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
6169
+ );
6170
+ const restore = useCallback15(
6171
+ async (restoreOptions) => {
6172
+ if (!userAddress) {
6173
+ return { error: "Please sign in to restore from iCloud" };
6174
+ }
6175
+ if (!isAvailable) {
6176
+ return { error: "CloudKit JS is not loaded" };
6177
+ }
6178
+ if (!isConfigured) {
6179
+ return { error: "iCloud is not configured" };
6180
+ }
6181
+ const authenticated = await ensureAuthenticated();
6182
+ if (!authenticated) {
6183
+ return { error: "iCloud access denied" };
6184
+ }
6185
+ try {
6186
+ return await performICloudImport(
6187
+ userAddress,
6188
+ deps,
6189
+ restoreOptions?.onProgress
6190
+ );
6191
+ } catch (err) {
6192
+ return {
6193
+ error: err instanceof Error ? err.message : "Failed to restore from iCloud"
6194
+ };
6195
+ }
6196
+ },
6197
+ [userAddress, isAvailable, isConfigured, ensureAuthenticated, deps]
6198
+ );
6199
+ return {
6200
+ backup,
6201
+ restore,
6202
+ isConfigured,
6203
+ isAuthenticated,
6204
+ isAvailable
6205
+ };
6206
+ }
6207
+
6208
+ // src/react/useBackupAuth.ts
6209
+ import {
6210
+ createContext as createContext4,
6211
+ createElement as createElement4,
6212
+ useCallback as useCallback16,
6213
+ useContext as useContext4,
6214
+ useEffect as useEffect9,
6215
+ useState as useState13
6216
+ } from "react";
6217
+ var BackupAuthContext = createContext4(null);
5588
6218
  function BackupAuthProvider({
5589
6219
  dropboxAppKey,
5590
6220
  dropboxCallbackPath = "/auth/dropbox/callback",
5591
6221
  googleClientId,
5592
6222
  googleCallbackPath = "/auth/google/callback",
6223
+ icloudApiToken,
6224
+ icloudContainerIdentifier = DEFAULT_CONTAINER_ID,
6225
+ icloudEnvironment = "production",
5593
6226
  apiClient,
5594
6227
  children
5595
6228
  }) {
5596
- const [dropboxToken, setDropboxToken] = useState12(null);
6229
+ const [dropboxToken, setDropboxToken] = useState13(null);
5597
6230
  const isDropboxConfigured = !!dropboxAppKey;
5598
- const [googleToken, setGoogleToken] = useState12(null);
6231
+ const [googleToken, setGoogleToken] = useState13(null);
5599
6232
  const isGoogleConfigured = !!googleClientId;
5600
- useEffect8(() => {
6233
+ const [icloudAuthenticated, setIcloudAuthenticated] = useState13(false);
6234
+ const [icloudUserRecordName, setIcloudUserRecordName] = useState13(null);
6235
+ const [isIcloudAvailable, setIsIcloudAvailable] = useState13(false);
6236
+ const isIcloudConfigured = isIcloudAvailable && !!icloudApiToken;
6237
+ useEffect9(() => {
5601
6238
  const checkStoredTokens = async () => {
5602
6239
  if (hasDropboxCredentials()) {
5603
6240
  const token = await getDropboxAccessToken(apiClient);
@@ -5614,7 +6251,35 @@ function BackupAuthProvider({
5614
6251
  };
5615
6252
  checkStoredTokens();
5616
6253
  }, [apiClient]);
5617
- useEffect8(() => {
6254
+ useEffect9(() => {
6255
+ if (!icloudApiToken || typeof window === "undefined") {
6256
+ return;
6257
+ }
6258
+ const initCloudKit = async () => {
6259
+ try {
6260
+ await loadCloudKit();
6261
+ setIsIcloudAvailable(true);
6262
+ const config = {
6263
+ containerIdentifier: icloudContainerIdentifier,
6264
+ apiToken: icloudApiToken,
6265
+ environment: icloudEnvironment
6266
+ };
6267
+ await configureCloudKit(config);
6268
+ try {
6269
+ const userIdentity = await authenticateICloud();
6270
+ if (userIdentity) {
6271
+ setIcloudAuthenticated(true);
6272
+ setIcloudUserRecordName(userIdentity.userRecordName);
6273
+ }
6274
+ } catch {
6275
+ }
6276
+ } catch {
6277
+ setIsIcloudAvailable(false);
6278
+ }
6279
+ };
6280
+ initCloudKit();
6281
+ }, [icloudApiToken, icloudContainerIdentifier, icloudEnvironment]);
6282
+ useEffect9(() => {
5618
6283
  if (!isDropboxConfigured) return;
5619
6284
  const handleCallback = async () => {
5620
6285
  if (isDropboxCallback()) {
@@ -5629,7 +6294,7 @@ function BackupAuthProvider({
5629
6294
  };
5630
6295
  handleCallback();
5631
6296
  }, [dropboxCallbackPath, isDropboxConfigured, apiClient]);
5632
- useEffect8(() => {
6297
+ useEffect9(() => {
5633
6298
  if (!isGoogleConfigured) return;
5634
6299
  const handleCallback = async () => {
5635
6300
  if (isGoogleDriveCallback()) {
@@ -5644,14 +6309,14 @@ function BackupAuthProvider({
5644
6309
  };
5645
6310
  handleCallback();
5646
6311
  }, [googleCallbackPath, isGoogleConfigured, apiClient]);
5647
- const refreshDropboxTokenFn = useCallback14(async () => {
6312
+ const refreshDropboxTokenFn = useCallback16(async () => {
5648
6313
  const token = await getDropboxAccessToken(apiClient);
5649
6314
  if (token) {
5650
6315
  setDropboxToken(token);
5651
6316
  }
5652
6317
  return token;
5653
6318
  }, [apiClient]);
5654
- const requestDropboxAccess = useCallback14(async () => {
6319
+ const requestDropboxAccess = useCallback16(async () => {
5655
6320
  if (!isDropboxConfigured || !dropboxAppKey) {
5656
6321
  throw new Error("Dropbox is not configured");
5657
6322
  }
@@ -5671,18 +6336,18 @@ function BackupAuthProvider({
5671
6336
  isDropboxConfigured,
5672
6337
  apiClient
5673
6338
  ]);
5674
- const logoutDropbox = useCallback14(async () => {
6339
+ const logoutDropbox = useCallback16(async () => {
5675
6340
  await revokeDropboxToken(apiClient);
5676
6341
  setDropboxToken(null);
5677
6342
  }, [apiClient]);
5678
- const refreshGoogleTokenFn = useCallback14(async () => {
6343
+ const refreshGoogleTokenFn = useCallback16(async () => {
5679
6344
  const token = await getGoogleDriveAccessToken(apiClient);
5680
6345
  if (token) {
5681
6346
  setGoogleToken(token);
5682
6347
  }
5683
6348
  return token;
5684
6349
  }, [apiClient]);
5685
- const requestGoogleAccess = useCallback14(async () => {
6350
+ const requestGoogleAccess = useCallback16(async () => {
5686
6351
  if (!isGoogleConfigured || !googleClientId) {
5687
6352
  throw new Error("Google Drive is not configured");
5688
6353
  }
@@ -5702,16 +6367,51 @@ function BackupAuthProvider({
5702
6367
  isGoogleConfigured,
5703
6368
  apiClient
5704
6369
  ]);
5705
- const logoutGoogle = useCallback14(async () => {
6370
+ const logoutGoogle = useCallback16(async () => {
5706
6371
  await revokeGoogleDriveToken(apiClient);
5707
6372
  setGoogleToken(null);
5708
6373
  }, [apiClient]);
5709
- const logoutAll = useCallback14(async () => {
6374
+ const refreshIcloudTokenFn = useCallback16(async () => {
6375
+ try {
6376
+ const userIdentity = await authenticateICloud();
6377
+ if (userIdentity) {
6378
+ setIcloudAuthenticated(true);
6379
+ setIcloudUserRecordName(userIdentity.userRecordName);
6380
+ return userIdentity.userRecordName;
6381
+ }
6382
+ } catch {
6383
+ }
6384
+ return null;
6385
+ }, []);
6386
+ const requestIcloudAccess = useCallback16(async () => {
6387
+ if (!isIcloudConfigured) {
6388
+ throw new Error("iCloud is not configured");
6389
+ }
6390
+ if (icloudAuthenticated && icloudUserRecordName) {
6391
+ return icloudUserRecordName;
6392
+ }
6393
+ try {
6394
+ const userIdentity = await requestICloudSignIn();
6395
+ setIcloudAuthenticated(true);
6396
+ setIcloudUserRecordName(userIdentity.userRecordName);
6397
+ return userIdentity.userRecordName;
6398
+ } catch (err) {
6399
+ throw new Error(
6400
+ err instanceof Error ? err.message : "Failed to sign in to iCloud"
6401
+ );
6402
+ }
6403
+ }, [icloudAuthenticated, icloudUserRecordName, isIcloudConfigured]);
6404
+ const logoutIcloud = useCallback16(async () => {
6405
+ setIcloudAuthenticated(false);
6406
+ setIcloudUserRecordName(null);
6407
+ }, []);
6408
+ const logoutAll = useCallback16(async () => {
5710
6409
  await Promise.all([
5711
6410
  isDropboxConfigured ? logoutDropbox() : Promise.resolve(),
5712
- isGoogleConfigured ? logoutGoogle() : Promise.resolve()
6411
+ isGoogleConfigured ? logoutGoogle() : Promise.resolve(),
6412
+ isIcloudConfigured ? logoutIcloud() : Promise.resolve()
5713
6413
  ]);
5714
- }, [isDropboxConfigured, isGoogleConfigured, logoutDropbox, logoutGoogle]);
6414
+ }, [isDropboxConfigured, isGoogleConfigured, isIcloudConfigured, logoutDropbox, logoutGoogle, logoutIcloud]);
5715
6415
  const dropboxState = {
5716
6416
  accessToken: dropboxToken,
5717
6417
  isAuthenticated: !!dropboxToken,
@@ -5728,14 +6428,24 @@ function BackupAuthProvider({
5728
6428
  logout: logoutGoogle,
5729
6429
  refreshToken: refreshGoogleTokenFn
5730
6430
  };
5731
- return createElement3(
6431
+ const icloudState = {
6432
+ accessToken: icloudUserRecordName,
6433
+ // Use userRecordName as the "token" for iCloud
6434
+ isAuthenticated: icloudAuthenticated,
6435
+ isConfigured: isIcloudConfigured,
6436
+ requestAccess: requestIcloudAccess,
6437
+ logout: logoutIcloud,
6438
+ refreshToken: refreshIcloudTokenFn
6439
+ };
6440
+ return createElement4(
5732
6441
  BackupAuthContext.Provider,
5733
6442
  {
5734
6443
  value: {
5735
6444
  dropbox: dropboxState,
5736
6445
  googleDrive: googleDriveState,
5737
- hasAnyProvider: isDropboxConfigured || isGoogleConfigured,
5738
- hasAnyAuthentication: !!dropboxToken || !!googleToken,
6446
+ icloud: icloudState,
6447
+ hasAnyProvider: isDropboxConfigured || isGoogleConfigured || isIcloudConfigured,
6448
+ hasAnyAuthentication: !!dropboxToken || !!googleToken || icloudAuthenticated,
5739
6449
  logoutAll
5740
6450
  }
5741
6451
  },
@@ -5743,7 +6453,7 @@ function BackupAuthProvider({
5743
6453
  );
5744
6454
  }
5745
6455
  function useBackupAuth() {
5746
- const context = useContext3(BackupAuthContext);
6456
+ const context = useContext4(BackupAuthContext);
5747
6457
  if (!context) {
5748
6458
  throw new Error("useBackupAuth must be used within BackupAuthProvider");
5749
6459
  }
@@ -5751,7 +6461,7 @@ function useBackupAuth() {
5751
6461
  }
5752
6462
 
5753
6463
  // src/react/useBackup.ts
5754
- import { useCallback as useCallback15, useMemo as useMemo6 } from "react";
6464
+ import { useCallback as useCallback17, useMemo as useMemo7 } from "react";
5755
6465
  function useBackup(options) {
5756
6466
  const {
5757
6467
  database,
@@ -5766,11 +6476,12 @@ function useBackup(options) {
5766
6476
  const {
5767
6477
  dropbox: dropboxAuth,
5768
6478
  googleDrive: googleDriveAuth,
6479
+ icloud: icloudAuth,
5769
6480
  hasAnyProvider,
5770
6481
  hasAnyAuthentication,
5771
6482
  logoutAll
5772
6483
  } = useBackupAuth();
5773
- const dropboxDeps = useMemo6(
6484
+ const dropboxDeps = useMemo7(
5774
6485
  () => ({
5775
6486
  requestDropboxAccess: dropboxAuth.requestAccess,
5776
6487
  requestEncryptionKey: requestEncryptionKey2,
@@ -5779,7 +6490,7 @@ function useBackup(options) {
5779
6490
  }),
5780
6491
  [dropboxAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
5781
6492
  );
5782
- const googleDriveDeps = useMemo6(
6493
+ const googleDriveDeps = useMemo7(
5783
6494
  () => ({
5784
6495
  requestDriveAccess: googleDriveAuth.requestAccess,
5785
6496
  requestEncryptionKey: requestEncryptionKey2,
@@ -5788,7 +6499,18 @@ function useBackup(options) {
5788
6499
  }),
5789
6500
  [googleDriveAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
5790
6501
  );
5791
- const dropboxBackup = useCallback15(
6502
+ const icloudDeps = useMemo7(
6503
+ () => ({
6504
+ requestICloudAccess: async () => {
6505
+ await icloudAuth.requestAccess();
6506
+ },
6507
+ requestEncryptionKey: requestEncryptionKey2,
6508
+ exportConversation,
6509
+ importConversation
6510
+ }),
6511
+ [icloudAuth.requestAccess, requestEncryptionKey2, exportConversation, importConversation]
6512
+ );
6513
+ const dropboxBackup = useCallback17(
5792
6514
  async (backupOptions) => {
5793
6515
  if (!userAddress) {
5794
6516
  return { error: "Please sign in to backup to Dropbox" };
@@ -5818,7 +6540,7 @@ function useBackup(options) {
5818
6540
  },
5819
6541
  [database, userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
5820
6542
  );
5821
- const dropboxRestore = useCallback15(
6543
+ const dropboxRestore = useCallback17(
5822
6544
  async (restoreOptions) => {
5823
6545
  if (!userAddress) {
5824
6546
  return { error: "Please sign in to restore from Dropbox" };
@@ -5847,7 +6569,7 @@ function useBackup(options) {
5847
6569
  },
5848
6570
  [userAddress, dropboxAuth, dropboxDeps, dropboxFolder]
5849
6571
  );
5850
- const googleDriveBackup = useCallback15(
6572
+ const googleDriveBackup = useCallback17(
5851
6573
  async (backupOptions) => {
5852
6574
  if (!userAddress) {
5853
6575
  return { error: "Please sign in to backup to Google Drive" };
@@ -5885,7 +6607,7 @@ function useBackup(options) {
5885
6607
  googleConversationsFolder
5886
6608
  ]
5887
6609
  );
5888
- const googleDriveRestore = useCallback15(
6610
+ const googleDriveRestore = useCallback17(
5889
6611
  async (restoreOptions) => {
5890
6612
  if (!userAddress) {
5891
6613
  return { error: "Please sign in to restore from Google Drive" };
@@ -5937,9 +6659,77 @@ function useBackup(options) {
5937
6659
  connect: googleDriveAuth.requestAccess,
5938
6660
  disconnect: googleDriveAuth.logout
5939
6661
  };
6662
+ const icloudBackup = useCallback17(
6663
+ async (backupOptions) => {
6664
+ if (!userAddress) {
6665
+ return { error: "Please sign in to backup to iCloud" };
6666
+ }
6667
+ if (!icloudAuth.isConfigured) {
6668
+ return { error: "iCloud is not configured" };
6669
+ }
6670
+ if (!icloudAuth.isAuthenticated) {
6671
+ try {
6672
+ await icloudAuth.requestAccess();
6673
+ } catch {
6674
+ return { error: "iCloud access denied" };
6675
+ }
6676
+ }
6677
+ try {
6678
+ return await performICloudExport(
6679
+ database,
6680
+ userAddress,
6681
+ icloudDeps,
6682
+ backupOptions?.onProgress
6683
+ );
6684
+ } catch (err) {
6685
+ return {
6686
+ error: err instanceof Error ? err.message : "Failed to backup to iCloud"
6687
+ };
6688
+ }
6689
+ },
6690
+ [database, userAddress, icloudAuth, icloudDeps]
6691
+ );
6692
+ const icloudRestore = useCallback17(
6693
+ async (restoreOptions) => {
6694
+ if (!userAddress) {
6695
+ return { error: "Please sign in to restore from iCloud" };
6696
+ }
6697
+ if (!icloudAuth.isConfigured) {
6698
+ return { error: "iCloud is not configured" };
6699
+ }
6700
+ if (!icloudAuth.isAuthenticated) {
6701
+ try {
6702
+ await icloudAuth.requestAccess();
6703
+ } catch {
6704
+ return { error: "iCloud access denied" };
6705
+ }
6706
+ }
6707
+ try {
6708
+ return await performICloudImport(
6709
+ userAddress,
6710
+ icloudDeps,
6711
+ restoreOptions?.onProgress
6712
+ );
6713
+ } catch (err) {
6714
+ return {
6715
+ error: err instanceof Error ? err.message : "Failed to restore from iCloud"
6716
+ };
6717
+ }
6718
+ },
6719
+ [userAddress, icloudAuth, icloudDeps]
6720
+ );
6721
+ const icloudState = {
6722
+ isConfigured: icloudAuth.isConfigured,
6723
+ isAuthenticated: icloudAuth.isAuthenticated,
6724
+ backup: icloudBackup,
6725
+ restore: icloudRestore,
6726
+ connect: icloudAuth.requestAccess,
6727
+ disconnect: icloudAuth.logout
6728
+ };
5940
6729
  return {
5941
6730
  dropbox: dropboxState,
5942
6731
  googleDrive: googleDriveState,
6732
+ icloud: icloudState,
5943
6733
  hasAnyProvider,
5944
6734
  hasAnyAuthentication,
5945
6735
  disconnectAll: logoutAll
@@ -5948,6 +6738,7 @@ function useBackup(options) {
5948
6738
  export {
5949
6739
  DEFAULT_CONVERSATIONS_FOLDER as BACKUP_DRIVE_CONVERSATIONS_FOLDER,
5950
6740
  DEFAULT_ROOT_FOLDER as BACKUP_DRIVE_ROOT_FOLDER,
6741
+ DEFAULT_BACKUP_FOLDER2 as BACKUP_ICLOUD_FOLDER,
5951
6742
  BackupAuthProvider,
5952
6743
  Conversation as ChatConversation,
5953
6744
  Message as ChatMessage,
@@ -5955,15 +6746,18 @@ export {
5955
6746
  DEFAULT_CONVERSATIONS_FOLDER as DEFAULT_DRIVE_CONVERSATIONS_FOLDER,
5956
6747
  DEFAULT_ROOT_FOLDER as DEFAULT_DRIVE_ROOT_FOLDER,
5957
6748
  DEFAULT_BACKUP_FOLDER as DEFAULT_DROPBOX_FOLDER,
6749
+ DEFAULT_BACKUP_FOLDER2 as DEFAULT_ICLOUD_BACKUP_FOLDER,
5958
6750
  DEFAULT_TOOL_SELECTOR_MODEL,
5959
6751
  DropboxAuthProvider,
5960
6752
  GoogleDriveAuthProvider,
6753
+ ICloudAuthProvider,
5961
6754
  Memory as StoredMemoryModel,
5962
6755
  ModelPreference as StoredModelPreferenceModel,
5963
6756
  chatStorageMigrations,
5964
6757
  chatStorageSchema,
5965
6758
  clearToken as clearDropboxToken,
5966
6759
  clearGoogleDriveToken,
6760
+ clearICloudAuth,
5967
6761
  createMemoryContextSystemMessage,
5968
6762
  decryptData,
5969
6763
  decryptDataBytes,
@@ -5978,6 +6772,7 @@ export {
5978
6772
  hasDropboxCredentials,
5979
6773
  hasEncryptionKey,
5980
6774
  hasGoogleDriveCredentials,
6775
+ hasICloudCredentials,
5981
6776
  memoryStorageSchema,
5982
6777
  requestEncryptionKey,
5983
6778
  sdkMigrations,
@@ -5994,6 +6789,8 @@ export {
5994
6789
  useEncryption,
5995
6790
  useGoogleDriveAuth,
5996
6791
  useGoogleDriveBackup,
6792
+ useICloudAuth,
6793
+ useICloudBackup,
5997
6794
  useImageGeneration,
5998
6795
  useMemoryStorage,
5999
6796
  useModels,