@react-grab/opencode 0.1.0-beta.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -5787,8 +5787,13 @@ ${context.content.join("\n\n")}`;
5787
5787
  signal
5788
5788
  })) {
5789
5789
  if (signal.aborted) break;
5790
+ const getBrowserMessageType = (messageType) => {
5791
+ if (messageType === "status") return "agent-status";
5792
+ if (messageType === "error") return "agent-error";
5793
+ return "agent-done";
5794
+ };
5790
5795
  sendToBrowser(browserSocket, {
5791
- type: message.type === "status" ? "agent-status" : message.type === "error" ? "agent-error" : "agent-done",
5796
+ type: getBrowserMessageType(message.type),
5792
5797
  agentId: handler.agentId,
5793
5798
  sessionId,
5794
5799
  content: message.content
@@ -5991,9 +5996,13 @@ ${context.content.join("\n\n")}`;
5991
5996
  } else if (message.type === "agent-status" || message.type === "agent-done" || message.type === "agent-error") {
5992
5997
  const messageQueue = sessionMessageQueues.get(message.sessionId);
5993
5998
  if (messageQueue) {
5994
- const mappedType = message.type === "agent-status" ? "status" : message.type === "agent-done" ? "done" : "error";
5999
+ const getQueueMessageType = (handlerMessageType) => {
6000
+ if (handlerMessageType === "agent-status") return "status";
6001
+ if (handlerMessageType === "agent-done") return "done";
6002
+ return "error";
6003
+ };
5995
6004
  messageQueue.push({
5996
- type: mappedType,
6005
+ type: getQueueMessageType(message.type),
5997
6006
  content: message.content ?? ""
5998
6007
  });
5999
6008
  if (message.type === "agent-done" || message.type === "agent-error") {
@@ -6016,7 +6025,12 @@ ${context.content.join("\n\n")}`;
6016
6025
  }
6017
6026
  }
6018
6027
  res.writeHead(200, { "Content-Type": "application/json" });
6019
- res.end(JSON.stringify({ status: "ok", handlers: getRegisteredHandlerIds() }));
6028
+ res.end(
6029
+ JSON.stringify({
6030
+ status: "ok",
6031
+ handlers: getRegisteredHandlerIds()
6032
+ })
6033
+ );
6020
6034
  return;
6021
6035
  }
6022
6036
  res.writeHead(404);
@@ -6031,7 +6045,10 @@ ${context.content.join("\n\n")}`;
6031
6045
  });
6032
6046
  webSocketServer.on("connection", (socket, request) => {
6033
6047
  if (token) {
6034
- const connectionUrl = new URL(request.url ?? "", `http://localhost:${port}`);
6048
+ const connectionUrl = new URL(
6049
+ request.url ?? "",
6050
+ `http://localhost:${port}`
6051
+ );
6035
6052
  const clientToken = connectionUrl.searchParams.get(RELAY_TOKEN_PARAM);
6036
6053
  if (clientToken !== token) {
6037
6054
  socket.close(4001, "Unauthorized");
@@ -6072,7 +6089,9 @@ ${context.content.join("\n\n")}`;
6072
6089
  });
6073
6090
  socket.on("message", (data) => {
6074
6091
  try {
6075
- const message = JSON.parse(data.toString());
6092
+ const message = JSON.parse(
6093
+ data.toString()
6094
+ );
6076
6095
  handleBrowserMessage(socket, message);
6077
6096
  } catch {
6078
6097
  }
@@ -6117,7 +6136,10 @@ ${context.content.join("\n\n")}`;
6117
6136
  httpServer?.close();
6118
6137
  };
6119
6138
  const registerHandler = (handler) => {
6120
- registeredHandlers.set(handler.agentId, { agentId: handler.agentId, handler });
6139
+ registeredHandlers.set(handler.agentId, {
6140
+ agentId: handler.agentId,
6141
+ handler
6142
+ });
6121
6143
  broadcastHandlerList();
6122
6144
  };
6123
6145
  const unregisterHandler = (agentId) => {
@@ -8678,7 +8700,7 @@ async function fkill(inputs, options = {}) {
8678
8700
  }
8679
8701
  }
8680
8702
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8681
- var VERSION = "0.1.0-beta.7";
8703
+ var VERSION = "0.1.0";
8682
8704
  var checkIfRelayServerIsRunning = async (port, token) => {
8683
8705
  try {
8684
8706
  const healthUrl = token ? `http://localhost:${port}/health?${RELAY_TOKEN_PARAM}=${encodeURIComponent(token)}` : `http://localhost:${port}/health`;
@@ -8696,7 +8718,10 @@ var connectRelay = async (options) => {
8696
8718
  const { handler, token } = options;
8697
8719
  let relayServer = null;
8698
8720
  let isRelayHost = false;
8699
- const isRelayServerRunning = await checkIfRelayServerIsRunning(relayPort, token);
8721
+ const isRelayServerRunning = await checkIfRelayServerIsRunning(
8722
+ relayPort,
8723
+ token
8724
+ );
8700
8725
  if (isRelayServerRunning) {
8701
8726
  relayServer = await connectToExistingRelay(relayPort, handler, token);
8702
8727
  } else {
package/dist/cli.js CHANGED
@@ -5777,8 +5777,13 @@ ${context.content.join("\n\n")}`;
5777
5777
  signal
5778
5778
  })) {
5779
5779
  if (signal.aborted) break;
5780
+ const getBrowserMessageType = (messageType) => {
5781
+ if (messageType === "status") return "agent-status";
5782
+ if (messageType === "error") return "agent-error";
5783
+ return "agent-done";
5784
+ };
5780
5785
  sendToBrowser(browserSocket, {
5781
- type: message.type === "status" ? "agent-status" : message.type === "error" ? "agent-error" : "agent-done",
5786
+ type: getBrowserMessageType(message.type),
5782
5787
  agentId: handler.agentId,
5783
5788
  sessionId,
5784
5789
  content: message.content
@@ -5981,9 +5986,13 @@ ${context.content.join("\n\n")}`;
5981
5986
  } else if (message.type === "agent-status" || message.type === "agent-done" || message.type === "agent-error") {
5982
5987
  const messageQueue = sessionMessageQueues.get(message.sessionId);
5983
5988
  if (messageQueue) {
5984
- const mappedType = message.type === "agent-status" ? "status" : message.type === "agent-done" ? "done" : "error";
5989
+ const getQueueMessageType = (handlerMessageType) => {
5990
+ if (handlerMessageType === "agent-status") return "status";
5991
+ if (handlerMessageType === "agent-done") return "done";
5992
+ return "error";
5993
+ };
5985
5994
  messageQueue.push({
5986
- type: mappedType,
5995
+ type: getQueueMessageType(message.type),
5987
5996
  content: message.content ?? ""
5988
5997
  });
5989
5998
  if (message.type === "agent-done" || message.type === "agent-error") {
@@ -6006,7 +6015,12 @@ ${context.content.join("\n\n")}`;
6006
6015
  }
6007
6016
  }
6008
6017
  res.writeHead(200, { "Content-Type": "application/json" });
6009
- res.end(JSON.stringify({ status: "ok", handlers: getRegisteredHandlerIds() }));
6018
+ res.end(
6019
+ JSON.stringify({
6020
+ status: "ok",
6021
+ handlers: getRegisteredHandlerIds()
6022
+ })
6023
+ );
6010
6024
  return;
6011
6025
  }
6012
6026
  res.writeHead(404);
@@ -6021,7 +6035,10 @@ ${context.content.join("\n\n")}`;
6021
6035
  });
6022
6036
  webSocketServer.on("connection", (socket, request) => {
6023
6037
  if (token) {
6024
- const connectionUrl = new URL(request.url ?? "", `http://localhost:${port}`);
6038
+ const connectionUrl = new URL(
6039
+ request.url ?? "",
6040
+ `http://localhost:${port}`
6041
+ );
6025
6042
  const clientToken = connectionUrl.searchParams.get(RELAY_TOKEN_PARAM);
6026
6043
  if (clientToken !== token) {
6027
6044
  socket.close(4001, "Unauthorized");
@@ -6062,7 +6079,9 @@ ${context.content.join("\n\n")}`;
6062
6079
  });
6063
6080
  socket.on("message", (data) => {
6064
6081
  try {
6065
- const message = JSON.parse(data.toString());
6082
+ const message = JSON.parse(
6083
+ data.toString()
6084
+ );
6066
6085
  handleBrowserMessage(socket, message);
6067
6086
  } catch {
6068
6087
  }
@@ -6107,7 +6126,10 @@ ${context.content.join("\n\n")}`;
6107
6126
  httpServer?.close();
6108
6127
  };
6109
6128
  const registerHandler = (handler) => {
6110
- registeredHandlers.set(handler.agentId, { agentId: handler.agentId, handler });
6129
+ registeredHandlers.set(handler.agentId, {
6130
+ agentId: handler.agentId,
6131
+ handler
6132
+ });
6111
6133
  broadcastHandlerList();
6112
6134
  };
6113
6135
  const unregisterHandler = (agentId) => {
@@ -8668,7 +8690,7 @@ async function fkill(inputs, options = {}) {
8668
8690
  }
8669
8691
  }
8670
8692
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8671
- var VERSION = "0.1.0-beta.7";
8693
+ var VERSION = "0.1.0";
8672
8694
  var checkIfRelayServerIsRunning = async (port, token) => {
8673
8695
  try {
8674
8696
  const healthUrl = token ? `http://localhost:${port}/health?${RELAY_TOKEN_PARAM}=${encodeURIComponent(token)}` : `http://localhost:${port}/health`;
@@ -8686,7 +8708,10 @@ var connectRelay = async (options) => {
8686
8708
  const { handler, token } = options;
8687
8709
  let relayServer = null;
8688
8710
  let isRelayHost = false;
8689
- const isRelayServerRunning = await checkIfRelayServerIsRunning(relayPort, token);
8711
+ const isRelayServerRunning = await checkIfRelayServerIsRunning(
8712
+ relayPort,
8713
+ token
8714
+ );
8690
8715
  if (isRelayServerRunning) {
8691
8716
  relayServer = await connectToExistingRelay(relayPort, handler, token);
8692
8717
  } else {
package/dist/client.cjs CHANGED
@@ -210,7 +210,7 @@ var createRelayAgentProvider = (options) => {
210
210
  rejectNextMessage = null;
211
211
  }
212
212
  };
213
- signal.addEventListener("abort", handleAbort);
213
+ signal.addEventListener("abort", handleAbort, { once: true });
214
214
  const handleConnectionChange = (connected) => {
215
215
  if (!connected && !isDone) {
216
216
  errorMessage = "Relay connection lost";
@@ -260,7 +260,10 @@ var createRelayAgentProvider = (options) => {
260
260
  signal.removeEventListener("abort", handleAbort);
261
261
  throw new Error("Relay connection is not open");
262
262
  }
263
- const didSendRequest = relayClient.sendAgentRequest(agentId, contextWithSession);
263
+ const didSendRequest = relayClient.sendAgentRequest(
264
+ agentId,
265
+ contextWithSession
266
+ );
264
267
  if (!didSendRequest) {
265
268
  unsubscribeConnection();
266
269
  unsubscribeMessage();
@@ -318,12 +321,16 @@ var createRelayAgentProvider = (options) => {
318
321
  reject(new Error(message.content ?? "Operation failed"));
319
322
  }
320
323
  });
321
- const unsubscribeConnection = relayClient.onConnectionChange((connected) => {
322
- if (!connected) {
323
- cleanup();
324
- reject(new Error("Connection lost while waiting for operation response"));
324
+ const unsubscribeConnection = relayClient.onConnectionChange(
325
+ (connected) => {
326
+ if (!connected) {
327
+ cleanup();
328
+ reject(
329
+ new Error("Connection lost while waiting for operation response")
330
+ );
331
+ }
325
332
  }
326
- });
333
+ );
327
334
  });
328
335
  };
329
336
  const undo = async () => {
@@ -1,2 +1,449 @@
1
- var ReactGrabOpenCode=(function(exports){'use strict';var q=4722,x=3e3,D="token",G=(i={})=>{let n=i.serverUrl??`ws://localhost:${q}`,c=i.autoReconnect??true,E=i.reconnectIntervalMs??x,h=i.token,s=null,f=false,p=[],A=null,t=null,o=null,w=false,y=new Set,g=new Set,r=new Set,d=()=>{!c||A||w||(A=setTimeout(()=>{A=null,C().catch(()=>{});},E));},l=e=>{try{let a=JSON.parse(e.data);if(a.type==="handlers"&&a.handlers){p=a.handlers;for(let S of g)S(p);}for(let S of y)S(a);}catch{}},C=()=>s?.readyState===WebSocket.OPEN?Promise.resolve():t||(w=false,t=new Promise((e,a)=>{o=a;let S=h?`${n}?${D}=${encodeURIComponent(h)}`:n;s=new WebSocket(S),s.onopen=()=>{t=null,o=null,f=true;for(let P of r)P(true);e();},s.onmessage=l,s.onclose=()=>{o&&(o(new Error("WebSocket connection closed")),o=null),t=null,f=false,p=[];for(let P of g)P(p);for(let P of r)P(false);d();},s.onerror=()=>{t=null,o=null,f=false,a(new Error("WebSocket connection failed"));};}),t),v=()=>{w=true,A&&(clearTimeout(A),A=null),o&&(o(new Error("Connection aborted")),o=null),t=null,s?.close(),s=null,f=false,p=[];},m=()=>f,R=e=>s?.readyState===WebSocket.OPEN?(s.send(JSON.stringify(e)),true):false;return {connect:C,disconnect:v,isConnected:m,sendAgentRequest:(e,a)=>R({type:"agent-request",agentId:e,sessionId:a.sessionId,context:a}),abortAgent:(e,a)=>{R({type:"agent-abort",agentId:e,sessionId:a});},undoAgent:(e,a)=>R({type:"agent-undo",agentId:e,sessionId:a}),redoAgent:(e,a)=>R({type:"agent-redo",agentId:e,sessionId:a}),onMessage:e=>(y.add(e),()=>y.delete(e)),onHandlersChange:e=>(g.add(e),()=>g.delete(e)),onConnectionChange:e=>(r.add(e),queueMicrotask(()=>{r.has(e)&&e(f);}),()=>r.delete(e)),getAvailableHandlers:()=>p}},I=i=>{let{relayClient:n,agentId:c}=i,E=async()=>{if(!n.isConnected())try{await n.connect();}catch{return false}return n.getAvailableHandlers().includes(c)},h=async function*(t,o){if(o.aborted)throw new DOMException("Aborted","AbortError");yield "Connecting\u2026";let w=t.sessionId??`session-${Date.now()}-${Math.random().toString(36).slice(2)}`,y={...t,sessionId:w},g=[],r=null,d=null,l=false,C=null,v=()=>{n.abortAgent(c,w),l=true,r&&(r({value:void 0,done:true}),r=null,d=null);};o.addEventListener("abort",v);let m=u=>{!u&&!l&&(C="Relay connection lost",l=true,d&&(d(new Error(C)),r=null,d=null));},R=n.onConnectionChange(m),M=n.onMessage(u=>{if(u.sessionId===w)if(u.type==="agent-status"&&u.content){if(g.push(u.content),r){let b=g.shift();b!==void 0&&(r({value:b,done:false}),r=null,d=null);}}else u.type==="agent-done"?(l=true,r&&(r({value:void 0,done:true}),r=null,d=null)):u.type==="agent-error"&&(C=u.content??"Unknown error",l=true,d&&(d(new Error(C)),r=null,d=null));});if(!n.isConnected())throw R(),M(),o.removeEventListener("abort",v),new Error("Relay connection is not open");if(!n.sendAgentRequest(c,y))throw R(),M(),o.removeEventListener("abort",v),new Error("Failed to send agent request: connection not open");try{for(;;){if(g.length>0){let b=g.shift();b!==void 0&&(yield b);continue}if(l||o.aborted)break;let u=await new Promise((b,O)=>{r=b,d=O;});if(u.done)break;yield u.value;}if(C)throw new Error(C)}finally{o.removeEventListener("abort",v),R(),M();}},s=async t=>{n.abortAgent(c,t);},f=t=>new Promise((o,w)=>{let y=false,g=()=>{y||(y=true,r(),d());},r=n.onMessage(l=>{l.sessionId===t&&(g(),l.type==="agent-done"?o():l.type==="agent-error"&&w(new Error(l.content??"Operation failed")));}),d=n.onConnectionChange(l=>{l||(g(),w(new Error("Connection lost while waiting for operation response")));});});return {send:h,abort:s,undo:async()=>{let t=`undo-${c}-${Date.now()}-${Math.random().toString(36).slice(2)}`;if(!n.undoAgent(c,t))throw new Error("Failed to send undo request: connection not open");return f(t)},redo:async()=>{let t=`redo-${c}-${Date.now()}-${Math.random().toString(36).slice(2)}`;if(!n.redoAgent(c,t))throw new Error("Failed to send redo request: connection not open");return f(t)},checkConnection:E,supportsResume:true,supportsFollowUp:true}},_=null,k=()=>typeof window>"u"?null:window.__REACT_GRAB_RELAY__?(_=window.__REACT_GRAB_RELAY__,_):(_||(_=G(),window.__REACT_GRAB_RELAY__=_),_);var L="opencode",T=i=>typeof i=="object"&&i!==null&&"registerPlugin"in i,Y=(i={})=>{let n=i.relayClient??k();if(!n)throw new Error("RelayClient is required in browser environments");return I({relayClient:n,agentId:L})},N=async()=>{if(typeof window>"u")return;let i=k();if(!i)return;try{await i.connect();}catch{return}let n=I({relayClient:i,agentId:L}),c=s=>{let f={provider:n,storage:sessionStorage},p={name:"opencode-agent",actions:[{id:"edit-with-opencode",label:"Edit with OpenCode",shortcut:"Enter",onAction:A=>{A.enterPromptMode?.(f);},agent:f}]};s.registerPlugin(p);},E=window.__REACT_GRAB__;if(T(E)){c(E);return}window.addEventListener("react-grab:init",s=>{s instanceof CustomEvent&&T(s.detail)&&c(s.detail);},{once:true});let h=window.__REACT_GRAB__;T(h)&&c(h);};N();
2
- exports.attachAgent=N;exports.createOpenCodeAgentProvider=Y;return exports;})({});
1
+ var ReactGrabOpenCode = (function (exports) {
2
+ 'use strict';
3
+
4
+ // ../relay/dist/client.js
5
+ var DEFAULT_RELAY_PORT = 4722;
6
+ var DEFAULT_RECONNECT_INTERVAL_MS = 3e3;
7
+ var RELAY_TOKEN_PARAM = "token";
8
+ var createRelayClient = (options = {}) => {
9
+ const serverUrl = options.serverUrl ?? `ws://localhost:${DEFAULT_RELAY_PORT}`;
10
+ const autoReconnect = options.autoReconnect ?? true;
11
+ const reconnectIntervalMs = options.reconnectIntervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;
12
+ const token = options.token;
13
+ let webSocketConnection = null;
14
+ let isConnectedState = false;
15
+ let availableHandlers = [];
16
+ let reconnectTimeoutId = null;
17
+ let pendingConnectionPromise = null;
18
+ let pendingConnectionReject = null;
19
+ let isIntentionalDisconnect = false;
20
+ const messageCallbacks = /* @__PURE__ */ new Set();
21
+ const handlersChangeCallbacks = /* @__PURE__ */ new Set();
22
+ const connectionChangeCallbacks = /* @__PURE__ */ new Set();
23
+ const scheduleReconnect = () => {
24
+ if (!autoReconnect || reconnectTimeoutId || isIntentionalDisconnect) return;
25
+ reconnectTimeoutId = setTimeout(() => {
26
+ reconnectTimeoutId = null;
27
+ connect().catch(() => {
28
+ });
29
+ }, reconnectIntervalMs);
30
+ };
31
+ const handleMessage = (event) => {
32
+ try {
33
+ const message = JSON.parse(event.data);
34
+ if (message.type === "handlers" && message.handlers) {
35
+ availableHandlers = message.handlers;
36
+ for (const callback of handlersChangeCallbacks) {
37
+ callback(availableHandlers);
38
+ }
39
+ }
40
+ for (const callback of messageCallbacks) {
41
+ callback(message);
42
+ }
43
+ } catch {
44
+ }
45
+ };
46
+ const connect = () => {
47
+ if (webSocketConnection?.readyState === WebSocket.OPEN) {
48
+ return Promise.resolve();
49
+ }
50
+ if (pendingConnectionPromise) {
51
+ return pendingConnectionPromise;
52
+ }
53
+ isIntentionalDisconnect = false;
54
+ pendingConnectionPromise = new Promise((resolve, reject) => {
55
+ pendingConnectionReject = reject;
56
+ const connectionUrl = token ? `${serverUrl}?${RELAY_TOKEN_PARAM}=${encodeURIComponent(token)}` : serverUrl;
57
+ webSocketConnection = new WebSocket(connectionUrl);
58
+ webSocketConnection.onopen = () => {
59
+ pendingConnectionPromise = null;
60
+ pendingConnectionReject = null;
61
+ isConnectedState = true;
62
+ for (const callback of connectionChangeCallbacks) {
63
+ callback(true);
64
+ }
65
+ resolve();
66
+ };
67
+ webSocketConnection.onmessage = handleMessage;
68
+ webSocketConnection.onclose = () => {
69
+ if (pendingConnectionReject) {
70
+ pendingConnectionReject(new Error("WebSocket connection closed"));
71
+ pendingConnectionReject = null;
72
+ }
73
+ pendingConnectionPromise = null;
74
+ isConnectedState = false;
75
+ availableHandlers = [];
76
+ for (const callback of handlersChangeCallbacks) {
77
+ callback(availableHandlers);
78
+ }
79
+ for (const callback of connectionChangeCallbacks) {
80
+ callback(false);
81
+ }
82
+ scheduleReconnect();
83
+ };
84
+ webSocketConnection.onerror = () => {
85
+ pendingConnectionPromise = null;
86
+ pendingConnectionReject = null;
87
+ isConnectedState = false;
88
+ reject(new Error("WebSocket connection failed"));
89
+ };
90
+ });
91
+ return pendingConnectionPromise;
92
+ };
93
+ const disconnect = () => {
94
+ isIntentionalDisconnect = true;
95
+ if (reconnectTimeoutId) {
96
+ clearTimeout(reconnectTimeoutId);
97
+ reconnectTimeoutId = null;
98
+ }
99
+ if (pendingConnectionReject) {
100
+ pendingConnectionReject(new Error("Connection aborted"));
101
+ pendingConnectionReject = null;
102
+ }
103
+ pendingConnectionPromise = null;
104
+ webSocketConnection?.close();
105
+ webSocketConnection = null;
106
+ isConnectedState = false;
107
+ availableHandlers = [];
108
+ };
109
+ const isConnected = () => isConnectedState;
110
+ const sendMessage = (message) => {
111
+ if (webSocketConnection?.readyState === WebSocket.OPEN) {
112
+ webSocketConnection.send(JSON.stringify(message));
113
+ return true;
114
+ }
115
+ return false;
116
+ };
117
+ const sendAgentRequest = (agentId, context) => {
118
+ return sendMessage({
119
+ type: "agent-request",
120
+ agentId,
121
+ sessionId: context.sessionId,
122
+ context
123
+ });
124
+ };
125
+ const abortAgent = (agentId, sessionId) => {
126
+ sendMessage({
127
+ type: "agent-abort",
128
+ agentId,
129
+ sessionId
130
+ });
131
+ };
132
+ const undoAgent = (agentId, sessionId) => {
133
+ return sendMessage({
134
+ type: "agent-undo",
135
+ agentId,
136
+ sessionId
137
+ });
138
+ };
139
+ const redoAgent = (agentId, sessionId) => {
140
+ return sendMessage({
141
+ type: "agent-redo",
142
+ agentId,
143
+ sessionId
144
+ });
145
+ };
146
+ const onMessage = (callback) => {
147
+ messageCallbacks.add(callback);
148
+ return () => messageCallbacks.delete(callback);
149
+ };
150
+ const onHandlersChange = (callback) => {
151
+ handlersChangeCallbacks.add(callback);
152
+ return () => handlersChangeCallbacks.delete(callback);
153
+ };
154
+ const onConnectionChange = (callback) => {
155
+ connectionChangeCallbacks.add(callback);
156
+ queueMicrotask(() => {
157
+ if (connectionChangeCallbacks.has(callback)) {
158
+ callback(isConnectedState);
159
+ }
160
+ });
161
+ return () => connectionChangeCallbacks.delete(callback);
162
+ };
163
+ const getAvailableHandlers = () => availableHandlers;
164
+ return {
165
+ connect,
166
+ disconnect,
167
+ isConnected,
168
+ sendAgentRequest,
169
+ abortAgent,
170
+ undoAgent,
171
+ redoAgent,
172
+ onMessage,
173
+ onHandlersChange,
174
+ onConnectionChange,
175
+ getAvailableHandlers
176
+ };
177
+ };
178
+ var createRelayAgentProvider = (options) => {
179
+ const { relayClient, agentId } = options;
180
+ const checkConnection = async () => {
181
+ if (!relayClient.isConnected()) {
182
+ try {
183
+ await relayClient.connect();
184
+ } catch {
185
+ return false;
186
+ }
187
+ }
188
+ return relayClient.getAvailableHandlers().includes(agentId);
189
+ };
190
+ const send = async function* (context, signal) {
191
+ if (signal.aborted) {
192
+ throw new DOMException("Aborted", "AbortError");
193
+ }
194
+ yield "Connecting\u2026";
195
+ const sessionId = context.sessionId ?? `session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
196
+ const contextWithSession = {
197
+ ...context,
198
+ sessionId
199
+ };
200
+ const messageQueue = [];
201
+ let resolveNextMessage = null;
202
+ let rejectNextMessage = null;
203
+ let isDone = false;
204
+ let errorMessage = null;
205
+ const handleAbort = () => {
206
+ relayClient.abortAgent(agentId, sessionId);
207
+ isDone = true;
208
+ if (resolveNextMessage) {
209
+ resolveNextMessage({ value: void 0, done: true });
210
+ resolveNextMessage = null;
211
+ rejectNextMessage = null;
212
+ }
213
+ };
214
+ signal.addEventListener("abort", handleAbort, { once: true });
215
+ const handleConnectionChange = (connected) => {
216
+ if (!connected && !isDone) {
217
+ errorMessage = "Relay connection lost";
218
+ isDone = true;
219
+ if (rejectNextMessage) {
220
+ rejectNextMessage(new Error(errorMessage));
221
+ resolveNextMessage = null;
222
+ rejectNextMessage = null;
223
+ }
224
+ }
225
+ };
226
+ const unsubscribeConnection = relayClient.onConnectionChange(
227
+ handleConnectionChange
228
+ );
229
+ const unsubscribeMessage = relayClient.onMessage((message) => {
230
+ if (message.sessionId !== sessionId) return;
231
+ if (message.type === "agent-status" && message.content) {
232
+ messageQueue.push(message.content);
233
+ if (resolveNextMessage) {
234
+ const nextMessage = messageQueue.shift();
235
+ if (nextMessage !== void 0) {
236
+ resolveNextMessage({ value: nextMessage, done: false });
237
+ resolveNextMessage = null;
238
+ rejectNextMessage = null;
239
+ }
240
+ }
241
+ } else if (message.type === "agent-done") {
242
+ isDone = true;
243
+ if (resolveNextMessage) {
244
+ resolveNextMessage({ value: void 0, done: true });
245
+ resolveNextMessage = null;
246
+ rejectNextMessage = null;
247
+ }
248
+ } else if (message.type === "agent-error") {
249
+ errorMessage = message.content ?? "Unknown error";
250
+ isDone = true;
251
+ if (rejectNextMessage) {
252
+ rejectNextMessage(new Error(errorMessage));
253
+ resolveNextMessage = null;
254
+ rejectNextMessage = null;
255
+ }
256
+ }
257
+ });
258
+ if (!relayClient.isConnected()) {
259
+ unsubscribeConnection();
260
+ unsubscribeMessage();
261
+ signal.removeEventListener("abort", handleAbort);
262
+ throw new Error("Relay connection is not open");
263
+ }
264
+ const didSendRequest = relayClient.sendAgentRequest(
265
+ agentId,
266
+ contextWithSession
267
+ );
268
+ if (!didSendRequest) {
269
+ unsubscribeConnection();
270
+ unsubscribeMessage();
271
+ signal.removeEventListener("abort", handleAbort);
272
+ throw new Error("Failed to send agent request: connection not open");
273
+ }
274
+ try {
275
+ while (true) {
276
+ if (messageQueue.length > 0) {
277
+ const next = messageQueue.shift();
278
+ if (next !== void 0) {
279
+ yield next;
280
+ }
281
+ continue;
282
+ }
283
+ if (isDone || signal.aborted) {
284
+ break;
285
+ }
286
+ const result = await new Promise(
287
+ (resolve, reject) => {
288
+ resolveNextMessage = resolve;
289
+ rejectNextMessage = reject;
290
+ }
291
+ );
292
+ if (result.done) break;
293
+ yield result.value;
294
+ }
295
+ if (errorMessage) {
296
+ throw new Error(errorMessage);
297
+ }
298
+ } finally {
299
+ signal.removeEventListener("abort", handleAbort);
300
+ unsubscribeConnection();
301
+ unsubscribeMessage();
302
+ }
303
+ };
304
+ const abort = async (sessionId) => {
305
+ relayClient.abortAgent(agentId, sessionId);
306
+ };
307
+ const waitForOperationResponse = (sessionId) => {
308
+ return new Promise((resolve, reject) => {
309
+ let didCleanup = false;
310
+ const cleanup = () => {
311
+ if (didCleanup) return;
312
+ didCleanup = true;
313
+ unsubscribeMessage();
314
+ unsubscribeConnection();
315
+ };
316
+ const unsubscribeMessage = relayClient.onMessage((message) => {
317
+ if (message.sessionId !== sessionId) return;
318
+ cleanup();
319
+ if (message.type === "agent-done") {
320
+ resolve();
321
+ } else if (message.type === "agent-error") {
322
+ reject(new Error(message.content ?? "Operation failed"));
323
+ }
324
+ });
325
+ const unsubscribeConnection = relayClient.onConnectionChange(
326
+ (connected) => {
327
+ if (!connected) {
328
+ cleanup();
329
+ reject(
330
+ new Error("Connection lost while waiting for operation response")
331
+ );
332
+ }
333
+ }
334
+ );
335
+ });
336
+ };
337
+ const undo = async () => {
338
+ const sessionId = `undo-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
339
+ const didSend = relayClient.undoAgent(agentId, sessionId);
340
+ if (!didSend) {
341
+ throw new Error("Failed to send undo request: connection not open");
342
+ }
343
+ return waitForOperationResponse(sessionId);
344
+ };
345
+ const redo = async () => {
346
+ const sessionId = `redo-${agentId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
347
+ const didSend = relayClient.redoAgent(agentId, sessionId);
348
+ if (!didSend) {
349
+ throw new Error("Failed to send redo request: connection not open");
350
+ }
351
+ return waitForOperationResponse(sessionId);
352
+ };
353
+ return {
354
+ send,
355
+ abort,
356
+ undo,
357
+ redo,
358
+ checkConnection,
359
+ supportsResume: true,
360
+ supportsFollowUp: true
361
+ };
362
+ };
363
+ var defaultRelayClient = null;
364
+ var getDefaultRelayClient = () => {
365
+ if (typeof window === "undefined") {
366
+ return null;
367
+ }
368
+ if (window.__REACT_GRAB_RELAY__) {
369
+ defaultRelayClient = window.__REACT_GRAB_RELAY__;
370
+ return defaultRelayClient;
371
+ }
372
+ if (!defaultRelayClient) {
373
+ defaultRelayClient = createRelayClient();
374
+ window.__REACT_GRAB_RELAY__ = defaultRelayClient;
375
+ }
376
+ return defaultRelayClient;
377
+ };
378
+
379
+ // src/client.ts
380
+ var AGENT_ID = "opencode";
381
+ var isReactGrabApi = (value) => typeof value === "object" && value !== null && "registerPlugin" in value;
382
+ var createOpenCodeAgentProvider = (providerOptions = {}) => {
383
+ const relayClient = providerOptions.relayClient ?? getDefaultRelayClient();
384
+ if (!relayClient) {
385
+ throw new Error("RelayClient is required in browser environments");
386
+ }
387
+ return createRelayAgentProvider({
388
+ relayClient,
389
+ agentId: AGENT_ID
390
+ });
391
+ };
392
+ var attachAgent = async () => {
393
+ if (typeof window === "undefined") return;
394
+ const relayClient = getDefaultRelayClient();
395
+ if (!relayClient) return;
396
+ try {
397
+ await relayClient.connect();
398
+ } catch {
399
+ return;
400
+ }
401
+ const provider = createRelayAgentProvider({
402
+ relayClient,
403
+ agentId: AGENT_ID
404
+ });
405
+ const attach = (api) => {
406
+ const agent = { provider, storage: sessionStorage };
407
+ const plugin = {
408
+ name: "opencode-agent",
409
+ actions: [
410
+ {
411
+ id: "edit-with-opencode",
412
+ label: "Edit with OpenCode",
413
+ shortcut: "Enter",
414
+ onAction: (actionContext) => {
415
+ actionContext.enterPromptMode?.(agent);
416
+ },
417
+ agent
418
+ }
419
+ ]
420
+ };
421
+ api.registerPlugin(plugin);
422
+ };
423
+ const existingApi = window.__REACT_GRAB__;
424
+ if (isReactGrabApi(existingApi)) {
425
+ attach(existingApi);
426
+ return;
427
+ }
428
+ window.addEventListener(
429
+ "react-grab:init",
430
+ (event) => {
431
+ if (!(event instanceof CustomEvent)) return;
432
+ if (!isReactGrabApi(event.detail)) return;
433
+ attach(event.detail);
434
+ },
435
+ { once: true }
436
+ );
437
+ const apiAfterListener = window.__REACT_GRAB__;
438
+ if (isReactGrabApi(apiAfterListener)) {
439
+ attach(apiAfterListener);
440
+ }
441
+ };
442
+ attachAgent();
443
+
444
+ exports.attachAgent = attachAgent;
445
+ exports.createOpenCodeAgentProvider = createOpenCodeAgentProvider;
446
+
447
+ return exports;
448
+
449
+ })({});
package/dist/client.js CHANGED
@@ -208,7 +208,7 @@ var createRelayAgentProvider = (options) => {
208
208
  rejectNextMessage = null;
209
209
  }
210
210
  };
211
- signal.addEventListener("abort", handleAbort);
211
+ signal.addEventListener("abort", handleAbort, { once: true });
212
212
  const handleConnectionChange = (connected) => {
213
213
  if (!connected && !isDone) {
214
214
  errorMessage = "Relay connection lost";
@@ -258,7 +258,10 @@ var createRelayAgentProvider = (options) => {
258
258
  signal.removeEventListener("abort", handleAbort);
259
259
  throw new Error("Relay connection is not open");
260
260
  }
261
- const didSendRequest = relayClient.sendAgentRequest(agentId, contextWithSession);
261
+ const didSendRequest = relayClient.sendAgentRequest(
262
+ agentId,
263
+ contextWithSession
264
+ );
262
265
  if (!didSendRequest) {
263
266
  unsubscribeConnection();
264
267
  unsubscribeMessage();
@@ -316,12 +319,16 @@ var createRelayAgentProvider = (options) => {
316
319
  reject(new Error(message.content ?? "Operation failed"));
317
320
  }
318
321
  });
319
- const unsubscribeConnection = relayClient.onConnectionChange((connected) => {
320
- if (!connected) {
321
- cleanup();
322
- reject(new Error("Connection lost while waiting for operation response"));
322
+ const unsubscribeConnection = relayClient.onConnectionChange(
323
+ (connected) => {
324
+ if (!connected) {
325
+ cleanup();
326
+ reject(
327
+ new Error("Connection lost while waiting for operation response")
328
+ );
329
+ }
323
330
  }
324
- });
331
+ );
325
332
  });
326
333
  };
327
334
  const undo = async () => {
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@react-grab/opencode",
3
- "version": "0.1.0-beta.8",
4
- "type": "module",
3
+ "version": "0.1.0",
5
4
  "bin": {
6
5
  "react-grab-opencode": "./dist/cli.cjs"
7
6
  },
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "type": "module",
11
+ "browser": "dist/client.global.js",
8
12
  "exports": {
9
13
  "./client": {
10
14
  "types": "./dist/client.d.ts",
@@ -19,20 +23,16 @@
19
23
  "./dist/*": "./dist/*.js",
20
24
  "./dist/*.js": "./dist/*.js"
21
25
  },
22
- "browser": "dist/client.global.js",
23
- "files": [
24
- "dist"
25
- ],
26
- "devDependencies": {
27
- "@types/node": "^22.10.7",
28
- "tsup": "^8.4.0",
29
- "@react-grab/utils": "0.1.0-beta.8"
30
- },
31
26
  "dependencies": {
32
27
  "@opencode-ai/sdk": "^1.0.132",
33
28
  "fkill": "^9.0.0",
34
- "@react-grab/relay": "0.1.0-beta.8",
35
- "react-grab": "0.1.0-beta.8"
29
+ "@react-grab/relay": "0.1.0",
30
+ "react-grab": "0.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.10.7",
34
+ "tsup": "^8.4.0",
35
+ "@react-grab/utils": "0.1.0"
36
36
  },
37
37
  "scripts": {
38
38
  "dev": "tsup --watch",