@react-grab/opencode 0.0.85 → 0.0.87
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 +2 -2
- package/dist/cli.js +2 -2
- package/dist/client.cjs +12 -11
- package/dist/client.global.js +4 -4
- package/dist/client.js +12 -11
- package/dist/server.cjs +44 -20
- package/dist/server.js +44 -20
- package/package.json +4 -3
package/dist/cli.cjs
CHANGED
|
@@ -7138,7 +7138,7 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7138
7138
|
var DEFAULT_PORT = 6567;
|
|
7139
7139
|
|
|
7140
7140
|
// src/cli.ts
|
|
7141
|
-
var VERSION = "0.0.
|
|
7141
|
+
var VERSION = "0.0.87";
|
|
7142
7142
|
var serverPath = path2.join(__dirname, "server.cjs");
|
|
7143
7143
|
execa(process.execPath, [serverPath], {
|
|
7144
7144
|
detached: true,
|
|
@@ -7146,6 +7146,6 @@ execa(process.execPath, [serverPath], {
|
|
|
7146
7146
|
cleanup: false
|
|
7147
7147
|
}).unref();
|
|
7148
7148
|
console.log(
|
|
7149
|
-
`${import_picocolors.default.magenta("\
|
|
7149
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(OpenCode)")}`
|
|
7150
7150
|
);
|
|
7151
7151
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${DEFAULT_PORT}`)}`);
|
package/dist/cli.js
CHANGED
|
@@ -7130,7 +7130,7 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7130
7130
|
var DEFAULT_PORT = 6567;
|
|
7131
7131
|
|
|
7132
7132
|
// src/cli.ts
|
|
7133
|
-
var VERSION = "0.0.
|
|
7133
|
+
var VERSION = "0.0.87";
|
|
7134
7134
|
var serverPath = join(__dirname, "server.cjs");
|
|
7135
7135
|
execa(process.execPath, [serverPath], {
|
|
7136
7136
|
detached: true,
|
|
@@ -7138,6 +7138,6 @@ execa(process.execPath, [serverPath], {
|
|
|
7138
7138
|
cleanup: false
|
|
7139
7139
|
}).unref();
|
|
7140
7140
|
console.log(
|
|
7141
|
-
`${import_picocolors.default.magenta("\
|
|
7141
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(OpenCode)")}`
|
|
7142
7142
|
);
|
|
7143
7143
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${DEFAULT_PORT}`)}`);
|
package/dist/client.cjs
CHANGED
|
@@ -55,6 +55,7 @@ async function* streamSSE(stream, signal) {
|
|
|
55
55
|
|
|
56
56
|
// src/constants.ts
|
|
57
57
|
var DEFAULT_PORT = 6567;
|
|
58
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
58
59
|
|
|
59
60
|
// src/client.ts
|
|
60
61
|
var DEFAULT_SERVER_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
@@ -86,6 +87,7 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
86
87
|
const iterator = streamSSE(response.body, signal)[Symbol.asyncIterator]();
|
|
87
88
|
let done = false;
|
|
88
89
|
let pendingNext = iterator.next();
|
|
90
|
+
let lastStatus = null;
|
|
89
91
|
while (!done) {
|
|
90
92
|
const result = await Promise.race([
|
|
91
93
|
pendingNext.then((iteratorResult) => ({
|
|
@@ -96,23 +98,22 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
96
98
|
(resolve) => setTimeout(() => resolve({ type: "timeout" }), 100)
|
|
97
99
|
)
|
|
98
100
|
]);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
102
|
-
} else {
|
|
101
|
+
const elapsedSeconds = (Date.now() - startTime) / 1e3;
|
|
102
|
+
if (result.type === "status") {
|
|
103
103
|
const iteratorResult = result.iteratorResult;
|
|
104
104
|
done = iteratorResult.done ?? false;
|
|
105
105
|
if (!done && iteratorResult.value) {
|
|
106
|
-
|
|
107
|
-
if (status === "Completed successfully") {
|
|
108
|
-
const totalSeconds = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
109
|
-
yield `Completed in ${totalSeconds}s`;
|
|
110
|
-
} else {
|
|
111
|
-
yield status;
|
|
112
|
-
}
|
|
106
|
+
lastStatus = iteratorResult.value;
|
|
113
107
|
pendingNext = iterator.next();
|
|
114
108
|
}
|
|
115
109
|
}
|
|
110
|
+
if (lastStatus === COMPLETED_STATUS) {
|
|
111
|
+
yield `Completed in ${elapsedSeconds.toFixed(1)}s`;
|
|
112
|
+
} else if (lastStatus) {
|
|
113
|
+
yield `${lastStatus} ${elapsedSeconds.toFixed(1)}s`;
|
|
114
|
+
} else {
|
|
115
|
+
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
116
|
+
}
|
|
116
117
|
}
|
|
117
118
|
} finally {
|
|
118
119
|
signal.removeEventListener("abort", handleAbort);
|
package/dist/client.global.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var ReactGrabOpenCode=(function(exports){'use strict';var
|
|
2
|
-
`))
|
|
1
|
+
var ReactGrabOpenCode=(function(exports){'use strict';var g=5e3,m="react-grab:agent-sessions",C=c=>{let e="",o="";for(let n of c.split(`
|
|
2
|
+
`))n.startsWith("event:")?e=n.slice(6).trim():n.startsWith("data:")&&(o=n.slice(5).trim());return {eventType:e,data:o}};async function*A(c,e){let o=c.getReader(),n=new TextDecoder,r="",t=false,s=()=>{t=true,o.cancel().catch(()=>{});};e.addEventListener("abort",s);try{if(e.aborted)throw new DOMException("Aborted","AbortError");for(;;){let i=await o.read();if(t||e.aborted)throw new DOMException("Aborted","AbortError");let{done:l,value:f}=i;f&&(r+=n.decode(f,{stream:!0}));let a;for(;(a=r.indexOf(`
|
|
3
3
|
|
|
4
|
-
`))!==-1;){let{eventType:
|
|
5
|
-
exports.attachAgent=
|
|
4
|
+
`))!==-1;){let{eventType:p,data:d}=C(r.slice(0,a));if(r=r.slice(a+2),p==="done")return;if(p==="error")throw new Error(d||"Agent error");d&&(yield d);}if(l)break}}finally{e.removeEventListener("abort",s);try{o.releaseLock();}catch{}}}var y="Completed successfully";var w=`http://localhost:${6567}`,E=async function*(c,e,o){let n=Date.now(),r=e.sessionId,t=()=>{r&&fetch(`${c}/abort/${r}`,{method:"POST"}).catch(()=>{});};o.addEventListener("abort",t);try{let s=await fetch(`${c}/agent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),signal:o});if(!s.ok)throw new Error(`Server error: ${s.status}`);if(!s.body)throw new Error("No response body");let i=A(s.body,o)[Symbol.asyncIterator](),l=!1,f=i.next(),a=null;for(;!l;){let p=await Promise.race([f.then(u=>({type:"status",iteratorResult:u})),new Promise(u=>setTimeout(()=>u({type:"timeout"}),100))]),d=(Date.now()-n)/1e3;if(p.type==="status"){let u=p.iteratorResult;l=u.done??!1,!l&&u.value&&(a=u.value,f=i.next());}a===y?yield `Completed in ${d.toFixed(1)}s`:a?yield `${a} ${d.toFixed(1)}s`:yield `Working\u2026 ${d.toFixed(1)}s`;}}finally{o.removeEventListener("abort",t);}},S=(c={})=>{let{serverUrl:e=w,getOptions:o}=c,n=null,r=t=>({...o?.()??{},...t??{}});return {send:async function*(t,s){let i={...t,options:r(t.options)};yield*E(e,i,s);},resume:async function*(t,s,i){let l=i.getItem(m);if(!l)throw new Error("No sessions to resume");let a=JSON.parse(l)[t];if(!a)throw new Error(`Session ${t} not found`);let p=a.context,d={...p,options:r(p.options)};yield "Resuming...",yield*E(e,d,s);},supportsResume:true,supportsFollowUp:true,checkConnection:async()=>{let t=Date.now();if(n&&t-n.timestamp<g)return n.result;try{let i=(await fetch(`${e}/health`,{method:"GET"})).ok;return n={result:i,timestamp:t},i}catch{return n={result:false,timestamp:t},false}},undo:async()=>{try{await fetch(`${e}/undo`,{method:"POST"});}catch{}}}},b=async()=>{if(typeof window>"u")return;let c=S(),e=r=>{r.setAgent({provider:c,storage:sessionStorage});},o=window.__REACT_GRAB__;if(o){e(o);return}window.addEventListener("react-grab:init",r=>{e(r.detail);},{once:true});let n=window.__REACT_GRAB__;n&&e(n);};b();
|
|
5
|
+
exports.attachAgent=b;exports.createOpenCodeAgentProvider=S;return exports;})({});
|
package/dist/client.js
CHANGED
|
@@ -53,6 +53,7 @@ async function* streamSSE(stream, signal) {
|
|
|
53
53
|
|
|
54
54
|
// src/constants.ts
|
|
55
55
|
var DEFAULT_PORT = 6567;
|
|
56
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
56
57
|
|
|
57
58
|
// src/client.ts
|
|
58
59
|
var DEFAULT_SERVER_URL = `http://localhost:${DEFAULT_PORT}`;
|
|
@@ -84,6 +85,7 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
84
85
|
const iterator = streamSSE(response.body, signal)[Symbol.asyncIterator]();
|
|
85
86
|
let done = false;
|
|
86
87
|
let pendingNext = iterator.next();
|
|
88
|
+
let lastStatus = null;
|
|
87
89
|
while (!done) {
|
|
88
90
|
const result = await Promise.race([
|
|
89
91
|
pendingNext.then((iteratorResult) => ({
|
|
@@ -94,23 +96,22 @@ var streamFromServer = async function* (serverUrl, context, signal) {
|
|
|
94
96
|
(resolve) => setTimeout(() => resolve({ type: "timeout" }), 100)
|
|
95
97
|
)
|
|
96
98
|
]);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
100
|
-
} else {
|
|
99
|
+
const elapsedSeconds = (Date.now() - startTime) / 1e3;
|
|
100
|
+
if (result.type === "status") {
|
|
101
101
|
const iteratorResult = result.iteratorResult;
|
|
102
102
|
done = iteratorResult.done ?? false;
|
|
103
103
|
if (!done && iteratorResult.value) {
|
|
104
|
-
|
|
105
|
-
if (status === "Completed successfully") {
|
|
106
|
-
const totalSeconds = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
107
|
-
yield `Completed in ${totalSeconds}s`;
|
|
108
|
-
} else {
|
|
109
|
-
yield status;
|
|
110
|
-
}
|
|
104
|
+
lastStatus = iteratorResult.value;
|
|
111
105
|
pendingNext = iterator.next();
|
|
112
106
|
}
|
|
113
107
|
}
|
|
108
|
+
if (lastStatus === COMPLETED_STATUS) {
|
|
109
|
+
yield `Completed in ${elapsedSeconds.toFixed(1)}s`;
|
|
110
|
+
} else if (lastStatus) {
|
|
111
|
+
yield `${lastStatus} ${elapsedSeconds.toFixed(1)}s`;
|
|
112
|
+
} else {
|
|
113
|
+
yield `Working\u2026 ${elapsedSeconds.toFixed(1)}s`;
|
|
114
|
+
}
|
|
114
115
|
}
|
|
115
116
|
} finally {
|
|
116
117
|
signal.removeEventListener("abort", handleAbort);
|
package/dist/server.cjs
CHANGED
|
@@ -7339,10 +7339,11 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7339
7339
|
|
|
7340
7340
|
// src/constants.ts
|
|
7341
7341
|
var DEFAULT_PORT = 6567;
|
|
7342
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
7342
7343
|
|
|
7343
7344
|
// ../utils/dist/server.js
|
|
7344
7345
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7345
|
-
var VERSION = "0.0.
|
|
7346
|
+
var VERSION = "0.0.87";
|
|
7346
7347
|
try {
|
|
7347
7348
|
fetch(`https://www.react-grab.com/api/version?source=opencode&t=${Date.now()}`).catch(() => {
|
|
7348
7349
|
});
|
|
@@ -7352,7 +7353,7 @@ var OPENCODE_SDK_PORT = 4096;
|
|
|
7352
7353
|
var opencodeInstance = null;
|
|
7353
7354
|
var sessionMap = /* @__PURE__ */ new Map();
|
|
7354
7355
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
7355
|
-
var
|
|
7356
|
+
var lastMessageInfo;
|
|
7356
7357
|
var getOpenCodeClient = async () => {
|
|
7357
7358
|
if (!opencodeInstance) {
|
|
7358
7359
|
await fkill(`:${OPENCODE_SDK_PORT}`, { force: true, silent: true }).catch(
|
|
@@ -7368,9 +7369,9 @@ var getOpenCodeClient = async () => {
|
|
|
7368
7369
|
}
|
|
7369
7370
|
return opencodeInstance.client;
|
|
7370
7371
|
};
|
|
7371
|
-
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId) => {
|
|
7372
|
+
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId, signal) => {
|
|
7372
7373
|
const client2 = await getOpenCodeClient();
|
|
7373
|
-
onStatus?.("Thinking
|
|
7374
|
+
onStatus?.("Thinking\u2026");
|
|
7374
7375
|
let opencodeSessionId;
|
|
7375
7376
|
if (reactGrabSessionId && sessionMap.has(reactGrabSessionId)) {
|
|
7376
7377
|
opencodeSessionId = sessionMap.get(reactGrabSessionId);
|
|
@@ -7386,22 +7387,39 @@ var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId
|
|
|
7386
7387
|
sessionMap.set(reactGrabSessionId, opencodeSessionId);
|
|
7387
7388
|
}
|
|
7388
7389
|
}
|
|
7389
|
-
lastOpenCodeSessionId = opencodeSessionId;
|
|
7390
7390
|
const modelConfig = options?.model ? {
|
|
7391
7391
|
providerID: options.model.split("/")[0],
|
|
7392
7392
|
modelID: options.model.split("/")[1] || options.model
|
|
7393
7393
|
} : void 0;
|
|
7394
|
-
const
|
|
7394
|
+
const eventStreamResult = await client2.event.subscribe();
|
|
7395
|
+
await client2.session.promptAsync({
|
|
7395
7396
|
path: { id: opencodeSessionId },
|
|
7396
7397
|
body: {
|
|
7397
7398
|
...modelConfig && { model: modelConfig },
|
|
7398
7399
|
parts: [{ type: "text", text: prompt }]
|
|
7399
7400
|
}
|
|
7400
7401
|
});
|
|
7401
|
-
|
|
7402
|
-
|
|
7402
|
+
for await (const event of eventStreamResult.stream) {
|
|
7403
|
+
if (signal?.aborted) break;
|
|
7404
|
+
const eventData = event;
|
|
7405
|
+
if (eventData.type === "session.idle") {
|
|
7406
|
+
const idleSessionId = eventData.properties?.sessionID;
|
|
7407
|
+
if (idleSessionId === opencodeSessionId) {
|
|
7408
|
+
break;
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7411
|
+
if (eventData.type === "message.part.updated" && eventData.properties?.part) {
|
|
7412
|
+
const part = eventData.properties.part;
|
|
7413
|
+
if (part.sessionID !== opencodeSessionId) continue;
|
|
7414
|
+
if (part.messageID) {
|
|
7415
|
+
lastMessageInfo = { sessionId: opencodeSessionId, messageId: part.messageID };
|
|
7416
|
+
}
|
|
7403
7417
|
if (part.type === "text" && part.text) {
|
|
7404
|
-
|
|
7418
|
+
const truncatedText = part.text.length > 100 ? `${part.text.slice(0, 100)}...` : part.text;
|
|
7419
|
+
onStatus?.(truncatedText);
|
|
7420
|
+
} else if (part.type === "tool-invocation" && part.toolName) {
|
|
7421
|
+
const stateLabel = part.state === "running" ? "Running" : "Using";
|
|
7422
|
+
onStatus?.(`${stateLabel} ${part.toolName}`);
|
|
7405
7423
|
}
|
|
7406
7424
|
}
|
|
7407
7425
|
}
|
|
@@ -7421,7 +7439,14 @@ Context:
|
|
|
7421
7439
|
${content}
|
|
7422
7440
|
`;
|
|
7423
7441
|
return streamSSE(context, async (stream2) => {
|
|
7424
|
-
const
|
|
7442
|
+
const signal = { aborted: false };
|
|
7443
|
+
const isAborted = () => {
|
|
7444
|
+
if (sessionId && abortedSessions.has(sessionId)) {
|
|
7445
|
+
signal.aborted = true;
|
|
7446
|
+
return true;
|
|
7447
|
+
}
|
|
7448
|
+
return false;
|
|
7449
|
+
};
|
|
7425
7450
|
try {
|
|
7426
7451
|
await executeOpenCodePrompt(
|
|
7427
7452
|
formattedPrompt,
|
|
@@ -7434,11 +7459,12 @@ ${content}
|
|
|
7434
7459
|
}).catch(() => {
|
|
7435
7460
|
});
|
|
7436
7461
|
},
|
|
7437
|
-
sessionId
|
|
7462
|
+
sessionId,
|
|
7463
|
+
signal
|
|
7438
7464
|
);
|
|
7439
7465
|
if (!isAborted()) {
|
|
7440
7466
|
await stream2.writeSSE({
|
|
7441
|
-
data:
|
|
7467
|
+
data: COMPLETED_STATUS,
|
|
7442
7468
|
event: "status"
|
|
7443
7469
|
});
|
|
7444
7470
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
@@ -7470,16 +7496,14 @@ ${stderr.trim()}` : errorMessage;
|
|
|
7470
7496
|
return context.json({ status: "ok" });
|
|
7471
7497
|
});
|
|
7472
7498
|
honoApplication.post("/undo", async (context) => {
|
|
7473
|
-
if (!
|
|
7474
|
-
return context.json({ status: "error", message: "No
|
|
7499
|
+
if (!lastMessageInfo) {
|
|
7500
|
+
return context.json({ status: "error", message: "No message to undo" });
|
|
7475
7501
|
}
|
|
7476
7502
|
try {
|
|
7477
7503
|
const client2 = await getOpenCodeClient();
|
|
7478
|
-
await client2.session.
|
|
7479
|
-
path: { id:
|
|
7480
|
-
body: {
|
|
7481
|
-
parts: [{ type: "text", text: "/undo" }]
|
|
7482
|
-
}
|
|
7504
|
+
await client2.session.revert({
|
|
7505
|
+
path: { id: lastMessageInfo.sessionId },
|
|
7506
|
+
body: { messageID: lastMessageInfo.messageId }
|
|
7483
7507
|
});
|
|
7484
7508
|
return context.json({ status: "ok" });
|
|
7485
7509
|
} catch (error) {
|
|
@@ -7499,7 +7523,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
7499
7523
|
const honoApplication = createServer();
|
|
7500
7524
|
serve({ fetch: honoApplication.fetch, port });
|
|
7501
7525
|
console.log(
|
|
7502
|
-
`${import_picocolors.default.magenta("\
|
|
7526
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(OpenCode)")}`
|
|
7503
7527
|
);
|
|
7504
7528
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
7505
7529
|
};
|
package/dist/server.js
CHANGED
|
@@ -7328,10 +7328,11 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
7328
7328
|
|
|
7329
7329
|
// src/constants.ts
|
|
7330
7330
|
var DEFAULT_PORT = 6567;
|
|
7331
|
+
var COMPLETED_STATUS = "Completed successfully";
|
|
7331
7332
|
|
|
7332
7333
|
// ../utils/dist/server.js
|
|
7333
7334
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7334
|
-
var VERSION = "0.0.
|
|
7335
|
+
var VERSION = "0.0.87";
|
|
7335
7336
|
try {
|
|
7336
7337
|
fetch(`https://www.react-grab.com/api/version?source=opencode&t=${Date.now()}`).catch(() => {
|
|
7337
7338
|
});
|
|
@@ -7341,7 +7342,7 @@ var OPENCODE_SDK_PORT = 4096;
|
|
|
7341
7342
|
var opencodeInstance = null;
|
|
7342
7343
|
var sessionMap = /* @__PURE__ */ new Map();
|
|
7343
7344
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
7344
|
-
var
|
|
7345
|
+
var lastMessageInfo;
|
|
7345
7346
|
var getOpenCodeClient = async () => {
|
|
7346
7347
|
if (!opencodeInstance) {
|
|
7347
7348
|
await fkill(`:${OPENCODE_SDK_PORT}`, { force: true, silent: true }).catch(
|
|
@@ -7357,9 +7358,9 @@ var getOpenCodeClient = async () => {
|
|
|
7357
7358
|
}
|
|
7358
7359
|
return opencodeInstance.client;
|
|
7359
7360
|
};
|
|
7360
|
-
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId) => {
|
|
7361
|
+
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId, signal) => {
|
|
7361
7362
|
const client2 = await getOpenCodeClient();
|
|
7362
|
-
onStatus?.("Thinking
|
|
7363
|
+
onStatus?.("Thinking\u2026");
|
|
7363
7364
|
let opencodeSessionId;
|
|
7364
7365
|
if (reactGrabSessionId && sessionMap.has(reactGrabSessionId)) {
|
|
7365
7366
|
opencodeSessionId = sessionMap.get(reactGrabSessionId);
|
|
@@ -7375,22 +7376,39 @@ var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId
|
|
|
7375
7376
|
sessionMap.set(reactGrabSessionId, opencodeSessionId);
|
|
7376
7377
|
}
|
|
7377
7378
|
}
|
|
7378
|
-
lastOpenCodeSessionId = opencodeSessionId;
|
|
7379
7379
|
const modelConfig = options?.model ? {
|
|
7380
7380
|
providerID: options.model.split("/")[0],
|
|
7381
7381
|
modelID: options.model.split("/")[1] || options.model
|
|
7382
7382
|
} : void 0;
|
|
7383
|
-
const
|
|
7383
|
+
const eventStreamResult = await client2.event.subscribe();
|
|
7384
|
+
await client2.session.promptAsync({
|
|
7384
7385
|
path: { id: opencodeSessionId },
|
|
7385
7386
|
body: {
|
|
7386
7387
|
...modelConfig && { model: modelConfig },
|
|
7387
7388
|
parts: [{ type: "text", text: prompt }]
|
|
7388
7389
|
}
|
|
7389
7390
|
});
|
|
7390
|
-
|
|
7391
|
-
|
|
7391
|
+
for await (const event of eventStreamResult.stream) {
|
|
7392
|
+
if (signal?.aborted) break;
|
|
7393
|
+
const eventData = event;
|
|
7394
|
+
if (eventData.type === "session.idle") {
|
|
7395
|
+
const idleSessionId = eventData.properties?.sessionID;
|
|
7396
|
+
if (idleSessionId === opencodeSessionId) {
|
|
7397
|
+
break;
|
|
7398
|
+
}
|
|
7399
|
+
}
|
|
7400
|
+
if (eventData.type === "message.part.updated" && eventData.properties?.part) {
|
|
7401
|
+
const part = eventData.properties.part;
|
|
7402
|
+
if (part.sessionID !== opencodeSessionId) continue;
|
|
7403
|
+
if (part.messageID) {
|
|
7404
|
+
lastMessageInfo = { sessionId: opencodeSessionId, messageId: part.messageID };
|
|
7405
|
+
}
|
|
7392
7406
|
if (part.type === "text" && part.text) {
|
|
7393
|
-
|
|
7407
|
+
const truncatedText = part.text.length > 100 ? `${part.text.slice(0, 100)}...` : part.text;
|
|
7408
|
+
onStatus?.(truncatedText);
|
|
7409
|
+
} else if (part.type === "tool-invocation" && part.toolName) {
|
|
7410
|
+
const stateLabel = part.state === "running" ? "Running" : "Using";
|
|
7411
|
+
onStatus?.(`${stateLabel} ${part.toolName}`);
|
|
7394
7412
|
}
|
|
7395
7413
|
}
|
|
7396
7414
|
}
|
|
@@ -7410,7 +7428,14 @@ Context:
|
|
|
7410
7428
|
${content}
|
|
7411
7429
|
`;
|
|
7412
7430
|
return streamSSE(context, async (stream2) => {
|
|
7413
|
-
const
|
|
7431
|
+
const signal = { aborted: false };
|
|
7432
|
+
const isAborted = () => {
|
|
7433
|
+
if (sessionId && abortedSessions.has(sessionId)) {
|
|
7434
|
+
signal.aborted = true;
|
|
7435
|
+
return true;
|
|
7436
|
+
}
|
|
7437
|
+
return false;
|
|
7438
|
+
};
|
|
7414
7439
|
try {
|
|
7415
7440
|
await executeOpenCodePrompt(
|
|
7416
7441
|
formattedPrompt,
|
|
@@ -7423,11 +7448,12 @@ ${content}
|
|
|
7423
7448
|
}).catch(() => {
|
|
7424
7449
|
});
|
|
7425
7450
|
},
|
|
7426
|
-
sessionId
|
|
7451
|
+
sessionId,
|
|
7452
|
+
signal
|
|
7427
7453
|
);
|
|
7428
7454
|
if (!isAborted()) {
|
|
7429
7455
|
await stream2.writeSSE({
|
|
7430
|
-
data:
|
|
7456
|
+
data: COMPLETED_STATUS,
|
|
7431
7457
|
event: "status"
|
|
7432
7458
|
});
|
|
7433
7459
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
@@ -7459,16 +7485,14 @@ ${stderr.trim()}` : errorMessage;
|
|
|
7459
7485
|
return context.json({ status: "ok" });
|
|
7460
7486
|
});
|
|
7461
7487
|
honoApplication.post("/undo", async (context) => {
|
|
7462
|
-
if (!
|
|
7463
|
-
return context.json({ status: "error", message: "No
|
|
7488
|
+
if (!lastMessageInfo) {
|
|
7489
|
+
return context.json({ status: "error", message: "No message to undo" });
|
|
7464
7490
|
}
|
|
7465
7491
|
try {
|
|
7466
7492
|
const client2 = await getOpenCodeClient();
|
|
7467
|
-
await client2.session.
|
|
7468
|
-
path: { id:
|
|
7469
|
-
body: {
|
|
7470
|
-
parts: [{ type: "text", text: "/undo" }]
|
|
7471
|
-
}
|
|
7493
|
+
await client2.session.revert({
|
|
7494
|
+
path: { id: lastMessageInfo.sessionId },
|
|
7495
|
+
body: { messageID: lastMessageInfo.messageId }
|
|
7472
7496
|
});
|
|
7473
7497
|
return context.json({ status: "ok" });
|
|
7474
7498
|
} catch (error) {
|
|
@@ -7488,7 +7512,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
7488
7512
|
const honoApplication = createServer();
|
|
7489
7513
|
serve({ fetch: honoApplication.fetch, port });
|
|
7490
7514
|
console.log(
|
|
7491
|
-
`${import_picocolors.default.magenta("\
|
|
7515
|
+
`${import_picocolors.default.magenta("\u273F")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(OpenCode)")}`
|
|
7492
7516
|
);
|
|
7493
7517
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
7494
7518
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-grab/opencode",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.87",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"react-grab-opencode": "./dist/cli.cjs"
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
26
|
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.10.7",
|
|
27
28
|
"tsup": "^8.4.0",
|
|
28
|
-
"@react-grab/utils": "0.0.
|
|
29
|
+
"@react-grab/utils": "0.0.87"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@hono/node-server": "^1.19.6",
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
"fkill": "^9.0.0",
|
|
35
36
|
"hono": "^4.0.0",
|
|
36
37
|
"picocolors": "^1.1.1",
|
|
37
|
-
"react-grab": "0.0.
|
|
38
|
+
"react-grab": "0.0.87"
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
40
41
|
"dev": "tsup --watch",
|