@rozenite/storage-plugin 1.4.0 → 1.5.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @rozenite/storage-plugin
2
2
 
3
+ ## 1.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @rozenite/agent-bridge@1.5.1
9
+ - @rozenite/plugin-bridge@1.5.1
10
+
11
+ ## 1.5.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [#190](https://github.com/callstackincubator/rozenite/pull/190) [`5ae53a4`](https://github.com/callstackincubator/rozenite/commit/5ae53a4b509adbd8536ea24812f7ca523a95b625) Thanks [@V3RON](https://github.com/V3RON)! - Added Rozenite for Agents support to the Controls, MMKV, React Navigation, and Storage plugins.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [[`5ae53a4`](https://github.com/callstackincubator/rozenite/commit/5ae53a4b509adbd8536ea24812f7ca523a95b625)]:
20
+ - @rozenite/agent-bridge@1.5.0
21
+ - @rozenite/plugin-bridge@1.5.0
22
+
3
23
  ## 1.4.0
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -72,6 +72,6 @@ createAsyncStorageAdapter({
72
72
  ## Notes
73
73
 
74
74
  - Unsupported value types are disabled in UI create/edit flows.
75
- - Type support is enforced in UI, runtime and MCP tools.
75
+ - Type support is enforced in UI, runtime and Agent tools.
76
76
  - Storages without subscriptions automatically use internal polling updates.
77
77
  - Per-storage blacklists are supported through adapter configuration.
@@ -1 +1 @@
1
- {"name":"@rozenite/storage-plugin","version":"1.4.0","description":"Generic Storage Inspector for Rozenite.","panels":[{"name":"Storage","source":"/panel.html"}]}
1
+ {"name":"@rozenite/storage-plugin","version":"1.5.0","description":"Generic Storage Inspector for Rozenite.","panels":[{"name":"Storage","source":"/panel.html"}]}
@@ -0,0 +1,2 @@
1
+ import { StorageView } from './storage-view';
2
+ export declare const useStorageAgentTools: (views: StorageView[]) => void;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("@rozenite/plugin-bridge"),g=require("react"),S=require("./types.cjs"),b=1500,d=e=>e.kind==="async",y=e=>e.type==="buffer"?`${e.type}:${e.value.join(",")}`:`${e.type}:${String(e.value)}`,m=e=>new Map(e.map(t=>[t.key,t])),p=(e,t)=>e.blacklist?(e.blacklist.lastIndex=0,e.blacklist.test(t)):!1,$=(e,t,r)=>{if(!S.supportsType(e,t))throw new Error(`Type "${t}" is not supported by storage "${r.storageId}" in adapter "${r.adapterId}".`)},f=async e=>(d(e),e.getAllKeys()),w=async(e,t)=>(d(e),e.get(t)),v=async(e,t)=>{if(d(e)){await e.set(t);return}e.set(t)},E=async(e,t)=>{if(d(e)){await e.delete(t);return}e.delete(t)},I=async e=>{const t=await e();return m(t)},P=(e,t,r)=>{t.forEach((i,c)=>{const u=e.get(c);if(!u){r.onSet(i);return}y(u)!==y(i)&&r.onSet(i)}),e.forEach((i,c)=>{t.has(c)||r.onDelete(c)})},z=async(e,t)=>{let r=await I(e);const i=setInterval(async()=>{try{const c=await I(e);P(r,c,t),r=c}catch{}},b);return{remove:()=>{clearInterval(i)}}},M=(e,t)=>{const r=t.storage,i={adapterId:e.id,storageId:t.id},c=async o=>{if(!p(t,o))return w(r,o)},u=async()=>{const o=await f(r);return(await Promise.all(o.filter(s=>!p(t,s)).map(s=>w(r,s)))).filter(s=>!!s)};return{id:S.getStorageViewId(i),target:i,adapterName:e.name,storageName:t.name,capabilities:t.capabilities,get:c,set:async o=>{$(t.capabilities,o.type,i),await v(r,o)},delete:async o=>{await E(r,o)},getAllKeys:async()=>(await f(r)).filter(a=>!p(t,a)),getAllEntries:u,watch:async({onSet:o,onDelete:a})=>r.subscribe?r.subscribe(async s=>{try{const n=await c(s);if(!n){a(s);return}o(n)}catch{}}):z(u,{onSet:o,onDelete:a})}},R=e=>e.flatMap(t=>t.storages.map(r=>M(t,r))),A=({storages:e})=>{const t=g.useMemo(()=>R(e),[e]),r=h.useRozeniteDevToolsClient({pluginId:"@rozenite/storage-plugin"});return g.useEffect(()=>{if(!r)return;const i=async a=>{const s=a?t.filter(n=>n.id===a):t;for(const n of s)try{const l=await n.getAllEntries();r.send("snapshot",{type:"snapshot",target:n.target,adapterName:n.adapterName,storageName:n.storageName,capabilities:n.capabilities,entries:l})}catch(l){console.warn(`[Rozenite] Storage Plugin: Failed to snapshot ${n.target.adapterId}/${n.target.storageId}.`,l)}};i();const c=[];let u=!1;Promise.all(t.map(async a=>{try{const s=await a.watch({onSet:n=>{r.send("set-entry",{type:"set-entry",target:a.target,entry:n})},onDelete:n=>{r.send("delete-entry",{type:"delete-entry",target:a.target,key:n})}});if(u){s.remove();return}c.push(s)}catch(s){console.warn(`[Rozenite] Storage Plugin: Failed to attach watcher for ${a.target.adapterId}/${a.target.storageId}.`,s)}}));const o=[r.onMessage("set-entry",async({target:a,entry:s})=>{const n=t.find(l=>l.target.adapterId===a.adapterId&&l.target.storageId===a.storageId);if(!n){console.warn(`[Rozenite] Storage Plugin: Storage target not found for ${a.adapterId}/${a.storageId}`);return}try{await n.set(s)}catch(l){console.warn(`[Rozenite] Storage Plugin: Failed to set entry in ${a.adapterId}/${a.storageId}.`,l)}}),r.onMessage("delete-entry",async({target:a,key:s})=>{const n=t.find(l=>l.target.adapterId===a.adapterId&&l.target.storageId===a.storageId);if(!n){console.warn(`[Rozenite] Storage Plugin: Storage target not found for ${a.adapterId}/${a.storageId}`);return}try{await n.delete(s)}catch(l){console.warn(`[Rozenite] Storage Plugin: Failed to delete entry in ${a.adapterId}/${a.storageId}.`,l)}}),r.onMessage("get-snapshot",async({target:a})=>{if(a==="all"){await i();return}await i(`${a.adapterId}:${a.storageId}`)})];return()=>{u=!0,c.forEach(a=>a.remove()),o.forEach(a=>a.remove())}},[r,t]),r};exports.useRozeniteStoragePlugin=A;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("@rozenite/plugin-bridge"),m=require("react"),$=require("./types.cjs"),p=require("@rozenite/agent-bridge"),P=1500,f=e=>e.kind==="async",I=e=>e.type==="buffer"?`${e.type}:${e.value.join(",")}`:`${e.type}:${String(e.value)}`,T=e=>new Map(e.map(t=>[t.key,t])),h=(e,t)=>e.blacklist?(e.blacklist.lastIndex=0,e.blacklist.test(t)):!1,R=(e,t,r)=>{if(!$.supportsType(e,t))throw new Error(`Type "${t}" is not supported by storage "${r.storageId}" in adapter "${r.adapterId}".`)},w=async e=>(f(e),e.getAllKeys()),b=async(e,t)=>(f(e),e.get(t)),A=async(e,t)=>{if(f(e)){await e.set(t);return}e.set(t)},z=async(e,t)=>{if(f(e)){await e.delete(t);return}e.delete(t)},S=async e=>{const t=await e();return T(t)},M=(e,t,r)=>{t.forEach((s,n)=>{const o=e.get(n);if(!o){r.onSet(s);return}I(o)!==I(s)&&r.onSet(s)}),e.forEach((s,n)=>{t.has(n)||r.onDelete(n)})},k=async(e,t)=>{let r=await S(e);const s=setInterval(async()=>{try{const n=await S(e);M(r,n,t),r=n}catch{}},P);return{remove:()=>{clearInterval(s)}}},x=(e,t)=>{const r=t.storage,s={adapterId:e.id,storageId:t.id},n=async d=>{if(!h(t,d))return b(r,d)},o=async()=>{const d=await w(r);return(await Promise.all(d.filter(c=>!h(t,c)).map(c=>b(r,c)))).filter(c=>!!c)};return{id:$.getStorageViewId(s),target:s,adapterName:e.name,storageName:t.name,capabilities:t.capabilities,get:n,set:async d=>{R(t.capabilities,d.type,s),await A(r,d)},delete:async d=>{await z(r,d)},getAllKeys:async()=>(await w(r)).filter(a=>!h(t,a)),getAllEntries:o,watch:async({onSet:d,onDelete:a})=>r.subscribe?r.subscribe(async c=>{try{const i=await n(c);if(!i){a(c);return}d(i)}catch{}}):k(o,{onSet:d,onDelete:a})}},j=e=>e.flatMap(t=>t.storages.map(r=>x(t,r))),g="@rozenite/storage-plugin",y={adapterId:{type:"string",description:"Storage adapter ID. Required when multiple adapters are configured."},storageId:{type:"string",description:"Storage node ID within the adapter. Required when multiple storages are configured."}},q={name:"list-storages",description:"List all storage adapters and their storage nodes currently available on the device, including supported entry types and entry counts.",inputSchema:{type:"object",properties:{}}},K={name:"list-entries",description:"List keys in a storage. This call intentionally does not return entry values to keep responses token-efficient.",inputSchema:{type:"object",properties:{...y,offset:{type:"number",description:"Pagination offset. Defaults to 0."},limit:{type:"number",description:"Pagination size. Defaults to 100."}}}},N={name:"read-entry",description:"Read a single storage entry value by key.",inputSchema:{type:"object",properties:{...y,key:{type:"string",description:"Entry key."}},required:["key"]}},V={name:"create-entry",description:"Create a new storage entry. Fails if the key already exists.",inputSchema:{type:"object",properties:{...y,key:{type:"string",description:"Entry key."},type:{type:"string",enum:["string","number","boolean","buffer"],description:"Entry value type. Must be one of the supported types for the target storage (see list-storages)."},value:{description:"Entry value matching the provided type."}},required:["key","type","value"]}},F={name:"edit-entry",description:"Edit an existing storage entry. Fails if the key does not exist.",inputSchema:{type:"object",properties:{...y,key:{type:"string",description:"Entry key."},type:{type:"string",enum:["string","number","boolean","buffer"],description:"Entry value type. Must be one of the supported types for the target storage (see list-storages)."},value:{description:"Entry value matching the provided type."}},required:["key","type","value"]}},D={name:"remove-entry",description:"Remove a storage entry by key.",inputSchema:{type:"object",properties:{...y,key:{type:"string",description:"Entry key."}},required:["key"]}},E=(e,t)=>{if(e==="string"){if(typeof t!="string")throw new Error("Expected string value for type=string");return t}if(e==="number"){if(typeof t!="number"||Number.isNaN(t))throw new Error("Expected number value for type=number");return t}if(e==="boolean"){if(typeof t!="boolean")throw new Error("Expected boolean value for type=boolean");return t}if(!Array.isArray(t)||!t.every(r=>Number.isInteger(r)))throw new Error("Expected number[] value for type=buffer");return t},u=e=>`${e.target.adapterId}/${e.target.storageId}`,L=e=>{const t=m.useCallback((r,s)=>{if(e.length===0)throw new Error("No storages are registered.");if(r!==void 0||s!==void 0){const n=e.find(o=>(r===void 0||o.target.adapterId===r)&&(s===void 0||o.target.storageId===s));if(!n){const o=[r&&`adapterId="${r}"`,s&&`storageId="${s}"`].filter(Boolean).join(", ");throw new Error(`No storage matched ${o}. Available: ${e.map(u).join(", ")}`)}return n}if(e.length>1)throw new Error(`Multiple storages detected. Provide adapterId and/or storageId. Available: ${e.map(u).join(", ")}`);return e[0]},[e]);p.useRozenitePluginAgentTool({pluginId:g,tool:q,handler:async()=>({storages:await Promise.all(e.map(async r=>({adapterId:r.target.adapterId,storageId:r.target.storageId,adapterName:r.adapterName,storageName:r.storageName,supportedTypes:r.capabilities.supportedTypes,entryCount:(await r.getAllKeys()).length})))})}),p.useRozenitePluginAgentTool({pluginId:g,tool:K,handler:async({adapterId:r,storageId:s,offset:n=0,limit:o=100})=>{const d=t(r,s),a=await d.getAllKeys(),c=Math.max(0,Math.floor(n)),i=Math.max(1,Math.floor(o)),l=a.slice(c,c+i);return{adapterId:d.target.adapterId,storageId:d.target.storageId,total:a.length,offset:c,limit:i,keys:l}}}),p.useRozenitePluginAgentTool({pluginId:g,tool:N,handler:async({adapterId:r,storageId:s,key:n})=>{const o=t(r,s),d=await o.get(n);if(!d)throw new Error(`Key "${n}" not found in storage "${u(o)}".`);return{adapterId:o.target.adapterId,storageId:o.target.storageId,entry:d}}}),p.useRozenitePluginAgentTool({pluginId:g,tool:V,handler:async({adapterId:r,storageId:s,key:n,type:o,value:d})=>{const a=t(r,s);if(await a.get(n))throw new Error(`Key "${n}" already exists in storage "${u(a)}". Use edit-entry instead.`);const i=E(o,d);return await a.set({key:n,type:o,value:i}),{adapterId:a.target.adapterId,storageId:a.target.storageId,created:!0,key:n}}}),p.useRozenitePluginAgentTool({pluginId:g,tool:F,handler:async({adapterId:r,storageId:s,key:n,type:o,value:d})=>{const a=t(r,s);if(!await a.get(n))throw new Error(`Key "${n}" not found in storage "${u(a)}". Use create-entry instead.`);const i=E(o,d);return await a.set({key:n,type:o,value:i}),{adapterId:a.target.adapterId,storageId:a.target.storageId,edited:!0,key:n}}}),p.useRozenitePluginAgentTool({pluginId:g,tool:D,handler:async({adapterId:r,storageId:s,key:n})=>{const o=t(r,s),d=!!await o.get(n);return await o.delete(n),{adapterId:o.target.adapterId,storageId:o.target.storageId,removed:d,key:n}}})},C=({storages:e})=>{const t=m.useMemo(()=>j(e),[e]);L(t);const r=v.useRozeniteDevToolsClient({pluginId:"@rozenite/storage-plugin"});return m.useEffect(()=>{if(!r)return;const s=async a=>{const c=a?t.filter(i=>i.id===a):t;for(const i of c)try{const l=await i.getAllEntries();r.send("snapshot",{type:"snapshot",target:i.target,adapterName:i.adapterName,storageName:i.storageName,capabilities:i.capabilities,entries:l})}catch(l){console.warn(`[Rozenite] Storage Plugin: Failed to snapshot ${i.target.adapterId}/${i.target.storageId}.`,l)}};s();const n=[];let o=!1;Promise.all(t.map(async a=>{try{const c=await a.watch({onSet:i=>{r.send("set-entry",{type:"set-entry",target:a.target,entry:i})},onDelete:i=>{r.send("delete-entry",{type:"delete-entry",target:a.target,key:i})}});if(o){c.remove();return}n.push(c)}catch(c){console.warn(`[Rozenite] Storage Plugin: Failed to attach watcher for ${a.target.adapterId}/${a.target.storageId}.`,c)}}));const d=[r.onMessage("set-entry",async({target:a,entry:c})=>{const i=t.find(l=>l.target.adapterId===a.adapterId&&l.target.storageId===a.storageId);if(!i){console.warn(`[Rozenite] Storage Plugin: Storage target not found for ${a.adapterId}/${a.storageId}`);return}try{await i.set(c)}catch(l){console.warn(`[Rozenite] Storage Plugin: Failed to set entry in ${a.adapterId}/${a.storageId}.`,l)}}),r.onMessage("delete-entry",async({target:a,key:c})=>{const i=t.find(l=>l.target.adapterId===a.adapterId&&l.target.storageId===a.storageId);if(!i){console.warn(`[Rozenite] Storage Plugin: Storage target not found for ${a.adapterId}/${a.storageId}`);return}try{await i.delete(c)}catch(l){console.warn(`[Rozenite] Storage Plugin: Failed to delete entry in ${a.adapterId}/${a.storageId}.`,l)}}),r.onMessage("get-snapshot",async({target:a})=>{if(a==="all"){await s();return}await s(`${a.adapterId}:${a.storageId}`)})];return()=>{o=!0,n.forEach(a=>a.remove()),d.forEach(a=>a.remove())}},[r,t]),r};exports.useRozeniteStoragePlugin=C;
@@ -1,212 +1,443 @@
1
- import { useRozeniteDevToolsClient as I } from "@rozenite/plugin-bridge";
2
- import { useMemo as h, useEffect as S } from "react";
3
- import { g as m, s as b } from "./types.js";
4
- const $ = 1500, d = (t) => t.kind === "async", g = (t) => t.type === "buffer" ? `${t.type}:${t.value.join(",")}` : `${t.type}:${String(t.value)}`, v = (t) => new Map(t.map((e) => [e.key, e])), u = (t, e) => t.blacklist ? (t.blacklist.lastIndex = 0, t.blacklist.test(e)) : !1, E = (t, e, r) => {
5
- if (!b(t, e))
1
+ import { useRozeniteDevToolsClient as S } from "@rozenite/plugin-bridge";
2
+ import { useCallback as $, useMemo as v, useEffect as P } from "react";
3
+ import { g as T, s as k } from "./types.js";
4
+ import { useRozenitePluginAgentTool as l } from "@rozenite/agent-bridge";
5
+ const x = 1500, f = (e) => e.kind === "async", m = (e) => e.type === "buffer" ? `${e.type}:${e.value.join(",")}` : `${e.type}:${String(e.value)}`, M = (e) => new Map(e.map((t) => [t.key, t])), h = (e, t) => e.blacklist ? (e.blacklist.lastIndex = 0, e.blacklist.test(t)) : !1, A = (e, t, r) => {
6
+ if (!k(e, t))
6
7
  throw new Error(
7
- `Type "${e}" is not supported by storage "${r.storageId}" in adapter "${r.adapterId}".`
8
+ `Type "${t}" is not supported by storage "${r.storageId}" in adapter "${r.adapterId}".`
8
9
  );
9
- }, f = async (t) => (d(t), t.getAllKeys()), y = async (t, e) => (d(t), t.get(e)), P = async (t, e) => {
10
- if (d(t)) {
11
- await t.set(e);
10
+ }, I = async (e) => (f(e), e.getAllKeys()), w = async (e, t) => (f(e), e.get(t)), R = async (e, t) => {
11
+ if (f(e)) {
12
+ await e.set(t);
12
13
  return;
13
14
  }
14
- t.set(e);
15
- }, z = async (t, e) => {
16
- if (d(t)) {
17
- await t.delete(e);
15
+ e.set(t);
16
+ }, z = async (e, t) => {
17
+ if (f(e)) {
18
+ await e.delete(t);
18
19
  return;
19
20
  }
20
- t.delete(e);
21
- }, w = async (t) => {
22
- const e = await t();
23
- return v(e);
24
- }, M = (t, e, r) => {
25
- e.forEach((i, c) => {
26
- const p = t.get(c);
27
- if (!p) {
28
- r.onSet(i);
21
+ e.delete(t);
22
+ }, b = async (e) => {
23
+ const t = await e();
24
+ return M(t);
25
+ }, K = (e, t, r) => {
26
+ t.forEach((s, n) => {
27
+ const o = e.get(n);
28
+ if (!o) {
29
+ r.onSet(s);
29
30
  return;
30
31
  }
31
- g(p) !== g(i) && r.onSet(i);
32
- }), t.forEach((i, c) => {
33
- e.has(c) || r.onDelete(c);
32
+ m(o) !== m(s) && r.onSet(s);
33
+ }), e.forEach((s, n) => {
34
+ t.has(n) || r.onDelete(n);
34
35
  });
35
- }, R = async (t, e) => {
36
- let r = await w(t);
37
- const i = setInterval(async () => {
36
+ }, N = async (e, t) => {
37
+ let r = await b(e);
38
+ const s = setInterval(async () => {
38
39
  try {
39
- const c = await w(t);
40
- M(r, c, e), r = c;
40
+ const n = await b(e);
41
+ K(r, n, t), r = n;
41
42
  } catch {
42
43
  }
43
- }, $);
44
+ }, x);
44
45
  return {
45
46
  remove: () => {
46
- clearInterval(i);
47
+ clearInterval(s);
47
48
  }
48
49
  };
49
- }, A = (t, e) => {
50
- const r = e.storage, i = {
51
- adapterId: t.id,
52
- storageId: e.id
53
- }, c = async (o) => {
54
- if (!u(e, o))
55
- return y(r, o);
56
- }, p = async () => {
57
- const o = await f(r);
50
+ }, j = (e, t) => {
51
+ const r = t.storage, s = {
52
+ adapterId: e.id,
53
+ storageId: t.id
54
+ }, n = async (d) => {
55
+ if (!h(t, d))
56
+ return w(r, d);
57
+ }, o = async () => {
58
+ const d = await I(r);
58
59
  return (await Promise.all(
59
- o.filter((s) => !u(e, s)).map((s) => y(r, s))
60
- )).filter((s) => !!s);
60
+ d.filter((c) => !h(t, c)).map((c) => w(r, c))
61
+ )).filter((c) => !!c);
61
62
  };
62
63
  return {
63
- id: m(i),
64
- target: i,
65
- adapterName: t.name,
66
- storageName: e.name,
67
- capabilities: e.capabilities,
68
- get: c,
69
- set: async (o) => {
70
- E(e.capabilities, o.type, i), await P(r, o);
64
+ id: T(s),
65
+ target: s,
66
+ adapterName: e.name,
67
+ storageName: t.name,
68
+ capabilities: t.capabilities,
69
+ get: n,
70
+ set: async (d) => {
71
+ A(t.capabilities, d.type, s), await R(r, d);
71
72
  },
72
- delete: async (o) => {
73
- await z(r, o);
73
+ delete: async (d) => {
74
+ await z(r, d);
74
75
  },
75
- getAllKeys: async () => (await f(r)).filter((a) => !u(e, a)),
76
- getAllEntries: p,
77
- watch: async ({ onSet: o, onDelete: a }) => r.subscribe ? r.subscribe(async (s) => {
76
+ getAllKeys: async () => (await I(r)).filter((a) => !h(t, a)),
77
+ getAllEntries: o,
78
+ watch: async ({ onSet: d, onDelete: a }) => r.subscribe ? r.subscribe(async (c) => {
78
79
  try {
79
- const n = await c(s);
80
- if (!n) {
81
- a(s);
80
+ const i = await n(c);
81
+ if (!i) {
82
+ a(c);
82
83
  return;
83
84
  }
84
- o(n);
85
+ d(i);
85
86
  } catch {
86
87
  }
87
- }) : R(p, { onSet: o, onDelete: a })
88
+ }) : N(o, { onSet: d, onDelete: a })
88
89
  };
89
- }, F = (t) => t.flatMap(
90
- (e) => e.storages.map((r) => A(e, r))
91
- ), k = ({
92
- storages: t
90
+ }, V = (e) => e.flatMap(
91
+ (t) => t.storages.map((r) => j(t, r))
92
+ ), g = "@rozenite/storage-plugin", y = {
93
+ adapterId: {
94
+ type: "string",
95
+ description: "Storage adapter ID. Required when multiple adapters are configured."
96
+ },
97
+ storageId: {
98
+ type: "string",
99
+ description: "Storage node ID within the adapter. Required when multiple storages are configured."
100
+ }
101
+ }, F = {
102
+ name: "list-storages",
103
+ description: "List all storage adapters and their storage nodes currently available on the device, including supported entry types and entry counts.",
104
+ inputSchema: { type: "object", properties: {} }
105
+ }, q = {
106
+ name: "list-entries",
107
+ description: "List keys in a storage. This call intentionally does not return entry values to keep responses token-efficient.",
108
+ inputSchema: {
109
+ type: "object",
110
+ properties: {
111
+ ...y,
112
+ offset: {
113
+ type: "number",
114
+ description: "Pagination offset. Defaults to 0."
115
+ },
116
+ limit: {
117
+ type: "number",
118
+ description: "Pagination size. Defaults to 100."
119
+ }
120
+ }
121
+ }
122
+ }, D = {
123
+ name: "read-entry",
124
+ description: "Read a single storage entry value by key.",
125
+ inputSchema: {
126
+ type: "object",
127
+ properties: {
128
+ ...y,
129
+ key: { type: "string", description: "Entry key." }
130
+ },
131
+ required: ["key"]
132
+ }
133
+ }, L = {
134
+ name: "create-entry",
135
+ description: "Create a new storage entry. Fails if the key already exists.",
136
+ inputSchema: {
137
+ type: "object",
138
+ properties: {
139
+ ...y,
140
+ key: { type: "string", description: "Entry key." },
141
+ type: {
142
+ type: "string",
143
+ enum: ["string", "number", "boolean", "buffer"],
144
+ description: "Entry value type. Must be one of the supported types for the target storage (see list-storages)."
145
+ },
146
+ value: {
147
+ description: "Entry value matching the provided type."
148
+ }
149
+ },
150
+ required: ["key", "type", "value"]
151
+ }
152
+ }, C = {
153
+ name: "edit-entry",
154
+ description: "Edit an existing storage entry. Fails if the key does not exist.",
155
+ inputSchema: {
156
+ type: "object",
157
+ properties: {
158
+ ...y,
159
+ key: { type: "string", description: "Entry key." },
160
+ type: {
161
+ type: "string",
162
+ enum: ["string", "number", "boolean", "buffer"],
163
+ description: "Entry value type. Must be one of the supported types for the target storage (see list-storages)."
164
+ },
165
+ value: {
166
+ description: "Entry value matching the provided type."
167
+ }
168
+ },
169
+ required: ["key", "type", "value"]
170
+ }
171
+ }, _ = {
172
+ name: "remove-entry",
173
+ description: "Remove a storage entry by key.",
174
+ inputSchema: {
175
+ type: "object",
176
+ properties: {
177
+ ...y,
178
+ key: { type: "string", description: "Entry key." }
179
+ },
180
+ required: ["key"]
181
+ }
182
+ }, E = (e, t) => {
183
+ if (e === "string") {
184
+ if (typeof t != "string")
185
+ throw new Error("Expected string value for type=string");
186
+ return t;
187
+ }
188
+ if (e === "number") {
189
+ if (typeof t != "number" || Number.isNaN(t))
190
+ throw new Error("Expected number value for type=number");
191
+ return t;
192
+ }
193
+ if (e === "boolean") {
194
+ if (typeof t != "boolean")
195
+ throw new Error("Expected boolean value for type=boolean");
196
+ return t;
197
+ }
198
+ if (!Array.isArray(t) || !t.every((r) => Number.isInteger(r)))
199
+ throw new Error("Expected number[] value for type=buffer");
200
+ return t;
201
+ }, u = (e) => `${e.target.adapterId}/${e.target.storageId}`, O = (e) => {
202
+ const t = $(
203
+ (r, s) => {
204
+ if (e.length === 0)
205
+ throw new Error("No storages are registered.");
206
+ if (r !== void 0 || s !== void 0) {
207
+ const n = e.find(
208
+ (o) => (r === void 0 || o.target.adapterId === r) && (s === void 0 || o.target.storageId === s)
209
+ );
210
+ if (!n) {
211
+ const o = [
212
+ r && `adapterId="${r}"`,
213
+ s && `storageId="${s}"`
214
+ ].filter(Boolean).join(", ");
215
+ throw new Error(
216
+ `No storage matched ${o}. Available: ${e.map(u).join(", ")}`
217
+ );
218
+ }
219
+ return n;
220
+ }
221
+ if (e.length > 1)
222
+ throw new Error(
223
+ `Multiple storages detected. Provide adapterId and/or storageId. Available: ${e.map(u).join(", ")}`
224
+ );
225
+ return e[0];
226
+ },
227
+ [e]
228
+ );
229
+ l({
230
+ pluginId: g,
231
+ tool: F,
232
+ handler: async () => ({
233
+ storages: await Promise.all(
234
+ e.map(async (r) => ({
235
+ adapterId: r.target.adapterId,
236
+ storageId: r.target.storageId,
237
+ adapterName: r.adapterName,
238
+ storageName: r.storageName,
239
+ supportedTypes: r.capabilities.supportedTypes,
240
+ entryCount: (await r.getAllKeys()).length
241
+ }))
242
+ )
243
+ })
244
+ }), l({
245
+ pluginId: g,
246
+ tool: q,
247
+ handler: async ({ adapterId: r, storageId: s, offset: n = 0, limit: o = 100 }) => {
248
+ const d = t(r, s), a = await d.getAllKeys(), c = Math.max(0, Math.floor(n)), i = Math.max(1, Math.floor(o)), p = a.slice(c, c + i);
249
+ return {
250
+ adapterId: d.target.adapterId,
251
+ storageId: d.target.storageId,
252
+ total: a.length,
253
+ offset: c,
254
+ limit: i,
255
+ keys: p
256
+ };
257
+ }
258
+ }), l({
259
+ pluginId: g,
260
+ tool: D,
261
+ handler: async ({ adapterId: r, storageId: s, key: n }) => {
262
+ const o = t(r, s), d = await o.get(n);
263
+ if (!d)
264
+ throw new Error(
265
+ `Key "${n}" not found in storage "${u(o)}".`
266
+ );
267
+ return {
268
+ adapterId: o.target.adapterId,
269
+ storageId: o.target.storageId,
270
+ entry: d
271
+ };
272
+ }
273
+ }), l({
274
+ pluginId: g,
275
+ tool: L,
276
+ handler: async ({ adapterId: r, storageId: s, key: n, type: o, value: d }) => {
277
+ const a = t(r, s);
278
+ if (await a.get(n))
279
+ throw new Error(
280
+ `Key "${n}" already exists in storage "${u(a)}". Use edit-entry instead.`
281
+ );
282
+ const i = E(o, d);
283
+ return await a.set({ key: n, type: o, value: i }), {
284
+ adapterId: a.target.adapterId,
285
+ storageId: a.target.storageId,
286
+ created: !0,
287
+ key: n
288
+ };
289
+ }
290
+ }), l({
291
+ pluginId: g,
292
+ tool: C,
293
+ handler: async ({ adapterId: r, storageId: s, key: n, type: o, value: d }) => {
294
+ const a = t(r, s);
295
+ if (!await a.get(n))
296
+ throw new Error(
297
+ `Key "${n}" not found in storage "${u(a)}". Use create-entry instead.`
298
+ );
299
+ const i = E(o, d);
300
+ return await a.set({ key: n, type: o, value: i }), {
301
+ adapterId: a.target.adapterId,
302
+ storageId: a.target.storageId,
303
+ edited: !0,
304
+ key: n
305
+ };
306
+ }
307
+ }), l({
308
+ pluginId: g,
309
+ tool: _,
310
+ handler: async ({ adapterId: r, storageId: s, key: n }) => {
311
+ const o = t(r, s), d = !!await o.get(n);
312
+ return await o.delete(n), {
313
+ adapterId: o.target.adapterId,
314
+ storageId: o.target.storageId,
315
+ removed: d,
316
+ key: n
317
+ };
318
+ }
319
+ });
320
+ }, J = ({
321
+ storages: e
93
322
  }) => {
94
- const e = h(() => F(t), [t]), r = I({
323
+ const t = v(() => V(e), [e]);
324
+ O(t);
325
+ const r = S({
95
326
  pluginId: "@rozenite/storage-plugin"
96
327
  });
97
- return S(() => {
328
+ return P(() => {
98
329
  if (!r)
99
330
  return;
100
- const i = async (a) => {
101
- const s = a ? e.filter((n) => n.id === a) : e;
102
- for (const n of s)
331
+ const s = async (a) => {
332
+ const c = a ? t.filter((i) => i.id === a) : t;
333
+ for (const i of c)
103
334
  try {
104
- const l = await n.getAllEntries();
335
+ const p = await i.getAllEntries();
105
336
  r.send("snapshot", {
106
337
  type: "snapshot",
107
- target: n.target,
108
- adapterName: n.adapterName,
109
- storageName: n.storageName,
110
- capabilities: n.capabilities,
111
- entries: l
338
+ target: i.target,
339
+ adapterName: i.adapterName,
340
+ storageName: i.storageName,
341
+ capabilities: i.capabilities,
342
+ entries: p
112
343
  });
113
- } catch (l) {
344
+ } catch (p) {
114
345
  console.warn(
115
- `[Rozenite] Storage Plugin: Failed to snapshot ${n.target.adapterId}/${n.target.storageId}.`,
116
- l
346
+ `[Rozenite] Storage Plugin: Failed to snapshot ${i.target.adapterId}/${i.target.storageId}.`,
347
+ p
117
348
  );
118
349
  }
119
350
  };
120
- i();
121
- const c = [];
122
- let p = !1;
351
+ s();
352
+ const n = [];
353
+ let o = !1;
123
354
  Promise.all(
124
- e.map(async (a) => {
355
+ t.map(async (a) => {
125
356
  try {
126
- const s = await a.watch({
127
- onSet: (n) => {
357
+ const c = await a.watch({
358
+ onSet: (i) => {
128
359
  r.send("set-entry", {
129
360
  type: "set-entry",
130
361
  target: a.target,
131
- entry: n
362
+ entry: i
132
363
  });
133
364
  },
134
- onDelete: (n) => {
365
+ onDelete: (i) => {
135
366
  r.send("delete-entry", {
136
367
  type: "delete-entry",
137
368
  target: a.target,
138
- key: n
369
+ key: i
139
370
  });
140
371
  }
141
372
  });
142
- if (p) {
143
- s.remove();
373
+ if (o) {
374
+ c.remove();
144
375
  return;
145
376
  }
146
- c.push(s);
147
- } catch (s) {
377
+ n.push(c);
378
+ } catch (c) {
148
379
  console.warn(
149
380
  `[Rozenite] Storage Plugin: Failed to attach watcher for ${a.target.adapterId}/${a.target.storageId}.`,
150
- s
381
+ c
151
382
  );
152
383
  }
153
384
  })
154
385
  );
155
- const o = [
156
- r.onMessage("set-entry", async ({ target: a, entry: s }) => {
157
- const n = e.find(
158
- (l) => l.target.adapterId === a.adapterId && l.target.storageId === a.storageId
386
+ const d = [
387
+ r.onMessage("set-entry", async ({ target: a, entry: c }) => {
388
+ const i = t.find(
389
+ (p) => p.target.adapterId === a.adapterId && p.target.storageId === a.storageId
159
390
  );
160
- if (!n) {
391
+ if (!i) {
161
392
  console.warn(
162
393
  `[Rozenite] Storage Plugin: Storage target not found for ${a.adapterId}/${a.storageId}`
163
394
  );
164
395
  return;
165
396
  }
166
397
  try {
167
- await n.set(s);
168
- } catch (l) {
398
+ await i.set(c);
399
+ } catch (p) {
169
400
  console.warn(
170
401
  `[Rozenite] Storage Plugin: Failed to set entry in ${a.adapterId}/${a.storageId}.`,
171
- l
402
+ p
172
403
  );
173
404
  }
174
405
  }),
175
406
  r.onMessage(
176
407
  "delete-entry",
177
- async ({ target: a, key: s }) => {
178
- const n = e.find(
179
- (l) => l.target.adapterId === a.adapterId && l.target.storageId === a.storageId
408
+ async ({ target: a, key: c }) => {
409
+ const i = t.find(
410
+ (p) => p.target.adapterId === a.adapterId && p.target.storageId === a.storageId
180
411
  );
181
- if (!n) {
412
+ if (!i) {
182
413
  console.warn(
183
414
  `[Rozenite] Storage Plugin: Storage target not found for ${a.adapterId}/${a.storageId}`
184
415
  );
185
416
  return;
186
417
  }
187
418
  try {
188
- await n.delete(s);
189
- } catch (l) {
419
+ await i.delete(c);
420
+ } catch (p) {
190
421
  console.warn(
191
422
  `[Rozenite] Storage Plugin: Failed to delete entry in ${a.adapterId}/${a.storageId}.`,
192
- l
423
+ p
193
424
  );
194
425
  }
195
426
  }
196
427
  ),
197
428
  r.onMessage("get-snapshot", async ({ target: a }) => {
198
429
  if (a === "all") {
199
- await i();
430
+ await s();
200
431
  return;
201
432
  }
202
- await i(`${a.adapterId}:${a.storageId}`);
433
+ await s(`${a.adapterId}:${a.storageId}`);
203
434
  })
204
435
  ];
205
436
  return () => {
206
- p = !0, c.forEach((a) => a.remove()), o.forEach((a) => a.remove());
437
+ o = !0, n.forEach((a) => a.remove()), d.forEach((a) => a.remove());
207
438
  };
208
- }, [r, e]), r;
439
+ }, [r, t]), r;
209
440
  };
210
441
  export {
211
- k as useRozeniteStoragePlugin
442
+ J as useRozeniteStoragePlugin
212
443
  };
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@rozenite/storage-plugin",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "Generic Storage Inspector for Rozenite.",
5
5
  "type": "module",
6
6
  "main": "./dist/react-native.cjs",
7
7
  "module": "./dist/react-native.js",
8
8
  "types": "./dist/react-native.d.ts",
9
9
  "dependencies": {
10
- "@rozenite/plugin-bridge": "1.4.0"
10
+ "@rozenite/plugin-bridge": "1.5.1",
11
+ "@rozenite/agent-bridge": "1.5.1"
11
12
  },
12
13
  "devDependencies": {
13
14
  "@tanstack/react-table": "^8.21.3",
@@ -28,8 +29,8 @@
28
29
  "tailwindcss-animate": "^1.0.7",
29
30
  "typescript": "~5.9.3",
30
31
  "vite": "^7.3.1",
31
- "@rozenite/vite-plugin": "1.4.0",
32
- "rozenite": "1.4.0"
32
+ "@rozenite/vite-plugin": "1.5.1",
33
+ "rozenite": "1.5.1"
33
34
  },
34
35
  "peerDependencies": {
35
36
  "@react-native-async-storage/async-storage": "*",
@@ -8,6 +8,7 @@ import type {
8
8
  } from '../shared/messaging';
9
9
  import type { StorageAdapter } from '../shared/types';
10
10
  import { createStorageViews } from './storage-view';
11
+ import { useStorageAgentTools } from './useStorageAgentTools';
11
12
 
12
13
  export type RozeniteStoragePluginOptions = {
13
14
  storages: StorageAdapter[];
@@ -18,6 +19,8 @@ export const useRozeniteStoragePlugin = ({
18
19
  }: RozeniteStoragePluginOptions) => {
19
20
  const views = useMemo(() => createStorageViews(storages), [storages]);
20
21
 
22
+ useStorageAgentTools(views);
23
+
21
24
  const client = useRozeniteDevToolsClient<StorageEventMap>({
22
25
  pluginId: '@rozenite/storage-plugin',
23
26
  });
@@ -0,0 +1,332 @@
1
+ import { useCallback } from 'react';
2
+ import { useRozenitePluginAgentTool, type AgentTool } from '@rozenite/agent-bridge';
3
+ import type { StorageEntry, StorageEntryType, StorageEntryValue } from '../shared/types';
4
+ import type { StorageView } from './storage-view';
5
+
6
+ type StorageInput = { adapterId?: string; storageId?: string };
7
+ type KeyInput = StorageInput & { key: string };
8
+ type SetInput = StorageInput & {
9
+ key: string;
10
+ type: StorageEntryType;
11
+ value: unknown;
12
+ };
13
+ type ListEntriesInput = StorageInput & {
14
+ offset?: number;
15
+ limit?: number;
16
+ };
17
+
18
+ const toolPrefix = '@rozenite/storage-plugin';
19
+ const sharedStorageProperties = {
20
+ adapterId: {
21
+ type: 'string',
22
+ description:
23
+ 'Storage adapter ID. Required when multiple adapters are configured.',
24
+ },
25
+ storageId: {
26
+ type: 'string',
27
+ description:
28
+ 'Storage node ID within the adapter. Required when multiple storages are configured.',
29
+ },
30
+ } as const;
31
+
32
+ const listStoragesTool: AgentTool = {
33
+ name: 'list-storages',
34
+ description:
35
+ 'List all storage adapters and their storage nodes currently available on the device, including supported entry types and entry counts.',
36
+ inputSchema: { type: 'object', properties: {} },
37
+ };
38
+
39
+ const listEntriesTool: AgentTool = {
40
+ name: 'list-entries',
41
+ description:
42
+ 'List keys in a storage. This call intentionally does not return entry values to keep responses token-efficient.',
43
+ inputSchema: {
44
+ type: 'object',
45
+ properties: {
46
+ ...sharedStorageProperties,
47
+ offset: {
48
+ type: 'number',
49
+ description: 'Pagination offset. Defaults to 0.',
50
+ },
51
+ limit: {
52
+ type: 'number',
53
+ description: 'Pagination size. Defaults to 100.',
54
+ },
55
+ },
56
+ },
57
+ };
58
+
59
+ const readEntryTool: AgentTool = {
60
+ name: 'read-entry',
61
+ description: 'Read a single storage entry value by key.',
62
+ inputSchema: {
63
+ type: 'object',
64
+ properties: {
65
+ ...sharedStorageProperties,
66
+ key: { type: 'string', description: 'Entry key.' },
67
+ },
68
+ required: ['key'],
69
+ },
70
+ };
71
+
72
+ const createEntryTool: AgentTool = {
73
+ name: 'create-entry',
74
+ description: 'Create a new storage entry. Fails if the key already exists.',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ ...sharedStorageProperties,
79
+ key: { type: 'string', description: 'Entry key.' },
80
+ type: {
81
+ type: 'string',
82
+ enum: ['string', 'number', 'boolean', 'buffer'],
83
+ description:
84
+ 'Entry value type. Must be one of the supported types for the target storage (see list-storages).',
85
+ },
86
+ value: {
87
+ description: 'Entry value matching the provided type.',
88
+ },
89
+ },
90
+ required: ['key', 'type', 'value'],
91
+ },
92
+ };
93
+
94
+ const editEntryTool: AgentTool = {
95
+ name: 'edit-entry',
96
+ description: 'Edit an existing storage entry. Fails if the key does not exist.',
97
+ inputSchema: {
98
+ type: 'object',
99
+ properties: {
100
+ ...sharedStorageProperties,
101
+ key: { type: 'string', description: 'Entry key.' },
102
+ type: {
103
+ type: 'string',
104
+ enum: ['string', 'number', 'boolean', 'buffer'],
105
+ description:
106
+ 'Entry value type. Must be one of the supported types for the target storage (see list-storages).',
107
+ },
108
+ value: {
109
+ description: 'Entry value matching the provided type.',
110
+ },
111
+ },
112
+ required: ['key', 'type', 'value'],
113
+ },
114
+ };
115
+
116
+ const removeEntryTool: AgentTool = {
117
+ name: 'remove-entry',
118
+ description: 'Remove a storage entry by key.',
119
+ inputSchema: {
120
+ type: 'object',
121
+ properties: {
122
+ ...sharedStorageProperties,
123
+ key: { type: 'string', description: 'Entry key.' },
124
+ },
125
+ required: ['key'],
126
+ },
127
+ };
128
+
129
+ const parseValueForType = (
130
+ type: StorageEntryType,
131
+ value: unknown
132
+ ): StorageEntryValue => {
133
+ if (type === 'string') {
134
+ if (typeof value !== 'string') {
135
+ throw new Error('Expected string value for type=string');
136
+ }
137
+ return value;
138
+ }
139
+
140
+ if (type === 'number') {
141
+ if (typeof value !== 'number' || Number.isNaN(value)) {
142
+ throw new Error('Expected number value for type=number');
143
+ }
144
+ return value;
145
+ }
146
+
147
+ if (type === 'boolean') {
148
+ if (typeof value !== 'boolean') {
149
+ throw new Error('Expected boolean value for type=boolean');
150
+ }
151
+ return value;
152
+ }
153
+
154
+ if (!Array.isArray(value) || !value.every((item) => Number.isInteger(item))) {
155
+ throw new Error('Expected number[] value for type=buffer');
156
+ }
157
+
158
+ return value as number[];
159
+ };
160
+
161
+ const formatViewRef = (view: StorageView) =>
162
+ `${view.target.adapterId}/${view.target.storageId}`;
163
+
164
+ export const useStorageAgentTools = (views: StorageView[]) => {
165
+ const resolveStorage = useCallback(
166
+ (adapterId?: string, storageId?: string) => {
167
+ if (views.length === 0) {
168
+ throw new Error('No storages are registered.');
169
+ }
170
+
171
+ if (adapterId !== undefined || storageId !== undefined) {
172
+ const selected = views.find(
173
+ (view) =>
174
+ (adapterId === undefined || view.target.adapterId === adapterId) &&
175
+ (storageId === undefined || view.target.storageId === storageId)
176
+ );
177
+
178
+ if (!selected) {
179
+ const qualifier = [
180
+ adapterId && `adapterId="${adapterId}"`,
181
+ storageId && `storageId="${storageId}"`,
182
+ ]
183
+ .filter(Boolean)
184
+ .join(', ');
185
+ throw new Error(
186
+ `No storage matched ${qualifier}. Available: ${views.map(formatViewRef).join(', ')}`
187
+ );
188
+ }
189
+
190
+ return selected;
191
+ }
192
+
193
+ if (views.length > 1) {
194
+ throw new Error(
195
+ `Multiple storages detected. Provide adapterId and/or storageId. Available: ${views.map(formatViewRef).join(', ')}`
196
+ );
197
+ }
198
+
199
+ return views[0];
200
+ },
201
+ [views]
202
+ );
203
+
204
+ useRozenitePluginAgentTool({
205
+ pluginId: toolPrefix,
206
+ tool: listStoragesTool,
207
+ handler: async () => ({
208
+ storages: await Promise.all(
209
+ views.map(async (view) => ({
210
+ adapterId: view.target.adapterId,
211
+ storageId: view.target.storageId,
212
+ adapterName: view.adapterName,
213
+ storageName: view.storageName,
214
+ supportedTypes: view.capabilities.supportedTypes,
215
+ entryCount: (await view.getAllKeys()).length,
216
+ }))
217
+ ),
218
+ }),
219
+ });
220
+
221
+ useRozenitePluginAgentTool<ListEntriesInput>({
222
+ pluginId: toolPrefix,
223
+ tool: listEntriesTool,
224
+ handler: async ({ adapterId, storageId, offset = 0, limit = 100 }) => {
225
+ const view = resolveStorage(adapterId, storageId);
226
+ const allKeys = await view.getAllKeys();
227
+ const safeOffset = Math.max(0, Math.floor(offset));
228
+ const safeLimit = Math.max(1, Math.floor(limit));
229
+ const keys = allKeys.slice(safeOffset, safeOffset + safeLimit);
230
+
231
+ return {
232
+ adapterId: view.target.adapterId,
233
+ storageId: view.target.storageId,
234
+ total: allKeys.length,
235
+ offset: safeOffset,
236
+ limit: safeLimit,
237
+ keys,
238
+ };
239
+ },
240
+ });
241
+
242
+ useRozenitePluginAgentTool<
243
+ KeyInput,
244
+ { adapterId: string; storageId: string; entry: StorageEntry }
245
+ >({
246
+ pluginId: toolPrefix,
247
+ tool: readEntryTool,
248
+ handler: async ({ adapterId, storageId, key }) => {
249
+ const view = resolveStorage(adapterId, storageId);
250
+ const entry = await view.get(key);
251
+
252
+ if (!entry) {
253
+ throw new Error(
254
+ `Key "${key}" not found in storage "${formatViewRef(view)}".`
255
+ );
256
+ }
257
+
258
+ return {
259
+ adapterId: view.target.adapterId,
260
+ storageId: view.target.storageId,
261
+ entry,
262
+ };
263
+ },
264
+ });
265
+
266
+ useRozenitePluginAgentTool<SetInput>({
267
+ pluginId: toolPrefix,
268
+ tool: createEntryTool,
269
+ handler: async ({ adapterId, storageId, key, type, value }) => {
270
+ const view = resolveStorage(adapterId, storageId);
271
+ const existing = await view.get(key);
272
+
273
+ if (existing) {
274
+ throw new Error(
275
+ `Key "${key}" already exists in storage "${formatViewRef(view)}". Use edit-entry instead.`
276
+ );
277
+ }
278
+
279
+ const parsedValue = parseValueForType(type, value);
280
+ await view.set({ key, type, value: parsedValue } as StorageEntry);
281
+
282
+ return {
283
+ adapterId: view.target.adapterId,
284
+ storageId: view.target.storageId,
285
+ created: true,
286
+ key,
287
+ };
288
+ },
289
+ });
290
+
291
+ useRozenitePluginAgentTool<SetInput>({
292
+ pluginId: toolPrefix,
293
+ tool: editEntryTool,
294
+ handler: async ({ adapterId, storageId, key, type, value }) => {
295
+ const view = resolveStorage(adapterId, storageId);
296
+ const existing = await view.get(key);
297
+
298
+ if (!existing) {
299
+ throw new Error(
300
+ `Key "${key}" not found in storage "${formatViewRef(view)}". Use create-entry instead.`
301
+ );
302
+ }
303
+
304
+ const parsedValue = parseValueForType(type, value);
305
+ await view.set({ key, type, value: parsedValue } as StorageEntry);
306
+
307
+ return {
308
+ adapterId: view.target.adapterId,
309
+ storageId: view.target.storageId,
310
+ edited: true,
311
+ key,
312
+ };
313
+ },
314
+ });
315
+
316
+ useRozenitePluginAgentTool<KeyInput>({
317
+ pluginId: toolPrefix,
318
+ tool: removeEntryTool,
319
+ handler: async ({ adapterId, storageId, key }) => {
320
+ const view = resolveStorage(adapterId, storageId);
321
+ const existed = !!(await view.get(key));
322
+ await view.delete(key);
323
+
324
+ return {
325
+ adapterId: view.target.adapterId,
326
+ storageId: view.target.storageId,
327
+ removed: existed,
328
+ key,
329
+ };
330
+ },
331
+ });
332
+ };