@react-grab/amp 0.0.88 → 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 +104 -60
- package/dist/client.global.js +4 -4
- package/dist/client.js +104 -60
- package/dist/server.cjs +98 -92
- package/dist/server.d.cts +4 -3
- package/dist/server.d.ts +4 -3
- package/dist/server.js +98 -93
- 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 = 9567;
|
|
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 = 9567;
|
|
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 @@ async function* streamFromServer(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`;
|
|
@@ -118,57 +144,74 @@ async function* streamFromServer(serverUrl, context, signal) {
|
|
|
118
144
|
} finally {
|
|
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 = 9567;
|
|
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 createAmpAgentProvider = (providerOptions = {}) => {
|
|
123
172
|
const { serverUrl = DEFAULT_SERVER_URL, getOptions } = providerOptions;
|
|
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 mergedContext = {
|
|
132
184
|
...context,
|
|
133
185
|
options: mergeOptions(context.options)
|
|
134
186
|
};
|
|
135
|
-
yield*
|
|
187
|
+
yield* streamAgentStatusFromServer(
|
|
188
|
+
{ serverUrl, completedStatus: COMPLETED_STATUS },
|
|
189
|
+
mergedContext,
|
|
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 mergedContext = {
|
|
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
|
+
mergedContext,
|
|
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
|
abort: async (sessionId) => {
|
|
173
216
|
try {
|
|
174
217
|
await fetch(`${serverUrl}/abort/${sessionId}`, { method: "POST" });
|
|
@@ -186,24 +229,25 @@ var createAmpAgentProvider = (providerOptions = {}) => {
|
|
|
186
229
|
var attachAgent = async () => {
|
|
187
230
|
if (typeof window === "undefined") return;
|
|
188
231
|
const provider = createAmpAgentProvider();
|
|
189
|
-
const attach = (
|
|
190
|
-
|
|
232
|
+
const attach = (api) => {
|
|
233
|
+
api.setAgent({ provider, storage: sessionStorage });
|
|
191
234
|
};
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
194
|
-
attach(
|
|
235
|
+
const existingApi = window.__REACT_GRAB__;
|
|
236
|
+
if (isReactGrabApi(existingApi)) {
|
|
237
|
+
attach(existingApi);
|
|
195
238
|
return;
|
|
196
239
|
}
|
|
197
240
|
window.addEventListener(
|
|
198
241
|
"react-grab:init",
|
|
199
242
|
(event) => {
|
|
200
|
-
|
|
201
|
-
|
|
243
|
+
if (!(event instanceof CustomEvent)) return;
|
|
244
|
+
if (!isReactGrabApi(event.detail)) return;
|
|
245
|
+
attach(event.detail);
|
|
202
246
|
},
|
|
203
247
|
{ once: true }
|
|
204
248
|
);
|
|
205
249
|
const apiAfterListener = window.__REACT_GRAB__;
|
|
206
|
-
if (apiAfterListener) {
|
|
250
|
+
if (isReactGrabApi(apiAfterListener)) {
|
|
207
251
|
attach(apiAfterListener);
|
|
208
252
|
}
|
|
209
253
|
};
|
package/dist/client.global.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var ReactGrabAmp=(function(exports){'use strict';var
|
|
2
|
-
`))
|
|
1
|
+
var ReactGrabAmp=(function(exports){'use strict';var g=5e3,v="react-grab:agent-sessions",C=e=>{let t="",o="";for(let n of e.split(`
|
|
2
|
+
`))n.startsWith("event:")?t=n.slice(6).trim():n.startsWith("data:")&&(o=n.slice(5).trim());return {eventType:t,data:o}},T=async function*(e,t){let o=e.getReader(),n=new TextDecoder,r="",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:p}=d;p&&(r+=n.decode(p,{stream:!0}));let c;for(;(c=r.indexOf(`
|
|
3
3
|
|
|
4
|
-
`))!==-1;){let{eventType:
|
|
5
|
-
exports.attachAgent=
|
|
4
|
+
`))!==-1;){let{eventType:u,data:l}=C(r.slice(0,c));if(r=r.slice(c+2),u==="done")return;if(u==="error")throw new Error(l||"Agent error");l&&(yield l);}if(i)break}}finally{t.removeEventListener("abort",a);try{o.releaseLock();}catch{}}},A=e=>typeof e=="object"&&e!==null,E=(e,t,o=v)=>{let n=e.getItem(o);if(!n)throw new Error("No sessions to resume");let r;try{r=JSON.parse(n);}catch{throw new Error("Failed to parse stored sessions")}if(!A(r))throw new Error("Invalid stored sessions");let s=r[t];if(!A(s))throw new Error(`Session ${t} not found`);let a=s.context;if(!A(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 p=a.options,c=a.sessionId;return {content:d,prompt:i,options:p,sessionId:typeof c=="string"?c:void 0}},h=async function*(e,t,o){let n=Date.now(),r=t.sessionId,s=e.pollIntervalMs??100,a=`${e.serverUrl}${e.agentPath??"/agent"}`,d=()=>{if(!r)return;let i=e.abortPath?.(r)??`/abort/${r}`;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 p=T(i.body,o)[Symbol.asyncIterator](),c=!1,u=p.next(),l=null;for(;!c;){let S=await Promise.race([u.then(f=>({type:"status",iteratorResult:f})),new Promise(f=>setTimeout(()=>f({type:"timeout"}),s))]),m=(Date.now()-n)/1e3;if(S.type==="status"){let f=S.iteratorResult;c=f.done??!1,!c&&f.value&&(l=f.value,u=p.next());}l===e.completedStatus?yield `Completed in ${m.toFixed(1)}s`:l?yield `${l} ${m.toFixed(1)}s`:yield `Working\u2026 ${m.toFixed(1)}s`;}}finally{o.removeEventListener("abort",d);}},b=(e,t=g)=>{let o=null;return async()=>{let n=Date.now();if(o&&n-o.timestamp<t)return o.result;try{let r=await e();return o={result:r,timestamp:n},r}catch{return o={result:false,timestamp:n},false}}};var w="Completed successfully";var O=`http://localhost:${9567}`,y=e=>typeof e=="object"&&e!==null&&"setAgent"in e,_=(e={})=>{let{serverUrl:t=O,getOptions:o}=e,n=s=>({...o?.()??{},...s??{}}),r=b(async()=>(await fetch(`${t}/health`,{method:"GET"})).ok,g);return {send:async function*(s,a){let d={...s,options:n(s.options)};yield*h({serverUrl:t,completedStatus:w},d,a);},resume:async function*(s,a,d){let i=E(d,s),p={content:i.content,prompt:i.prompt,options:i.options,sessionId:i.sessionId??s},c={...p,options:n(p.options)};yield "Resuming...",yield*h({serverUrl:t,completedStatus:w},c,a);},supportsResume:true,supportsFollowUp:true,checkConnection:r,abort:async s=>{try{await fetch(`${t}/abort/${s}`,{method:"POST"});}catch{}},undo:async()=>{try{await fetch(`${t}/undo`,{method:"POST"});}catch{}}}},R=async()=>{if(typeof window>"u")return;let e=_(),t=r=>{r.setAgent({provider:e,storage:sessionStorage});},o=window.__REACT_GRAB__;if(y(o)){t(o);return}window.addEventListener("react-grab:init",r=>{r instanceof CustomEvent&&y(r.detail)&&t(r.detail);},{once:true});let n=window.__REACT_GRAB__;y(n)&&t(n);};R();
|
|
5
|
+
exports.attachAgent=R;exports.createAmpAgentProvider=_;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 @@ async function* streamFromServer(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`;
|
|
@@ -116,57 +142,74 @@ async function* streamFromServer(serverUrl, context, signal) {
|
|
|
116
142
|
} finally {
|
|
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 = 9567;
|
|
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 createAmpAgentProvider = (providerOptions = {}) => {
|
|
121
170
|
const { serverUrl = DEFAULT_SERVER_URL, getOptions } = providerOptions;
|
|
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 mergedContext = {
|
|
130
182
|
...context,
|
|
131
183
|
options: mergeOptions(context.options)
|
|
132
184
|
};
|
|
133
|
-
yield*
|
|
185
|
+
yield* streamAgentStatusFromServer(
|
|
186
|
+
{ serverUrl, completedStatus: COMPLETED_STATUS },
|
|
187
|
+
mergedContext,
|
|
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 mergedContext = {
|
|
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
|
+
mergedContext,
|
|
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
|
abort: async (sessionId) => {
|
|
171
214
|
try {
|
|
172
215
|
await fetch(`${serverUrl}/abort/${sessionId}`, { method: "POST" });
|
|
@@ -184,24 +227,25 @@ var createAmpAgentProvider = (providerOptions = {}) => {
|
|
|
184
227
|
var attachAgent = async () => {
|
|
185
228
|
if (typeof window === "undefined") return;
|
|
186
229
|
const provider = createAmpAgentProvider();
|
|
187
|
-
const attach = (
|
|
188
|
-
|
|
230
|
+
const attach = (api) => {
|
|
231
|
+
api.setAgent({ provider, storage: sessionStorage });
|
|
189
232
|
};
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
192
|
-
attach(
|
|
233
|
+
const existingApi = window.__REACT_GRAB__;
|
|
234
|
+
if (isReactGrabApi(existingApi)) {
|
|
235
|
+
attach(existingApi);
|
|
193
236
|
return;
|
|
194
237
|
}
|
|
195
238
|
window.addEventListener(
|
|
196
239
|
"react-grab:init",
|
|
197
240
|
(event) => {
|
|
198
|
-
|
|
199
|
-
|
|
241
|
+
if (!(event instanceof CustomEvent)) return;
|
|
242
|
+
if (!isReactGrabApi(event.detail)) return;
|
|
243
|
+
attach(event.detail);
|
|
200
244
|
},
|
|
201
245
|
{ once: true }
|
|
202
246
|
);
|
|
203
247
|
const apiAfterListener = window.__REACT_GRAB__;
|
|
204
|
-
if (apiAfterListener) {
|
|
248
|
+
if (isReactGrabApi(apiAfterListener)) {
|
|
205
249
|
attach(apiAfterListener);
|
|
206
250
|
}
|
|
207
251
|
};
|
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=amp&t=${Date.now()}`
|
|
30
|
+
).catch(() => {
|
|
29
31
|
});
|
|
30
32
|
} catch {
|
|
31
33
|
}
|
|
@@ -35,6 +37,91 @@ var lastThreadId;
|
|
|
35
37
|
var extractTextFromContent = (content) => {
|
|
36
38
|
return content.filter((item) => item.type === "text" && item.text).map((item) => item.text).join(" ").trim();
|
|
37
39
|
};
|
|
40
|
+
var runAgent = async function* (prompt, options) {
|
|
41
|
+
const sessionId = options?.sessionId;
|
|
42
|
+
const abortController = new AbortController();
|
|
43
|
+
if (sessionId) {
|
|
44
|
+
abortControllers.set(sessionId, abortController);
|
|
45
|
+
}
|
|
46
|
+
const isAborted = () => {
|
|
47
|
+
if (options?.signal?.aborted) return true;
|
|
48
|
+
if (abortController.signal.aborted) return true;
|
|
49
|
+
return false;
|
|
50
|
+
};
|
|
51
|
+
try {
|
|
52
|
+
yield { type: "status", content: "Thinking\u2026" };
|
|
53
|
+
const executeOptions = {
|
|
54
|
+
dangerouslyAllowAll: true
|
|
55
|
+
};
|
|
56
|
+
executeOptions.cwd = options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd();
|
|
57
|
+
const existingThread = sessionId ? threadMap.get(sessionId) : void 0;
|
|
58
|
+
if (existingThread) {
|
|
59
|
+
executeOptions.continue = existingThread.threadId;
|
|
60
|
+
}
|
|
61
|
+
let capturedThreadId;
|
|
62
|
+
for await (const message of ampSdk.execute({
|
|
63
|
+
prompt,
|
|
64
|
+
options: executeOptions,
|
|
65
|
+
signal: abortController.signal
|
|
66
|
+
})) {
|
|
67
|
+
if (isAborted()) break;
|
|
68
|
+
switch (message.type) {
|
|
69
|
+
case "system":
|
|
70
|
+
if (message.subtype === "init") {
|
|
71
|
+
const systemMessage = message;
|
|
72
|
+
if (systemMessage.thread_id) {
|
|
73
|
+
capturedThreadId = systemMessage.thread_id;
|
|
74
|
+
}
|
|
75
|
+
yield { type: "status", content: "Session started..." };
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
case "assistant": {
|
|
79
|
+
const messageContent = message.message?.content;
|
|
80
|
+
if (messageContent && Array.isArray(messageContent)) {
|
|
81
|
+
const toolUse = messageContent.find(
|
|
82
|
+
(item) => item.type === "tool_use"
|
|
83
|
+
);
|
|
84
|
+
if (toolUse && "name" in toolUse) {
|
|
85
|
+
yield { type: "status", content: `Using ${toolUse.name}...` };
|
|
86
|
+
} else {
|
|
87
|
+
const textContent = extractTextFromContent(messageContent);
|
|
88
|
+
if (textContent && !isAborted()) {
|
|
89
|
+
yield { type: "status", content: textContent };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case "result":
|
|
96
|
+
if (message.is_error) {
|
|
97
|
+
yield { type: "error", content: message.error || "Unknown error" };
|
|
98
|
+
} else {
|
|
99
|
+
yield { type: "status", content: COMPLETED_STATUS };
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (sessionId && capturedThreadId && !isAborted()) {
|
|
105
|
+
threadMap.set(sessionId, { threadId: capturedThreadId });
|
|
106
|
+
}
|
|
107
|
+
if (capturedThreadId) {
|
|
108
|
+
lastThreadId = capturedThreadId;
|
|
109
|
+
}
|
|
110
|
+
if (!isAborted()) {
|
|
111
|
+
yield { type: "done", content: "" };
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (!isAborted()) {
|
|
115
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
116
|
+
yield { type: "error", content: errorMessage };
|
|
117
|
+
yield { type: "done", content: "" };
|
|
118
|
+
}
|
|
119
|
+
} finally {
|
|
120
|
+
if (sessionId) {
|
|
121
|
+
abortControllers.delete(sessionId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
38
125
|
var createServer = () => {
|
|
39
126
|
const honoApplication = new hono.Hono();
|
|
40
127
|
honoApplication.use("*", cors.cors());
|
|
@@ -47,99 +134,17 @@ var createServer = () => {
|
|
|
47
134
|
|
|
48
135
|
${content}`;
|
|
49
136
|
return streaming.streamSSE(context, async (stream) => {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
await stream.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
57
|
-
const executeOptions = {
|
|
58
|
-
dangerouslyAllowAll: true
|
|
59
|
-
};
|
|
60
|
-
executeOptions.cwd = options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd();
|
|
61
|
-
if (isFollowUp && existingThread) {
|
|
62
|
-
executeOptions.continue = existingThread.threadId;
|
|
63
|
-
}
|
|
64
|
-
let capturedThreadId;
|
|
65
|
-
for await (const message of ampSdk.execute({
|
|
66
|
-
prompt: userPrompt,
|
|
67
|
-
options: executeOptions,
|
|
68
|
-
signal: abortController.signal
|
|
69
|
-
})) {
|
|
70
|
-
if (isAborted()) break;
|
|
71
|
-
switch (message.type) {
|
|
72
|
-
case "system":
|
|
73
|
-
if (message.subtype === "init") {
|
|
74
|
-
const systemMessage = message;
|
|
75
|
-
if (systemMessage.thread_id) {
|
|
76
|
-
capturedThreadId = systemMessage.thread_id;
|
|
77
|
-
}
|
|
78
|
-
await stream.writeSSE({
|
|
79
|
-
data: "Session started...",
|
|
80
|
-
event: "status"
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
break;
|
|
84
|
-
case "assistant": {
|
|
85
|
-
const messageContent = message.message?.content;
|
|
86
|
-
if (messageContent && Array.isArray(messageContent)) {
|
|
87
|
-
const toolUse = messageContent.find(
|
|
88
|
-
(item) => item.type === "tool_use"
|
|
89
|
-
);
|
|
90
|
-
if (toolUse && "name" in toolUse) {
|
|
91
|
-
await stream.writeSSE({
|
|
92
|
-
data: `Using ${toolUse.name}...`,
|
|
93
|
-
event: "status"
|
|
94
|
-
});
|
|
95
|
-
} else {
|
|
96
|
-
const textContent = extractTextFromContent(messageContent);
|
|
97
|
-
if (textContent && !isAborted()) {
|
|
98
|
-
await stream.writeSSE({
|
|
99
|
-
data: textContent,
|
|
100
|
-
event: "status"
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
case "result":
|
|
108
|
-
if (message.is_error) {
|
|
109
|
-
await stream.writeSSE({
|
|
110
|
-
data: `Error: ${message.error || "Unknown error"}`,
|
|
111
|
-
event: "error"
|
|
112
|
-
});
|
|
113
|
-
} else {
|
|
114
|
-
await stream.writeSSE({
|
|
115
|
-
data: COMPLETED_STATUS,
|
|
116
|
-
event: "status"
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (sessionId && capturedThreadId && !isAborted()) {
|
|
123
|
-
threadMap.set(sessionId, { threadId: capturedThreadId });
|
|
124
|
-
}
|
|
125
|
-
if (capturedThreadId) {
|
|
126
|
-
lastThreadId = capturedThreadId;
|
|
127
|
-
}
|
|
128
|
-
if (!isAborted()) {
|
|
129
|
-
await stream.writeSSE({ data: "", event: "done" });
|
|
130
|
-
}
|
|
131
|
-
} catch (error) {
|
|
132
|
-
if (!isAborted()) {
|
|
133
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
137
|
+
for await (const message of runAgent(userPrompt, {
|
|
138
|
+
...options,
|
|
139
|
+
sessionId
|
|
140
|
+
})) {
|
|
141
|
+
if (message.type === "error") {
|
|
134
142
|
await stream.writeSSE({
|
|
135
|
-
data: `Error: ${
|
|
143
|
+
data: `Error: ${message.content}`,
|
|
136
144
|
event: "error"
|
|
137
145
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
} finally {
|
|
141
|
-
if (sessionId) {
|
|
142
|
-
abortControllers.delete(sessionId);
|
|
146
|
+
} else {
|
|
147
|
+
await stream.writeSSE({ data: message.content, event: message.type });
|
|
143
148
|
}
|
|
144
149
|
}
|
|
145
150
|
});
|
|
@@ -194,4 +199,5 @@ if ((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filen
|
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
exports.createServer = createServer;
|
|
202
|
+
exports.runAgent = runAgent;
|
|
197
203
|
exports.startServer = startServer;
|
package/dist/server.d.cts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
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 AmpAgentOptions {
|
|
5
|
-
cwd?: string;
|
|
5
|
+
interface AmpAgentOptions extends AgentCoreOptions {
|
|
6
6
|
}
|
|
7
|
+
declare const runAgent: (prompt: string, options?: AmpAgentOptions) => AsyncGenerator<AgentMessage>;
|
|
7
8
|
declare const createServer: () => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
8
9
|
declare const startServer: (port?: number) => Promise<void>;
|
|
9
10
|
|
|
10
|
-
export { type AmpAgentOptions, createServer, startServer };
|
|
11
|
+
export { type AmpAgentOptions, createServer, runAgent, startServer };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
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 AmpAgentOptions {
|
|
5
|
-
cwd?: string;
|
|
5
|
+
interface AmpAgentOptions extends AgentCoreOptions {
|
|
6
6
|
}
|
|
7
|
+
declare const runAgent: (prompt: string, options?: AmpAgentOptions) => AsyncGenerator<AgentMessage>;
|
|
7
8
|
declare const createServer: () => Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
8
9
|
declare const startServer: (port?: number) => Promise<void>;
|
|
9
10
|
|
|
10
|
-
export { type AmpAgentOptions, createServer, startServer };
|
|
11
|
+
export { type AmpAgentOptions, 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=amp&t=${Date.now()}`
|
|
22
|
+
).catch(() => {
|
|
21
23
|
});
|
|
22
24
|
} catch {
|
|
23
25
|
}
|
|
@@ -27,6 +29,91 @@ var lastThreadId;
|
|
|
27
29
|
var extractTextFromContent = (content) => {
|
|
28
30
|
return content.filter((item) => item.type === "text" && item.text).map((item) => item.text).join(" ").trim();
|
|
29
31
|
};
|
|
32
|
+
var runAgent = async function* (prompt, options) {
|
|
33
|
+
const sessionId = options?.sessionId;
|
|
34
|
+
const abortController = new AbortController();
|
|
35
|
+
if (sessionId) {
|
|
36
|
+
abortControllers.set(sessionId, abortController);
|
|
37
|
+
}
|
|
38
|
+
const isAborted = () => {
|
|
39
|
+
if (options?.signal?.aborted) return true;
|
|
40
|
+
if (abortController.signal.aborted) return true;
|
|
41
|
+
return false;
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
yield { type: "status", content: "Thinking\u2026" };
|
|
45
|
+
const executeOptions = {
|
|
46
|
+
dangerouslyAllowAll: true
|
|
47
|
+
};
|
|
48
|
+
executeOptions.cwd = options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd();
|
|
49
|
+
const existingThread = sessionId ? threadMap.get(sessionId) : void 0;
|
|
50
|
+
if (existingThread) {
|
|
51
|
+
executeOptions.continue = existingThread.threadId;
|
|
52
|
+
}
|
|
53
|
+
let capturedThreadId;
|
|
54
|
+
for await (const message of execute({
|
|
55
|
+
prompt,
|
|
56
|
+
options: executeOptions,
|
|
57
|
+
signal: abortController.signal
|
|
58
|
+
})) {
|
|
59
|
+
if (isAborted()) break;
|
|
60
|
+
switch (message.type) {
|
|
61
|
+
case "system":
|
|
62
|
+
if (message.subtype === "init") {
|
|
63
|
+
const systemMessage = message;
|
|
64
|
+
if (systemMessage.thread_id) {
|
|
65
|
+
capturedThreadId = systemMessage.thread_id;
|
|
66
|
+
}
|
|
67
|
+
yield { type: "status", content: "Session started..." };
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case "assistant": {
|
|
71
|
+
const messageContent = message.message?.content;
|
|
72
|
+
if (messageContent && Array.isArray(messageContent)) {
|
|
73
|
+
const toolUse = messageContent.find(
|
|
74
|
+
(item) => item.type === "tool_use"
|
|
75
|
+
);
|
|
76
|
+
if (toolUse && "name" in toolUse) {
|
|
77
|
+
yield { type: "status", content: `Using ${toolUse.name}...` };
|
|
78
|
+
} else {
|
|
79
|
+
const textContent = extractTextFromContent(messageContent);
|
|
80
|
+
if (textContent && !isAborted()) {
|
|
81
|
+
yield { type: "status", content: textContent };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case "result":
|
|
88
|
+
if (message.is_error) {
|
|
89
|
+
yield { type: "error", content: message.error || "Unknown error" };
|
|
90
|
+
} else {
|
|
91
|
+
yield { type: "status", content: COMPLETED_STATUS };
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (sessionId && capturedThreadId && !isAborted()) {
|
|
97
|
+
threadMap.set(sessionId, { threadId: capturedThreadId });
|
|
98
|
+
}
|
|
99
|
+
if (capturedThreadId) {
|
|
100
|
+
lastThreadId = capturedThreadId;
|
|
101
|
+
}
|
|
102
|
+
if (!isAborted()) {
|
|
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
|
+
};
|
|
30
117
|
var createServer = () => {
|
|
31
118
|
const honoApplication = new Hono();
|
|
32
119
|
honoApplication.use("*", cors());
|
|
@@ -39,99 +126,17 @@ var createServer = () => {
|
|
|
39
126
|
|
|
40
127
|
${content}`;
|
|
41
128
|
return streamSSE(context, async (stream) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
await stream.writeSSE({ data: "Thinking\u2026", event: "status" });
|
|
49
|
-
const executeOptions = {
|
|
50
|
-
dangerouslyAllowAll: true
|
|
51
|
-
};
|
|
52
|
-
executeOptions.cwd = options?.cwd ?? process.env.REACT_GRAB_CWD ?? process.cwd();
|
|
53
|
-
if (isFollowUp && existingThread) {
|
|
54
|
-
executeOptions.continue = existingThread.threadId;
|
|
55
|
-
}
|
|
56
|
-
let capturedThreadId;
|
|
57
|
-
for await (const message of execute({
|
|
58
|
-
prompt: userPrompt,
|
|
59
|
-
options: executeOptions,
|
|
60
|
-
signal: abortController.signal
|
|
61
|
-
})) {
|
|
62
|
-
if (isAborted()) break;
|
|
63
|
-
switch (message.type) {
|
|
64
|
-
case "system":
|
|
65
|
-
if (message.subtype === "init") {
|
|
66
|
-
const systemMessage = message;
|
|
67
|
-
if (systemMessage.thread_id) {
|
|
68
|
-
capturedThreadId = systemMessage.thread_id;
|
|
69
|
-
}
|
|
70
|
-
await stream.writeSSE({
|
|
71
|
-
data: "Session started...",
|
|
72
|
-
event: "status"
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
break;
|
|
76
|
-
case "assistant": {
|
|
77
|
-
const messageContent = message.message?.content;
|
|
78
|
-
if (messageContent && Array.isArray(messageContent)) {
|
|
79
|
-
const toolUse = messageContent.find(
|
|
80
|
-
(item) => item.type === "tool_use"
|
|
81
|
-
);
|
|
82
|
-
if (toolUse && "name" in toolUse) {
|
|
83
|
-
await stream.writeSSE({
|
|
84
|
-
data: `Using ${toolUse.name}...`,
|
|
85
|
-
event: "status"
|
|
86
|
-
});
|
|
87
|
-
} else {
|
|
88
|
-
const textContent = extractTextFromContent(messageContent);
|
|
89
|
-
if (textContent && !isAborted()) {
|
|
90
|
-
await stream.writeSSE({
|
|
91
|
-
data: textContent,
|
|
92
|
-
event: "status"
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case "result":
|
|
100
|
-
if (message.is_error) {
|
|
101
|
-
await stream.writeSSE({
|
|
102
|
-
data: `Error: ${message.error || "Unknown error"}`,
|
|
103
|
-
event: "error"
|
|
104
|
-
});
|
|
105
|
-
} else {
|
|
106
|
-
await stream.writeSSE({
|
|
107
|
-
data: COMPLETED_STATUS,
|
|
108
|
-
event: "status"
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (sessionId && capturedThreadId && !isAborted()) {
|
|
115
|
-
threadMap.set(sessionId, { threadId: capturedThreadId });
|
|
116
|
-
}
|
|
117
|
-
if (capturedThreadId) {
|
|
118
|
-
lastThreadId = capturedThreadId;
|
|
119
|
-
}
|
|
120
|
-
if (!isAborted()) {
|
|
121
|
-
await stream.writeSSE({ data: "", event: "done" });
|
|
122
|
-
}
|
|
123
|
-
} catch (error) {
|
|
124
|
-
if (!isAborted()) {
|
|
125
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
129
|
+
for await (const message of runAgent(userPrompt, {
|
|
130
|
+
...options,
|
|
131
|
+
sessionId
|
|
132
|
+
})) {
|
|
133
|
+
if (message.type === "error") {
|
|
126
134
|
await stream.writeSSE({
|
|
127
|
-
data: `Error: ${
|
|
135
|
+
data: `Error: ${message.content}`,
|
|
128
136
|
event: "error"
|
|
129
137
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
} finally {
|
|
133
|
-
if (sessionId) {
|
|
134
|
-
abortControllers.delete(sessionId);
|
|
138
|
+
} else {
|
|
139
|
+
await stream.writeSSE({ data: message.content, event: message.type });
|
|
135
140
|
}
|
|
136
141
|
}
|
|
137
142
|
});
|
|
@@ -185,4 +190,4 @@ if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
|
185
190
|
startServer(DEFAULT_PORT).catch(console.error);
|
|
186
191
|
}
|
|
187
192
|
|
|
188
|
-
export { createServer, startServer };
|
|
193
|
+
export { createServer, runAgent, startServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-grab/amp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.89",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"react-grab-amp": "./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",
|