@react-grab/opencode 0.0.84 → 0.0.86
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 +49 -20
- package/dist/server.js +49 -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.86";
|
|
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.86";
|
|
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,15 +7339,21 @@ 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.86";
|
|
7347
|
+
try {
|
|
7348
|
+
fetch(`https://www.react-grab.com/api/version?source=opencode&t=${Date.now()}`).catch(() => {
|
|
7349
|
+
});
|
|
7350
|
+
} catch {
|
|
7351
|
+
}
|
|
7346
7352
|
var OPENCODE_SDK_PORT = 4096;
|
|
7347
7353
|
var opencodeInstance = null;
|
|
7348
7354
|
var sessionMap = /* @__PURE__ */ new Map();
|
|
7349
7355
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
7350
|
-
var
|
|
7356
|
+
var lastMessageInfo;
|
|
7351
7357
|
var getOpenCodeClient = async () => {
|
|
7352
7358
|
if (!opencodeInstance) {
|
|
7353
7359
|
await fkill(`:${OPENCODE_SDK_PORT}`, { force: true, silent: true }).catch(
|
|
@@ -7363,9 +7369,9 @@ var getOpenCodeClient = async () => {
|
|
|
7363
7369
|
}
|
|
7364
7370
|
return opencodeInstance.client;
|
|
7365
7371
|
};
|
|
7366
|
-
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId) => {
|
|
7372
|
+
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId, signal) => {
|
|
7367
7373
|
const client2 = await getOpenCodeClient();
|
|
7368
|
-
onStatus?.("Thinking
|
|
7374
|
+
onStatus?.("Thinking\u2026");
|
|
7369
7375
|
let opencodeSessionId;
|
|
7370
7376
|
if (reactGrabSessionId && sessionMap.has(reactGrabSessionId)) {
|
|
7371
7377
|
opencodeSessionId = sessionMap.get(reactGrabSessionId);
|
|
@@ -7381,22 +7387,39 @@ var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId
|
|
|
7381
7387
|
sessionMap.set(reactGrabSessionId, opencodeSessionId);
|
|
7382
7388
|
}
|
|
7383
7389
|
}
|
|
7384
|
-
lastOpenCodeSessionId = opencodeSessionId;
|
|
7385
7390
|
const modelConfig = options?.model ? {
|
|
7386
7391
|
providerID: options.model.split("/")[0],
|
|
7387
7392
|
modelID: options.model.split("/")[1] || options.model
|
|
7388
7393
|
} : void 0;
|
|
7389
|
-
const
|
|
7394
|
+
const eventStreamResult = await client2.event.subscribe();
|
|
7395
|
+
await client2.session.promptAsync({
|
|
7390
7396
|
path: { id: opencodeSessionId },
|
|
7391
7397
|
body: {
|
|
7392
7398
|
...modelConfig && { model: modelConfig },
|
|
7393
7399
|
parts: [{ type: "text", text: prompt }]
|
|
7394
7400
|
}
|
|
7395
7401
|
});
|
|
7396
|
-
|
|
7397
|
-
|
|
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
|
+
}
|
|
7398
7417
|
if (part.type === "text" && part.text) {
|
|
7399
|
-
|
|
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}`);
|
|
7400
7423
|
}
|
|
7401
7424
|
}
|
|
7402
7425
|
}
|
|
@@ -7416,7 +7439,14 @@ Context:
|
|
|
7416
7439
|
${content}
|
|
7417
7440
|
`;
|
|
7418
7441
|
return streamSSE(context, async (stream2) => {
|
|
7419
|
-
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
|
+
};
|
|
7420
7450
|
try {
|
|
7421
7451
|
await executeOpenCodePrompt(
|
|
7422
7452
|
formattedPrompt,
|
|
@@ -7429,11 +7459,12 @@ ${content}
|
|
|
7429
7459
|
}).catch(() => {
|
|
7430
7460
|
});
|
|
7431
7461
|
},
|
|
7432
|
-
sessionId
|
|
7462
|
+
sessionId,
|
|
7463
|
+
signal
|
|
7433
7464
|
);
|
|
7434
7465
|
if (!isAborted()) {
|
|
7435
7466
|
await stream2.writeSSE({
|
|
7436
|
-
data:
|
|
7467
|
+
data: COMPLETED_STATUS,
|
|
7437
7468
|
event: "status"
|
|
7438
7469
|
});
|
|
7439
7470
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
@@ -7465,16 +7496,14 @@ ${stderr.trim()}` : errorMessage;
|
|
|
7465
7496
|
return context.json({ status: "ok" });
|
|
7466
7497
|
});
|
|
7467
7498
|
honoApplication.post("/undo", async (context) => {
|
|
7468
|
-
if (!
|
|
7469
|
-
return context.json({ status: "error", message: "No
|
|
7499
|
+
if (!lastMessageInfo) {
|
|
7500
|
+
return context.json({ status: "error", message: "No message to undo" });
|
|
7470
7501
|
}
|
|
7471
7502
|
try {
|
|
7472
7503
|
const client2 = await getOpenCodeClient();
|
|
7473
|
-
await client2.session.
|
|
7474
|
-
path: { id:
|
|
7475
|
-
body: {
|
|
7476
|
-
parts: [{ type: "text", text: "/undo" }]
|
|
7477
|
-
}
|
|
7504
|
+
await client2.session.revert({
|
|
7505
|
+
path: { id: lastMessageInfo.sessionId },
|
|
7506
|
+
body: { messageID: lastMessageInfo.messageId }
|
|
7478
7507
|
});
|
|
7479
7508
|
return context.json({ status: "ok" });
|
|
7480
7509
|
} catch (error) {
|
|
@@ -7494,7 +7523,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
7494
7523
|
const honoApplication = createServer();
|
|
7495
7524
|
serve({ fetch: honoApplication.fetch, port });
|
|
7496
7525
|
console.log(
|
|
7497
|
-
`${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)")}`
|
|
7498
7527
|
);
|
|
7499
7528
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
7500
7529
|
};
|
package/dist/server.js
CHANGED
|
@@ -7328,15 +7328,21 @@ 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.86";
|
|
7336
|
+
try {
|
|
7337
|
+
fetch(`https://www.react-grab.com/api/version?source=opencode&t=${Date.now()}`).catch(() => {
|
|
7338
|
+
});
|
|
7339
|
+
} catch {
|
|
7340
|
+
}
|
|
7335
7341
|
var OPENCODE_SDK_PORT = 4096;
|
|
7336
7342
|
var opencodeInstance = null;
|
|
7337
7343
|
var sessionMap = /* @__PURE__ */ new Map();
|
|
7338
7344
|
var abortedSessions = /* @__PURE__ */ new Set();
|
|
7339
|
-
var
|
|
7345
|
+
var lastMessageInfo;
|
|
7340
7346
|
var getOpenCodeClient = async () => {
|
|
7341
7347
|
if (!opencodeInstance) {
|
|
7342
7348
|
await fkill(`:${OPENCODE_SDK_PORT}`, { force: true, silent: true }).catch(
|
|
@@ -7352,9 +7358,9 @@ var getOpenCodeClient = async () => {
|
|
|
7352
7358
|
}
|
|
7353
7359
|
return opencodeInstance.client;
|
|
7354
7360
|
};
|
|
7355
|
-
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId) => {
|
|
7361
|
+
var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId, signal) => {
|
|
7356
7362
|
const client2 = await getOpenCodeClient();
|
|
7357
|
-
onStatus?.("Thinking
|
|
7363
|
+
onStatus?.("Thinking\u2026");
|
|
7358
7364
|
let opencodeSessionId;
|
|
7359
7365
|
if (reactGrabSessionId && sessionMap.has(reactGrabSessionId)) {
|
|
7360
7366
|
opencodeSessionId = sessionMap.get(reactGrabSessionId);
|
|
@@ -7370,22 +7376,39 @@ var executeOpenCodePrompt = async (prompt, options, onStatus, reactGrabSessionId
|
|
|
7370
7376
|
sessionMap.set(reactGrabSessionId, opencodeSessionId);
|
|
7371
7377
|
}
|
|
7372
7378
|
}
|
|
7373
|
-
lastOpenCodeSessionId = opencodeSessionId;
|
|
7374
7379
|
const modelConfig = options?.model ? {
|
|
7375
7380
|
providerID: options.model.split("/")[0],
|
|
7376
7381
|
modelID: options.model.split("/")[1] || options.model
|
|
7377
7382
|
} : void 0;
|
|
7378
|
-
const
|
|
7383
|
+
const eventStreamResult = await client2.event.subscribe();
|
|
7384
|
+
await client2.session.promptAsync({
|
|
7379
7385
|
path: { id: opencodeSessionId },
|
|
7380
7386
|
body: {
|
|
7381
7387
|
...modelConfig && { model: modelConfig },
|
|
7382
7388
|
parts: [{ type: "text", text: prompt }]
|
|
7383
7389
|
}
|
|
7384
7390
|
});
|
|
7385
|
-
|
|
7386
|
-
|
|
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
|
+
}
|
|
7387
7406
|
if (part.type === "text" && part.text) {
|
|
7388
|
-
|
|
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}`);
|
|
7389
7412
|
}
|
|
7390
7413
|
}
|
|
7391
7414
|
}
|
|
@@ -7405,7 +7428,14 @@ Context:
|
|
|
7405
7428
|
${content}
|
|
7406
7429
|
`;
|
|
7407
7430
|
return streamSSE(context, async (stream2) => {
|
|
7408
|
-
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
|
+
};
|
|
7409
7439
|
try {
|
|
7410
7440
|
await executeOpenCodePrompt(
|
|
7411
7441
|
formattedPrompt,
|
|
@@ -7418,11 +7448,12 @@ ${content}
|
|
|
7418
7448
|
}).catch(() => {
|
|
7419
7449
|
});
|
|
7420
7450
|
},
|
|
7421
|
-
sessionId
|
|
7451
|
+
sessionId,
|
|
7452
|
+
signal
|
|
7422
7453
|
);
|
|
7423
7454
|
if (!isAborted()) {
|
|
7424
7455
|
await stream2.writeSSE({
|
|
7425
|
-
data:
|
|
7456
|
+
data: COMPLETED_STATUS,
|
|
7426
7457
|
event: "status"
|
|
7427
7458
|
});
|
|
7428
7459
|
await stream2.writeSSE({ data: "", event: "done" });
|
|
@@ -7454,16 +7485,14 @@ ${stderr.trim()}` : errorMessage;
|
|
|
7454
7485
|
return context.json({ status: "ok" });
|
|
7455
7486
|
});
|
|
7456
7487
|
honoApplication.post("/undo", async (context) => {
|
|
7457
|
-
if (!
|
|
7458
|
-
return context.json({ status: "error", message: "No
|
|
7488
|
+
if (!lastMessageInfo) {
|
|
7489
|
+
return context.json({ status: "error", message: "No message to undo" });
|
|
7459
7490
|
}
|
|
7460
7491
|
try {
|
|
7461
7492
|
const client2 = await getOpenCodeClient();
|
|
7462
|
-
await client2.session.
|
|
7463
|
-
path: { id:
|
|
7464
|
-
body: {
|
|
7465
|
-
parts: [{ type: "text", text: "/undo" }]
|
|
7466
|
-
}
|
|
7493
|
+
await client2.session.revert({
|
|
7494
|
+
path: { id: lastMessageInfo.sessionId },
|
|
7495
|
+
body: { messageID: lastMessageInfo.messageId }
|
|
7467
7496
|
});
|
|
7468
7497
|
return context.json({ status: "ok" });
|
|
7469
7498
|
} catch (error) {
|
|
@@ -7483,7 +7512,7 @@ var startServer = async (port = DEFAULT_PORT) => {
|
|
|
7483
7512
|
const honoApplication = createServer();
|
|
7484
7513
|
serve({ fetch: honoApplication.fetch, port });
|
|
7485
7514
|
console.log(
|
|
7486
|
-
`${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)")}`
|
|
7487
7516
|
);
|
|
7488
7517
|
console.log(`- Local: ${import_picocolors.default.cyan(`http://localhost:${port}`)}`);
|
|
7489
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.86",
|
|
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.86"
|
|
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.86"
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
40
41
|
"dev": "tsup --watch",
|