@react-grab/codex 0.0.87 → 0.0.89
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/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/client.cjs +103 -59
- package/dist/client.global.js +4 -4
- package/dist/client.js +103 -59
- package/dist/server.cjs +60 -40
- package/dist/server.d.cts +4 -2
- package/dist/server.d.ts +4 -2
- package/dist/server.js +60 -41
- package/package.json +3 -3
package/dist/cli.cjs
CHANGED
|
@@ -7138,7 +7138,7 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7138
7138
|
var DEFAULT_PORT = 7567;
|
|
7139
7139
|
|
|
7140
7140
|
// src/cli.ts
|
|
7141
|
-
var VERSION = "0.0.
|
|
7141
|
+
var VERSION = "0.0.89";
|
|
7142
7142
|
var serverPath = path2.join(__dirname, "server.js");
|
|
7143
7143
|
execa(process.execPath, [serverPath], {
|
|
7144
7144
|
detached: true,
|
package/dist/cli.js
CHANGED
|
@@ -7130,7 +7130,7 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7130
7130
|
var DEFAULT_PORT = 7567;
|
|
7131
7131
|
|
|
7132
7132
|
// src/cli.ts
|
|
7133
|
-
var VERSION = "0.0.
|
|
7133
|
+
var VERSION = "0.0.89";
|
|
7134
7134
|
var serverPath = join(__dirname, "server.js");
|
|
7135
7135
|
execa(process.execPath, [serverPath], {
|
|
7136
7136
|
detached: true,
|
package/dist/client.cjs
CHANGED
|
@@ -12,7 +12,7 @@ var parseSSEEvent = (eventBlock) => {
|
|
|
12
12
|
}
|
|
13
13
|
return { eventType, data };
|
|
14
14
|
};
|
|
15
|
-
async function*
|
|
15
|
+
var streamSSE = async function* (stream, signal) {
|
|
16
16
|
const reader = stream.getReader();
|
|
17
17
|
const decoder = new TextDecoder();
|
|
18
18
|
let buffer = "";
|
|
@@ -51,28 +51,54 @@ async function* streamSSE(stream, signal) {
|
|
|
51
51
|
} catch {
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
};
|
|
55
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
56
|
+
var getStoredAgentContext = (storage, sessionId, storageKey = STORAGE_KEY) => {
|
|
57
|
+
const rawSessions = storage.getItem(storageKey);
|
|
58
|
+
if (!rawSessions) throw new Error("No sessions to resume");
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(rawSessions);
|
|
62
|
+
} catch {
|
|
63
|
+
throw new Error("Failed to parse stored sessions");
|
|
64
|
+
}
|
|
65
|
+
if (!isRecord(parsed)) throw new Error("Invalid stored sessions");
|
|
66
|
+
const storedSession = parsed[sessionId];
|
|
67
|
+
if (!isRecord(storedSession)) {
|
|
68
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
69
|
+
}
|
|
70
|
+
const context = storedSession.context;
|
|
71
|
+
if (!isRecord(context)) throw new Error(`Session ${sessionId} is invalid`);
|
|
72
|
+
const content = context.content;
|
|
73
|
+
const prompt = context.prompt;
|
|
74
|
+
if (typeof content !== "string" || typeof prompt !== "string") {
|
|
75
|
+
throw new Error(`Session ${sessionId} is invalid`);
|
|
76
|
+
}
|
|
77
|
+
const options = context.options;
|
|
78
|
+
const storedSessionId = context.sessionId;
|
|
79
|
+
return {
|
|
80
|
+
content,
|
|
81
|
+
prompt,
|
|
82
|
+
options,
|
|
83
|
+
sessionId: typeof storedSessionId === "string" ? storedSessionId : void 0
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
var streamAgentStatusFromServer = async function* (options, context, signal) {
|
|
63
87
|
const startTime = Date.now();
|
|
64
88
|
const sessionId = context.sessionId;
|
|
89
|
+
const pollIntervalMs = options.pollIntervalMs ?? 100;
|
|
90
|
+
const agentUrl = `${options.serverUrl}${options.agentPath ?? "/agent"}`;
|
|
65
91
|
const handleAbort = () => {
|
|
66
|
-
if (sessionId)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
if (!sessionId) return;
|
|
93
|
+
const abortPath = options.abortPath?.(sessionId) ?? `/abort/${sessionId}`;
|
|
94
|
+
fetch(`${options.serverUrl}${abortPath}`, { method: "POST" }).catch(
|
|
95
|
+
() => {
|
|
96
|
+
}
|
|
97
|
+
);
|
|
72
98
|
};
|
|
73
99
|
signal.addEventListener("abort", handleAbort);
|
|
74
100
|
try {
|
|
75
|
-
const response = await fetch(
|
|
101
|
+
const response = await fetch(agentUrl, {
|
|
76
102
|
method: "POST",
|
|
77
103
|
headers: { "Content-Type": "application/json" },
|
|
78
104
|
body: JSON.stringify(context),
|
|
@@ -85,29 +111,29 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
85
111
|
throw new Error("No response body");
|
|
86
112
|
}
|
|
87
113
|
const iterator = streamSSE(response.body, signal)[Symbol.asyncIterator]();
|
|
88
|
-
let
|
|
114
|
+
let isDone = false;
|
|
89
115
|
let pendingNext = iterator.next();
|
|
90
116
|
let lastStatus = null;
|
|
91
|
-
while (!
|
|
117
|
+
while (!isDone) {
|
|
92
118
|
const result = await Promise.race([
|
|
93
119
|
pendingNext.then((iteratorResult) => ({
|
|
94
120
|
type: "status",
|
|
95
121
|
iteratorResult
|
|
96
122
|
})),
|
|
97
123
|
new Promise(
|
|
98
|
-
(resolve) => setTimeout(() => resolve({ type: "timeout" }),
|
|
124
|
+
(resolve) => setTimeout(() => resolve({ type: "timeout" }), pollIntervalMs)
|
|
99
125
|
)
|
|
100
126
|
]);
|
|
101
127
|
const elapsedSeconds = (Date.now() - startTime) / 1e3;
|
|
102
128
|
if (result.type === "status") {
|
|
103
129
|
const iteratorResult = result.iteratorResult;
|
|
104
|
-
|
|
105
|
-
if (!
|
|
130
|
+
isDone = iteratorResult.done ?? false;
|
|
131
|
+
if (!isDone && iteratorResult.value) {
|
|
106
132
|
lastStatus = iteratorResult.value;
|
|
107
133
|
pendingNext = iterator.next();
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
|
-
if (lastStatus ===
|
|
136
|
+
if (lastStatus === options.completedStatus) {
|
|
111
137
|
yield `Completed in ${elapsedSeconds.toFixed(1)}s`;
|
|
112
138
|
} else if (lastStatus) {
|
|
113
139
|
yield `${lastStatus} ${elapsedSeconds.toFixed(1)}s`;
|
|
@@ -119,56 +145,73 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
119
145
|
signal.removeEventListener("abort", handleAbort);
|
|
120
146
|
}
|
|
121
147
|
};
|
|
148
|
+
var createCachedConnectionChecker = (checkConnection, ttlMs = CONNECTION_CHECK_TTL_MS) => {
|
|
149
|
+
let cache = null;
|
|
150
|
+
return async () => {
|
|
151
|
+
const now = Date.now();
|
|
152
|
+
if (cache && now - cache.timestamp < ttlMs) return cache.result;
|
|
153
|
+
try {
|
|
154
|
+
const result = await checkConnection();
|
|
155
|
+
cache = { result, timestamp: now };
|
|
156
|
+
return result;
|
|
157
|
+
} catch {
|
|
158
|
+
cache = { result: false, timestamp: now };
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/constants.ts
|
|
165
|
+
var DEFAULT_PORT = 7567;
|
|
166
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
167
|
+
|
|
168
|
+
// src/client.ts
|
|
169
|
+
var DEFAULT_SERVER_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
170
|
+
var isReactGrabApi = (value) => typeof value === "object" && value !== null && "setAgent" in value;
|
|
122
171
|
var createCodexAgentProvider = (options = {}) => {
|
|
123
172
|
const { serverUrl = DEFAULT_SERVER_URL, getOptions } = options;
|
|
124
|
-
let connectionCache = null;
|
|
125
173
|
const mergeOptions = (contextOptions) => ({
|
|
126
174
|
...getOptions?.() ?? {},
|
|
127
175
|
...contextOptions ?? {}
|
|
128
176
|
});
|
|
177
|
+
const checkConnection = createCachedConnectionChecker(async () => {
|
|
178
|
+
const response = await fetch(`${serverUrl}/health`, { method: "GET" });
|
|
179
|
+
return response.ok;
|
|
180
|
+
}, CONNECTION_CHECK_TTL_MS);
|
|
129
181
|
return {
|
|
130
182
|
send: async function* (context, signal) {
|
|
131
183
|
const combinedContext = {
|
|
132
184
|
...context,
|
|
133
185
|
options: mergeOptions(context.options)
|
|
134
186
|
};
|
|
135
|
-
yield*
|
|
187
|
+
yield* streamAgentStatusFromServer(
|
|
188
|
+
{ serverUrl, completedStatus: COMPLETED_STATUS },
|
|
189
|
+
combinedContext,
|
|
190
|
+
signal
|
|
191
|
+
);
|
|
136
192
|
},
|
|
137
193
|
resume: async function* (sessionId, signal, storage) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
146
|
-
}
|
|
147
|
-
const context = session.context;
|
|
194
|
+
const storedContext = getStoredAgentContext(storage, sessionId);
|
|
195
|
+
const context = {
|
|
196
|
+
content: storedContext.content,
|
|
197
|
+
prompt: storedContext.prompt,
|
|
198
|
+
options: storedContext.options,
|
|
199
|
+
sessionId: storedContext.sessionId ?? sessionId
|
|
200
|
+
};
|
|
148
201
|
const combinedContext = {
|
|
149
202
|
...context,
|
|
150
203
|
options: mergeOptions(context.options)
|
|
151
204
|
};
|
|
152
205
|
yield "Resuming...";
|
|
153
|
-
yield*
|
|
206
|
+
yield* streamAgentStatusFromServer(
|
|
207
|
+
{ serverUrl, completedStatus: COMPLETED_STATUS },
|
|
208
|
+
combinedContext,
|
|
209
|
+
signal
|
|
210
|
+
);
|
|
154
211
|
},
|
|
155
212
|
supportsResume: true,
|
|
156
213
|
supportsFollowUp: true,
|
|
157
|
-
checkConnection
|
|
158
|
-
const now = Date.now();
|
|
159
|
-
if (connectionCache && now - connectionCache.timestamp < CONNECTION_CHECK_TTL_MS) {
|
|
160
|
-
return connectionCache.result;
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
const response = await fetch(`${serverUrl}/health`, { method: "GET" });
|
|
164
|
-
const result = response.ok;
|
|
165
|
-
connectionCache = { result, timestamp: now };
|
|
166
|
-
return result;
|
|
167
|
-
} catch {
|
|
168
|
-
connectionCache = { result: false, timestamp: now };
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
},
|
|
214
|
+
checkConnection,
|
|
172
215
|
undo: async () => {
|
|
173
216
|
try {
|
|
174
217
|
await fetch(`${serverUrl}/undo`, { method: "POST" });
|
|
@@ -180,24 +223,25 @@ var createCodexAgentProvider = (options = {}) => {
|
|
|
180
223
|
var attachAgent = async () => {
|
|
181
224
|
if (typeof window === "undefined") return;
|
|
182
225
|
const provider = createCodexAgentProvider();
|
|
183
|
-
const attach = (
|
|
184
|
-
|
|
226
|
+
const attach = (api) => {
|
|
227
|
+
api.setAgent({ provider, storage: sessionStorage });
|
|
185
228
|
};
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
188
|
-
attach(
|
|
229
|
+
const existingApi = window.__REACT_GRAB__;
|
|
230
|
+
if (isReactGrabApi(existingApi)) {
|
|
231
|
+
attach(existingApi);
|
|
189
232
|
return;
|
|
190
233
|
}
|
|
191
234
|
window.addEventListener(
|
|
192
235
|
"react-grab:init",
|
|
193
236
|
(event) => {
|
|
194
|
-
|
|
195
|
-
|
|
237
|
+
if (!(event instanceof CustomEvent)) return;
|
|
238
|
+
if (!isReactGrabApi(event.detail)) return;
|
|
239
|
+
attach(event.detail);
|
|
196
240
|
},
|
|
197
241
|
{ once: true }
|
|
198
242
|
);
|
|
199
243
|
const apiAfterListener = window.__REACT_GRAB__;
|
|
200
|
-
if (apiAfterListener) {
|
|
244
|
+
if (isReactGrabApi(apiAfterListener)) {
|
|
201
245
|
attach(apiAfterListener);
|
|
202
246
|
}
|
|
203
247
|
};
|
package/dist/client.global.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var ReactGrabCodex=(function(exports){'use strict';var
|
|
2
|
-
`))
|
|
1
|
+
var ReactGrabCodex=(function(exports){'use strict';var A=5e3,x="react-grab:agent-sessions",b=e=>{let t="",o="";for(let r of e.split(`
|
|
2
|
+
`))r.startsWith("event:")?t=r.slice(6).trim():r.startsWith("data:")&&(o=r.slice(5).trim());return {eventType:t,data:o}},v=async function*(e,t){let o=e.getReader(),r=new TextDecoder,n="",s=false,a=()=>{s=true,o.cancel().catch(()=>{});};t.addEventListener("abort",a);try{if(t.aborted)throw new DOMException("Aborted","AbortError");for(;;){let d=await o.read();if(s||t.aborted)throw new DOMException("Aborted","AbortError");let{done:i,value:l}=d;l&&(n+=r.decode(l,{stream:!0}));let c;for(;(c=n.indexOf(`
|
|
3
3
|
|
|
4
|
-
`))!==-1;){let{eventType:
|
|
5
|
-
exports.attachAgent=
|
|
4
|
+
`))!==-1;){let{eventType:f,data:p}=b(n.slice(0,c));if(n=n.slice(c+2),f==="done")return;if(f==="error")throw new Error(p||"Agent error");p&&(yield p);}if(i)break}}finally{t.removeEventListener("abort",a);try{o.releaseLock();}catch{}}},m=e=>typeof e=="object"&&e!==null,S=(e,t,o=x)=>{let r=e.getItem(o);if(!r)throw new Error("No sessions to resume");let n;try{n=JSON.parse(r);}catch{throw new Error("Failed to parse stored sessions")}if(!m(n))throw new Error("Invalid stored sessions");let s=n[t];if(!m(s))throw new Error(`Session ${t} not found`);let a=s.context;if(!m(a))throw new Error(`Session ${t} is invalid`);let d=a.content,i=a.prompt;if(typeof d!="string"||typeof i!="string")throw new Error(`Session ${t} is invalid`);let l=a.options,c=a.sessionId;return {content:d,prompt:i,options:l,sessionId:typeof c=="string"?c:void 0}},w=async function*(e,t,o){let r=Date.now(),n=t.sessionId,s=e.pollIntervalMs??100,a=`${e.serverUrl}${e.agentPath??"/agent"}`,d=()=>{if(!n)return;let i=e.abortPath?.(n)??`/abort/${n}`;fetch(`${e.serverUrl}${i}`,{method:"POST"}).catch(()=>{});};o.addEventListener("abort",d);try{let i=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:o});if(!i.ok)throw new Error(`Server error: ${i.status}`);if(!i.body)throw new Error("No response body");let l=v(i.body,o)[Symbol.asyncIterator](),c=!1,f=l.next(),p=null;for(;!c;){let C=await Promise.race([f.then(u=>({type:"status",iteratorResult:u})),new Promise(u=>setTimeout(()=>u({type:"timeout"}),s))]),g=(Date.now()-r)/1e3;if(C.type==="status"){let u=C.iteratorResult;c=u.done??!1,!c&&u.value&&(p=u.value,f=l.next());}p===e.completedStatus?yield `Completed in ${g.toFixed(1)}s`:p?yield `${p} ${g.toFixed(1)}s`:yield `Working\u2026 ${g.toFixed(1)}s`;}}finally{o.removeEventListener("abort",d);}},E=(e,t=A)=>{let o=null;return async()=>{let r=Date.now();if(o&&r-o.timestamp<t)return o.result;try{let n=await e();return o={result:n,timestamp:r},n}catch{return o={result:false,timestamp:r},false}}};var y="Completed successfully";var O=`http://localhost:${7567}`,h=e=>typeof e=="object"&&e!==null&&"setAgent"in e,_=(e={})=>{let{serverUrl:t=O,getOptions:o}=e,r=s=>({...o?.()??{},...s??{}}),n=E(async()=>(await fetch(`${t}/health`,{method:"GET"})).ok,A);return {send:async function*(s,a){let d={...s,options:r(s.options)};yield*w({serverUrl:t,completedStatus:y},d,a);},resume:async function*(s,a,d){let i=S(d,s),l={content:i.content,prompt:i.prompt,options:i.options,sessionId:i.sessionId??s},c={...l,options:r(l.options)};yield "Resuming...",yield*w({serverUrl:t,completedStatus:y},c,a);},supportsResume:true,supportsFollowUp:true,checkConnection:n,undo:async()=>{try{await fetch(`${t}/undo`,{method:"POST"});}catch{}}}},R=async()=>{if(typeof window>"u")return;let e=_(),t=n=>{n.setAgent({provider:e,storage:sessionStorage});},o=window.__REACT_GRAB__;if(h(o)){t(o);return}window.addEventListener("react-grab:init",n=>{n instanceof CustomEvent&&h(n.detail)&&t(n.detail);},{once:true});let r=window.__REACT_GRAB__;h(r)&&t(r);};R();
|
|
5
|
+
exports.attachAgent=R;exports.createCodexAgentProvider=_;return exports;})({});
|
package/dist/client.js
CHANGED
|
@@ -10,7 +10,7 @@ var parseSSEEvent = (eventBlock) => {
|
|
|
10
10
|
}
|
|
11
11
|
return { eventType, data };
|
|
12
12
|
};
|
|
13
|
-
async function*
|
|
13
|
+
var streamSSE = async function* (stream, signal) {
|
|
14
14
|
const reader = stream.getReader();
|
|
15
15
|
const decoder = new TextDecoder();
|
|
16
16
|
let buffer = "";
|
|
@@ -49,28 +49,54 @@ async function* streamSSE(stream, signal) {
|
|
|
49
49
|
} catch {
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
52
|
+
};
|
|
53
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
54
|
+
var getStoredAgentContext = (storage, sessionId, storageKey = STORAGE_KEY) => {
|
|
55
|
+
const rawSessions = storage.getItem(storageKey);
|
|
56
|
+
if (!rawSessions) throw new Error("No sessions to resume");
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(rawSessions);
|
|
60
|
+
} catch {
|
|
61
|
+
throw new Error("Failed to parse stored sessions");
|
|
62
|
+
}
|
|
63
|
+
if (!isRecord(parsed)) throw new Error("Invalid stored sessions");
|
|
64
|
+
const storedSession = parsed[sessionId];
|
|
65
|
+
if (!isRecord(storedSession)) {
|
|
66
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
67
|
+
}
|
|
68
|
+
const context = storedSession.context;
|
|
69
|
+
if (!isRecord(context)) throw new Error(`Session ${sessionId} is invalid`);
|
|
70
|
+
const content = context.content;
|
|
71
|
+
const prompt = context.prompt;
|
|
72
|
+
if (typeof content !== "string" || typeof prompt !== "string") {
|
|
73
|
+
throw new Error(`Session ${sessionId} is invalid`);
|
|
74
|
+
}
|
|
75
|
+
const options = context.options;
|
|
76
|
+
const storedSessionId = context.sessionId;
|
|
77
|
+
return {
|
|
78
|
+
content,
|
|
79
|
+
prompt,
|
|
80
|
+
options,
|
|
81
|
+
sessionId: typeof storedSessionId === "string" ? storedSessionId : void 0
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
var streamAgentStatusFromServer = async function* (options, context, signal) {
|
|
61
85
|
const startTime = Date.now();
|
|
62
86
|
const sessionId = context.sessionId;
|
|
87
|
+
const pollIntervalMs = options.pollIntervalMs ?? 100;
|
|
88
|
+
const agentUrl = `${options.serverUrl}${options.agentPath ?? "/agent"}`;
|
|
63
89
|
const handleAbort = () => {
|
|
64
|
-
if (sessionId)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
if (!sessionId) return;
|
|
91
|
+
const abortPath = options.abortPath?.(sessionId) ?? `/abort/${sessionId}`;
|
|
92
|
+
fetch(`${options.serverUrl}${abortPath}`, { method: "POST" }).catch(
|
|
93
|
+
() => {
|
|
94
|
+
}
|
|
95
|
+
);
|
|
70
96
|
};
|
|
71
97
|
signal.addEventListener("abort", handleAbort);
|
|
72
98
|
try {
|
|
73
|
-
const response = await fetch(
|
|
99
|
+
const response = await fetch(agentUrl, {
|
|
74
100
|
method: "POST",
|
|
75
101
|
headers: { "Content-Type": "application/json" },
|
|
76
102
|
body: JSON.stringify(context),
|
|
@@ -83,29 +109,29 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
83
109
|
throw new Error("No response body");
|
|
84
110
|
}
|
|
85
111
|
const iterator = streamSSE(response.body, signal)[Symbol.asyncIterator]();
|
|
86
|
-
let
|
|
112
|
+
let isDone = false;
|
|
87
113
|
let pendingNext = iterator.next();
|
|
88
114
|
let lastStatus = null;
|
|
89
|
-
while (!
|
|
115
|
+
while (!isDone) {
|
|
90
116
|
const result = await Promise.race([
|
|
91
117
|
pendingNext.then((iteratorResult) => ({
|
|
92
118
|
type: "status",
|
|
93
119
|
iteratorResult
|
|
94
120
|
})),
|
|
95
121
|
new Promise(
|
|
96
|
-
(resolve) => setTimeout(() => resolve({ type: "timeout" }),
|
|
122
|
+
(resolve) => setTimeout(() => resolve({ type: "timeout" }), pollIntervalMs)
|
|
97
123
|
)
|
|
98
124
|
]);
|
|
99
125
|
const elapsedSeconds = (Date.now() - startTime) / 1e3;
|
|
100
126
|
if (result.type === "status") {
|
|
101
127
|
const iteratorResult = result.iteratorResult;
|
|
102
|
-
|
|
103
|
-
if (!
|
|
128
|
+
isDone = iteratorResult.done ?? false;
|
|
129
|
+
if (!isDone && iteratorResult.value) {
|
|
104
130
|
lastStatus = iteratorResult.value;
|
|
105
131
|
pendingNext = iterator.next();
|
|
106
132
|
}
|
|
107
133
|
}
|
|
108
|
-
if (lastStatus ===
|
|
134
|
+
if (lastStatus === options.completedStatus) {
|
|
109
135
|
yield `Completed in ${elapsedSeconds.toFixed(1)}s`;
|
|
110
136
|
} else if (lastStatus) {
|
|
111
137
|
yield `${lastStatus} ${elapsedSeconds.toFixed(1)}s`;
|
|
@@ -117,56 +143,73 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
117
143
|
signal.removeEventListener("abort", handleAbort);
|
|
118
144
|
}
|
|
119
145
|
};
|
|
146
|
+
var createCachedConnectionChecker = (checkConnection, ttlMs = CONNECTION_CHECK_TTL_MS) => {
|
|
147
|
+
let cache = null;
|
|
148
|
+
return async () => {
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
if (cache && now - cache.timestamp < ttlMs) return cache.result;
|
|
151
|
+
try {
|
|
152
|
+
const result = await checkConnection();
|
|
153
|
+
cache = { result, timestamp: now };
|
|
154
|
+
return result;
|
|
155
|
+
} catch {
|
|
156
|
+
cache = { result: false, timestamp: now };
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// src/constants.ts
|
|
163
|
+
var DEFAULT_PORT = 7567;
|
|
164
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
165
|
+
|
|
166
|
+
// src/client.ts
|
|
167
|
+
var DEFAULT_SERVER_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
168
|
+
var isReactGrabApi = (value) => typeof value === "object" && value !== null && "setAgent" in value;
|
|
120
169
|
var createCodexAgentProvider = (options = {}) => {
|
|
121
170
|
const { serverUrl = DEFAULT_SERVER_URL, getOptions } = options;
|
|
122
|
-
let connectionCache = null;
|
|
123
171
|
const mergeOptions = (contextOptions) => ({
|
|
124
172
|
...getOptions?.() ?? {},
|
|
125
173
|
...contextOptions ?? {}
|
|
126
174
|
});
|
|
175
|
+
const checkConnection = createCachedConnectionChecker(async () => {
|
|
176
|
+
const response = await fetch(`${serverUrl}/health`, { method: "GET" });
|
|
177
|
+
return response.ok;
|
|
178
|
+
}, CONNECTION_CHECK_TTL_MS);
|
|
127
179
|
return {
|
|
128
180
|
send: async function* (context, signal) {
|
|
129
181
|
const combinedContext = {
|
|
130
182
|
...context,
|
|
131
183
|
options: mergeOptions(context.options)
|
|
132
184
|
};
|
|
133
|
-
yield*
|
|
185
|
+
yield* streamAgentStatusFromServer(
|
|
186
|
+
{ serverUrl, completedStatus: COMPLETED_STATUS },
|
|
187
|
+
combinedContext,
|
|
188
|
+
signal
|
|
189
|
+
);
|
|
134
190
|
},
|
|
135
191
|
resume: async function* (sessionId, signal, storage) {
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
throw new Error(`Session ${sessionId} not found`);
|
|
144
|
-
}
|
|
145
|
-
const context = session.context;
|
|
192
|
+
const storedContext = getStoredAgentContext(storage, sessionId);
|
|
193
|
+
const context = {
|
|
194
|
+
content: storedContext.content,
|
|
195
|
+
prompt: storedContext.prompt,
|
|
196
|
+
options: storedContext.options,
|
|
197
|
+
sessionId: storedContext.sessionId ?? sessionId
|
|
198
|
+
};
|
|
146
199
|
const combinedContext = {
|
|
147
200
|
...context,
|
|
148
201
|
options: mergeOptions(context.options)
|
|
149
202
|
};
|
|
150
203
|
yield "Resuming...";
|
|
151
|
-
yield*
|
|
204
|
+
yield* streamAgentStatusFromServer(
|
|
205
|
+
{ serverUrl, completedStatus: COMPLETED_STATUS },
|
|
206
|
+
combinedContext,
|
|
207
|
+
signal
|
|
208
|
+
);
|
|
152
209
|
},
|
|
153
210
|
supportsResume: true,
|
|
154
211
|
supportsFollowUp: true,
|
|
155
|
-
checkConnection
|
|
156
|
-
const now = Date.now();
|
|
157
|
-
if (connectionCache && now - connectionCache.timestamp < CONNECTION_CHECK_TTL_MS) {
|
|
158
|
-
return connectionCache.result;
|
|
159
|
-
}
|
|
160
|
-
try {
|
|
161
|
-
const response = await fetch(`${serverUrl}/health`, { method: "GET" });
|
|
162
|
-
const result = response.ok;
|
|
163
|
-
connectionCache = { result, timestamp: now };
|
|
164
|
-
return result;
|
|
165
|
-
} catch {
|
|
166
|
-
connectionCache = { result: false, timestamp: now };
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
},
|
|
212
|
+
checkConnection,
|
|
170
213
|
undo: async () => {
|
|
171
214
|
try {
|
|
172
215
|
await fetch(`${serverUrl}/undo`, { method: "POST" });
|
|
@@ -178,24 +221,25 @@ var createCodexAgentProvider = (options = {}) => {
|
|
|
178
221
|
var attachAgent = async () => {
|
|
179
222
|
if (typeof window === "undefined") return;
|
|
180
223
|
const provider = createCodexAgentProvider();
|
|
181
|
-
const attach = (
|
|
182
|
-
|
|
224
|
+
const attach = (api) => {
|
|
225
|
+
api.setAgent({ provider, storage: sessionStorage });
|
|
183
226
|
};
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
186
|
-
attach(
|
|
227
|
+
const existingApi = window.__REACT_GRAB__;
|
|
228
|
+
if (isReactGrabApi(existingApi)) {
|
|
229
|
+
attach(existingApi);
|
|
187
230
|
return;
|
|
188
231
|
}
|
|
189
232
|
window.addEventListener(
|
|
190
233
|
"react-grab:init",
|
|
191
234
|
(event) => {
|
|
192
|
-
|
|
193
|
-
|
|
235
|
+
if (!(event instanceof CustomEvent)) return;
|
|
236
|
+
if (!isReactGrabApi(event.detail)) return;
|
|
237
|
+
attach(event.detail);
|
|
194
238
|
},
|
|
195
239
|
{ once: true }
|
|
196
240
|
);
|
|
197
241
|
const apiAfterListener = window.__REACT_GRAB__;
|
|
198
|
-
if (apiAfterListener) {
|
|
242
|
+
if (isReactGrabApi(apiAfterListener)) {
|
|
199
243
|
attach(apiAfterListener);
|
|
200
244
|
}
|
|
201
245
|
};
|
package/dist/server.cjs
CHANGED
|
@@ -23,9 +23,11 @@ var COMPLETED_STATUS = "Completed successfully";
|
|
|
23
23
|
|
|
24
24
|
// ../utils/dist/server.js
|
|
25
25
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
26
|
-
var VERSION = "0.0.
|
|
26
|
+
var VERSION = "0.0.89";
|
|
27
27
|
try {
|
|
28
|
-
fetch(
|
|
28
|
+
fetch(
|
|
29
|
+
`https://www.react-grab.com/api/version?source=codex&t=${Date.now()}`
|
|
30
|
+
).catch(() => {
|
|
29
31
|
});
|
|
30
32
|
} catch {
|
|
31
33
|
}
|
|
@@ -65,6 +67,53 @@ var formatStreamEvent = (event) => {
|
|
|
65
67
|
return void 0;
|
|
66
68
|
}
|
|
67
69
|
};
|
|
70
|
+
var runAgent = async function* (prompt, options) {
|
|
71
|
+
const sessionId = options?.sessionId;
|
|
72
|
+
const abortController = new AbortController();
|
|
73
|
+
if (sessionId) {
|
|
74
|
+
abortControllers.set(sessionId, abortController);
|
|
75
|
+
}
|
|
76
|
+
const isAborted = () => {
|
|
77
|
+
if (options?.signal?.aborted) return true;
|
|
78
|
+
if (abortController.signal.aborted) return true;
|
|
79
|
+
return false;
|
|
80
|
+
};
|
|
81
|
+
try {
|
|
82
|
+
yield { type: "status", content: "Thinking\u2026" };
|
|
83
|
+
const { thread } = getOrCreateThread(sessionId, {
|
|
84
|
+
...options,
|
|
85
|
+
workingDirectory: options?.workingDirectory ?? options?.cwd
|
|
86
|
+
});
|
|
87
|
+
if (sessionId && thread.id) {
|
|
88
|
+
lastThreadId = thread.id;
|
|
89
|
+
}
|
|
90
|
+
const { events } = await thread.runStreamed(prompt);
|
|
91
|
+
for await (const event of events) {
|
|
92
|
+
if (isAborted()) break;
|
|
93
|
+
const statusText = formatStreamEvent(event);
|
|
94
|
+
if (statusText && !isAborted()) {
|
|
95
|
+
yield { type: "status", content: statusText };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (sessionId && !isAborted() && thread.id) {
|
|
99
|
+
threadMap.set(sessionId, { thread, threadId: thread.id });
|
|
100
|
+
}
|
|
101
|
+
if (!isAborted()) {
|
|
102
|
+
yield { type: "status", content: COMPLETED_STATUS };
|
|
103
|
+
yield { type: "done", content: "" };
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (!isAborted()) {
|
|
107
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
108
|
+
yield { type: "error", content: errorMessage };
|
|
109
|
+
yield { type: "done", content: "" };
|
|
110
|
+
}
|
|
111
|
+
} finally {
|
|
112
|
+
if (sessionId) {
|
|
113
|
+
abortControllers.delete(sessionId);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
68
117
|
var createServer = () => {
|
|
69
118
|
const honoApplication = new hono.Hono();
|
|
70
119
|
honoApplication.use("*", cors.cors());
|
|
@@ -77,47 +126,17 @@ var createServer = () => {
|
|
|
77
126
|
Context:
|
|
78
127
|
${content}`;
|
|
79
128
|
return streaming.streamSSE(context, async (stream) => {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
await stream.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
87
|
-
const { thread } = getOrCreateThread(sessionId, options);
|
|
88
|
-
if (sessionId && thread.id) {
|
|
89
|
-
lastThreadId = thread.id;
|
|
90
|
-
}
|
|
91
|
-
const { events } = await thread.runStreamed(formattedPrompt);
|
|
92
|
-
for await (const event of events) {
|
|
93
|
-
if (isAborted()) break;
|
|
94
|
-
const statusText = formatStreamEvent(event);
|
|
95
|
-
if (statusText && !isAborted()) {
|
|
96
|
-
await stream.writeSSE({ data: statusText, event: "status" });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (sessionId && !isAborted() && thread.id) {
|
|
100
|
-
threadMap.set(sessionId, { thread, threadId: thread.id });
|
|
101
|
-
}
|
|
102
|
-
if (!isAborted()) {
|
|
129
|
+
for await (const message of runAgent(formattedPrompt, {
|
|
130
|
+
...options,
|
|
131
|
+
sessionId
|
|
132
|
+
})) {
|
|
133
|
+
if (message.type === "error") {
|
|
103
134
|
await stream.writeSSE({
|
|
104
|
-
data:
|
|
105
|
-
event: "status"
|
|
106
|
-
});
|
|
107
|
-
await stream.writeSSE({ data: "", event: "done" });
|
|
108
|
-
}
|
|
109
|
-
} catch (error) {
|
|
110
|
-
if (!isAborted()) {
|
|
111
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
112
|
-
await stream.writeSSE({
|
|
113
|
-
data: `Error: ${errorMessage}`,
|
|
135
|
+
data: `Error: ${message.content}`,
|
|
114
136
|
event: "error"
|
|
115
137
|
});
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
} finally {
|
|
119
|
-
if (sessionId) {
|
|
120
|
-
abortControllers.delete(sessionId);
|
|
138
|
+
} else {
|
|
139
|
+
await stream.writeSSE({ data: message.content, event: message.type });
|
|
121
140
|
}
|
|
122
141
|
}
|
|
123
142
|
});
|
|
@@ -166,4 +185,5 @@ if ((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filen
|
|
|
166
185
|
}
|
|
167
186
|
|
|
168
187
|
exports.createServer = createServer;
|
|
188
|
+
exports.runAgent = runAgent;
|
|
169
189
|
exports.startServer = startServer;
|
package/dist/server.d.cts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
|
+
import { AgentCoreOptions, AgentMessage } from '@react-grab/utils/server';
|
|
3
4
|
|
|
4
|
-
interface CodexAgentOptions {
|
|
5
|
+
interface CodexAgentOptions extends AgentCoreOptions {
|
|
5
6
|
model?: string;
|
|
6
7
|
workingDirectory?: string;
|
|
7
8
|
}
|
|
9
|
+
declare const runAgent: (prompt: string, options?: CodexAgentOptions) => AsyncGenerator<AgentMessage>;
|
|
8
10
|
declare const createServer: () => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
9
11
|
declare const startServer: (port?: number) => Promise<void>;
|
|
10
12
|
|
|
11
|
-
export { type CodexAgentOptions, createServer, startServer };
|
|
13
|
+
export { type CodexAgentOptions, createServer, runAgent, startServer };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
|
+
import { AgentCoreOptions, AgentMessage } from '@react-grab/utils/server';
|
|
3
4
|
|
|
4
|
-
interface CodexAgentOptions {
|
|
5
|
+
interface CodexAgentOptions extends AgentCoreOptions {
|
|
5
6
|
model?: string;
|
|
6
7
|
workingDirectory?: string;
|
|
7
8
|
}
|
|
9
|
+
declare const runAgent: (prompt: string, options?: CodexAgentOptions) => AsyncGenerator<AgentMessage>;
|
|
8
10
|
declare const createServer: () => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
9
11
|
declare const startServer: (port?: number) => Promise<void>;
|
|
10
12
|
|
|
11
|
-
export { type CodexAgentOptions, createServer, startServer };
|
|
13
|
+
export { type CodexAgentOptions, createServer, runAgent, startServer };
|
package/dist/server.js
CHANGED
|
@@ -15,9 +15,11 @@ var COMPLETED_STATUS = "Completed successfully";
|
|
|
15
15
|
|
|
16
16
|
// ../utils/dist/server.js
|
|
17
17
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
-
var VERSION = "0.0.
|
|
18
|
+
var VERSION = "0.0.89";
|
|
19
19
|
try {
|
|
20
|
-
fetch(
|
|
20
|
+
fetch(
|
|
21
|
+
`https://www.react-grab.com/api/version?source=codex&t=${Date.now()}`
|
|
22
|
+
).catch(() => {
|
|
21
23
|
});
|
|
22
24
|
} catch {
|
|
23
25
|
}
|
|
@@ -57,6 +59,53 @@ var formatStreamEvent = (event) => {
|
|
|
57
59
|
return void 0;
|
|
58
60
|
}
|
|
59
61
|
};
|
|
62
|
+
var runAgent = async function* (prompt, options) {
|
|
63
|
+
const sessionId = options?.sessionId;
|
|
64
|
+
const abortController = new AbortController();
|
|
65
|
+
if (sessionId) {
|
|
66
|
+
abortControllers.set(sessionId, abortController);
|
|
67
|
+
}
|
|
68
|
+
const isAborted = () => {
|
|
69
|
+
if (options?.signal?.aborted) return true;
|
|
70
|
+
if (abortController.signal.aborted) return true;
|
|
71
|
+
return false;
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
yield { type: "status", content: "Thinking\u2026" };
|
|
75
|
+
const { thread } = getOrCreateThread(sessionId, {
|
|
76
|
+
...options,
|
|
77
|
+
workingDirectory: options?.workingDirectory ?? options?.cwd
|
|
78
|
+
});
|
|
79
|
+
if (sessionId && thread.id) {
|
|
80
|
+
lastThreadId = thread.id;
|
|
81
|
+
}
|
|
82
|
+
const { events } = await thread.runStreamed(prompt);
|
|
83
|
+
for await (const event of events) {
|
|
84
|
+
if (isAborted()) break;
|
|
85
|
+
const statusText = formatStreamEvent(event);
|
|
86
|
+
if (statusText && !isAborted()) {
|
|
87
|
+
yield { type: "status", content: statusText };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (sessionId && !isAborted() && thread.id) {
|
|
91
|
+
threadMap.set(sessionId, { thread, threadId: thread.id });
|
|
92
|
+
}
|
|
93
|
+
if (!isAborted()) {
|
|
94
|
+
yield { type: "status", content: COMPLETED_STATUS };
|
|
95
|
+
yield { type: "done", content: "" };
|
|
96
|
+
}
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (!isAborted()) {
|
|
99
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
100
|
+
yield { type: "error", content: errorMessage };
|
|
101
|
+
yield { type: "done", content: "" };
|
|
102
|
+
}
|
|
103
|
+
} finally {
|
|
104
|
+
if (sessionId) {
|
|
105
|
+
abortControllers.delete(sessionId);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
60
109
|
var createServer = () => {
|
|
61
110
|
const honoApplication = new Hono();
|
|
62
111
|
honoApplication.use("*", cors());
|
|
@@ -69,47 +118,17 @@ var createServer = () => {
|
|
|
69
118
|
Context:
|
|
70
119
|
${content}`;
|
|
71
120
|
return streamSSE(context, async (stream) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
await stream.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
79
|
-
const { thread } = getOrCreateThread(sessionId, options);
|
|
80
|
-
if (sessionId && thread.id) {
|
|
81
|
-
lastThreadId = thread.id;
|
|
82
|
-
}
|
|
83
|
-
const { events } = await thread.runStreamed(formattedPrompt);
|
|
84
|
-
for await (const event of events) {
|
|
85
|
-
if (isAborted()) break;
|
|
86
|
-
const statusText = formatStreamEvent(event);
|
|
87
|
-
if (statusText && !isAborted()) {
|
|
88
|
-
await stream.writeSSE({ data: statusText, event: "status" });
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (sessionId && !isAborted() && thread.id) {
|
|
92
|
-
threadMap.set(sessionId, { thread, threadId: thread.id });
|
|
93
|
-
}
|
|
94
|
-
if (!isAborted()) {
|
|
121
|
+
for await (const message of runAgent(formattedPrompt, {
|
|
122
|
+
...options,
|
|
123
|
+
sessionId
|
|
124
|
+
})) {
|
|
125
|
+
if (message.type === "error") {
|
|
95
126
|
await stream.writeSSE({
|
|
96
|
-
data:
|
|
97
|
-
event: "status"
|
|
98
|
-
});
|
|
99
|
-
await stream.writeSSE({ data: "", event: "done" });
|
|
100
|
-
}
|
|
101
|
-
} catch (error) {
|
|
102
|
-
if (!isAborted()) {
|
|
103
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
104
|
-
await stream.writeSSE({
|
|
105
|
-
data: `Error: ${errorMessage}`,
|
|
127
|
+
data: `Error: ${message.content}`,
|
|
106
128
|
event: "error"
|
|
107
129
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} finally {
|
|
111
|
-
if (sessionId) {
|
|
112
|
-
abortControllers.delete(sessionId);
|
|
130
|
+
} else {
|
|
131
|
+
await stream.writeSSE({ data: message.content, event: message.type });
|
|
113
132
|
}
|
|
114
133
|
}
|
|
115
134
|
});
|
|
@@ -157,4 +176,4 @@ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
|
157
176
|
startServer(DEFAULT_PORT).catch(console.error);
|
|
158
177
|
}
|
|
159
178
|
|
|
160
|
-
export { createServer, startServer };
|
|
179
|
+
export { createServer, runAgent, startServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-grab/codex",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.89",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"react-grab-codex": "./dist/cli.cjs"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.10.7",
|
|
28
28
|
"tsup": "^8.4.0",
|
|
29
|
-
"@react-grab/utils": "0.0.
|
|
29
|
+
"@react-grab/utils": "0.0.89"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@hono/node-server": "^1.19.6",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"fkill": "^9.0.0",
|
|
36
36
|
"hono": "^4.0.0",
|
|
37
37
|
"picocolors": "^1.1.1",
|
|
38
|
-
"react-grab": "0.0.
|
|
38
|
+
"react-grab": "0.0.89"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"dev": "tsup --watch",
|