@cmdoss/memwal-sdk 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +547 -547
- package/BENCHMARKS.md +238 -238
- package/README.md +310 -181
- package/dist/ai-sdk/tools.d.ts +2 -2
- package/dist/ai-sdk/tools.js +2 -2
- package/dist/client/ClientMemoryManager.js +2 -2
- package/dist/client/ClientMemoryManager.js.map +1 -1
- package/dist/client/PersonalDataWallet.d.ts.map +1 -1
- package/dist/client/SimplePDWClient.d.ts +29 -1
- package/dist/client/SimplePDWClient.d.ts.map +1 -1
- package/dist/client/SimplePDWClient.js +45 -13
- package/dist/client/SimplePDWClient.js.map +1 -1
- package/dist/client/namespaces/EmbeddingsNamespace.d.ts +1 -1
- package/dist/client/namespaces/EmbeddingsNamespace.js +1 -1
- package/dist/client/namespaces/MemoryNamespace.d.ts +31 -0
- package/dist/client/namespaces/MemoryNamespace.d.ts.map +1 -1
- package/dist/client/namespaces/MemoryNamespace.js +272 -39
- package/dist/client/namespaces/MemoryNamespace.js.map +1 -1
- package/dist/client/namespaces/consolidated/AINamespace.d.ts +2 -2
- package/dist/client/namespaces/consolidated/AINamespace.js +2 -2
- package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts +12 -2
- package/dist/client/namespaces/consolidated/BlockchainNamespace.d.ts.map +1 -1
- package/dist/client/namespaces/consolidated/BlockchainNamespace.js +62 -4
- package/dist/client/namespaces/consolidated/BlockchainNamespace.js.map +1 -1
- package/dist/client/namespaces/consolidated/StorageNamespace.d.ts +67 -2
- package/dist/client/namespaces/consolidated/StorageNamespace.d.ts.map +1 -1
- package/dist/client/namespaces/consolidated/StorageNamespace.js +549 -16
- package/dist/client/namespaces/consolidated/StorageNamespace.js.map +1 -1
- package/dist/config/ConfigurationHelper.js +61 -61
- package/dist/config/defaults.js +2 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/graph/GraphService.js +21 -21
- package/dist/graph/GraphService.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/seal/EncryptionService.d.ts +9 -5
- package/dist/infrastructure/seal/EncryptionService.d.ts.map +1 -1
- package/dist/infrastructure/seal/EncryptionService.js +37 -15
- package/dist/infrastructure/seal/EncryptionService.js.map +1 -1
- package/dist/infrastructure/seal/SealService.d.ts +13 -5
- package/dist/infrastructure/seal/SealService.d.ts.map +1 -1
- package/dist/infrastructure/seal/SealService.js +36 -34
- package/dist/infrastructure/seal/SealService.js.map +1 -1
- package/dist/langchain/createPDWRAG.js +30 -30
- package/dist/retrieval/MemoryDecryptionPipeline.d.ts.map +1 -1
- package/dist/retrieval/MemoryDecryptionPipeline.js +2 -1
- package/dist/retrieval/MemoryDecryptionPipeline.js.map +1 -1
- package/dist/retrieval/MemoryRetrievalService.d.ts +31 -0
- package/dist/retrieval/MemoryRetrievalService.d.ts.map +1 -1
- package/dist/retrieval/MemoryRetrievalService.js +44 -4
- package/dist/retrieval/MemoryRetrievalService.js.map +1 -1
- package/dist/services/CapabilityService.d.ts.map +1 -1
- package/dist/services/CapabilityService.js +30 -14
- package/dist/services/CapabilityService.js.map +1 -1
- package/dist/services/CrossContextPermissionService.d.ts.map +1 -1
- package/dist/services/CrossContextPermissionService.js +9 -7
- package/dist/services/CrossContextPermissionService.js.map +1 -1
- package/dist/services/EmbeddingService.d.ts +28 -1
- package/dist/services/EmbeddingService.d.ts.map +1 -1
- package/dist/services/EmbeddingService.js +54 -0
- package/dist/services/EmbeddingService.js.map +1 -1
- package/dist/services/EncryptionService.d.ts.map +1 -1
- package/dist/services/EncryptionService.js +6 -5
- package/dist/services/EncryptionService.js.map +1 -1
- package/dist/services/GeminiAIService.js +309 -309
- package/dist/services/IndexManager.d.ts +5 -1
- package/dist/services/IndexManager.d.ts.map +1 -1
- package/dist/services/IndexManager.js +17 -40
- package/dist/services/IndexManager.js.map +1 -1
- package/dist/services/QueryService.js +1 -1
- package/dist/services/QueryService.js.map +1 -1
- package/dist/services/StorageService.d.ts +11 -0
- package/dist/services/StorageService.d.ts.map +1 -1
- package/dist/services/StorageService.js +73 -10
- package/dist/services/StorageService.js.map +1 -1
- package/dist/services/TransactionService.d.ts +20 -0
- package/dist/services/TransactionService.d.ts.map +1 -1
- package/dist/services/TransactionService.js +43 -0
- package/dist/services/TransactionService.js.map +1 -1
- package/dist/services/ViewService.js +2 -2
- package/dist/services/ViewService.js.map +1 -1
- package/dist/services/storage/QuiltBatchManager.d.ts +101 -1
- package/dist/services/storage/QuiltBatchManager.d.ts.map +1 -1
- package/dist/services/storage/QuiltBatchManager.js +410 -20
- package/dist/services/storage/QuiltBatchManager.js.map +1 -1
- package/dist/services/storage/index.d.ts +1 -1
- package/dist/services/storage/index.d.ts.map +1 -1
- package/dist/services/storage/index.js.map +1 -1
- package/dist/utils/LRUCache.d.ts +106 -0
- package/dist/utils/LRUCache.d.ts.map +1 -0
- package/dist/utils/LRUCache.js +281 -0
- package/dist/utils/LRUCache.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/memoryIndexOnChain.d.ts +212 -0
- package/dist/utils/memoryIndexOnChain.d.ts.map +1 -0
- package/dist/utils/memoryIndexOnChain.js +312 -0
- package/dist/utils/memoryIndexOnChain.js.map +1 -0
- package/dist/utils/rebuildIndexNode.d.ts +29 -0
- package/dist/utils/rebuildIndexNode.d.ts.map +1 -1
- package/dist/utils/rebuildIndexNode.js +366 -98
- package/dist/utils/rebuildIndexNode.js.map +1 -1
- package/dist/vector/HnswWasmService.d.ts +20 -5
- package/dist/vector/HnswWasmService.d.ts.map +1 -1
- package/dist/vector/HnswWasmService.js +73 -40
- package/dist/vector/HnswWasmService.js.map +1 -1
- package/dist/vector/IHnswService.d.ts +10 -1
- package/dist/vector/IHnswService.d.ts.map +1 -1
- package/dist/vector/IHnswService.js.map +1 -1
- package/dist/vector/NodeHnswService.d.ts +16 -0
- package/dist/vector/NodeHnswService.d.ts.map +1 -1
- package/dist/vector/NodeHnswService.js +84 -5
- package/dist/vector/NodeHnswService.js.map +1 -1
- package/dist/vector/createHnswService.d.ts +1 -1
- package/dist/vector/createHnswService.js +1 -1
- package/dist/vector/index.d.ts +1 -1
- package/dist/vector/index.js +1 -1
- package/package.json +157 -157
- package/src/access/PermissionService.ts +635 -635
- package/src/aggregation/AggregationService.ts +389 -389
- package/src/ai-sdk/PDWVectorStore.ts +715 -715
- package/src/ai-sdk/index.ts +65 -65
- package/src/ai-sdk/tools.ts +460 -460
- package/src/ai-sdk/types.ts +404 -404
- package/src/batch/BatchManager.ts +597 -597
- package/src/batch/BatchingService.ts +429 -429
- package/src/batch/MemoryProcessingCache.ts +492 -492
- package/src/batch/index.ts +30 -30
- package/src/browser.ts +200 -200
- package/src/client/ClientMemoryManager.ts +987 -987
- package/src/client/PersonalDataWallet.ts +345 -345
- package/src/client/SimplePDWClient.ts +1289 -1222
- package/src/client/factory.ts +154 -154
- package/src/client/namespaces/AnalyticsNamespace.ts +377 -377
- package/src/client/namespaces/BatchNamespace.ts +356 -356
- package/src/client/namespaces/CacheNamespace.ts +123 -123
- package/src/client/namespaces/CapabilityNamespace.ts +217 -217
- package/src/client/namespaces/ClassifyNamespace.ts +169 -169
- package/src/client/namespaces/ContextNamespace.ts +297 -297
- package/src/client/namespaces/EmbeddingsNamespace.ts +99 -99
- package/src/client/namespaces/EncryptionNamespace.ts +221 -221
- package/src/client/namespaces/GraphNamespace.ts +468 -468
- package/src/client/namespaces/IndexNamespace.ts +361 -361
- package/src/client/namespaces/MemoryNamespace.ts +1422 -1135
- package/src/client/namespaces/PermissionsNamespace.ts +254 -254
- package/src/client/namespaces/PipelineNamespace.ts +220 -220
- package/src/client/namespaces/SearchNamespace.ts +1049 -1049
- package/src/client/namespaces/StorageNamespace.ts +458 -458
- package/src/client/namespaces/TxNamespace.ts +260 -260
- package/src/client/namespaces/WalletNamespace.ts +243 -243
- package/src/client/namespaces/consolidated/AINamespace.ts +449 -449
- package/src/client/namespaces/consolidated/BlockchainNamespace.ts +607 -546
- package/src/client/namespaces/consolidated/SecurityNamespace.ts +648 -648
- package/src/client/namespaces/consolidated/StorageNamespace.ts +1141 -497
- package/src/client/namespaces/consolidated/index.ts +39 -39
- package/src/client/signers/KeypairSigner.ts +108 -108
- package/src/client/signers/UnifiedSigner.ts +110 -110
- package/src/client/signers/WalletAdapterSigner.ts +159 -159
- package/src/client/signers/index.ts +26 -26
- package/src/config/ConfigurationHelper.ts +412 -412
- package/src/config/defaults.ts +51 -51
- package/src/config/index.ts +8 -8
- package/src/config/validation.ts +70 -70
- package/src/core/index.ts +14 -14
- package/src/core/interfaces/IService.ts +307 -307
- package/src/core/interfaces/index.ts +8 -8
- package/src/core/types/capability.ts +297 -297
- package/src/core/types/index.ts +870 -870
- package/src/core/types/wallet.ts +270 -270
- package/src/core/types.ts +9 -9
- package/src/core/wallet.ts +222 -222
- package/src/embedding/index.ts +19 -19
- package/src/embedding/types.ts +357 -357
- package/src/errors/index.ts +602 -602
- package/src/errors/recovery.ts +461 -461
- package/src/errors/validation.ts +567 -567
- package/src/generated/pdw/capability.ts +319 -319
- package/src/graph/GraphService.ts +887 -887
- package/src/graph/KnowledgeGraphManager.ts +728 -728
- package/src/graph/index.ts +25 -25
- package/src/index.ts +498 -474
- package/src/infrastructure/index.ts +22 -22
- package/src/infrastructure/seal/EncryptionService.ts +628 -603
- package/src/infrastructure/seal/SealService.ts +613 -615
- package/src/infrastructure/seal/index.ts +9 -9
- package/src/infrastructure/sui/BlockchainManager.ts +627 -627
- package/src/infrastructure/sui/SuiService.ts +888 -888
- package/src/infrastructure/sui/index.ts +9 -9
- package/src/infrastructure/walrus/StorageManager.ts +604 -604
- package/src/infrastructure/walrus/WalrusStorageService.ts +612 -612
- package/src/infrastructure/walrus/index.ts +9 -9
- package/src/langchain/PDWEmbeddings.ts +145 -145
- package/src/langchain/PDWVectorStore.ts +456 -456
- package/src/langchain/createPDWRAG.ts +303 -303
- package/src/langchain/index.ts +47 -47
- package/src/permissions/ConsentRepository.browser.ts +249 -249
- package/src/permissions/ConsentRepository.ts +364 -364
- package/src/pipeline/MemoryPipeline.ts +862 -862
- package/src/pipeline/PipelineManager.ts +683 -683
- package/src/pipeline/index.ts +26 -26
- package/src/retrieval/AdvancedSearchService.ts +629 -629
- package/src/retrieval/MemoryAnalyticsService.ts +711 -711
- package/src/retrieval/MemoryDecryptionPipeline.ts +825 -824
- package/src/retrieval/MemoryRetrievalService.ts +904 -830
- package/src/retrieval/index.ts +42 -42
- package/src/services/BatchService.ts +352 -352
- package/src/services/CapabilityService.ts +464 -448
- package/src/services/ClassifierService.ts +465 -465
- package/src/services/CrossContextPermissionService.ts +486 -484
- package/src/services/EmbeddingService.ts +771 -706
- package/src/services/EncryptionService.ts +712 -711
- package/src/services/GeminiAIService.ts +753 -753
- package/src/services/IndexManager.ts +977 -1004
- package/src/services/MemoryIndexService.ts +1003 -1003
- package/src/services/MemoryService.ts +369 -369
- package/src/services/QueryService.ts +890 -890
- package/src/services/StorageService.ts +1182 -1111
- package/src/services/TransactionService.ts +838 -790
- package/src/services/VectorService.ts +462 -462
- package/src/services/ViewService.ts +484 -484
- package/src/services/index.ts +25 -25
- package/src/services/storage/BlobAttributesManager.ts +333 -333
- package/src/services/storage/KnowledgeGraphManager.ts +425 -425
- package/src/services/storage/MemorySearchManager.ts +387 -387
- package/src/services/storage/QuiltBatchManager.ts +1130 -660
- package/src/services/storage/WalrusMetadataManager.ts +268 -268
- package/src/services/storage/WalrusStorageManager.ts +287 -287
- package/src/services/storage/index.ts +57 -52
- package/src/types/index.ts +13 -13
- package/src/utils/LRUCache.ts +378 -0
- package/src/utils/index.ts +76 -68
- package/src/utils/memoryIndexOnChain.ts +507 -0
- package/src/utils/rebuildIndex.ts +290 -290
- package/src/utils/rebuildIndexNode.ts +771 -424
- package/src/vector/BrowserHnswIndexService.ts +758 -758
- package/src/vector/HnswWasmService.ts +731 -679
- package/src/vector/IHnswService.ts +233 -224
- package/src/vector/NodeHnswService.ts +833 -735
- package/src/vector/VectorManager.ts +478 -478
- package/src/vector/createHnswService.ts +135 -135
- package/src/vector/index.ts +56 -56
- package/src/wallet/ContextWalletService.ts +656 -656
- package/src/wallet/MainWalletService.ts +317 -317
|
@@ -1,497 +1,1141 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Namespace - Consolidated Storage Operations
|
|
3
|
-
*
|
|
4
|
-
* Merges functionality from:
|
|
5
|
-
* - StorageService: Walrus blob storage operations
|
|
6
|
-
* - CacheNamespace: LRU in-memory caching
|
|
7
|
-
*
|
|
8
|
-
* Provides unified interface for data persistence (Walrus) and caching.
|
|
9
|
-
*
|
|
10
|
-
* @module client/namespaces/consolidated
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { ServiceContainer } from '../../SimplePDWClient';
|
|
14
|
-
|
|
15
|
-
// ============================================================================
|
|
16
|
-
// Types
|
|
17
|
-
// ============================================================================
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Cache statistics
|
|
21
|
-
*/
|
|
22
|
-
export interface CacheStats {
|
|
23
|
-
size: number;
|
|
24
|
-
totalAccess: number;
|
|
25
|
-
hitRate: number;
|
|
26
|
-
oldestItem?: Date;
|
|
27
|
-
newestItem?: Date;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Upload result from Walrus
|
|
32
|
-
*/
|
|
33
|
-
export interface UploadResult {
|
|
34
|
-
blobId: string;
|
|
35
|
-
size: number;
|
|
36
|
-
contentType?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Memory package structure
|
|
41
|
-
*/
|
|
42
|
-
export interface MemoryPackage {
|
|
43
|
-
content: string;
|
|
44
|
-
contentType: string;
|
|
45
|
-
metadata?: Record<string, any>;
|
|
46
|
-
embedding?: number[];
|
|
47
|
-
createdAt?: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Upload options
|
|
52
|
-
*/
|
|
53
|
-
export interface UploadOptions {
|
|
54
|
-
contentType?: string;
|
|
55
|
-
encrypt?: boolean;
|
|
56
|
-
epochs?: number;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ============================================================================
|
|
60
|
-
// Sub-Namespaces
|
|
61
|
-
// ============================================================================
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Cache sub-namespace for LRU caching operations
|
|
65
|
-
*/
|
|
66
|
-
class CacheSubNamespace {
|
|
67
|
-
constructor(private services: ServiceContainer) {}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Get cached value
|
|
71
|
-
*
|
|
72
|
-
* @param key - Cache key
|
|
73
|
-
* @returns Cached value or null if not found/expired
|
|
74
|
-
*
|
|
75
|
-
* @example
|
|
76
|
-
* ```typescript
|
|
77
|
-
* const cached = pdw.storage.cache.get<User>('user:123');
|
|
78
|
-
* if (cached) {
|
|
79
|
-
* console.log('Cache hit:', cached);
|
|
80
|
-
* }
|
|
81
|
-
* ```
|
|
82
|
-
*/
|
|
83
|
-
get<T = any>(key: string): T | null {
|
|
84
|
-
if (!this.services.batchService) {
|
|
85
|
-
throw new Error('Batch service (cache) not configured.');
|
|
86
|
-
}
|
|
87
|
-
return this.services.batchService.getCache<T>(key);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Set cache value
|
|
92
|
-
*
|
|
93
|
-
* @param key - Cache key
|
|
94
|
-
* @param value - Value to cache
|
|
95
|
-
* @param ttl - Time-to-live in milliseconds (optional)
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* ```typescript
|
|
99
|
-
* pdw.storage.cache.set('user:123', userData, 60000); // 1 minute TTL
|
|
100
|
-
* ```
|
|
101
|
-
*/
|
|
102
|
-
set<T = any>(key: string, value: T, ttl?: number): void {
|
|
103
|
-
if (!this.services.batchService) {
|
|
104
|
-
throw new Error('Batch service (cache) not configured.');
|
|
105
|
-
}
|
|
106
|
-
this.services.batchService.setCache(key, value, ttl);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Check if key exists in cache
|
|
111
|
-
*
|
|
112
|
-
* @param key - Cache key
|
|
113
|
-
* @returns True if key exists and not expired
|
|
114
|
-
*/
|
|
115
|
-
has(key: string): boolean {
|
|
116
|
-
if (!this.services.batchService) {
|
|
117
|
-
throw new Error('Batch service (cache) not configured.');
|
|
118
|
-
}
|
|
119
|
-
return this.services.batchService.hasCache(key);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Delete cache entry
|
|
124
|
-
*
|
|
125
|
-
* @param key - Cache key
|
|
126
|
-
* @returns True if deleted, false if not found
|
|
127
|
-
*/
|
|
128
|
-
delete(key: string): boolean {
|
|
129
|
-
if (!this.services.batchService) {
|
|
130
|
-
throw new Error('Batch service (cache) not configured.');
|
|
131
|
-
}
|
|
132
|
-
return this.services.batchService.deleteCache(key);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Clear all cache entries
|
|
137
|
-
*/
|
|
138
|
-
clear(): void {
|
|
139
|
-
if (!this.services.batchService) {
|
|
140
|
-
throw new Error('Batch service (cache) not configured.');
|
|
141
|
-
}
|
|
142
|
-
this.services.batchService.clearCache();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Get cache statistics
|
|
147
|
-
*
|
|
148
|
-
* @returns Cache statistics
|
|
149
|
-
*/
|
|
150
|
-
stats(): CacheStats {
|
|
151
|
-
if (!this.services.batchService) {
|
|
152
|
-
throw new Error('Batch service (cache) not configured.');
|
|
153
|
-
}
|
|
154
|
-
return this.services.batchService.getCacheStats();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ============================================================================
|
|
159
|
-
// Storage Namespace
|
|
160
|
-
// ============================================================================
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Storage Namespace - Unified Storage Operations
|
|
164
|
-
*
|
|
165
|
-
* Consolidates Walrus blob storage and in-memory caching.
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* ```typescript
|
|
169
|
-
* // Upload to Walrus
|
|
170
|
-
* const result = await pdw.storage.upload(data);
|
|
171
|
-
* console.log('Blob ID:', result.blobId);
|
|
172
|
-
*
|
|
173
|
-
* // Download from Walrus
|
|
174
|
-
* const data = await pdw.storage.download(blobId);
|
|
175
|
-
*
|
|
176
|
-
* // Use cache
|
|
177
|
-
* pdw.storage.cache.set('key', value, 60000);
|
|
178
|
-
* const cached = pdw.storage.cache.get('key');
|
|
179
|
-
* ```
|
|
180
|
-
*/
|
|
181
|
-
export class StorageNamespace {
|
|
182
|
-
private _cache: CacheSubNamespace;
|
|
183
|
-
|
|
184
|
-
constructor(private services: ServiceContainer) {
|
|
185
|
-
this._cache = new CacheSubNamespace(services);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Cache operations
|
|
190
|
-
*/
|
|
191
|
-
get cache(): CacheSubNamespace {
|
|
192
|
-
return this._cache;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ==========================================================================
|
|
196
|
-
// Walrus Storage Operations
|
|
197
|
-
// ==========================================================================
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Upload data to Walrus
|
|
201
|
-
*
|
|
202
|
-
* @param data - Data to upload (string, Uint8Array, or object)
|
|
203
|
-
* @param options - Upload options
|
|
204
|
-
* @returns Upload result with blob ID
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```typescript
|
|
208
|
-
* // Upload raw bytes
|
|
209
|
-
* const result = await pdw.storage.upload(new Uint8Array([1, 2, 3]));
|
|
210
|
-
*
|
|
211
|
-
* // Upload JSON
|
|
212
|
-
* const result = await pdw.storage.upload({ name: 'test' });
|
|
213
|
-
*
|
|
214
|
-
* // Upload with options
|
|
215
|
-
* const result = await pdw.storage.upload(data, { encrypt: true });
|
|
216
|
-
* ```
|
|
217
|
-
*/
|
|
218
|
-
async upload(data: string | Uint8Array | object, options: UploadOptions = {}): Promise<UploadResult> {
|
|
219
|
-
if (!this.services.storage) {
|
|
220
|
-
throw new Error('Storage service not configured.');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Convert data to Uint8Array
|
|
224
|
-
let bytes: Uint8Array;
|
|
225
|
-
let contentType = options.contentType || 'application/octet-stream';
|
|
226
|
-
|
|
227
|
-
if (typeof data === 'string') {
|
|
228
|
-
bytes = new TextEncoder().encode(data);
|
|
229
|
-
contentType = options.contentType || 'text/plain';
|
|
230
|
-
} else if (data instanceof Uint8Array) {
|
|
231
|
-
bytes = data;
|
|
232
|
-
} else {
|
|
233
|
-
bytes = new TextEncoder().encode(JSON.stringify(data));
|
|
234
|
-
contentType = options.contentType || 'application/json';
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Encrypt if requested
|
|
238
|
-
if (options.encrypt && this.services.encryption) {
|
|
239
|
-
const encryptResult = await this.services.encryption.encrypt(
|
|
240
|
-
bytes,
|
|
241
|
-
this.services.config.userAddress,
|
|
242
|
-
2
|
|
243
|
-
);
|
|
244
|
-
bytes = encryptResult.encryptedObject;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// Upload to Walrus using uploadBlob method
|
|
248
|
-
const result = await this.services.storage.uploadBlob(bytes, {
|
|
249
|
-
signer: this.services.config.signer,
|
|
250
|
-
epochs: options.epochs,
|
|
251
|
-
deletable: true
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
blobId: result.blobId,
|
|
256
|
-
size: bytes.length,
|
|
257
|
-
contentType
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Download data from Walrus
|
|
263
|
-
*
|
|
264
|
-
* @param blobId - Blob ID to download
|
|
265
|
-
* @returns Raw data as Uint8Array
|
|
266
|
-
*
|
|
267
|
-
* @example
|
|
268
|
-
* ```typescript
|
|
269
|
-
* const data = await pdw.storage.download('blobId123');
|
|
270
|
-
* const text = new TextDecoder().decode(data);
|
|
271
|
-
* ```
|
|
272
|
-
*/
|
|
273
|
-
async download(blobId: string): Promise<Uint8Array> {
|
|
274
|
-
if (!this.services.storage) {
|
|
275
|
-
throw new Error('Storage service not configured.');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Use retrieveFromWalrusOnly which returns { content, metadata }
|
|
279
|
-
const result = await this.services.storage.retrieveFromWalrusOnly(blobId);
|
|
280
|
-
return result.content;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Download and parse JSON from Walrus
|
|
285
|
-
*
|
|
286
|
-
* @param blobId - Blob ID to download
|
|
287
|
-
* @returns Parsed JSON object
|
|
288
|
-
*
|
|
289
|
-
* @example
|
|
290
|
-
* ```typescript
|
|
291
|
-
* const data = await pdw.storage.downloadJson<MyType>('blobId123');
|
|
292
|
-
* ```
|
|
293
|
-
*/
|
|
294
|
-
async downloadJson<T = any>(blobId: string): Promise<T> {
|
|
295
|
-
const bytes = await this.download(blobId);
|
|
296
|
-
const text = new TextDecoder().decode(bytes);
|
|
297
|
-
return JSON.parse(text);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Download and decrypt data from Walrus
|
|
302
|
-
*
|
|
303
|
-
* @param blobId - Blob ID to download
|
|
304
|
-
* @param options - Decryption options
|
|
305
|
-
* @returns Decrypted data
|
|
306
|
-
*
|
|
307
|
-
* @example
|
|
308
|
-
* ```typescript
|
|
309
|
-
* const data = await pdw.storage.downloadDecrypted('blobId123');
|
|
310
|
-
* ```
|
|
311
|
-
*/
|
|
312
|
-
async downloadDecrypted(blobId: string, options?: {
|
|
313
|
-
memoryCapId?: string;
|
|
314
|
-
keyId?: Uint8Array;
|
|
315
|
-
}): Promise<Uint8Array> {
|
|
316
|
-
if (!this.services.storage) {
|
|
317
|
-
throw new Error('Storage service not configured.');
|
|
318
|
-
}
|
|
319
|
-
if (!this.services.encryption) {
|
|
320
|
-
throw new Error('Encryption service not configured.');
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const encryptedData = await this.download(blobId);
|
|
324
|
-
|
|
325
|
-
return await this.services.encryption.decrypt({
|
|
326
|
-
encryptedContent: encryptedData,
|
|
327
|
-
userAddress: this.services.config.userAddress,
|
|
328
|
-
requestingWallet: this.services.config.userAddress,
|
|
329
|
-
memoryCapId: options?.memoryCapId,
|
|
330
|
-
keyId: options?.keyId
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Store memory package to Walrus
|
|
336
|
-
*
|
|
337
|
-
* Higher-level method that stores a complete memory package.
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* @
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Storage Namespace - Consolidated Storage Operations
|
|
3
|
+
*
|
|
4
|
+
* Merges functionality from:
|
|
5
|
+
* - StorageService: Walrus blob storage operations
|
|
6
|
+
* - CacheNamespace: LRU in-memory caching
|
|
7
|
+
*
|
|
8
|
+
* Provides unified interface for data persistence (Walrus) and caching.
|
|
9
|
+
*
|
|
10
|
+
* @module client/namespaces/consolidated
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ServiceContainer } from '../../SimplePDWClient';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Types
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Cache statistics
|
|
21
|
+
*/
|
|
22
|
+
export interface CacheStats {
|
|
23
|
+
size: number;
|
|
24
|
+
totalAccess: number;
|
|
25
|
+
hitRate: number;
|
|
26
|
+
oldestItem?: Date;
|
|
27
|
+
newestItem?: Date;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Upload result from Walrus
|
|
32
|
+
*/
|
|
33
|
+
export interface UploadResult {
|
|
34
|
+
blobId: string;
|
|
35
|
+
size: number;
|
|
36
|
+
contentType?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Memory package structure
|
|
41
|
+
*/
|
|
42
|
+
export interface MemoryPackage {
|
|
43
|
+
content: string;
|
|
44
|
+
contentType: string;
|
|
45
|
+
metadata?: Record<string, any>;
|
|
46
|
+
embedding?: number[];
|
|
47
|
+
createdAt?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Upload options
|
|
52
|
+
*/
|
|
53
|
+
export interface UploadOptions {
|
|
54
|
+
contentType?: string;
|
|
55
|
+
encrypt?: boolean;
|
|
56
|
+
epochs?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Sub-Namespaces
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Cache sub-namespace for LRU caching operations
|
|
65
|
+
*/
|
|
66
|
+
class CacheSubNamespace {
|
|
67
|
+
constructor(private services: ServiceContainer) {}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get cached value
|
|
71
|
+
*
|
|
72
|
+
* @param key - Cache key
|
|
73
|
+
* @returns Cached value or null if not found/expired
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const cached = pdw.storage.cache.get<User>('user:123');
|
|
78
|
+
* if (cached) {
|
|
79
|
+
* console.log('Cache hit:', cached);
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
get<T = any>(key: string): T | null {
|
|
84
|
+
if (!this.services.batchService) {
|
|
85
|
+
throw new Error('Batch service (cache) not configured.');
|
|
86
|
+
}
|
|
87
|
+
return this.services.batchService.getCache<T>(key);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Set cache value
|
|
92
|
+
*
|
|
93
|
+
* @param key - Cache key
|
|
94
|
+
* @param value - Value to cache
|
|
95
|
+
* @param ttl - Time-to-live in milliseconds (optional)
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* pdw.storage.cache.set('user:123', userData, 60000); // 1 minute TTL
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
set<T = any>(key: string, value: T, ttl?: number): void {
|
|
103
|
+
if (!this.services.batchService) {
|
|
104
|
+
throw new Error('Batch service (cache) not configured.');
|
|
105
|
+
}
|
|
106
|
+
this.services.batchService.setCache(key, value, ttl);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if key exists in cache
|
|
111
|
+
*
|
|
112
|
+
* @param key - Cache key
|
|
113
|
+
* @returns True if key exists and not expired
|
|
114
|
+
*/
|
|
115
|
+
has(key: string): boolean {
|
|
116
|
+
if (!this.services.batchService) {
|
|
117
|
+
throw new Error('Batch service (cache) not configured.');
|
|
118
|
+
}
|
|
119
|
+
return this.services.batchService.hasCache(key);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Delete cache entry
|
|
124
|
+
*
|
|
125
|
+
* @param key - Cache key
|
|
126
|
+
* @returns True if deleted, false if not found
|
|
127
|
+
*/
|
|
128
|
+
delete(key: string): boolean {
|
|
129
|
+
if (!this.services.batchService) {
|
|
130
|
+
throw new Error('Batch service (cache) not configured.');
|
|
131
|
+
}
|
|
132
|
+
return this.services.batchService.deleteCache(key);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Clear all cache entries
|
|
137
|
+
*/
|
|
138
|
+
clear(): void {
|
|
139
|
+
if (!this.services.batchService) {
|
|
140
|
+
throw new Error('Batch service (cache) not configured.');
|
|
141
|
+
}
|
|
142
|
+
this.services.batchService.clearCache();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get cache statistics
|
|
147
|
+
*
|
|
148
|
+
* @returns Cache statistics
|
|
149
|
+
*/
|
|
150
|
+
stats(): CacheStats {
|
|
151
|
+
if (!this.services.batchService) {
|
|
152
|
+
throw new Error('Batch service (cache) not configured.');
|
|
153
|
+
}
|
|
154
|
+
return this.services.batchService.getCacheStats();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================================
|
|
159
|
+
// Storage Namespace
|
|
160
|
+
// ============================================================================
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Storage Namespace - Unified Storage Operations
|
|
164
|
+
*
|
|
165
|
+
* Consolidates Walrus blob storage and in-memory caching.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* // Upload to Walrus
|
|
170
|
+
* const result = await pdw.storage.upload(data);
|
|
171
|
+
* console.log('Blob ID:', result.blobId);
|
|
172
|
+
*
|
|
173
|
+
* // Download from Walrus
|
|
174
|
+
* const data = await pdw.storage.download(blobId);
|
|
175
|
+
*
|
|
176
|
+
* // Use cache
|
|
177
|
+
* pdw.storage.cache.set('key', value, 60000);
|
|
178
|
+
* const cached = pdw.storage.cache.get('key');
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export class StorageNamespace {
|
|
182
|
+
private _cache: CacheSubNamespace;
|
|
183
|
+
|
|
184
|
+
constructor(private services: ServiceContainer) {
|
|
185
|
+
this._cache = new CacheSubNamespace(services);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Cache operations
|
|
190
|
+
*/
|
|
191
|
+
get cache(): CacheSubNamespace {
|
|
192
|
+
return this._cache;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ==========================================================================
|
|
196
|
+
// Walrus Storage Operations
|
|
197
|
+
// ==========================================================================
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Upload data to Walrus
|
|
201
|
+
*
|
|
202
|
+
* @param data - Data to upload (string, Uint8Array, or object)
|
|
203
|
+
* @param options - Upload options
|
|
204
|
+
* @returns Upload result with blob ID
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* // Upload raw bytes
|
|
209
|
+
* const result = await pdw.storage.upload(new Uint8Array([1, 2, 3]));
|
|
210
|
+
*
|
|
211
|
+
* // Upload JSON
|
|
212
|
+
* const result = await pdw.storage.upload({ name: 'test' });
|
|
213
|
+
*
|
|
214
|
+
* // Upload with options
|
|
215
|
+
* const result = await pdw.storage.upload(data, { encrypt: true });
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
async upload(data: string | Uint8Array | object, options: UploadOptions = {}): Promise<UploadResult> {
|
|
219
|
+
if (!this.services.storage) {
|
|
220
|
+
throw new Error('Storage service not configured.');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Convert data to Uint8Array
|
|
224
|
+
let bytes: Uint8Array;
|
|
225
|
+
let contentType = options.contentType || 'application/octet-stream';
|
|
226
|
+
|
|
227
|
+
if (typeof data === 'string') {
|
|
228
|
+
bytes = new TextEncoder().encode(data);
|
|
229
|
+
contentType = options.contentType || 'text/plain';
|
|
230
|
+
} else if (data instanceof Uint8Array) {
|
|
231
|
+
bytes = data;
|
|
232
|
+
} else {
|
|
233
|
+
bytes = new TextEncoder().encode(JSON.stringify(data));
|
|
234
|
+
contentType = options.contentType || 'application/json';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Encrypt if requested
|
|
238
|
+
if (options.encrypt && this.services.encryption) {
|
|
239
|
+
const encryptResult = await this.services.encryption.encrypt(
|
|
240
|
+
bytes,
|
|
241
|
+
this.services.config.userAddress,
|
|
242
|
+
2
|
|
243
|
+
);
|
|
244
|
+
bytes = encryptResult.encryptedObject;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Upload to Walrus using uploadBlob method
|
|
248
|
+
const result = await this.services.storage.uploadBlob(bytes, {
|
|
249
|
+
signer: this.services.config.signer,
|
|
250
|
+
epochs: options.epochs,
|
|
251
|
+
deletable: true
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
blobId: result.blobId,
|
|
256
|
+
size: bytes.length,
|
|
257
|
+
contentType
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Download data from Walrus
|
|
263
|
+
*
|
|
264
|
+
* @param blobId - Blob ID to download
|
|
265
|
+
* @returns Raw data as Uint8Array
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```typescript
|
|
269
|
+
* const data = await pdw.storage.download('blobId123');
|
|
270
|
+
* const text = new TextDecoder().decode(data);
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
async download(blobId: string): Promise<Uint8Array> {
|
|
274
|
+
if (!this.services.storage) {
|
|
275
|
+
throw new Error('Storage service not configured.');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Use retrieveFromWalrusOnly which returns { content, metadata }
|
|
279
|
+
const result = await this.services.storage.retrieveFromWalrusOnly(blobId);
|
|
280
|
+
return result.content;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Download and parse JSON from Walrus
|
|
285
|
+
*
|
|
286
|
+
* @param blobId - Blob ID to download
|
|
287
|
+
* @returns Parsed JSON object
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* const data = await pdw.storage.downloadJson<MyType>('blobId123');
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
async downloadJson<T = any>(blobId: string): Promise<T> {
|
|
295
|
+
const bytes = await this.download(blobId);
|
|
296
|
+
const text = new TextDecoder().decode(bytes);
|
|
297
|
+
return JSON.parse(text);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Download and decrypt data from Walrus
|
|
302
|
+
*
|
|
303
|
+
* @param blobId - Blob ID to download
|
|
304
|
+
* @param options - Decryption options
|
|
305
|
+
* @returns Decrypted data
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* const data = await pdw.storage.downloadDecrypted('blobId123');
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
async downloadDecrypted(blobId: string, options?: {
|
|
313
|
+
memoryCapId?: string;
|
|
314
|
+
keyId?: Uint8Array;
|
|
315
|
+
}): Promise<Uint8Array> {
|
|
316
|
+
if (!this.services.storage) {
|
|
317
|
+
throw new Error('Storage service not configured.');
|
|
318
|
+
}
|
|
319
|
+
if (!this.services.encryption) {
|
|
320
|
+
throw new Error('Encryption service not configured.');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const encryptedData = await this.download(blobId);
|
|
324
|
+
|
|
325
|
+
return await this.services.encryption.decrypt({
|
|
326
|
+
encryptedContent: encryptedData,
|
|
327
|
+
userAddress: this.services.config.userAddress,
|
|
328
|
+
requestingWallet: this.services.config.userAddress,
|
|
329
|
+
memoryCapId: options?.memoryCapId,
|
|
330
|
+
keyId: options?.keyId
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Store memory package to Walrus
|
|
336
|
+
*
|
|
337
|
+
* Higher-level method that stores a complete memory package.
|
|
338
|
+
* Automatically encrypts content if encryption is enabled in config.
|
|
339
|
+
*
|
|
340
|
+
* @param memoryPackage - Memory package to store
|
|
341
|
+
* @returns Upload result
|
|
342
|
+
*/
|
|
343
|
+
async storeMemoryPackage(memoryPackage: MemoryPackage): Promise<UploadResult> {
|
|
344
|
+
if (!this.services.storage) {
|
|
345
|
+
throw new Error('Storage service not configured.');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Check if encryption is enabled
|
|
349
|
+
const encryptionEnabled = this.services.config.features?.enableEncryption ?? true;
|
|
350
|
+
let encryptedContent: Uint8Array | undefined;
|
|
351
|
+
let encryptedEmbedding: Uint8Array | undefined; // Option A v2: encrypted embedding
|
|
352
|
+
let encryptionType: string | undefined;
|
|
353
|
+
let memoryCapId: string | undefined;
|
|
354
|
+
let keyId: string | undefined;
|
|
355
|
+
|
|
356
|
+
console.log('🔍 Encryption check:', {
|
|
357
|
+
encryptionEnabled,
|
|
358
|
+
hasEncryptionService: !!this.services.encryption,
|
|
359
|
+
hasCapabilityService: !!this.services.capability,
|
|
360
|
+
configFeatures: this.services.config.features
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (encryptionEnabled && this.services.encryption && this.services.capability) {
|
|
364
|
+
console.log('🔒 Encrypting memory package with SEAL (capability-based)...');
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
// Step 1: Get or create capability for this app context
|
|
368
|
+
const category = memoryPackage.metadata?.category || 'general';
|
|
369
|
+
console.log(`🔐 Getting/creating capability for category: ${category}`);
|
|
370
|
+
|
|
371
|
+
const cap = await this.services.capability.getOrCreate(
|
|
372
|
+
{
|
|
373
|
+
appId: category,
|
|
374
|
+
userAddress: this.services.config.userAddress
|
|
375
|
+
},
|
|
376
|
+
this.services.config.signer
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
memoryCapId = cap.id;
|
|
380
|
+
console.log(`✅ Capability ready: ${memoryCapId}`);
|
|
381
|
+
|
|
382
|
+
// Step 2: Compute key_id from capability (keccak256(owner || nonce))
|
|
383
|
+
keyId = this.services.capability.computeKeyId(cap);
|
|
384
|
+
console.log(`🔑 Key ID computed: ${keyId.substring(0, 20)}...`);
|
|
385
|
+
|
|
386
|
+
// Step 3: Encrypt the content using key_id as SEAL identity
|
|
387
|
+
const contentBytes = new TextEncoder().encode(memoryPackage.content);
|
|
388
|
+
const encryptResult = await this.services.encryption.encrypt(
|
|
389
|
+
contentBytes,
|
|
390
|
+
keyId, // Use key_id from capability as SEAL identity!
|
|
391
|
+
2 // threshold: 2 of 2 key servers
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
encryptedContent = encryptResult.encryptedObject;
|
|
395
|
+
encryptionType = 'seal-capability';
|
|
396
|
+
|
|
397
|
+
console.log(`✅ Content encrypted: ${contentBytes.length} bytes → ${encryptedContent?.length || 0} bytes`);
|
|
398
|
+
|
|
399
|
+
// Step 4: Also encrypt the embedding for fast index rebuild (Option A v2)
|
|
400
|
+
const embeddingToEncrypt = memoryPackage.embedding && memoryPackage.embedding.length > 0
|
|
401
|
+
? memoryPackage.embedding
|
|
402
|
+
: (memoryPackage.metadata?.embedding || []);
|
|
403
|
+
|
|
404
|
+
if (embeddingToEncrypt.length > 0) {
|
|
405
|
+
const embeddingBytes = new TextEncoder().encode(JSON.stringify(embeddingToEncrypt));
|
|
406
|
+
const encryptEmbeddingResult = await this.services.encryption.encrypt(
|
|
407
|
+
embeddingBytes,
|
|
408
|
+
keyId,
|
|
409
|
+
2
|
|
410
|
+
);
|
|
411
|
+
encryptedEmbedding = encryptEmbeddingResult.encryptedObject;
|
|
412
|
+
console.log(`✅ Embedding encrypted: ${embeddingToEncrypt.length}D → ${encryptedEmbedding?.length || 0} bytes`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
console.log(` Using capability: ${memoryCapId}`);
|
|
416
|
+
console.log(` Key ID: ${keyId.substring(0, 20)}...`);
|
|
417
|
+
} catch (encryptError) {
|
|
418
|
+
console.error('❌ Encryption failed:', encryptError);
|
|
419
|
+
console.warn('⚠️ Falling back to plaintext storage');
|
|
420
|
+
// Fall back to plaintext if encryption fails
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
console.log('📝 Encryption disabled or services not available - storing plaintext');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Include capability metadata for decryption
|
|
427
|
+
const metadata = {
|
|
428
|
+
...memoryPackage.metadata,
|
|
429
|
+
// Add capability info for decryption (CRITICAL for Option A!)
|
|
430
|
+
...(memoryCapId && keyId ? {
|
|
431
|
+
memoryCapId,
|
|
432
|
+
keyId,
|
|
433
|
+
encryptionVersion: 'v2-capability' // Mark as new capability-based encryption
|
|
434
|
+
} : {})
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// Get embedding from root level (correct API) or metadata (legacy fallback)
|
|
438
|
+
const rootEmbedding = memoryPackage.embedding && memoryPackage.embedding.length > 0;
|
|
439
|
+
const metadataEmbedding = memoryPackage.metadata?.embedding && memoryPackage.metadata.embedding.length > 0;
|
|
440
|
+
const embedding = rootEmbedding
|
|
441
|
+
? memoryPackage.embedding
|
|
442
|
+
: (memoryPackage.metadata?.embedding || []);
|
|
443
|
+
|
|
444
|
+
if (metadataEmbedding && !rootEmbedding) {
|
|
445
|
+
console.warn('⚠️ Embedding in metadata (legacy) - should be at root level');
|
|
446
|
+
}
|
|
447
|
+
console.log(`📊 Embedding: ${embedding.length}D vector`);
|
|
448
|
+
|
|
449
|
+
// Use uploadMemoryPackage method
|
|
450
|
+
const result = await this.services.storage.uploadMemoryPackage(
|
|
451
|
+
{
|
|
452
|
+
content: memoryPackage.content,
|
|
453
|
+
embedding,
|
|
454
|
+
metadata,
|
|
455
|
+
identity: this.services.config.userAddress,
|
|
456
|
+
encryptedContent, // Pass encrypted content if available
|
|
457
|
+
encryptedEmbedding, // Pass encrypted embedding for Option A v2
|
|
458
|
+
encryptionType
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
signer: this.services.config.signer,
|
|
462
|
+
epochs: 3,
|
|
463
|
+
deletable: true
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
blobId: result.blobId,
|
|
469
|
+
size: 0, // Size not returned by uploadMemoryPackage
|
|
470
|
+
contentType: 'application/json',
|
|
471
|
+
memoryCapId, // Return capability ID for reference
|
|
472
|
+
keyId // Return key ID for reference
|
|
473
|
+
} as UploadResult;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Retrieve memory package from Walrus with optional decryption
|
|
478
|
+
*
|
|
479
|
+
* @param blobId - Blob ID of the memory package
|
|
480
|
+
* @param decryptionContext - Optional context for decrypting SEAL-encrypted content
|
|
481
|
+
* @returns Retrieved memory package with decrypted content
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```typescript
|
|
485
|
+
* // Without decryption (returns encrypted data info)
|
|
486
|
+
* const result = await pdw.storage.retrieveMemoryPackage(blobId);
|
|
487
|
+
*
|
|
488
|
+
* // With decryption
|
|
489
|
+
* const result = await pdw.storage.retrieveMemoryPackage(blobId, {
|
|
490
|
+
* sessionKey,
|
|
491
|
+
* memoryCapId,
|
|
492
|
+
* keyId
|
|
493
|
+
* });
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
async retrieveMemoryPackage(
|
|
497
|
+
blobId: string,
|
|
498
|
+
decryptionContext?: {
|
|
499
|
+
sessionKey: any;
|
|
500
|
+
memoryCapId: string;
|
|
501
|
+
keyId: Uint8Array;
|
|
502
|
+
}
|
|
503
|
+
): Promise<{
|
|
504
|
+
memoryPackage: MemoryPackage | null;
|
|
505
|
+
decryptionStatus: 'success' | 'failed' | 'not_encrypted';
|
|
506
|
+
error?: string;
|
|
507
|
+
}> {
|
|
508
|
+
if (!this.services.storage) {
|
|
509
|
+
throw new Error('Storage service not configured.');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const result = await this.services.storage.retrieveMemoryPackage(blobId);
|
|
513
|
+
|
|
514
|
+
// If not encrypted, return as-is
|
|
515
|
+
if (!result.isEncrypted && result.memoryPackage) {
|
|
516
|
+
return {
|
|
517
|
+
memoryPackage: {
|
|
518
|
+
content: result.memoryPackage.content,
|
|
519
|
+
contentType: result.memoryPackage.contentType || 'text/plain',
|
|
520
|
+
metadata: result.memoryPackage.metadata,
|
|
521
|
+
embedding: result.memoryPackage.embedding,
|
|
522
|
+
createdAt: result.memoryPackage.timestamp
|
|
523
|
+
},
|
|
524
|
+
decryptionStatus: 'not_encrypted'
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// v2.2 JSON package with encrypted content + encrypted embedding
|
|
529
|
+
if (result.memoryPackage?.version === '2.2' && result.memoryPackage?.encryptedContent) {
|
|
530
|
+
console.log('🔐 Detected v2.2 JSON package (Full Encryption - Content + Embedding)');
|
|
531
|
+
|
|
532
|
+
const embeddingDimension = result.memoryPackage.metadata?.embeddingDimension || 0;
|
|
533
|
+
console.log(` embeddingDimension: ${embeddingDimension}D (encrypted on Walrus)`);
|
|
534
|
+
|
|
535
|
+
// Return base package (content encrypted, embedding encrypted)
|
|
536
|
+
const basePackage: MemoryPackage = {
|
|
537
|
+
content: '[ENCRYPTED - requires decryption]',
|
|
538
|
+
contentType: 'text/plain',
|
|
539
|
+
metadata: result.memoryPackage.metadata,
|
|
540
|
+
embedding: [], // Embedding is encrypted, needs decryption
|
|
541
|
+
createdAt: result.memoryPackage.timestamp
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
if (decryptionContext && this.services.encryption) {
|
|
545
|
+
try {
|
|
546
|
+
console.log('🔐 Decrypting v2.2 content...');
|
|
547
|
+
|
|
548
|
+
// Decrypt content
|
|
549
|
+
const encryptedContentBase64 = result.memoryPackage.encryptedContent;
|
|
550
|
+
const contentBinaryString = atob(encryptedContentBase64);
|
|
551
|
+
const encryptedContentBytes = new Uint8Array(contentBinaryString.length);
|
|
552
|
+
for (let i = 0; i < contentBinaryString.length; i++) {
|
|
553
|
+
encryptedContentBytes[i] = contentBinaryString.charCodeAt(i);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const decryptedContentData = await this.services.encryption.decrypt({
|
|
557
|
+
encryptedContent: encryptedContentBytes,
|
|
558
|
+
userAddress: this.services.config.userAddress,
|
|
559
|
+
sessionKey: decryptionContext.sessionKey,
|
|
560
|
+
memoryCapId: decryptionContext.memoryCapId,
|
|
561
|
+
keyId: decryptionContext.keyId
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
const decryptedContent = new TextDecoder().decode(decryptedContentData);
|
|
565
|
+
console.log(`✅ v2.2 content decrypted: "${decryptedContent.substring(0, 50)}..."`);
|
|
566
|
+
|
|
567
|
+
// Decrypt embedding if available
|
|
568
|
+
let decryptedEmbedding: number[] = [];
|
|
569
|
+
if (result.memoryPackage.encryptedEmbedding) {
|
|
570
|
+
console.log('🔐 Decrypting v2.2 embedding...');
|
|
571
|
+
const encryptedEmbeddingBase64 = result.memoryPackage.encryptedEmbedding;
|
|
572
|
+
const embeddingBinaryString = atob(encryptedEmbeddingBase64);
|
|
573
|
+
const encryptedEmbeddingBytes = new Uint8Array(embeddingBinaryString.length);
|
|
574
|
+
for (let i = 0; i < embeddingBinaryString.length; i++) {
|
|
575
|
+
encryptedEmbeddingBytes[i] = embeddingBinaryString.charCodeAt(i);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const decryptedEmbeddingData = await this.services.encryption.decrypt({
|
|
579
|
+
encryptedContent: encryptedEmbeddingBytes,
|
|
580
|
+
userAddress: this.services.config.userAddress,
|
|
581
|
+
sessionKey: decryptionContext.sessionKey,
|
|
582
|
+
memoryCapId: decryptionContext.memoryCapId,
|
|
583
|
+
keyId: decryptionContext.keyId
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
const embeddingJson = new TextDecoder().decode(decryptedEmbeddingData);
|
|
587
|
+
decryptedEmbedding = JSON.parse(embeddingJson);
|
|
588
|
+
console.log(`✅ v2.2 embedding decrypted: ${decryptedEmbedding.length}D vector`);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return {
|
|
592
|
+
memoryPackage: {
|
|
593
|
+
...basePackage,
|
|
594
|
+
content: decryptedContent,
|
|
595
|
+
embedding: decryptedEmbedding
|
|
596
|
+
},
|
|
597
|
+
decryptionStatus: 'success'
|
|
598
|
+
};
|
|
599
|
+
} catch (decryptError: any) {
|
|
600
|
+
console.error('❌ v2.2 decryption failed:', decryptError.message);
|
|
601
|
+
return {
|
|
602
|
+
memoryPackage: basePackage,
|
|
603
|
+
decryptionStatus: 'failed',
|
|
604
|
+
error: decryptError.message
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// No decryption context
|
|
610
|
+
return {
|
|
611
|
+
memoryPackage: basePackage,
|
|
612
|
+
decryptionStatus: 'failed',
|
|
613
|
+
error: 'No decryption context provided for v2.2 encrypted package'
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// v2.1 JSON package with encrypted content only (no embedding on Walrus)
|
|
618
|
+
if (result.memoryPackage?.version === '2.1' && result.memoryPackage?.encryptedContent) {
|
|
619
|
+
console.log('🔐 Detected v2.1 JSON package (Full Encryption - Content only)');
|
|
620
|
+
|
|
621
|
+
const embeddingDimension = result.memoryPackage.metadata?.embeddingDimension || 0;
|
|
622
|
+
console.log(` embeddingDimension: ${embeddingDimension}D (stored locally, not on Walrus)`);
|
|
623
|
+
|
|
624
|
+
const basePackage: MemoryPackage = {
|
|
625
|
+
content: '[ENCRYPTED - requires decryption]',
|
|
626
|
+
contentType: 'text/plain',
|
|
627
|
+
metadata: result.memoryPackage.metadata,
|
|
628
|
+
embedding: [], // v2.1 has no embedding on Walrus
|
|
629
|
+
createdAt: result.memoryPackage.timestamp
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
if (decryptionContext && this.services.encryption) {
|
|
633
|
+
try {
|
|
634
|
+
console.log('🔐 Decrypting v2.1 content...');
|
|
635
|
+
|
|
636
|
+
const encryptedBase64 = result.memoryPackage.encryptedContent;
|
|
637
|
+
const binaryString = atob(encryptedBase64);
|
|
638
|
+
const encryptedBytes = new Uint8Array(binaryString.length);
|
|
639
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
640
|
+
encryptedBytes[i] = binaryString.charCodeAt(i);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const decryptedData = await this.services.encryption.decrypt({
|
|
644
|
+
encryptedContent: encryptedBytes,
|
|
645
|
+
userAddress: this.services.config.userAddress,
|
|
646
|
+
sessionKey: decryptionContext.sessionKey,
|
|
647
|
+
memoryCapId: decryptionContext.memoryCapId,
|
|
648
|
+
keyId: decryptionContext.keyId
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
const decryptedContent = new TextDecoder().decode(decryptedData);
|
|
652
|
+
console.log(`✅ v2.1 decryption successful: "${decryptedContent.substring(0, 50)}..."`);
|
|
653
|
+
|
|
654
|
+
return {
|
|
655
|
+
memoryPackage: {
|
|
656
|
+
...basePackage,
|
|
657
|
+
content: decryptedContent
|
|
658
|
+
},
|
|
659
|
+
decryptionStatus: 'success'
|
|
660
|
+
};
|
|
661
|
+
} catch (decryptError: any) {
|
|
662
|
+
console.error('❌ v2.1 decryption failed:', decryptError.message);
|
|
663
|
+
return {
|
|
664
|
+
memoryPackage: basePackage,
|
|
665
|
+
decryptionStatus: 'failed',
|
|
666
|
+
error: decryptError.message
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return {
|
|
672
|
+
memoryPackage: basePackage,
|
|
673
|
+
decryptionStatus: 'failed',
|
|
674
|
+
error: 'No decryption context provided for v2.1 encrypted package'
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// v2.0 JSON package with encrypted content + plaintext embedding (legacy)
|
|
679
|
+
if (result.memoryPackage?.version === '2.0' && result.memoryPackage?.encryptedContent) {
|
|
680
|
+
console.log('📦 Detected v2.0 JSON package with encrypted content (legacy)');
|
|
681
|
+
|
|
682
|
+
// Get embedding from root level OR metadata.embedding (fallback for encryption service bug)
|
|
683
|
+
const embeddingArray = result.memoryPackage.embedding?.length > 0
|
|
684
|
+
? result.memoryPackage.embedding
|
|
685
|
+
: result.memoryPackage.metadata?.embedding;
|
|
686
|
+
|
|
687
|
+
const embeddingSource = result.memoryPackage.embedding?.length > 0 ? 'root' : 'metadata';
|
|
688
|
+
console.log(` embedding: ${embeddingArray?.length || 0}D (from ${embeddingSource})`);
|
|
689
|
+
|
|
690
|
+
// Return embedding even without decryption (for index rebuilding)
|
|
691
|
+
const basePackage: MemoryPackage = {
|
|
692
|
+
content: '[ENCRYPTED - requires decryption]',
|
|
693
|
+
contentType: 'text/plain',
|
|
694
|
+
metadata: result.memoryPackage.metadata,
|
|
695
|
+
embedding: embeddingArray, // Plaintext embedding available!
|
|
696
|
+
createdAt: result.memoryPackage.timestamp
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
if (decryptionContext && this.services.encryption) {
|
|
700
|
+
try {
|
|
701
|
+
console.log('🔐 Decrypting v2.0 encryptedContent...');
|
|
702
|
+
|
|
703
|
+
// Convert base64 back to Uint8Array
|
|
704
|
+
const encryptedBase64 = result.memoryPackage.encryptedContent;
|
|
705
|
+
const binaryString = atob(encryptedBase64);
|
|
706
|
+
const encryptedBytes = new Uint8Array(binaryString.length);
|
|
707
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
708
|
+
encryptedBytes[i] = binaryString.charCodeAt(i);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const decryptedData = await this.services.encryption.decrypt({
|
|
712
|
+
encryptedContent: encryptedBytes,
|
|
713
|
+
userAddress: this.services.config.userAddress,
|
|
714
|
+
sessionKey: decryptionContext.sessionKey,
|
|
715
|
+
memoryCapId: decryptionContext.memoryCapId,
|
|
716
|
+
keyId: decryptionContext.keyId
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
const decryptedContent = new TextDecoder().decode(decryptedData);
|
|
720
|
+
console.log(`✅ v2.0 decryption successful, content: "${decryptedContent.substring(0, 50)}..."`);
|
|
721
|
+
|
|
722
|
+
return {
|
|
723
|
+
memoryPackage: {
|
|
724
|
+
...basePackage,
|
|
725
|
+
content: decryptedContent
|
|
726
|
+
},
|
|
727
|
+
decryptionStatus: 'success'
|
|
728
|
+
};
|
|
729
|
+
} catch (decryptError: any) {
|
|
730
|
+
console.error('❌ v2.0 decryption failed:', decryptError.message);
|
|
731
|
+
return {
|
|
732
|
+
memoryPackage: basePackage, // Return with embedding but encrypted content
|
|
733
|
+
decryptionStatus: 'failed',
|
|
734
|
+
error: decryptError.message
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// No decryption context - return with embedding only
|
|
740
|
+
return {
|
|
741
|
+
memoryPackage: basePackage,
|
|
742
|
+
decryptionStatus: 'failed',
|
|
743
|
+
error: 'No decryption context provided (embedding still available)'
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Legacy binary format (v0) - try to decrypt if context provided
|
|
748
|
+
if (decryptionContext && this.services.encryption) {
|
|
749
|
+
try {
|
|
750
|
+
console.log('🔐 Decrypting legacy binary format...');
|
|
751
|
+
|
|
752
|
+
const decryptedData = await this.services.encryption.decrypt({
|
|
753
|
+
encryptedContent: result.content,
|
|
754
|
+
userAddress: this.services.config.userAddress,
|
|
755
|
+
sessionKey: decryptionContext.sessionKey,
|
|
756
|
+
memoryCapId: decryptionContext.memoryCapId,
|
|
757
|
+
keyId: decryptionContext.keyId
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
const decryptedContent = new TextDecoder().decode(decryptedData);
|
|
761
|
+
console.log(`✅ Legacy decryption successful, content length: ${decryptedContent.length}`);
|
|
762
|
+
|
|
763
|
+
return {
|
|
764
|
+
memoryPackage: {
|
|
765
|
+
content: decryptedContent,
|
|
766
|
+
contentType: 'text/plain',
|
|
767
|
+
metadata: result.metadata,
|
|
768
|
+
createdAt: result.metadata.createdTimestamp
|
|
769
|
+
},
|
|
770
|
+
decryptionStatus: 'success'
|
|
771
|
+
};
|
|
772
|
+
} catch (decryptError: any) {
|
|
773
|
+
console.error('❌ Legacy decryption failed:', decryptError.message);
|
|
774
|
+
return {
|
|
775
|
+
memoryPackage: null,
|
|
776
|
+
decryptionStatus: 'failed',
|
|
777
|
+
error: decryptError.message
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Encrypted but no decryption context provided
|
|
783
|
+
return {
|
|
784
|
+
memoryPackage: null,
|
|
785
|
+
decryptionStatus: 'failed',
|
|
786
|
+
error: 'Content is encrypted but no decryption context provided'
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Check if blob exists on Walrus
|
|
792
|
+
*
|
|
793
|
+
* @param blobId - Blob ID to check
|
|
794
|
+
* @returns True if blob exists
|
|
795
|
+
*/
|
|
796
|
+
async exists(blobId: string): Promise<boolean> {
|
|
797
|
+
try {
|
|
798
|
+
await this.download(blobId);
|
|
799
|
+
return true;
|
|
800
|
+
} catch {
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Get metadata for a blob
|
|
807
|
+
*
|
|
808
|
+
* @param blobId - Blob ID
|
|
809
|
+
* @returns Blob metadata or null
|
|
810
|
+
*/
|
|
811
|
+
async getMetadata(blobId: string): Promise<{
|
|
812
|
+
blobId: string;
|
|
813
|
+
size?: number;
|
|
814
|
+
exists: boolean;
|
|
815
|
+
}> {
|
|
816
|
+
try {
|
|
817
|
+
const data = await this.download(blobId);
|
|
818
|
+
return {
|
|
819
|
+
blobId,
|
|
820
|
+
size: data.length,
|
|
821
|
+
exists: true
|
|
822
|
+
};
|
|
823
|
+
} catch {
|
|
824
|
+
return {
|
|
825
|
+
blobId,
|
|
826
|
+
exists: false
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// ==========================================================================
|
|
832
|
+
// High-Level Decrypt API
|
|
833
|
+
// ==========================================================================
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Retrieve and decrypt a memory package with minimal boilerplate.
|
|
837
|
+
* SDK handles all version detection, format conversion, and decryption internally.
|
|
838
|
+
*
|
|
839
|
+
* @param blobId - Blob ID on Walrus
|
|
840
|
+
* @param options - Decryption options
|
|
841
|
+
* @returns Decrypted content, embedding, and metadata
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```typescript
|
|
845
|
+
* // With sign function (SDK creates session key)
|
|
846
|
+
* const result = await pdw.storage.retrieveAndDecrypt(blobId, {
|
|
847
|
+
* signFn: async (message) => {
|
|
848
|
+
* const sig = await signPersonalMessage({ message: new TextEncoder().encode(message) });
|
|
849
|
+
* return { signature: sig.signature };
|
|
850
|
+
* }
|
|
851
|
+
* });
|
|
852
|
+
*
|
|
853
|
+
* // With existing session key
|
|
854
|
+
* const result = await pdw.storage.retrieveAndDecrypt(blobId, { sessionKey });
|
|
855
|
+
*
|
|
856
|
+
* console.log(result.content); // "my name is Aaron"
|
|
857
|
+
* console.log(result.embedding); // [0.12, -0.34, ...] (3072D)
|
|
858
|
+
* console.log(result.version); // "2.2"
|
|
859
|
+
* ```
|
|
860
|
+
*/
|
|
861
|
+
async retrieveAndDecrypt(
|
|
862
|
+
blobId: string,
|
|
863
|
+
options: {
|
|
864
|
+
/** Function to sign personal message (SDK will create session key) */
|
|
865
|
+
signFn?: (message: string) => Promise<{ signature: string }>;
|
|
866
|
+
/** Existing session key (skip signing if provided) */
|
|
867
|
+
sessionKey?: any;
|
|
868
|
+
/** Override memoryCapId (auto-detected from metadata if not provided) */
|
|
869
|
+
memoryCapId?: string;
|
|
870
|
+
/** Override keyId hex string (auto-detected from metadata if not provided) */
|
|
871
|
+
keyId?: string;
|
|
872
|
+
} = {}
|
|
873
|
+
): Promise<{
|
|
874
|
+
content: string;
|
|
875
|
+
embedding: number[];
|
|
876
|
+
version: '2.2' | '2.1' | '2.0' | 'legacy' | 'plaintext';
|
|
877
|
+
isEncrypted: boolean;
|
|
878
|
+
metadata: Record<string, any>;
|
|
879
|
+
blobId: string;
|
|
880
|
+
}> {
|
|
881
|
+
if (!this.services.storage) {
|
|
882
|
+
throw new Error('Storage service not configured.');
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
console.log(`🔐 retrieveAndDecrypt: Downloading blob ${blobId}...`);
|
|
886
|
+
|
|
887
|
+
// Step 1: Download blob from Walrus
|
|
888
|
+
const blobData = await this.download(blobId);
|
|
889
|
+
console.log(` Downloaded ${blobData.length} bytes`);
|
|
890
|
+
|
|
891
|
+
// Step 2: Detect format and parse
|
|
892
|
+
let version: '2.2' | '2.1' | '2.0' | 'legacy' | 'plaintext' = 'legacy';
|
|
893
|
+
let isEncrypted = false;
|
|
894
|
+
let encryptedContentBase64: string | null = null;
|
|
895
|
+
let encryptedEmbeddingBase64: string | null = null;
|
|
896
|
+
let metadata: Record<string, any> = {};
|
|
897
|
+
let plainEmbedding: number[] = [];
|
|
898
|
+
|
|
899
|
+
try {
|
|
900
|
+
const blobText = new TextDecoder().decode(blobData);
|
|
901
|
+
const parsed = JSON.parse(blobText);
|
|
902
|
+
|
|
903
|
+
// v2.2: Full encryption (content + embedding both encrypted)
|
|
904
|
+
if (parsed.version === '2.2' && parsed.encryptedContent) {
|
|
905
|
+
version = '2.2';
|
|
906
|
+
isEncrypted = true;
|
|
907
|
+
encryptedContentBase64 = parsed.encryptedContent;
|
|
908
|
+
encryptedEmbeddingBase64 = parsed.encryptedEmbedding || null;
|
|
909
|
+
metadata = parsed.metadata || {};
|
|
910
|
+
console.log(` Detected v2.2: encrypted content + encrypted embedding`);
|
|
911
|
+
}
|
|
912
|
+
// v2.1: Encrypted content only (no embedding on Walrus)
|
|
913
|
+
else if (parsed.version === '2.1' && parsed.encryptedContent) {
|
|
914
|
+
version = '2.1';
|
|
915
|
+
isEncrypted = true;
|
|
916
|
+
encryptedContentBase64 = parsed.encryptedContent;
|
|
917
|
+
metadata = parsed.metadata || {};
|
|
918
|
+
console.log(` Detected v2.1: encrypted content only`);
|
|
919
|
+
}
|
|
920
|
+
// v2.0: Encrypted content + plaintext embedding
|
|
921
|
+
else if (parsed.version === '2.0' && parsed.encryptedContent) {
|
|
922
|
+
version = '2.0';
|
|
923
|
+
isEncrypted = true;
|
|
924
|
+
encryptedContentBase64 = parsed.encryptedContent;
|
|
925
|
+
plainEmbedding = parsed.embedding || [];
|
|
926
|
+
metadata = parsed.metadata || {};
|
|
927
|
+
console.log(` Detected v2.0: encrypted content + ${plainEmbedding.length}D plaintext embedding`);
|
|
928
|
+
}
|
|
929
|
+
// v1.0: Plaintext JSON package
|
|
930
|
+
else if (parsed.version && parsed.content) {
|
|
931
|
+
version = 'plaintext';
|
|
932
|
+
isEncrypted = false;
|
|
933
|
+
metadata = parsed.metadata || {};
|
|
934
|
+
plainEmbedding = parsed.embedding || [];
|
|
935
|
+
console.log(` Detected plaintext JSON package`);
|
|
936
|
+
return {
|
|
937
|
+
content: parsed.content,
|
|
938
|
+
embedding: plainEmbedding,
|
|
939
|
+
version,
|
|
940
|
+
isEncrypted,
|
|
941
|
+
metadata,
|
|
942
|
+
blobId
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
} catch {
|
|
946
|
+
// Not JSON - check if binary SEAL data
|
|
947
|
+
const isBinary = blobData.some(byte => byte < 32 && byte !== 9 && byte !== 10 && byte !== 13);
|
|
948
|
+
if (isBinary || blobData.some(byte => byte > 127)) {
|
|
949
|
+
version = 'legacy';
|
|
950
|
+
isEncrypted = true;
|
|
951
|
+
console.log(` Detected legacy binary format`);
|
|
952
|
+
} else {
|
|
953
|
+
// Plain text content
|
|
954
|
+
version = 'plaintext';
|
|
955
|
+
isEncrypted = false;
|
|
956
|
+
console.log(` Detected plaintext content`);
|
|
957
|
+
return {
|
|
958
|
+
content: new TextDecoder().decode(blobData),
|
|
959
|
+
embedding: [],
|
|
960
|
+
version,
|
|
961
|
+
isEncrypted,
|
|
962
|
+
metadata: {},
|
|
963
|
+
blobId
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Step 3: If not encrypted, return as-is
|
|
969
|
+
if (!isEncrypted) {
|
|
970
|
+
return {
|
|
971
|
+
content: new TextDecoder().decode(blobData),
|
|
972
|
+
embedding: plainEmbedding,
|
|
973
|
+
version,
|
|
974
|
+
isEncrypted,
|
|
975
|
+
metadata,
|
|
976
|
+
blobId
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Step 4: Get decryption parameters (from options or metadata)
|
|
981
|
+
const memoryCapId = options.memoryCapId || metadata.memoryCapId;
|
|
982
|
+
const keyIdHex = options.keyId || metadata.keyId;
|
|
983
|
+
|
|
984
|
+
if (!memoryCapId || !keyIdHex) {
|
|
985
|
+
throw new Error(
|
|
986
|
+
`Missing decryption parameters. memoryCapId=${!!memoryCapId}, keyId=${!!keyIdHex}. ` +
|
|
987
|
+
`Provide via options or ensure metadata contains these values.`
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Step 5: Convert keyId hex string to Uint8Array (SDK handles this!)
|
|
992
|
+
const keyIdBytes = new Uint8Array(
|
|
993
|
+
(keyIdHex.startsWith('0x') ? keyIdHex.slice(2) : keyIdHex)
|
|
994
|
+
.match(/.{1,2}/g)!
|
|
995
|
+
.map((byte: string) => parseInt(byte, 16))
|
|
996
|
+
);
|
|
997
|
+
|
|
998
|
+
// Step 6: Get or create session key
|
|
999
|
+
let sessionKey = options.sessionKey;
|
|
1000
|
+
if (!sessionKey) {
|
|
1001
|
+
if (!options.signFn) {
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
'Decryption requires either sessionKey or signFn. ' +
|
|
1004
|
+
'Provide signFn to create session key automatically.'
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
if (!this.services.encryption) {
|
|
1008
|
+
throw new Error('Encryption service not configured.');
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
console.log(` Creating session key (will prompt for signature)...`);
|
|
1012
|
+
sessionKey = await this.services.encryption.createSessionKey(
|
|
1013
|
+
this.services.config.userAddress,
|
|
1014
|
+
{
|
|
1015
|
+
signPersonalMessageFn: async (message: string) => {
|
|
1016
|
+
return options.signFn!(message);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
console.log(` Session key created`);
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// Step 7: Decrypt content
|
|
1024
|
+
console.log(` Decrypting content...`);
|
|
1025
|
+
|
|
1026
|
+
// Convert base64 to Uint8Array if needed
|
|
1027
|
+
let dataToDecrypt: Uint8Array;
|
|
1028
|
+
if (encryptedContentBase64) {
|
|
1029
|
+
const binaryString = atob(encryptedContentBase64);
|
|
1030
|
+
dataToDecrypt = new Uint8Array(binaryString.length);
|
|
1031
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
1032
|
+
dataToDecrypt[i] = binaryString.charCodeAt(i);
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
dataToDecrypt = blobData;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const decryptedContentData = await this.services.encryption!.decrypt({
|
|
1039
|
+
encryptedContent: dataToDecrypt,
|
|
1040
|
+
userAddress: this.services.config.userAddress,
|
|
1041
|
+
sessionKey,
|
|
1042
|
+
memoryCapId,
|
|
1043
|
+
keyId: keyIdBytes
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
const content = new TextDecoder().decode(decryptedContentData);
|
|
1047
|
+
console.log(` Content decrypted: "${content.substring(0, 50)}${content.length > 50 ? '...' : ''}"`);
|
|
1048
|
+
|
|
1049
|
+
// Step 8: Decrypt embedding if v2.2
|
|
1050
|
+
let embedding: number[] = plainEmbedding;
|
|
1051
|
+
|
|
1052
|
+
if (version === '2.2' && encryptedEmbeddingBase64) {
|
|
1053
|
+
console.log(` Decrypting embedding...`);
|
|
1054
|
+
const embeddingBinaryString = atob(encryptedEmbeddingBase64);
|
|
1055
|
+
const encryptedEmbeddingBytes = new Uint8Array(embeddingBinaryString.length);
|
|
1056
|
+
for (let i = 0; i < embeddingBinaryString.length; i++) {
|
|
1057
|
+
encryptedEmbeddingBytes[i] = embeddingBinaryString.charCodeAt(i);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const decryptedEmbeddingData = await this.services.encryption!.decrypt({
|
|
1061
|
+
encryptedContent: encryptedEmbeddingBytes,
|
|
1062
|
+
userAddress: this.services.config.userAddress,
|
|
1063
|
+
sessionKey,
|
|
1064
|
+
memoryCapId,
|
|
1065
|
+
keyId: keyIdBytes
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
const embeddingJson = new TextDecoder().decode(decryptedEmbeddingData);
|
|
1069
|
+
embedding = JSON.parse(embeddingJson);
|
|
1070
|
+
console.log(` Embedding decrypted: ${embedding.length}D vector`);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
console.log(`✅ retrieveAndDecrypt complete: ${content.length} chars, ${embedding.length}D embedding`);
|
|
1074
|
+
|
|
1075
|
+
return {
|
|
1076
|
+
content,
|
|
1077
|
+
embedding,
|
|
1078
|
+
version,
|
|
1079
|
+
isEncrypted,
|
|
1080
|
+
metadata,
|
|
1081
|
+
blobId
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// ==========================================================================
|
|
1086
|
+
// Batch Operations (Quilt)
|
|
1087
|
+
// ==========================================================================
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Upload multiple memories as a Quilt (batch upload)
|
|
1091
|
+
*
|
|
1092
|
+
* Uses Walrus Quilt for ~90% gas savings compared to individual uploads.
|
|
1093
|
+
* Requires 2 user signatures:
|
|
1094
|
+
* - Transaction 1: Register blob on-chain
|
|
1095
|
+
* - Transaction 2: Certify upload on-chain
|
|
1096
|
+
*
|
|
1097
|
+
* @param memories - Array of memories to upload
|
|
1098
|
+
* @param options - Upload options including signer
|
|
1099
|
+
* @returns Quilt result with file mappings
|
|
1100
|
+
*
|
|
1101
|
+
* @example
|
|
1102
|
+
* ```typescript
|
|
1103
|
+
* const result = await pdw.storage.uploadMemoryBatch(
|
|
1104
|
+
* memories,
|
|
1105
|
+
* {
|
|
1106
|
+
* signer: pdw.getConfig().signer,
|
|
1107
|
+
* epochs: 3,
|
|
1108
|
+
* userAddress: pdw.getConfig().userAddress
|
|
1109
|
+
* }
|
|
1110
|
+
* );
|
|
1111
|
+
* console.log(`Uploaded ${result.files.length} files`);
|
|
1112
|
+
* ```
|
|
1113
|
+
*/
|
|
1114
|
+
async uploadMemoryBatch(
|
|
1115
|
+
memories: Array<{
|
|
1116
|
+
content: string;
|
|
1117
|
+
category: string;
|
|
1118
|
+
importance: number;
|
|
1119
|
+
topic: string;
|
|
1120
|
+
embedding: number[];
|
|
1121
|
+
encryptedContent: Uint8Array;
|
|
1122
|
+
summary?: string;
|
|
1123
|
+
id?: string;
|
|
1124
|
+
}>,
|
|
1125
|
+
options: {
|
|
1126
|
+
signer: any; // UnifiedSigner
|
|
1127
|
+
epochs?: number;
|
|
1128
|
+
userAddress: string;
|
|
1129
|
+
}
|
|
1130
|
+
): Promise<{
|
|
1131
|
+
quiltId: string;
|
|
1132
|
+
files: Array<{ identifier: string; blobId: string }>;
|
|
1133
|
+
uploadTimeMs: number;
|
|
1134
|
+
}> {
|
|
1135
|
+
if (!this.services.storage) {
|
|
1136
|
+
throw new Error('Storage service not configured.');
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
return this.services.storage.uploadMemoryBatch(memories, options);
|
|
1140
|
+
}
|
|
1141
|
+
}
|