@rozenite/storage-plugin 1.12.0 → 2.0.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +6 -1
  3. package/dist/devtools/assets/panel-B6Wp0eie.css +1 -0
  4. package/dist/devtools/assets/panel-D7MFc4HG.js +32 -0
  5. package/dist/devtools/panel.html +2 -2
  6. package/dist/react-native/chunks/async-storage.require.cjs +1 -1
  7. package/dist/react-native/chunks/async-storage.require.js +8 -7
  8. package/dist/react-native/chunks/index.require.cjs +1 -1
  9. package/dist/react-native/chunks/index.require.js +70 -64
  10. package/dist/react-native/chunks/secure-storage.require.cjs +1 -1
  11. package/dist/react-native/chunks/secure-storage.require.js +22 -14
  12. package/dist/react-native/chunks/useRozeniteStoragePlugin.require.cjs +1 -1
  13. package/dist/react-native/chunks/useRozeniteStoragePlugin.require.js +863 -315
  14. package/dist/react-native/index.d.ts +154 -18
  15. package/dist/react-native/index.js +10 -2
  16. package/dist/rozenite.json +1 -1
  17. package/dist/sdk/index.cjs +1 -1
  18. package/dist/sdk/index.d.ts +13 -5
  19. package/dist/sdk/index.js +21 -12
  20. package/package.json +13 -15
  21. package/react-native.ts +29 -14
  22. package/rozenite.config.ts +78 -0
  23. package/src/react-native/__tests__/entry-preview-pagination.test.ts +262 -0
  24. package/src/react-native/__tests__/export-snapshot.test.ts +142 -0
  25. package/src/react-native/__tests__/full-entry-request.test.ts +155 -0
  26. package/src/react-native/__tests__/import.test.ts +172 -10
  27. package/src/react-native/__tests__/storage-discovery.test.ts +155 -0
  28. package/src/react-native/__tests__/storage-view.test.ts +172 -0
  29. package/src/react-native/__tests__/stress-verification.test.ts +153 -0
  30. package/src/react-native/__tests__/use-storage-agent-tools.test.ts +231 -0
  31. package/src/react-native/adapters/async-storage.ts +9 -7
  32. package/src/react-native/adapters/mmkv.ts +5 -0
  33. package/src/react-native/adapters/secure-storage.ts +9 -1
  34. package/src/react-native/entry-preview-pagination.ts +277 -0
  35. package/src/react-native/export-snapshot.ts +94 -0
  36. package/src/react-native/full-entry-request.ts +96 -0
  37. package/src/react-native/import.ts +190 -16
  38. package/src/react-native/storage-discovery.ts +96 -0
  39. package/src/react-native/storage-view.ts +45 -103
  40. package/src/react-native/useRozeniteStoragePlugin.ts +108 -72
  41. package/src/react-native/useStorageAgentTools.ts +207 -86
  42. package/src/shared/__tests__/entry-preview.test.ts +117 -0
  43. package/src/shared/__tests__/snapshot.test.ts +21 -0
  44. package/src/shared/agent-tools.ts +27 -11
  45. package/src/shared/entry-preview.ts +59 -0
  46. package/src/shared/messaging.ts +136 -21
  47. package/src/shared/snapshot.ts +8 -1
  48. package/src/shared/types.ts +20 -1
  49. package/src/ui/__tests__/binary-value-editor-state.test.ts +13 -0
  50. package/src/ui/__tests__/binary.test.ts +18 -0
  51. package/src/ui/__tests__/large-value-viewer-state.test.ts +21 -0
  52. package/src/ui/__tests__/large-value-viewer.test.tsx +63 -0
  53. package/src/ui/__tests__/panel.test.tsx +409 -0
  54. package/src/ui/__tests__/storage-groups.test.ts +91 -0
  55. package/src/ui/__tests__/use-storage-entries.test.tsx +353 -0
  56. package/src/ui/add-entry-dialog.tsx +65 -105
  57. package/src/ui/binary-value-editor-state.ts +10 -1
  58. package/src/ui/binary-value-editor.tsx +57 -33
  59. package/src/ui/binary.ts +56 -4
  60. package/src/ui/edit-entry-dialog.tsx +71 -99
  61. package/src/ui/editor-switcher.tsx +7 -5
  62. package/src/ui/entry-detail-dialog.tsx +69 -179
  63. package/src/ui/format-value.tsx +32 -0
  64. package/src/ui/globals.css +1 -123
  65. package/src/ui/import-dialog.tsx +66 -91
  66. package/src/ui/large-value-viewer-state.ts +32 -0
  67. package/src/ui/large-value-viewer.tsx +71 -0
  68. package/src/ui/panel.tsx +724 -496
  69. package/src/ui/query-client.tsx +34 -0
  70. package/src/ui/storage-groups.ts +66 -0
  71. package/src/ui/typed-value-editor.tsx +14 -15
  72. package/src/ui/use-storage-entries.ts +207 -0
  73. package/tsconfig.json +5 -1
  74. package/dist/devtools/assets/panel-Bm-SWF7d.js +0 -33
  75. package/dist/devtools/assets/panel-DIqI4WSp.css +0 -1
  76. package/postcss.config.js +0 -6
  77. package/src/ui/confirm-dialog.tsx +0 -100
  78. package/src/ui/editable-table.tsx +0 -300
  79. package/tailwind.config.ts +0 -94
@@ -2,13 +2,23 @@ import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
2
2
  import { useEffect, useMemo } from 'react';
3
3
  import type {
4
4
  StorageDeleteEntryEvent,
5
+ StorageDiscoverStoragesRequestEvent,
5
6
  StorageEventMap,
6
- StorageGetSnapshotEvent,
7
+ StorageExportSnapshotRequestEvent,
8
+ StorageGetEntryRequestEvent,
7
9
  StorageImportEntriesEvent,
10
+ StorageImportPreviewRequestEvent,
11
+ StorageInvalidationOperation,
12
+ StorageListEntryPreviewsRequestEvent,
8
13
  StorageSetEntryEvent,
14
+ StoragePurgeEvent,
9
15
  } from '../shared/messaging';
10
16
  import type { StorageAdapter } from '../shared/types';
11
- import { handleImportEntries } from './import';
17
+ import { handleImportEntries, handleImportPreviewRequest } from './import';
18
+ import { handleStorageDiscoveryRequest } from './storage-discovery';
19
+ import { handleListEntryPreviewsRequest } from './entry-preview-pagination';
20
+ import { handleFullEntryRequest } from './full-entry-request';
21
+ import { handleExportSnapshotRequest } from './export-snapshot';
12
22
  import { createStorageViews } from './storage-view';
13
23
  import { useStorageAgentTools } from './useStorageAgentTools';
14
24
 
@@ -32,73 +42,50 @@ export const useRozeniteStoragePlugin = ({
32
42
  return;
33
43
  }
34
44
 
35
- const pushSnapshot = async (viewId?: string) => {
36
- const selectedViews = viewId
37
- ? views.filter((view) => view.id === viewId)
38
- : views;
39
-
40
- for (const view of selectedViews) {
41
- try {
42
- const entries = await view.getAllEntries();
43
- client.send('snapshot', {
44
- type: 'snapshot',
45
- target: view.target,
46
- adapterName: view.adapterName,
47
- storageName: view.storageName,
48
- capabilities: view.capabilities,
49
- blacklist: view.blacklist
50
- ? { source: view.blacklist.source, flags: view.blacklist.flags }
51
- : undefined,
52
- entries,
53
- });
54
- } catch (error) {
55
- console.warn(
56
- `[Rozenite] Storage Plugin: Failed to snapshot ${view.target.adapterId}/${view.target.storageId}.`,
57
- error,
58
- );
59
- }
60
- }
61
- };
62
-
63
- void pushSnapshot();
64
-
65
45
  const viewSubscriptions: { remove: () => void }[] = [];
66
46
  let disposed = false;
67
47
 
48
+ const sendInvalidation = (
49
+ target: StorageSetEntryEvent['target'],
50
+ key?: string,
51
+ operation?: StorageInvalidationOperation,
52
+ ) => {
53
+ client.send('storage-invalidated', {
54
+ type: 'storage-invalidated',
55
+ target,
56
+ key,
57
+ operation,
58
+ });
59
+ };
60
+
68
61
  // Prevent one storage watcher failure from breaking the whole plugin.
69
62
  void Promise.all(
70
- views.map(async (view) => {
71
- try {
72
- const subscription = await view.watch({
73
- onSet: (entry) => {
74
- client.send('set-entry', {
75
- type: 'set-entry',
76
- target: view.target,
77
- entry,
78
- });
79
- },
80
- onDelete: (key) => {
81
- client.send('delete-entry', {
82
- type: 'delete-entry',
83
- target: view.target,
84
- key,
85
- });
86
- },
87
- });
63
+ views
64
+ .filter((view) => view.supportsSubscriptions && view.watch)
65
+ .map(async (view) => {
66
+ try {
67
+ const watch = view.watch;
68
+ if (!watch) {
69
+ return;
70
+ }
88
71
 
89
- if (disposed) {
90
- subscription.remove();
91
- return;
92
- }
72
+ const subscription = await watch((key) => {
73
+ sendInvalidation(view.target, key);
74
+ });
93
75
 
94
- viewSubscriptions.push(subscription);
95
- } catch (error) {
96
- console.warn(
97
- `[Rozenite] Storage Plugin: Failed to attach watcher for ${view.target.adapterId}/${view.target.storageId}.`,
98
- error,
99
- );
100
- }
101
- }),
76
+ if (disposed) {
77
+ subscription.remove();
78
+ return;
79
+ }
80
+
81
+ viewSubscriptions.push(subscription);
82
+ } catch (error) {
83
+ console.warn(
84
+ `[Rozenite] Storage Plugin: Failed to attach watcher for ${view.target.adapterId}/${view.target.storageId}.`,
85
+ error,
86
+ );
87
+ }
88
+ }),
102
89
  );
103
90
 
104
91
  const messageSubscriptions = [
@@ -120,6 +107,7 @@ export const useRozeniteStoragePlugin = ({
120
107
 
121
108
  try {
122
109
  await view.set(entry);
110
+ sendInvalidation(view.target, entry.key, 'set');
123
111
  } catch (error) {
124
112
  console.warn(
125
113
  `[Rozenite] Storage Plugin: Failed to set entry in ${target.adapterId}/${target.storageId}.`,
@@ -146,6 +134,7 @@ export const useRozeniteStoragePlugin = ({
146
134
 
147
135
  try {
148
136
  await view.delete(key);
137
+ sendInvalidation(view.target, key, 'delete');
149
138
  } catch (error) {
150
139
  console.warn(
151
140
  `[Rozenite] Storage Plugin: Failed to delete entry in ${target.adapterId}/${target.storageId}.`,
@@ -155,25 +144,72 @@ export const useRozeniteStoragePlugin = ({
155
144
  },
156
145
  ),
157
146
  client.onMessage(
158
- 'get-snapshot',
159
- async ({ target }: StorageGetSnapshotEvent) => {
160
- if (target === 'all') {
161
- await pushSnapshot();
147
+ 'purge-storage',
148
+ async ({ target }: StoragePurgeEvent) => {
149
+ const view = views.find(
150
+ (candidate) =>
151
+ candidate.target.adapterId === target.adapterId &&
152
+ candidate.target.storageId === target.storageId,
153
+ );
154
+
155
+ if (!view) {
156
+ console.warn(
157
+ `[Rozenite] Storage Plugin: Storage target not found for ${target.adapterId}/${target.storageId}`,
158
+ );
162
159
  return;
163
160
  }
164
161
 
165
- await pushSnapshot(`${target.adapterId}:${target.storageId}`);
162
+ try {
163
+ await view.purge();
164
+ sendInvalidation(view.target, undefined, 'purge');
165
+ } catch (error) {
166
+ console.warn(
167
+ `[Rozenite] Storage Plugin: Failed to purge storage in ${target.adapterId}/${target.storageId}.`,
168
+ error,
169
+ );
170
+ }
171
+ },
172
+ ),
173
+ client.onMessage(
174
+ 'discover-storages',
175
+ async (event: StorageDiscoverStoragesRequestEvent) => {
176
+ const response = await handleStorageDiscoveryRequest(views, event);
177
+ client.send(response.type, response);
178
+ },
179
+ ),
180
+ client.onMessage(
181
+ 'list-entry-previews',
182
+ async (event: StorageListEntryPreviewsRequestEvent) => {
183
+ const response = await handleListEntryPreviewsRequest(views, event);
184
+ client.send(response.type, response);
185
+ },
186
+ ),
187
+ client.onMessage(
188
+ 'get-entry',
189
+ async (event: StorageGetEntryRequestEvent) => {
190
+ const response = await handleFullEntryRequest(views, event);
191
+ client.send(response.type, response);
192
+ },
193
+ ),
194
+ client.onMessage(
195
+ 'export-snapshot',
196
+ async (event: StorageExportSnapshotRequestEvent) => {
197
+ const response = await handleExportSnapshotRequest(views, event);
198
+ client.send(response.type, response);
199
+ },
200
+ ),
201
+ client.onMessage(
202
+ 'preview-import',
203
+ async (event: StorageImportPreviewRequestEvent) => {
204
+ const response = await handleImportPreviewRequest(views, event);
205
+ client.send(response.type, response);
166
206
  },
167
207
  ),
168
208
  client.onMessage(
169
209
  'import-entries',
170
210
  async (event: StorageImportEntriesEvent) => {
171
211
  await handleImportEntries(views, event, (out) => {
172
- if (out.type === 'set-entry') {
173
- client.send('set-entry', out);
174
- } else {
175
- client.send('import-result', out);
176
- }
212
+ client.send(out.type, out);
177
213
  });
178
214
  },
179
215
  ),
@@ -1,15 +1,21 @@
1
- import { useCallback } from 'react';
2
1
  import { useRozenitePluginAgentTool } from '@rozenite/agent-bridge';
2
+ import { DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT } from '@rozenite/agent-shared';
3
3
  import {
4
4
  STORAGE_AGENT_PLUGIN_ID,
5
5
  storageToolDefinitions,
6
+ type StorageListEntriesArgs,
6
7
  } from '../shared/agent-tools';
7
- import type { StorageEntry, StorageEntryType, StorageEntryValue } from '../shared/types';
8
+ import type {
9
+ StorageEntry,
10
+ StorageEntryType,
11
+ StorageEntryValue,
12
+ StorageTarget,
13
+ } from '../shared/types';
8
14
  import type { StorageView } from './storage-view';
9
15
 
10
16
  const parseValueForType = (
11
17
  type: StorageEntryType,
12
- value: unknown
18
+ value: unknown,
13
19
  ): StorageEntryValue => {
14
20
  if (type === 'string') {
15
21
  if (typeof value !== 'string') {
@@ -42,115 +48,230 @@ const parseValueForType = (
42
48
  const formatViewRef = (view: StorageView) =>
43
49
  `${view.target.adapterId}/${view.target.storageId}`;
44
50
 
45
- export const useStorageAgentTools = (views: StorageView[]) => {
46
- const resolveStorage = useCallback(
47
- (adapterId?: string, storageId?: string) => {
48
- if (views.length === 0) {
49
- throw new Error('No storages are registered.');
50
- }
51
+ const isSameTarget = (left: StorageTarget, right: StorageTarget) =>
52
+ left.adapterId === right.adapterId && left.storageId === right.storageId;
51
53
 
52
- if (adapterId !== undefined || storageId !== undefined) {
53
- const selected = views.find(
54
- (view) =>
55
- (adapterId === undefined || view.target.adapterId === adapterId) &&
56
- (storageId === undefined || view.target.storageId === storageId)
57
- );
54
+ type ListEntriesCursor = {
55
+ version: 1;
56
+ target: StorageTarget;
57
+ offset: number;
58
+ };
58
59
 
59
- if (!selected) {
60
- const qualifier = [
61
- adapterId && `adapterId="${adapterId}"`,
62
- storageId && `storageId="${storageId}"`,
63
- ]
64
- .filter(Boolean)
65
- .join(', ');
66
- throw new Error(
67
- `No storage matched ${qualifier}. Available: ${views.map(formatViewRef).join(', ')}`
68
- );
69
- }
70
-
71
- return selected;
72
- }
60
+ const encodeCursor = (cursor: ListEntriesCursor) =>
61
+ btoa(encodeURIComponent(JSON.stringify(cursor)));
73
62
 
74
- if (views.length > 1) {
75
- throw new Error(
76
- `Multiple storages detected. Provide adapterId and/or storageId. Available: ${views.map(formatViewRef).join(', ')}`
77
- );
78
- }
63
+ const decodeCursor = (value: string): ListEntriesCursor => {
64
+ try {
65
+ const cursor: unknown = JSON.parse(decodeURIComponent(atob(value)));
66
+ if (
67
+ typeof cursor !== 'object' ||
68
+ cursor == null ||
69
+ Array.isArray(cursor) ||
70
+ (cursor as { version?: unknown }).version !== 1 ||
71
+ typeof (cursor as { target?: { adapterId?: unknown } }).target
72
+ ?.adapterId !== 'string' ||
73
+ typeof (cursor as { target?: { storageId?: unknown } }).target
74
+ ?.storageId !== 'string' ||
75
+ !Number.isSafeInteger((cursor as { offset?: unknown }).offset) ||
76
+ (cursor as { offset: number }).offset < 0
77
+ ) {
78
+ throw new Error('Invalid cursor');
79
+ }
80
+
81
+ return cursor as ListEntriesCursor;
82
+ } catch {
83
+ throw new Error(
84
+ 'Invalid cursor. Run list-entries again without a cursor to restart pagination.',
85
+ );
86
+ }
87
+ };
88
+
89
+ const sanitizeLimit = (limit: number | undefined) => {
90
+ if (
91
+ typeof limit !== 'number' ||
92
+ !Number.isFinite(limit) ||
93
+ !Number.isInteger(limit) ||
94
+ limit < 1
95
+ ) {
96
+ return DEFAULT_PAGE_LIMIT;
97
+ }
98
+
99
+ return Math.min(limit, MAX_PAGE_LIMIT);
100
+ };
79
101
 
80
- return views[0];
102
+ const sanitizeOffset = (offset: number | undefined) => {
103
+ if (
104
+ typeof offset !== 'number' ||
105
+ !Number.isFinite(offset) ||
106
+ !Number.isSafeInteger(offset) ||
107
+ offset < 0
108
+ ) {
109
+ return 0;
110
+ }
111
+
112
+ return offset;
113
+ };
114
+
115
+ const resolveStorage = (
116
+ views: readonly StorageView[],
117
+ adapterId?: string,
118
+ storageId?: string,
119
+ ) => {
120
+ if (views.length === 0) {
121
+ throw new Error('No storages are registered.');
122
+ }
123
+
124
+ if (adapterId !== undefined || storageId !== undefined) {
125
+ const matches = views.filter(
126
+ (view) =>
127
+ (adapterId === undefined || view.target.adapterId === adapterId) &&
128
+ (storageId === undefined || view.target.storageId === storageId),
129
+ );
130
+ const qualifier = [
131
+ adapterId !== undefined && `adapterId="${adapterId}"`,
132
+ storageId !== undefined && `storageId="${storageId}"`,
133
+ ]
134
+ .filter(Boolean)
135
+ .join(', ');
136
+
137
+ if (matches.length === 0) {
138
+ throw new Error(
139
+ `No storage matched ${qualifier}. Available: ${views.map(formatViewRef).join(', ')}`,
140
+ );
141
+ }
142
+
143
+ if (matches.length > 1) {
144
+ throw new Error(
145
+ `Multiple storages matched ${qualifier}. Provide both adapterId and storageId. Available: ${matches.map(formatViewRef).join(', ')}`,
146
+ );
147
+ }
148
+
149
+ return matches[0]!;
150
+ }
151
+
152
+ if (views.length > 1) {
153
+ throw new Error(
154
+ `Multiple storages detected. Provide adapterId and/or storageId. Available: ${views.map(formatViewRef).join(', ')}`,
155
+ );
156
+ }
157
+
158
+ return views[0]!;
159
+ };
160
+
161
+ const listStorageKeys = async (
162
+ view: StorageView,
163
+ input: StorageListEntriesArgs,
164
+ ) => {
165
+ const limit = sanitizeLimit(input.limit);
166
+ if (input.cursor !== undefined && input.offset !== undefined) {
167
+ throw new Error('Provide either cursor or offset, not both.');
168
+ }
169
+ const cursor =
170
+ input.cursor === undefined ? undefined : decodeCursor(input.cursor);
171
+ if (cursor && !isSameTarget(cursor.target, view.target)) {
172
+ throw new Error(
173
+ 'Cursor does not match the requested storage. Run list-entries again without a cursor to restart pagination.',
174
+ );
175
+ }
176
+
177
+ const allKeys = await view.getAllKeys();
178
+ const offset = cursor?.offset ?? sanitizeOffset(input.offset);
179
+ const end = Math.min(offset + limit, allKeys.length);
180
+
181
+ return {
182
+ adapterId: view.target.adapterId,
183
+ storageId: view.target.storageId,
184
+ total: allKeys.length,
185
+ items: allKeys.slice(offset, end).map((key) => ({ key })),
186
+ page: {
187
+ limit,
188
+ hasMore: end < allKeys.length,
189
+ ...(end < allKeys.length
190
+ ? {
191
+ nextCursor: encodeCursor({
192
+ version: 1,
193
+ target: view.target,
194
+ offset: end,
195
+ }),
196
+ }
197
+ : {}),
81
198
  },
82
- [views]
83
- );
199
+ };
200
+ };
201
+
202
+ export const createStorageAgentHandlers = (views: readonly StorageView[]) => ({
203
+ listStorages: async () => ({
204
+ storages: views.map((view) => ({
205
+ adapterId: view.target.adapterId,
206
+ storageId: view.target.storageId,
207
+ adapterName: view.adapterName,
208
+ storageName: view.storageName,
209
+ supportedTypes: view.capabilities.supportedTypes,
210
+ })),
211
+ }),
212
+
213
+ listEntries: async (input: StorageListEntriesArgs) => {
214
+ const view = resolveStorage(views, input.adapterId, input.storageId);
215
+ return listStorageKeys(view, input);
216
+ },
217
+
218
+ readEntry: async ({
219
+ adapterId,
220
+ storageId,
221
+ key,
222
+ }: {
223
+ adapterId?: string;
224
+ storageId?: string;
225
+ key: string;
226
+ }) => {
227
+ const view = resolveStorage(views, adapterId, storageId);
228
+ const entry = await view.get(key);
229
+
230
+ if (!entry) {
231
+ throw new Error(
232
+ `Key "${key}" not found in storage "${formatViewRef(view)}".`,
233
+ );
234
+ }
235
+
236
+ return {
237
+ adapterId: view.target.adapterId,
238
+ storageId: view.target.storageId,
239
+ entry,
240
+ };
241
+ },
242
+ });
243
+
244
+ export const useStorageAgentTools = (views: StorageView[]) => {
245
+ const handlers = createStorageAgentHandlers(views);
84
246
 
85
247
  useRozenitePluginAgentTool({
86
248
  pluginId: STORAGE_AGENT_PLUGIN_ID,
87
249
  tool: storageToolDefinitions.listStorages,
88
- handler: async () => ({
89
- storages: await Promise.all(
90
- views.map(async (view) => ({
91
- adapterId: view.target.adapterId,
92
- storageId: view.target.storageId,
93
- adapterName: view.adapterName,
94
- storageName: view.storageName,
95
- supportedTypes: view.capabilities.supportedTypes,
96
- entryCount: (await view.getAllKeys()).length,
97
- }))
98
- ),
99
- }),
250
+ handler: handlers.listStorages,
100
251
  });
101
252
 
102
253
  useRozenitePluginAgentTool({
103
254
  pluginId: STORAGE_AGENT_PLUGIN_ID,
104
255
  tool: storageToolDefinitions.listEntries,
105
- handler: async ({ adapterId, storageId, offset = 0, limit = 100 }) => {
106
- const view = resolveStorage(adapterId, storageId);
107
- const allKeys = await view.getAllKeys();
108
- const safeOffset = Math.max(0, Math.floor(offset));
109
- const safeLimit = Math.max(1, Math.floor(limit));
110
- const keys = allKeys.slice(safeOffset, safeOffset + safeLimit);
111
-
112
- return {
113
- adapterId: view.target.adapterId,
114
- storageId: view.target.storageId,
115
- total: allKeys.length,
116
- offset: safeOffset,
117
- limit: safeLimit,
118
- keys,
119
- };
120
- },
256
+ handler: handlers.listEntries,
121
257
  });
122
258
 
123
259
  useRozenitePluginAgentTool({
124
260
  pluginId: STORAGE_AGENT_PLUGIN_ID,
125
261
  tool: storageToolDefinitions.readEntry,
126
- handler: async ({ adapterId, storageId, key }) => {
127
- const view = resolveStorage(adapterId, storageId);
128
- const entry = await view.get(key);
129
-
130
- if (!entry) {
131
- throw new Error(
132
- `Key "${key}" not found in storage "${formatViewRef(view)}".`
133
- );
134
- }
135
-
136
- return {
137
- adapterId: view.target.adapterId,
138
- storageId: view.target.storageId,
139
- entry,
140
- };
141
- },
262
+ handler: handlers.readEntry,
142
263
  });
143
264
 
144
265
  useRozenitePluginAgentTool({
145
266
  pluginId: STORAGE_AGENT_PLUGIN_ID,
146
267
  tool: storageToolDefinitions.createEntry,
147
268
  handler: async ({ adapterId, storageId, key, type, value }) => {
148
- const view = resolveStorage(adapterId, storageId);
269
+ const view = resolveStorage(views, adapterId, storageId);
149
270
  const existing = await view.get(key);
150
271
 
151
272
  if (existing) {
152
273
  throw new Error(
153
- `Key "${key}" already exists in storage "${formatViewRef(view)}". Use edit-entry instead.`
274
+ `Key "${key}" already exists in storage "${formatViewRef(view)}". Use edit-entry instead.`,
154
275
  );
155
276
  }
156
277
 
@@ -170,12 +291,12 @@ export const useStorageAgentTools = (views: StorageView[]) => {
170
291
  pluginId: STORAGE_AGENT_PLUGIN_ID,
171
292
  tool: storageToolDefinitions.editEntry,
172
293
  handler: async ({ adapterId, storageId, key, type, value }) => {
173
- const view = resolveStorage(adapterId, storageId);
294
+ const view = resolveStorage(views, adapterId, storageId);
174
295
  const existing = await view.get(key);
175
296
 
176
297
  if (!existing) {
177
298
  throw new Error(
178
- `Key "${key}" not found in storage "${formatViewRef(view)}". Use create-entry instead.`
299
+ `Key "${key}" not found in storage "${formatViewRef(view)}". Use create-entry instead.`,
179
300
  );
180
301
  }
181
302
 
@@ -195,7 +316,7 @@ export const useStorageAgentTools = (views: StorageView[]) => {
195
316
  pluginId: STORAGE_AGENT_PLUGIN_ID,
196
317
  tool: storageToolDefinitions.removeEntry,
197
318
  handler: async ({ adapterId, storageId, key }) => {
198
- const view = resolveStorage(adapterId, storageId);
319
+ const view = resolveStorage(views, adapterId, storageId);
199
320
  const existed = !!(await view.get(key));
200
321
  await view.delete(key);
201
322