@runanywhere/web 0.1.0-beta.4 → 0.1.0-beta.5

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.
Files changed (160) hide show
  1. package/dist/Foundation/StructOffsets.d.ts +33 -5
  2. package/dist/Foundation/StructOffsets.d.ts.map +1 -1
  3. package/dist/Foundation/StructOffsets.js +128 -94
  4. package/dist/Foundation/StructOffsets.js.map +1 -1
  5. package/dist/Infrastructure/ExtensionPoint.d.ts +78 -0
  6. package/dist/Infrastructure/ExtensionPoint.d.ts.map +1 -0
  7. package/dist/Infrastructure/ExtensionPoint.js +118 -0
  8. package/dist/Infrastructure/ExtensionPoint.js.map +1 -0
  9. package/dist/Infrastructure/LocalFileStorage.d.ts +116 -0
  10. package/dist/Infrastructure/LocalFileStorage.d.ts.map +1 -0
  11. package/dist/Infrastructure/LocalFileStorage.js +368 -0
  12. package/dist/Infrastructure/LocalFileStorage.js.map +1 -0
  13. package/dist/Infrastructure/ModelDownloader.d.ts +16 -5
  14. package/dist/Infrastructure/ModelDownloader.d.ts.map +1 -1
  15. package/dist/Infrastructure/ModelDownloader.js +54 -7
  16. package/dist/Infrastructure/ModelDownloader.js.map +1 -1
  17. package/dist/Infrastructure/ModelFileInference.d.ts +39 -0
  18. package/dist/Infrastructure/ModelFileInference.d.ts.map +1 -0
  19. package/dist/Infrastructure/ModelFileInference.js +110 -0
  20. package/dist/Infrastructure/ModelFileInference.js.map +1 -0
  21. package/dist/Infrastructure/ModelLoaderTypes.d.ts +76 -11
  22. package/dist/Infrastructure/ModelLoaderTypes.d.ts.map +1 -1
  23. package/dist/Infrastructure/ModelLoaderTypes.js +7 -1
  24. package/dist/Infrastructure/ModelLoaderTypes.js.map +1 -1
  25. package/dist/Infrastructure/ModelManager.d.ts +30 -104
  26. package/dist/Infrastructure/ModelManager.d.ts.map +1 -1
  27. package/dist/Infrastructure/ModelManager.js +209 -544
  28. package/dist/Infrastructure/ModelManager.js.map +1 -1
  29. package/dist/Infrastructure/ModelRegistry.d.ts +6 -0
  30. package/dist/Infrastructure/ModelRegistry.d.ts.map +1 -1
  31. package/dist/Infrastructure/ModelRegistry.js +11 -0
  32. package/dist/Infrastructure/ModelRegistry.js.map +1 -1
  33. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.d.ts +3 -29
  34. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.d.ts.map +1 -1
  35. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.js +46 -42
  36. package/dist/Public/Extensions/RunAnywhere+VoicePipeline.js.map +1 -1
  37. package/dist/Public/Extensions/VoicePipelineTypes.d.ts +28 -37
  38. package/dist/Public/Extensions/VoicePipelineTypes.d.ts.map +1 -1
  39. package/dist/Public/Extensions/VoicePipelineTypes.js +4 -1
  40. package/dist/Public/Extensions/VoicePipelineTypes.js.map +1 -1
  41. package/dist/Public/RunAnywhere.d.ts +65 -61
  42. package/dist/Public/RunAnywhere.d.ts.map +1 -1
  43. package/dist/Public/RunAnywhere.js +193 -141
  44. package/dist/Public/RunAnywhere.js.map +1 -1
  45. package/dist/index.d.ts +23 -39
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +19 -31
  48. package/dist/index.js.map +1 -1
  49. package/dist/types/index.d.ts +0 -1
  50. package/dist/types/index.d.ts.map +1 -1
  51. package/dist/types/index.js +1 -0
  52. package/dist/types/index.js.map +1 -1
  53. package/package.json +1 -5
  54. package/wasm/racommons.js +91 -91
  55. package/wasm/racommons.wasm +0 -0
  56. package/dist/Foundation/SherpaONNXBridge.d.ts +0 -147
  57. package/dist/Foundation/SherpaONNXBridge.d.ts.map +0 -1
  58. package/dist/Foundation/SherpaONNXBridge.js +0 -345
  59. package/dist/Foundation/SherpaONNXBridge.js.map +0 -1
  60. package/dist/Infrastructure/AudioCapture.d.ts +0 -87
  61. package/dist/Infrastructure/AudioCapture.d.ts.map +0 -1
  62. package/dist/Infrastructure/AudioCapture.js +0 -231
  63. package/dist/Infrastructure/AudioCapture.js.map +0 -1
  64. package/dist/Infrastructure/AudioPlayback.d.ts +0 -53
  65. package/dist/Infrastructure/AudioPlayback.d.ts.map +0 -1
  66. package/dist/Infrastructure/AudioPlayback.js +0 -117
  67. package/dist/Infrastructure/AudioPlayback.js.map +0 -1
  68. package/dist/Infrastructure/VLMWorkerBridge.d.ts +0 -211
  69. package/dist/Infrastructure/VLMWorkerBridge.d.ts.map +0 -1
  70. package/dist/Infrastructure/VLMWorkerBridge.js +0 -264
  71. package/dist/Infrastructure/VLMWorkerBridge.js.map +0 -1
  72. package/dist/Infrastructure/VLMWorkerRuntime.d.ts +0 -38
  73. package/dist/Infrastructure/VLMWorkerRuntime.d.ts.map +0 -1
  74. package/dist/Infrastructure/VLMWorkerRuntime.js +0 -503
  75. package/dist/Infrastructure/VLMWorkerRuntime.js.map +0 -1
  76. package/dist/Infrastructure/VideoCapture.d.ts +0 -118
  77. package/dist/Infrastructure/VideoCapture.d.ts.map +0 -1
  78. package/dist/Infrastructure/VideoCapture.js +0 -207
  79. package/dist/Infrastructure/VideoCapture.js.map +0 -1
  80. package/dist/Public/Extensions/DiffusionTypes.d.ts +0 -64
  81. package/dist/Public/Extensions/DiffusionTypes.d.ts.map +0 -1
  82. package/dist/Public/Extensions/DiffusionTypes.js +0 -28
  83. package/dist/Public/Extensions/DiffusionTypes.js.map +0 -1
  84. package/dist/Public/Extensions/EmbeddingsTypes.d.ts +0 -33
  85. package/dist/Public/Extensions/EmbeddingsTypes.d.ts.map +0 -1
  86. package/dist/Public/Extensions/EmbeddingsTypes.js +0 -13
  87. package/dist/Public/Extensions/EmbeddingsTypes.js.map +0 -1
  88. package/dist/Public/Extensions/RunAnywhere+Diffusion.d.ts +0 -44
  89. package/dist/Public/Extensions/RunAnywhere+Diffusion.d.ts.map +0 -1
  90. package/dist/Public/Extensions/RunAnywhere+Diffusion.js +0 -189
  91. package/dist/Public/Extensions/RunAnywhere+Diffusion.js.map +0 -1
  92. package/dist/Public/Extensions/RunAnywhere+Embeddings.d.ts +0 -56
  93. package/dist/Public/Extensions/RunAnywhere+Embeddings.d.ts.map +0 -1
  94. package/dist/Public/Extensions/RunAnywhere+Embeddings.js +0 -240
  95. package/dist/Public/Extensions/RunAnywhere+Embeddings.js.map +0 -1
  96. package/dist/Public/Extensions/RunAnywhere+STT.d.ts +0 -95
  97. package/dist/Public/Extensions/RunAnywhere+STT.d.ts.map +0 -1
  98. package/dist/Public/Extensions/RunAnywhere+STT.js +0 -417
  99. package/dist/Public/Extensions/RunAnywhere+STT.js.map +0 -1
  100. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.d.ts +0 -69
  101. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.d.ts.map +0 -1
  102. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.js +0 -196
  103. package/dist/Public/Extensions/RunAnywhere+StructuredOutput.js.map +0 -1
  104. package/dist/Public/Extensions/RunAnywhere+TTS.d.ts +0 -55
  105. package/dist/Public/Extensions/RunAnywhere+TTS.d.ts.map +0 -1
  106. package/dist/Public/Extensions/RunAnywhere+TTS.js +0 -253
  107. package/dist/Public/Extensions/RunAnywhere+TTS.js.map +0 -1
  108. package/dist/Public/Extensions/RunAnywhere+TextGeneration.d.ts +0 -80
  109. package/dist/Public/Extensions/RunAnywhere+TextGeneration.d.ts.map +0 -1
  110. package/dist/Public/Extensions/RunAnywhere+TextGeneration.js +0 -470
  111. package/dist/Public/Extensions/RunAnywhere+TextGeneration.js.map +0 -1
  112. package/dist/Public/Extensions/RunAnywhere+ToolCalling.d.ts +0 -82
  113. package/dist/Public/Extensions/RunAnywhere+ToolCalling.d.ts.map +0 -1
  114. package/dist/Public/Extensions/RunAnywhere+ToolCalling.js +0 -576
  115. package/dist/Public/Extensions/RunAnywhere+ToolCalling.js.map +0 -1
  116. package/dist/Public/Extensions/RunAnywhere+VAD.d.ts +0 -70
  117. package/dist/Public/Extensions/RunAnywhere+VAD.d.ts.map +0 -1
  118. package/dist/Public/Extensions/RunAnywhere+VAD.js +0 -231
  119. package/dist/Public/Extensions/RunAnywhere+VAD.js.map +0 -1
  120. package/dist/Public/Extensions/RunAnywhere+VLM.d.ts +0 -58
  121. package/dist/Public/Extensions/RunAnywhere+VLM.d.ts.map +0 -1
  122. package/dist/Public/Extensions/RunAnywhere+VLM.js +0 -262
  123. package/dist/Public/Extensions/RunAnywhere+VLM.js.map +0 -1
  124. package/dist/Public/Extensions/STTTypes.d.ts +0 -53
  125. package/dist/Public/Extensions/STTTypes.d.ts.map +0 -1
  126. package/dist/Public/Extensions/STTTypes.js +0 -16
  127. package/dist/Public/Extensions/STTTypes.js.map +0 -1
  128. package/dist/Public/Extensions/TTSTypes.d.ts +0 -31
  129. package/dist/Public/Extensions/TTSTypes.d.ts.map +0 -1
  130. package/dist/Public/Extensions/TTSTypes.js +0 -3
  131. package/dist/Public/Extensions/TTSTypes.js.map +0 -1
  132. package/dist/Public/Extensions/ToolCallingTypes.d.ts +0 -78
  133. package/dist/Public/Extensions/ToolCallingTypes.d.ts.map +0 -1
  134. package/dist/Public/Extensions/ToolCallingTypes.js +0 -8
  135. package/dist/Public/Extensions/ToolCallingTypes.js.map +0 -1
  136. package/dist/Public/Extensions/VADTypes.d.ts +0 -30
  137. package/dist/Public/Extensions/VADTypes.d.ts.map +0 -1
  138. package/dist/Public/Extensions/VADTypes.js +0 -8
  139. package/dist/Public/Extensions/VADTypes.js.map +0 -1
  140. package/dist/Public/Extensions/VLMTypes.d.ts +0 -56
  141. package/dist/Public/Extensions/VLMTypes.d.ts.map +0 -1
  142. package/dist/Public/Extensions/VLMTypes.js +0 -24
  143. package/dist/Public/Extensions/VLMTypes.js.map +0 -1
  144. package/dist/types/LLMTypes.d.ts +0 -48
  145. package/dist/types/LLMTypes.d.ts.map +0 -1
  146. package/dist/types/LLMTypes.js +0 -8
  147. package/dist/types/LLMTypes.js.map +0 -1
  148. package/dist/workers/vlm-worker.d.ts +0 -9
  149. package/dist/workers/vlm-worker.d.ts.map +0 -1
  150. package/dist/workers/vlm-worker.js +0 -10
  151. package/dist/workers/vlm-worker.js.map +0 -1
  152. package/wasm/racommons-webgpu.js +0 -156
  153. package/wasm/racommons-webgpu.wasm +0 -0
  154. package/wasm/sherpa/sherpa-onnx-asr.js +0 -1538
  155. package/wasm/sherpa/sherpa-onnx-glue-original.js +0 -19
  156. package/wasm/sherpa/sherpa-onnx-glue.js +0 -17
  157. package/wasm/sherpa/sherpa-onnx-tts.js +0 -657
  158. package/wasm/sherpa/sherpa-onnx-vad.js +0 -337
  159. package/wasm/sherpa/sherpa-onnx-wave.js +0 -88
  160. package/wasm/sherpa/sherpa-onnx.wasm +0 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * LocalFileStorage - Persistent model storage using the File System Access API
3
+ *
4
+ * Allows users to choose a real folder on their local filesystem for model
5
+ * storage. Models are saved as actual files (e.g. ~/ai-models/smollm2.gguf)
6
+ * that persist permanently — no browser eviction, no re-downloads.
7
+ *
8
+ * The directory handle is stored in IndexedDB (not cookies — handles are
9
+ * structured-cloneable objects that can't be serialized to strings).
10
+ *
11
+ * Browser support:
12
+ * - Chrome 122+: Full support with persistent permissions ("Allow on every visit")
13
+ * - Chrome 86-121: Supported but re-prompts for permission each session
14
+ * - Edge: Same as Chrome (Chromium-based)
15
+ * - Firefox/Safari: NOT supported — falls back to OPFS storage
16
+ *
17
+ * Usage:
18
+ * const storage = new LocalFileStorage();
19
+ * await storage.chooseDirectory(); // User picks a folder
20
+ * await storage.saveModel('model-id', data);
21
+ *
22
+ * // On return visit:
23
+ * const restored = await storage.restoreDirectory();
24
+ * if (restored) {
25
+ * const data = await storage.loadModel('model-id');
26
+ * }
27
+ */
28
+ export declare class LocalFileStorage {
29
+ private dirHandle;
30
+ private _isReady;
31
+ private _hasStoredHandle;
32
+ /** Whether the File System Access API is available in this browser. */
33
+ static get isSupported(): boolean;
34
+ /**
35
+ * Get the stored directory name from localStorage (fast, synchronous).
36
+ * Available immediately on page load before IndexedDB restores the handle.
37
+ * Returns the folder name only (e.g. "ai-models"), not the full path.
38
+ */
39
+ static get storedDirectoryName(): string | null;
40
+ /** Whether the storage is ready for use (directory selected + permission granted). */
41
+ get isReady(): boolean;
42
+ /** Whether a handle was found in IndexedDB (even if permission isn't granted yet). */
43
+ get hasStoredHandle(): boolean;
44
+ /** The name of the chosen directory (for display in UI). */
45
+ get directoryName(): string | null;
46
+ /**
47
+ * Prompt the user to choose a directory for model storage.
48
+ * Opens the OS folder picker dialog.
49
+ * Stores the directory handle in IndexedDB for future sessions.
50
+ *
51
+ * @returns true if a directory was selected, false if cancelled
52
+ */
53
+ chooseDirectory(): Promise<boolean>;
54
+ /**
55
+ * Attempt to restore a previously chosen directory from IndexedDB.
56
+ *
57
+ * On Chrome 122+, if the user selected "Allow on every visit",
58
+ * permission is automatically granted without any prompt.
59
+ *
60
+ * @returns true if directory was restored and permission is granted.
61
+ * false if no handle stored or permission not granted (UI
62
+ * should show a "Re-authorize" button).
63
+ */
64
+ restoreDirectory(): Promise<boolean>;
65
+ /**
66
+ * Request readwrite permission on a previously stored handle.
67
+ * MUST be called from a user gesture (button click handler).
68
+ *
69
+ * @returns true if permission was granted
70
+ */
71
+ requestAccess(): Promise<boolean>;
72
+ /**
73
+ * Save model data to the local filesystem.
74
+ * @param key - Model identifier (used as filename)
75
+ * @param data - Model file data
76
+ */
77
+ saveModel(key: string, data: ArrayBuffer): Promise<void>;
78
+ /**
79
+ * Load model data from the local filesystem.
80
+ * @param key - Model identifier
81
+ * @returns Model data, or null if not found
82
+ */
83
+ loadModel(key: string): Promise<ArrayBuffer | null>;
84
+ /**
85
+ * Check if a model file exists in local storage.
86
+ * @param key - Model identifier
87
+ */
88
+ hasModel(key: string): Promise<boolean>;
89
+ /**
90
+ * Delete a model file from local storage.
91
+ * @param key - Model identifier
92
+ */
93
+ deleteModel(key: string): Promise<void>;
94
+ /**
95
+ * Get file size without reading into memory.
96
+ * @param key - Model identifier
97
+ */
98
+ getFileSize(key: string): Promise<number | null>;
99
+ /**
100
+ * List all model files in the directory.
101
+ */
102
+ listModels(): Promise<Array<{
103
+ id: string;
104
+ sizeBytes: number;
105
+ lastModified: number;
106
+ }>>;
107
+ private openDB;
108
+ private storeHandle;
109
+ private retrieveHandle;
110
+ /**
111
+ * Sanitize a key for use as a filename.
112
+ * Keeps alphanumeric, dots, dashes, underscores. Replaces everything else.
113
+ */
114
+ private sanitizeFilename;
115
+ }
116
+ //# sourceMappingURL=LocalFileStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalFileStorage.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/LocalFileStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAoCH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAS;IAMjC,uEAAuE;IACvE,MAAM,KAAK,WAAW,IAAI,OAAO,CAEhC;IAED;;;;OAIG;IACH,MAAM,KAAK,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAM9C;IAMD,sFAAsF;IACtF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,sFAAsF;IACtF,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,4DAA4D;IAC5D,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAMD;;;;;;OAMG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAiCzC;;;;;;;;;OASG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAkC1C;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IA0BvC;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9D;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAczD;;;OAGG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAY7C;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7C;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAatD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;YAwB7E,MAAM;YAgBN,WAAW;YAiBX,cAAc;IAsB5B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CAGzB"}
@@ -0,0 +1,368 @@
1
+ /**
2
+ * LocalFileStorage - Persistent model storage using the File System Access API
3
+ *
4
+ * Allows users to choose a real folder on their local filesystem for model
5
+ * storage. Models are saved as actual files (e.g. ~/ai-models/smollm2.gguf)
6
+ * that persist permanently — no browser eviction, no re-downloads.
7
+ *
8
+ * The directory handle is stored in IndexedDB (not cookies — handles are
9
+ * structured-cloneable objects that can't be serialized to strings).
10
+ *
11
+ * Browser support:
12
+ * - Chrome 122+: Full support with persistent permissions ("Allow on every visit")
13
+ * - Chrome 86-121: Supported but re-prompts for permission each session
14
+ * - Edge: Same as Chrome (Chromium-based)
15
+ * - Firefox/Safari: NOT supported — falls back to OPFS storage
16
+ *
17
+ * Usage:
18
+ * const storage = new LocalFileStorage();
19
+ * await storage.chooseDirectory(); // User picks a folder
20
+ * await storage.saveModel('model-id', data);
21
+ *
22
+ * // On return visit:
23
+ * const restored = await storage.restoreDirectory();
24
+ * if (restored) {
25
+ * const data = await storage.loadModel('model-id');
26
+ * }
27
+ */
28
+ import { SDKLogger } from '../Foundation/SDKLogger';
29
+ const logger = new SDKLogger('LocalFileStorage');
30
+ /* eslint-enable @typescript-eslint/no-explicit-any */
31
+ // ---------------------------------------------------------------------------
32
+ // IndexedDB Constants
33
+ // ---------------------------------------------------------------------------
34
+ const DB_NAME = 'runanywhere-storage';
35
+ const DB_VERSION = 1;
36
+ const STORE_NAME = 'handles';
37
+ const HANDLE_KEY = 'modelDirectory';
38
+ const LS_DIR_NAME_KEY = 'runanywhere_storage_dir_name';
39
+ // ---------------------------------------------------------------------------
40
+ // LocalFileStorage
41
+ // ---------------------------------------------------------------------------
42
+ export class LocalFileStorage {
43
+ dirHandle = null;
44
+ _isReady = false;
45
+ _hasStoredHandle = false;
46
+ // -------------------------------------------------------------------------
47
+ // Static
48
+ // -------------------------------------------------------------------------
49
+ /** Whether the File System Access API is available in this browser. */
50
+ static get isSupported() {
51
+ return typeof window !== 'undefined' && 'showDirectoryPicker' in window;
52
+ }
53
+ /**
54
+ * Get the stored directory name from localStorage (fast, synchronous).
55
+ * Available immediately on page load before IndexedDB restores the handle.
56
+ * Returns the folder name only (e.g. "ai-models"), not the full path.
57
+ */
58
+ static get storedDirectoryName() {
59
+ try {
60
+ return localStorage.getItem(LS_DIR_NAME_KEY);
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ // -------------------------------------------------------------------------
67
+ // State
68
+ // -------------------------------------------------------------------------
69
+ /** Whether the storage is ready for use (directory selected + permission granted). */
70
+ get isReady() {
71
+ return this._isReady && this.dirHandle !== null;
72
+ }
73
+ /** Whether a handle was found in IndexedDB (even if permission isn't granted yet). */
74
+ get hasStoredHandle() {
75
+ return this._hasStoredHandle;
76
+ }
77
+ /** The name of the chosen directory (for display in UI). */
78
+ get directoryName() {
79
+ return this.dirHandle?.name ?? null;
80
+ }
81
+ // -------------------------------------------------------------------------
82
+ // Directory Selection
83
+ // -------------------------------------------------------------------------
84
+ /**
85
+ * Prompt the user to choose a directory for model storage.
86
+ * Opens the OS folder picker dialog.
87
+ * Stores the directory handle in IndexedDB for future sessions.
88
+ *
89
+ * @returns true if a directory was selected, false if cancelled
90
+ */
91
+ async chooseDirectory() {
92
+ if (!LocalFileStorage.isSupported) {
93
+ logger.warning('File System Access API not supported in this browser');
94
+ return false;
95
+ }
96
+ try {
97
+ // showDirectoryPicker requires user gesture (button click)
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ this.dirHandle = await window.showDirectoryPicker({
100
+ mode: 'readwrite',
101
+ });
102
+ await this.storeHandle(this.dirHandle);
103
+ this._isReady = true;
104
+ this._hasStoredHandle = true;
105
+ // Persist directory name in localStorage for fast UI display on next visit
106
+ try {
107
+ localStorage.setItem(LS_DIR_NAME_KEY, this.dirHandle.name);
108
+ }
109
+ catch { /* non-critical */ }
110
+ logger.info(`Local storage directory selected: ${this.dirHandle.name}`);
111
+ return true;
112
+ }
113
+ catch (err) {
114
+ if (err instanceof Error && err.name === 'AbortError') {
115
+ logger.debug('User cancelled directory picker');
116
+ return false;
117
+ }
118
+ const message = err instanceof Error ? err.message : String(err);
119
+ logger.error(`Failed to choose directory: ${message}`);
120
+ return false;
121
+ }
122
+ }
123
+ /**
124
+ * Attempt to restore a previously chosen directory from IndexedDB.
125
+ *
126
+ * On Chrome 122+, if the user selected "Allow on every visit",
127
+ * permission is automatically granted without any prompt.
128
+ *
129
+ * @returns true if directory was restored and permission is granted.
130
+ * false if no handle stored or permission not granted (UI
131
+ * should show a "Re-authorize" button).
132
+ */
133
+ async restoreDirectory() {
134
+ if (!LocalFileStorage.isSupported)
135
+ return false;
136
+ try {
137
+ const handle = await this.retrieveHandle();
138
+ if (!handle) {
139
+ logger.debug('No stored directory handle found');
140
+ return false;
141
+ }
142
+ this._hasStoredHandle = true;
143
+ this.dirHandle = handle;
144
+ // queryPermission does NOT require user gesture
145
+ const permission = await handle.queryPermission({ mode: 'readwrite' });
146
+ if (permission === 'granted') {
147
+ this._isReady = true;
148
+ logger.info(`Local storage restored: ${handle.name}`);
149
+ return true;
150
+ }
151
+ // Permission not granted yet — user needs to click a button
152
+ // to trigger requestPermission() via requestAccess()
153
+ logger.debug(`Local storage handle found but permission is '${permission}' — needs re-authorization`);
154
+ this._isReady = false;
155
+ return false;
156
+ }
157
+ catch (err) {
158
+ const message = err instanceof Error ? err.message : String(err);
159
+ logger.warning(`Failed to restore directory: ${message}`);
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Request readwrite permission on a previously stored handle.
165
+ * MUST be called from a user gesture (button click handler).
166
+ *
167
+ * @returns true if permission was granted
168
+ */
169
+ async requestAccess() {
170
+ if (!this.dirHandle) {
171
+ logger.warning('No directory handle to request access for');
172
+ return false;
173
+ }
174
+ try {
175
+ const permission = await this.dirHandle.requestPermission({ mode: 'readwrite' });
176
+ if (permission === 'granted') {
177
+ this._isReady = true;
178
+ logger.info(`Local storage re-authorized: ${this.dirHandle.name}`);
179
+ return true;
180
+ }
181
+ logger.debug(`Permission request result: ${permission}`);
182
+ return false;
183
+ }
184
+ catch (err) {
185
+ const message = err instanceof Error ? err.message : String(err);
186
+ logger.warning(`Permission request failed: ${message}`);
187
+ return false;
188
+ }
189
+ }
190
+ // -------------------------------------------------------------------------
191
+ // Model Operations
192
+ // -------------------------------------------------------------------------
193
+ /**
194
+ * Save model data to the local filesystem.
195
+ * @param key - Model identifier (used as filename)
196
+ * @param data - Model file data
197
+ */
198
+ async saveModel(key, data) {
199
+ if (!this.dirHandle || !this._isReady) {
200
+ throw new Error('LocalFileStorage not ready — call chooseDirectory() or restoreDirectory() first.');
201
+ }
202
+ const filename = this.sanitizeFilename(key);
203
+ const fileHandle = await this.dirHandle.getFileHandle(filename, { create: true });
204
+ const writable = await fileHandle.createWritable();
205
+ try {
206
+ await writable.write(data);
207
+ await writable.close();
208
+ logger.info(`Saved model to local storage: ${filename} (${(data.byteLength / 1024 / 1024).toFixed(1)} MB)`);
209
+ }
210
+ catch (err) {
211
+ try {
212
+ await writable.abort();
213
+ }
214
+ catch { /* ignore */ }
215
+ throw err;
216
+ }
217
+ }
218
+ /**
219
+ * Load model data from the local filesystem.
220
+ * @param key - Model identifier
221
+ * @returns Model data, or null if not found
222
+ */
223
+ async loadModel(key) {
224
+ if (!this.dirHandle || !this._isReady)
225
+ return null;
226
+ try {
227
+ const filename = this.sanitizeFilename(key);
228
+ const fileHandle = await this.dirHandle.getFileHandle(filename);
229
+ const file = await fileHandle.getFile();
230
+ logger.info(`Loaded model from local storage: ${filename} (${(file.size / 1024 / 1024).toFixed(1)} MB)`);
231
+ return await file.arrayBuffer();
232
+ }
233
+ catch {
234
+ return null; // File not found
235
+ }
236
+ }
237
+ /**
238
+ * Check if a model file exists in local storage.
239
+ * @param key - Model identifier
240
+ */
241
+ async hasModel(key) {
242
+ if (!this.dirHandle || !this._isReady)
243
+ return false;
244
+ try {
245
+ const filename = this.sanitizeFilename(key);
246
+ await this.dirHandle.getFileHandle(filename);
247
+ return true;
248
+ }
249
+ catch {
250
+ return false;
251
+ }
252
+ }
253
+ /**
254
+ * Delete a model file from local storage.
255
+ * @param key - Model identifier
256
+ */
257
+ async deleteModel(key) {
258
+ if (!this.dirHandle || !this._isReady)
259
+ return;
260
+ try {
261
+ const filename = this.sanitizeFilename(key);
262
+ await this.dirHandle.removeEntry(filename);
263
+ logger.info(`Deleted model from local storage: ${filename}`);
264
+ }
265
+ catch {
266
+ // File doesn't exist
267
+ }
268
+ }
269
+ /**
270
+ * Get file size without reading into memory.
271
+ * @param key - Model identifier
272
+ */
273
+ async getFileSize(key) {
274
+ if (!this.dirHandle || !this._isReady)
275
+ return null;
276
+ try {
277
+ const filename = this.sanitizeFilename(key);
278
+ const fileHandle = await this.dirHandle.getFileHandle(filename);
279
+ const file = await fileHandle.getFile();
280
+ return file.size;
281
+ }
282
+ catch {
283
+ return null;
284
+ }
285
+ }
286
+ /**
287
+ * List all model files in the directory.
288
+ */
289
+ async listModels() {
290
+ if (!this.dirHandle || !this._isReady)
291
+ return [];
292
+ const models = [];
293
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
294
+ for await (const [name, handle] of this.dirHandle.entries()) {
295
+ if (handle.kind === 'file') {
296
+ const file = await handle.getFile();
297
+ models.push({
298
+ id: name,
299
+ sizeBytes: file.size,
300
+ lastModified: file.lastModified,
301
+ });
302
+ }
303
+ }
304
+ return models;
305
+ }
306
+ // -------------------------------------------------------------------------
307
+ // IndexedDB Handle Persistence
308
+ // -------------------------------------------------------------------------
309
+ async openDB() {
310
+ return new Promise((resolve, reject) => {
311
+ const request = indexedDB.open(DB_NAME, DB_VERSION);
312
+ request.onupgradeneeded = () => {
313
+ const db = request.result;
314
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
315
+ db.createObjectStore(STORE_NAME);
316
+ }
317
+ };
318
+ request.onsuccess = () => resolve(request.result);
319
+ request.onerror = () => reject(request.error);
320
+ });
321
+ }
322
+ async storeHandle(handle) {
323
+ try {
324
+ const db = await this.openDB();
325
+ const tx = db.transaction(STORE_NAME, 'readwrite');
326
+ tx.objectStore(STORE_NAME).put(handle, HANDLE_KEY);
327
+ await new Promise((resolve, reject) => {
328
+ tx.oncomplete = () => resolve();
329
+ tx.onerror = () => reject(tx.error);
330
+ });
331
+ db.close();
332
+ logger.debug('Directory handle stored in IndexedDB');
333
+ }
334
+ catch (err) {
335
+ const message = err instanceof Error ? err.message : String(err);
336
+ logger.warning(`Failed to store handle in IndexedDB: ${message}`);
337
+ }
338
+ }
339
+ async retrieveHandle() {
340
+ try {
341
+ const db = await this.openDB();
342
+ const tx = db.transaction(STORE_NAME, 'readonly');
343
+ const handle = await new Promise((resolve, reject) => {
344
+ const req = tx.objectStore(STORE_NAME).get(HANDLE_KEY);
345
+ req.onsuccess = () => resolve(req.result ?? null);
346
+ req.onerror = () => reject(req.error);
347
+ });
348
+ db.close();
349
+ return handle;
350
+ }
351
+ catch (err) {
352
+ const message = err instanceof Error ? err.message : String(err);
353
+ logger.warning(`Failed to retrieve handle from IndexedDB: ${message}`);
354
+ return null;
355
+ }
356
+ }
357
+ // -------------------------------------------------------------------------
358
+ // Helpers
359
+ // -------------------------------------------------------------------------
360
+ /**
361
+ * Sanitize a key for use as a filename.
362
+ * Keeps alphanumeric, dots, dashes, underscores. Replaces everything else.
363
+ */
364
+ sanitizeFilename(key) {
365
+ return key.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_');
366
+ }
367
+ }
368
+ //# sourceMappingURL=LocalFileStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalFileStorage.js","sourceRoot":"","sources":["../../src/Infrastructure/LocalFileStorage.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { OPFSStorage } from './OPFSStorage';
9
9
  import type { MetadataMap } from './OPFSStorage';
10
+ import type { LocalFileStorage } from './LocalFileStorage';
10
11
  import type { ManagedModel } from './ModelRegistry';
11
12
  import type { ModelRegistry } from './ModelRegistry';
12
13
  /** Candidate model that could be evicted to free space. */
@@ -33,6 +34,11 @@ export interface QuotaCheckResult {
33
34
  export declare class ModelDownloader {
34
35
  private readonly storage;
35
36
  private readonly registry;
37
+ /**
38
+ * Optional local filesystem storage. When configured, models are saved
39
+ * to the user's chosen directory instead of OPFS. Set via setLocalFileStorage().
40
+ */
41
+ private localFileStorage;
36
42
  /**
37
43
  * In-memory fallback cache for models that were downloaded successfully
38
44
  * but failed to persist to OPFS (e.g. storage quota exceeded).
@@ -40,6 +46,11 @@ export declare class ModelDownloader {
40
46
  */
41
47
  private readonly memoryCache;
42
48
  constructor(registry: ModelRegistry, storage: OPFSStorage);
49
+ /**
50
+ * Set the local file storage backend.
51
+ * When configured and ready, models are saved/loaded from the local filesystem first.
52
+ */
53
+ setLocalFileStorage(storage: LocalFileStorage): void;
43
54
  /**
44
55
  * Check whether a model will fit in OPFS without eviction.
45
56
  *
@@ -64,7 +75,7 @@ export declare class ModelDownloader {
64
75
  * URLs are validated before fetching to prevent SSRF and enforce HTTPS.
65
76
  */
66
77
  downloadFile(url: string, onProgress?: (progress: number, bytesDownloaded: number, totalBytes: number) => void): Promise<Uint8Array>;
67
- /** Store data in OPFS via OPFSStorage. Auto-evicts old models if quota is exceeded, falls back to in-memory cache. */
78
+ /** Store data, preferring local filesystem when available, then OPFS, then memory cache. */
68
79
  storeInOPFS(key: string, data: Uint8Array): Promise<void>;
69
80
  /**
70
81
  * Evict old models from OPFS to free space for a new model.
@@ -76,15 +87,15 @@ export declare class ModelDownloader {
76
87
  * the other, separated by "__" (e.g. "modelA" and "modelA__mmproj-...").
77
88
  */
78
89
  private evictOPFSModels;
79
- /** Load data from OPFS via OPFSStorage, falling back to in-memory cache. */
90
+ /** Load data from storage. Priority: local filesystem > OPFS > memory cache. */
80
91
  loadFromOPFS(key: string): Promise<Uint8Array | null>;
81
- /** Check existence in OPFS or in-memory cache. */
92
+ /** Check existence in local storage, OPFS, or in-memory cache. */
82
93
  existsInOPFS(key: string): Promise<boolean>;
83
94
  /** Check if data exists in actual OPFS storage (NOT memory cache). */
84
95
  existsInActualOPFS(key: string): Promise<boolean>;
85
- /** Delete from OPFS and memory cache. */
96
+ /** Delete from all storage backends (local filesystem, OPFS, memory cache). */
86
97
  deleteFromOPFS(key: string): Promise<void>;
87
- /** Get file size from OPFS without reading into memory. */
98
+ /** Get file size from storage without reading into memory. */
88
99
  getOPFSFileSize(key: string): Promise<number | null>;
89
100
  /**
90
101
  * Build an OPFS key for additional files (e.g., mmproj).
@@ -1 +1 @@
1
- {"version":3,"file":"ModelDownloader.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/ModelDownloader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAMrD,2DAA2D;AAC3D,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAC;IACd,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AAkED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiC;gBAEjD,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW;IASzD;;;;;;;;;;OAUG;IACG,iBAAiB,CACrB,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,WAAW,EACrB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,gBAAgB,CAAC;IAmD5B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyGnD;;;;;OAKG;IACG,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GACnF,OAAO,CAAC,UAAU,CAAC;IA+BtB,sHAAsH;IAChH,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAuC/D;;;;;;;;OAQG;YACW,eAAe;IAmD7B,4EAA4E;IACtE,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA6B3D,kDAAkD;IAC5C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKjD,sEAAsE;IAChE,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvD,yCAAyC;IACnC,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhD,2DAA2D;IACrD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ1D;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI5D,6DAA6D;IAC7D,OAAO,CAAC,oBAAoB;CAS7B"}
1
+ {"version":3,"file":"ModelDownloader.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/ModelDownloader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAMrD,2DAA2D;AAC3D,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAC;IACd,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AAkED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC;;;OAGG;IACH,OAAO,CAAC,gBAAgB,CAAiC;IAEzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiC;gBAEjD,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW;IAKzD;;;OAGG;IACH,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAQpD;;;;;;;;;;OAUG;IACG,iBAAiB,CACrB,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,WAAW,EACrB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,gBAAgB,CAAC;IAmD5B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyGnD;;;;;OAKG;IACG,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GACnF,OAAO,CAAC,UAAU,CAAC;IA+BtB,4FAA4F;IACtF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAoD/D;;;;;;;;OAQG;YACW,eAAe;IAmD7B,gFAAgF;IAC1E,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAuC3D,kEAAkE;IAC5D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjD,sEAAsE;IAChE,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvD,+EAA+E;IACzE,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhD,8DAA8D;IACxD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAY1D;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI5D,6DAA6D;IAC7D,OAAO,CAAC,oBAAoB;CAS7B"}