@react-grab/opencode 0.0.78 → 0.0.80
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 +498 -4
- package/dist/cli.js +498 -4
- package/dist/client.cjs +37 -22
- package/dist/client.global.js +3 -3
- package/dist/client.js +37 -22
- package/dist/server.cjs +167 -39
- package/dist/server.js +167 -38
- package/package.json +6 -2
package/dist/client.js
CHANGED
|
@@ -57,19 +57,33 @@ var streamSSE = async function* (stream, signal) {
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
var streamFromServer = async function* (serverUrl, context, signal) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
const sessionId = context.sessionId;
|
|
61
|
+
const handleAbort = () => {
|
|
62
|
+
if (sessionId) {
|
|
63
|
+
fetch(`${serverUrl}/abort/${sessionId}`, { method: "POST" }).catch(
|
|
64
|
+
() => {
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
signal.addEventListener("abort", handleAbort);
|
|
70
|
+
try {
|
|
71
|
+
const response = await fetch(`${serverUrl}/agent`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: JSON.stringify(context),
|
|
75
|
+
signal
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(`Server error: ${response.status}`);
|
|
79
|
+
}
|
|
80
|
+
if (!response.body) {
|
|
81
|
+
throw new Error("No response body");
|
|
82
|
+
}
|
|
83
|
+
yield* streamSSE(response.body, signal);
|
|
84
|
+
} finally {
|
|
85
|
+
signal.removeEventListener("abort", handleAbort);
|
|
71
86
|
}
|
|
72
|
-
yield* streamSSE(response.body, signal);
|
|
73
87
|
};
|
|
74
88
|
var createOpencodeAgentProvider = (options = {}) => {
|
|
75
89
|
const { serverUrl = DEFAULT_SERVER_URL, getOptions } = options;
|
|
@@ -105,6 +119,7 @@ var createOpencodeAgentProvider = (options = {}) => {
|
|
|
105
119
|
yield* streamFromServer(serverUrl, combinedContext, signal);
|
|
106
120
|
},
|
|
107
121
|
supportsResume: true,
|
|
122
|
+
supportsFollowUp: true,
|
|
108
123
|
checkConnection: async () => {
|
|
109
124
|
const now = Date.now();
|
|
110
125
|
if (connectionCache && now - connectionCache.timestamp < CONNECTION_CHECK_TTL_MS) {
|
|
@@ -131,26 +146,26 @@ var createOpencodeAgentProvider = (options = {}) => {
|
|
|
131
146
|
var attachAgent = async () => {
|
|
132
147
|
if (typeof window === "undefined") return;
|
|
133
148
|
const provider = createOpencodeAgentProvider();
|
|
149
|
+
const attach = (api2) => {
|
|
150
|
+
api2.setAgent({ provider, storage: sessionStorage });
|
|
151
|
+
};
|
|
134
152
|
const api = window.__REACT_GRAB__;
|
|
135
153
|
if (api) {
|
|
136
|
-
api
|
|
154
|
+
attach(api);
|
|
137
155
|
return;
|
|
138
156
|
}
|
|
139
157
|
window.addEventListener(
|
|
140
158
|
"react-grab:init",
|
|
141
159
|
(event) => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (customEvent.detail && typeof customEvent.detail.setAgent === "function") {
|
|
145
|
-
customEvent.detail.setAgent({
|
|
146
|
-
provider,
|
|
147
|
-
storage: sessionStorage
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
160
|
+
const customEvent = event;
|
|
161
|
+
attach(customEvent.detail);
|
|
151
162
|
},
|
|
152
163
|
{ once: true }
|
|
153
164
|
);
|
|
165
|
+
const apiAfterListener = window.__REACT_GRAB__;
|
|
166
|
+
if (apiAfterListener) {
|
|
167
|
+
attach(apiAfterListener);
|
|
168
|
+
}
|
|
154
169
|
};
|
|
155
170
|
attachAgent();
|
|
156
171
|
|
package/dist/server.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var net = require('net');
|
|
4
3
|
var child_process = require('child_process');
|
|
5
4
|
var http = require('http');
|
|
6
5
|
var http2 = require('http2');
|
|
@@ -11,7 +10,6 @@ var url = require('url');
|
|
|
11
10
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
12
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
12
|
|
|
14
|
-
var net__default = /*#__PURE__*/_interopDefault(net);
|
|
15
13
|
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
16
14
|
|
|
17
15
|
var __create = Object.create;
|
|
@@ -20,7 +18,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
20
18
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
21
19
|
var __getProtoOf = Object.getPrototypeOf;
|
|
22
20
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
23
|
-
var
|
|
21
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
22
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
23
|
+
}) : x)(function(x) {
|
|
24
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
25
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
26
|
+
});
|
|
27
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
24
28
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
25
29
|
};
|
|
26
30
|
var __copyProps = (to, from, except, desc) => {
|
|
@@ -40,6 +44,84 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
40
44
|
mod
|
|
41
45
|
));
|
|
42
46
|
|
|
47
|
+
// ../../node_modules/.pnpm/shell-exec@1.0.2/node_modules/shell-exec/index.js
|
|
48
|
+
var require_shell_exec = __commonJS({
|
|
49
|
+
"../../node_modules/.pnpm/shell-exec@1.0.2/node_modules/shell-exec/index.js"(exports, module) {
|
|
50
|
+
var childProcess = __require("child_process");
|
|
51
|
+
function shellExec(cmd = "", opts = {}) {
|
|
52
|
+
if (Array.isArray(cmd)) {
|
|
53
|
+
cmd = cmd.join(";");
|
|
54
|
+
}
|
|
55
|
+
opts = Object.assign({ stdio: "pipe", cwd: process.cwd() }, opts);
|
|
56
|
+
let child;
|
|
57
|
+
const shell = process.platform === "win32" ? { cmd: "cmd", arg: "/C" } : { cmd: "sh", arg: "-c" };
|
|
58
|
+
try {
|
|
59
|
+
child = childProcess.spawn(shell.cmd, [shell.arg, cmd], opts);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return Promise.reject(error);
|
|
62
|
+
}
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
let stdout = "";
|
|
65
|
+
let stderr = "";
|
|
66
|
+
if (child.stdout) {
|
|
67
|
+
child.stdout.on("data", (data) => {
|
|
68
|
+
stdout += data;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (child.stderr) {
|
|
72
|
+
child.stderr.on("data", (data) => {
|
|
73
|
+
stderr += data;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
child.on("error", (error) => {
|
|
77
|
+
resolve({ error, stdout, stderr, cmd });
|
|
78
|
+
});
|
|
79
|
+
child.on("close", (code) => {
|
|
80
|
+
resolve({ stdout, stderr, cmd, code });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
module.exports = shellExec;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ../../node_modules/.pnpm/kill-port@2.0.1/node_modules/kill-port/index.js
|
|
89
|
+
var require_kill_port = __commonJS({
|
|
90
|
+
"../../node_modules/.pnpm/kill-port@2.0.1/node_modules/kill-port/index.js"(exports, module) {
|
|
91
|
+
var sh = require_shell_exec();
|
|
92
|
+
module.exports = function(port, method = "tcp") {
|
|
93
|
+
port = Number.parseInt(port);
|
|
94
|
+
if (!port) {
|
|
95
|
+
return Promise.reject(new Error("Invalid port number provided"));
|
|
96
|
+
}
|
|
97
|
+
if (process.platform === "win32") {
|
|
98
|
+
return sh("netstat -nao").then((res) => {
|
|
99
|
+
const { stdout } = res;
|
|
100
|
+
if (!stdout) return res;
|
|
101
|
+
const lines = stdout.split("\n");
|
|
102
|
+
const lineWithLocalPortRegEx = new RegExp(`^ *${method.toUpperCase()} *[^ ]*:${port}`, "gm");
|
|
103
|
+
const linesWithLocalPort = lines.filter((line) => line.match(lineWithLocalPortRegEx));
|
|
104
|
+
const pids = linesWithLocalPort.reduce((acc, line) => {
|
|
105
|
+
const match2 = line.match(/(\d*)\w*(\n|$)/gm);
|
|
106
|
+
return match2 && match2[0] && !acc.includes(match2[0]) ? acc.concat(match2[0]) : acc;
|
|
107
|
+
}, []);
|
|
108
|
+
return sh(`TaskKill /F /PID ${pids.join(" /PID ")}`);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return sh("lsof -i -P").then((res) => {
|
|
112
|
+
const { stdout } = res;
|
|
113
|
+
if (!stdout) return res;
|
|
114
|
+
const lines = stdout.split("\n");
|
|
115
|
+
const existProccess = lines.filter((line) => line.match(new RegExp(`:*${port}`))).length > 0;
|
|
116
|
+
if (!existProccess) return Promise.reject(new Error("No process running on port"));
|
|
117
|
+
return sh(
|
|
118
|
+
`lsof -i ${method === "udp" ? "udp" : "tcp"}:${port} | grep ${method === "udp" ? "UDP" : "LISTEN"} | awk '{print $2}' | xargs kill -9`
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
43
125
|
// ../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
44
126
|
var require_picocolors = __commonJS({
|
|
45
127
|
"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports, module) {
|
|
@@ -1625,6 +1707,9 @@ async function createOpencode(options) {
|
|
|
1625
1707
|
};
|
|
1626
1708
|
}
|
|
1627
1709
|
|
|
1710
|
+
// src/server.ts
|
|
1711
|
+
var import_kill_port = __toESM(require_kill_port());
|
|
1712
|
+
|
|
1628
1713
|
// ../../node_modules/.pnpm/hono@4.10.7/node_modules/hono/dist/compose.js
|
|
1629
1714
|
var compose = (middleware, onError, onNotFound) => {
|
|
1630
1715
|
return (context, next) => {
|
|
@@ -3877,8 +3962,11 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
3877
3962
|
|
|
3878
3963
|
// src/constants.ts
|
|
3879
3964
|
var DEFAULT_PORT = 6567;
|
|
3880
|
-
var VERSION = "0.0.
|
|
3965
|
+
var VERSION = "0.0.80";
|
|
3881
3966
|
var opencodeInstance = null;
|
|
3967
|
+
var sessionMap = /* @__PURE__ */ new Map();
|
|
3968
|
+
var abortedSessions = /* @__PURE__ */ new Set();
|
|
3969
|
+
var lastOpencodeSessionId;
|
|
3882
3970
|
var getOpencodeClient = async () => {
|
|
3883
3971
|
if (!opencodeInstance) {
|
|
3884
3972
|
const instance = await createOpencode({
|
|
@@ -3889,22 +3977,31 @@ var getOpencodeClient = async () => {
|
|
|
3889
3977
|
}
|
|
3890
3978
|
return opencodeInstance.client;
|
|
3891
3979
|
};
|
|
3892
|
-
var executeOpencodePrompt = async (prompt, options, onStatus) => {
|
|
3980
|
+
var executeOpencodePrompt = async (prompt, options, onStatus, reactGrabSessionId) => {
|
|
3893
3981
|
const client2 = await getOpencodeClient();
|
|
3894
3982
|
onStatus?.("Thinking...");
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3983
|
+
let opencodeSessionId;
|
|
3984
|
+
if (reactGrabSessionId && sessionMap.has(reactGrabSessionId)) {
|
|
3985
|
+
opencodeSessionId = sessionMap.get(reactGrabSessionId);
|
|
3986
|
+
} else {
|
|
3987
|
+
const sessionResponse = await client2.session.create({
|
|
3988
|
+
body: { title: "React Grab Session" }
|
|
3989
|
+
});
|
|
3990
|
+
if (sessionResponse.error || !sessionResponse.data) {
|
|
3991
|
+
throw new Error("Failed to create session");
|
|
3992
|
+
}
|
|
3993
|
+
opencodeSessionId = sessionResponse.data.id;
|
|
3994
|
+
if (reactGrabSessionId) {
|
|
3995
|
+
sessionMap.set(reactGrabSessionId, opencodeSessionId);
|
|
3996
|
+
}
|
|
3900
3997
|
}
|
|
3901
|
-
|
|
3998
|
+
lastOpencodeSessionId = opencodeSessionId;
|
|
3902
3999
|
const modelConfig = options?.model ? {
|
|
3903
4000
|
providerID: options.model.split("/")[0],
|
|
3904
4001
|
modelID: options.model.split("/")[1] || options.model
|
|
3905
4002
|
} : void 0;
|
|
3906
4003
|
const promptResponse = await client2.session.prompt({
|
|
3907
|
-
path: { id:
|
|
4004
|
+
path: { id: opencodeSessionId },
|
|
3908
4005
|
body: {
|
|
3909
4006
|
...modelConfig && { model: modelConfig },
|
|
3910
4007
|
parts: [{ type: "text", text: prompt }]
|
|
@@ -3917,65 +4014,96 @@ var executeOpencodePrompt = async (prompt, options, onStatus) => {
|
|
|
3917
4014
|
}
|
|
3918
4015
|
}
|
|
3919
4016
|
}
|
|
4017
|
+
return opencodeSessionId;
|
|
3920
4018
|
};
|
|
3921
4019
|
var createServer = () => {
|
|
3922
4020
|
const honoApplication = new Hono2();
|
|
3923
|
-
honoApplication.use("
|
|
4021
|
+
honoApplication.use("*", cors());
|
|
3924
4022
|
honoApplication.post("/agent", async (context) => {
|
|
3925
4023
|
const requestBody = await context.req.json();
|
|
3926
|
-
const { content, prompt, options } = requestBody;
|
|
3927
|
-
const
|
|
4024
|
+
const { content, prompt, options, sessionId } = requestBody;
|
|
4025
|
+
const isFollowUp = Boolean(sessionId && sessionMap.has(sessionId));
|
|
4026
|
+
const formattedPrompt = isFollowUp ? prompt : `
|
|
3928
4027
|
User Request: ${prompt}
|
|
3929
4028
|
|
|
3930
4029
|
Context:
|
|
3931
4030
|
${content}
|
|
3932
4031
|
`;
|
|
3933
4032
|
return streamSSE(context, async (stream2) => {
|
|
4033
|
+
const isAborted = () => sessionId && abortedSessions.has(sessionId);
|
|
3934
4034
|
try {
|
|
3935
4035
|
await executeOpencodePrompt(
|
|
3936
4036
|
formattedPrompt,
|
|
3937
4037
|
options,
|
|
3938
4038
|
(text) => {
|
|
4039
|
+
if (isAborted()) return;
|
|
3939
4040
|
stream2.writeSSE({
|
|
3940
4041
|
data: text,
|
|
3941
4042
|
event: "status"
|
|
3942
4043
|
}).catch(() => {
|
|
3943
4044
|
});
|
|
3944
|
-
}
|
|
4045
|
+
},
|
|
4046
|
+
sessionId
|
|
3945
4047
|
);
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
4048
|
+
if (!isAborted()) {
|
|
4049
|
+
await stream2.writeSSE({
|
|
4050
|
+
data: "Completed successfully",
|
|
4051
|
+
event: "status"
|
|
4052
|
+
});
|
|
4053
|
+
await stream2.writeSSE({ data: "", event: "done" });
|
|
4054
|
+
}
|
|
3951
4055
|
} catch (error) {
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
4056
|
+
if (!isAborted()) {
|
|
4057
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4058
|
+
const stderr = error instanceof Error && "stderr" in error ? String(error.stderr) : void 0;
|
|
4059
|
+
const fullError = stderr && stderr.trim() ? `${errorMessage}
|
|
4060
|
+
|
|
4061
|
+
stderr:
|
|
4062
|
+
${stderr.trim()}` : errorMessage;
|
|
4063
|
+
await stream2.writeSSE({
|
|
4064
|
+
data: `Error: ${fullError}`,
|
|
4065
|
+
event: "error"
|
|
4066
|
+
});
|
|
4067
|
+
await stream2.writeSSE({ data: "", event: "done" });
|
|
4068
|
+
}
|
|
4069
|
+
} finally {
|
|
4070
|
+
if (sessionId) {
|
|
4071
|
+
abortedSessions.delete(sessionId);
|
|
4072
|
+
}
|
|
3958
4073
|
}
|
|
3959
4074
|
});
|
|
3960
4075
|
});
|
|
4076
|
+
honoApplication.post("/abort/:sessionId", (context) => {
|
|
4077
|
+
const { sessionId } = context.req.param();
|
|
4078
|
+
abortedSessions.add(sessionId);
|
|
4079
|
+
return context.json({ status: "ok" });
|
|
4080
|
+
});
|
|
4081
|
+
honoApplication.post("/undo", async (context) => {
|
|
4082
|
+
if (!lastOpencodeSessionId) {
|
|
4083
|
+
return context.json({ status: "error", message: "No session to undo" });
|
|
4084
|
+
}
|
|
4085
|
+
try {
|
|
4086
|
+
const client2 = await getOpencodeClient();
|
|
4087
|
+
await client2.session.prompt({
|
|
4088
|
+
path: { id: lastOpencodeSessionId },
|
|
4089
|
+
body: {
|
|
4090
|
+
parts: [{ type: "text", text: "/undo" }]
|
|
4091
|
+
}
|
|
4092
|
+
});
|
|
4093
|
+
return context.json({ status: "ok" });
|
|
4094
|
+
} catch (error) {
|
|
4095
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4096
|
+
return context.json({ status: "error", message: errorMessage });
|
|
4097
|
+
}
|
|
4098
|
+
});
|
|
3961
4099
|
honoApplication.get("/health", (context) => {
|
|
3962
4100
|
return context.json({ status: "ok", provider: "opencode" });
|
|
3963
4101
|
});
|
|
3964
4102
|
return honoApplication;
|
|
3965
4103
|
};
|
|
3966
|
-
var isPortInUse = (port) => new Promise((resolve) => {
|
|
3967
|
-
const netServer = net__default.default.createServer();
|
|
3968
|
-
netServer.once("error", () => resolve(true));
|
|
3969
|
-
netServer.once("listening", () => {
|
|
3970
|
-
netServer.close();
|
|
3971
|
-
resolve(false);
|
|
3972
|
-
});
|
|
3973
|
-
netServer.listen(port);
|
|
3974
|
-
});
|
|
3975
4104
|
var startServer = async (port = DEFAULT_PORT) => {
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
}
|
|
4105
|
+
await (0, import_kill_port.default)(port).catch(() => {
|
|
4106
|
+
});
|
|
3979
4107
|
const honoApplication = createServer();
|
|
3980
4108
|
serve({ fetch: honoApplication.fetch, port });
|
|
3981
4109
|
console.log(`${import_picocolors.default.magenta("\u269B")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(Opencode)")}`);
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import net from 'net';
|
|
2
1
|
import { spawn } from 'child_process';
|
|
3
2
|
import { createServer as createServer$1 } from 'http';
|
|
4
3
|
import { Http2ServerRequest } from 'http2';
|
|
@@ -12,7 +11,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
12
11
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
12
|
var __getProtoOf = Object.getPrototypeOf;
|
|
14
13
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
15
|
-
var
|
|
14
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
15
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
16
|
+
}) : x)(function(x) {
|
|
17
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
18
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
19
|
+
});
|
|
20
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
21
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
22
|
};
|
|
18
23
|
var __copyProps = (to, from, except, desc) => {
|
|
@@ -32,6 +37,84 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
37
|
mod
|
|
33
38
|
));
|
|
34
39
|
|
|
40
|
+
// ../../node_modules/.pnpm/shell-exec@1.0.2/node_modules/shell-exec/index.js
|
|
41
|
+
var require_shell_exec = __commonJS({
|
|
42
|
+
"../../node_modules/.pnpm/shell-exec@1.0.2/node_modules/shell-exec/index.js"(exports, module) {
|
|
43
|
+
var childProcess = __require("child_process");
|
|
44
|
+
function shellExec(cmd = "", opts = {}) {
|
|
45
|
+
if (Array.isArray(cmd)) {
|
|
46
|
+
cmd = cmd.join(";");
|
|
47
|
+
}
|
|
48
|
+
opts = Object.assign({ stdio: "pipe", cwd: process.cwd() }, opts);
|
|
49
|
+
let child;
|
|
50
|
+
const shell = process.platform === "win32" ? { cmd: "cmd", arg: "/C" } : { cmd: "sh", arg: "-c" };
|
|
51
|
+
try {
|
|
52
|
+
child = childProcess.spawn(shell.cmd, [shell.arg, cmd], opts);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return Promise.reject(error);
|
|
55
|
+
}
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
let stdout = "";
|
|
58
|
+
let stderr = "";
|
|
59
|
+
if (child.stdout) {
|
|
60
|
+
child.stdout.on("data", (data) => {
|
|
61
|
+
stdout += data;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (child.stderr) {
|
|
65
|
+
child.stderr.on("data", (data) => {
|
|
66
|
+
stderr += data;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
child.on("error", (error) => {
|
|
70
|
+
resolve({ error, stdout, stderr, cmd });
|
|
71
|
+
});
|
|
72
|
+
child.on("close", (code) => {
|
|
73
|
+
resolve({ stdout, stderr, cmd, code });
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
module.exports = shellExec;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ../../node_modules/.pnpm/kill-port@2.0.1/node_modules/kill-port/index.js
|
|
82
|
+
var require_kill_port = __commonJS({
|
|
83
|
+
"../../node_modules/.pnpm/kill-port@2.0.1/node_modules/kill-port/index.js"(exports, module) {
|
|
84
|
+
var sh = require_shell_exec();
|
|
85
|
+
module.exports = function(port, method = "tcp") {
|
|
86
|
+
port = Number.parseInt(port);
|
|
87
|
+
if (!port) {
|
|
88
|
+
return Promise.reject(new Error("Invalid port number provided"));
|
|
89
|
+
}
|
|
90
|
+
if (process.platform === "win32") {
|
|
91
|
+
return sh("netstat -nao").then((res) => {
|
|
92
|
+
const { stdout } = res;
|
|
93
|
+
if (!stdout) return res;
|
|
94
|
+
const lines = stdout.split("\n");
|
|
95
|
+
const lineWithLocalPortRegEx = new RegExp(`^ *${method.toUpperCase()} *[^ ]*:${port}`, "gm");
|
|
96
|
+
const linesWithLocalPort = lines.filter((line) => line.match(lineWithLocalPortRegEx));
|
|
97
|
+
const pids = linesWithLocalPort.reduce((acc, line) => {
|
|
98
|
+
const match2 = line.match(/(\d*)\w*(\n|$)/gm);
|
|
99
|
+
return match2 && match2[0] && !acc.includes(match2[0]) ? acc.concat(match2[0]) : acc;
|
|
100
|
+
}, []);
|
|
101
|
+
return sh(`TaskKill /F /PID ${pids.join(" /PID ")}`);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return sh("lsof -i -P").then((res) => {
|
|
105
|
+
const { stdout } = res;
|
|
106
|
+
if (!stdout) return res;
|
|
107
|
+
const lines = stdout.split("\n");
|
|
108
|
+
const existProccess = lines.filter((line) => line.match(new RegExp(`:*${port}`))).length > 0;
|
|
109
|
+
if (!existProccess) return Promise.reject(new Error("No process running on port"));
|
|
110
|
+
return sh(
|
|
111
|
+
`lsof -i ${method === "udp" ? "udp" : "tcp"}:${port} | grep ${method === "udp" ? "UDP" : "LISTEN"} | awk '{print $2}' | xargs kill -9`
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
35
118
|
// ../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
36
119
|
var require_picocolors = __commonJS({
|
|
37
120
|
"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports, module) {
|
|
@@ -1617,6 +1700,9 @@ async function createOpencode(options) {
|
|
|
1617
1700
|
};
|
|
1618
1701
|
}
|
|
1619
1702
|
|
|
1703
|
+
// src/server.ts
|
|
1704
|
+
var import_kill_port = __toESM(require_kill_port());
|
|
1705
|
+
|
|
1620
1706
|
// ../../node_modules/.pnpm/hono@4.10.7/node_modules/hono/dist/compose.js
|
|
1621
1707
|
var compose = (middleware, onError, onNotFound) => {
|
|
1622
1708
|
return (context, next) => {
|
|
@@ -3869,8 +3955,11 @@ var import_picocolors = __toESM(require_picocolors());
|
|
|
3869
3955
|
|
|
3870
3956
|
// src/constants.ts
|
|
3871
3957
|
var DEFAULT_PORT = 6567;
|
|
3872
|
-
var VERSION = "0.0.
|
|
3958
|
+
var VERSION = "0.0.80";
|
|
3873
3959
|
var opencodeInstance = null;
|
|
3960
|
+
var sessionMap = /* @__PURE__ */ new Map();
|
|
3961
|
+
var abortedSessions = /* @__PURE__ */ new Set();
|
|
3962
|
+
var lastOpencodeSessionId;
|
|
3874
3963
|
var getOpencodeClient = async () => {
|
|
3875
3964
|
if (!opencodeInstance) {
|
|
3876
3965
|
const instance = await createOpencode({
|
|
@@ -3881,22 +3970,31 @@ var getOpencodeClient = async () => {
|
|
|
3881
3970
|
}
|
|
3882
3971
|
return opencodeInstance.client;
|
|
3883
3972
|
};
|
|
3884
|
-
var executeOpencodePrompt = async (prompt, options, onStatus) => {
|
|
3973
|
+
var executeOpencodePrompt = async (prompt, options, onStatus, reactGrabSessionId) => {
|
|
3885
3974
|
const client2 = await getOpencodeClient();
|
|
3886
3975
|
onStatus?.("Thinking...");
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3976
|
+
let opencodeSessionId;
|
|
3977
|
+
if (reactGrabSessionId && sessionMap.has(reactGrabSessionId)) {
|
|
3978
|
+
opencodeSessionId = sessionMap.get(reactGrabSessionId);
|
|
3979
|
+
} else {
|
|
3980
|
+
const sessionResponse = await client2.session.create({
|
|
3981
|
+
body: { title: "React Grab Session" }
|
|
3982
|
+
});
|
|
3983
|
+
if (sessionResponse.error || !sessionResponse.data) {
|
|
3984
|
+
throw new Error("Failed to create session");
|
|
3985
|
+
}
|
|
3986
|
+
opencodeSessionId = sessionResponse.data.id;
|
|
3987
|
+
if (reactGrabSessionId) {
|
|
3988
|
+
sessionMap.set(reactGrabSessionId, opencodeSessionId);
|
|
3989
|
+
}
|
|
3892
3990
|
}
|
|
3893
|
-
|
|
3991
|
+
lastOpencodeSessionId = opencodeSessionId;
|
|
3894
3992
|
const modelConfig = options?.model ? {
|
|
3895
3993
|
providerID: options.model.split("/")[0],
|
|
3896
3994
|
modelID: options.model.split("/")[1] || options.model
|
|
3897
3995
|
} : void 0;
|
|
3898
3996
|
const promptResponse = await client2.session.prompt({
|
|
3899
|
-
path: { id:
|
|
3997
|
+
path: { id: opencodeSessionId },
|
|
3900
3998
|
body: {
|
|
3901
3999
|
...modelConfig && { model: modelConfig },
|
|
3902
4000
|
parts: [{ type: "text", text: prompt }]
|
|
@@ -3909,65 +4007,96 @@ var executeOpencodePrompt = async (prompt, options, onStatus) => {
|
|
|
3909
4007
|
}
|
|
3910
4008
|
}
|
|
3911
4009
|
}
|
|
4010
|
+
return opencodeSessionId;
|
|
3912
4011
|
};
|
|
3913
4012
|
var createServer = () => {
|
|
3914
4013
|
const honoApplication = new Hono2();
|
|
3915
|
-
honoApplication.use("
|
|
4014
|
+
honoApplication.use("*", cors());
|
|
3916
4015
|
honoApplication.post("/agent", async (context) => {
|
|
3917
4016
|
const requestBody = await context.req.json();
|
|
3918
|
-
const { content, prompt, options } = requestBody;
|
|
3919
|
-
const
|
|
4017
|
+
const { content, prompt, options, sessionId } = requestBody;
|
|
4018
|
+
const isFollowUp = Boolean(sessionId && sessionMap.has(sessionId));
|
|
4019
|
+
const formattedPrompt = isFollowUp ? prompt : `
|
|
3920
4020
|
User Request: ${prompt}
|
|
3921
4021
|
|
|
3922
4022
|
Context:
|
|
3923
4023
|
${content}
|
|
3924
4024
|
`;
|
|
3925
4025
|
return streamSSE(context, async (stream2) => {
|
|
4026
|
+
const isAborted = () => sessionId && abortedSessions.has(sessionId);
|
|
3926
4027
|
try {
|
|
3927
4028
|
await executeOpencodePrompt(
|
|
3928
4029
|
formattedPrompt,
|
|
3929
4030
|
options,
|
|
3930
4031
|
(text) => {
|
|
4032
|
+
if (isAborted()) return;
|
|
3931
4033
|
stream2.writeSSE({
|
|
3932
4034
|
data: text,
|
|
3933
4035
|
event: "status"
|
|
3934
4036
|
}).catch(() => {
|
|
3935
4037
|
});
|
|
3936
|
-
}
|
|
4038
|
+
},
|
|
4039
|
+
sessionId
|
|
3937
4040
|
);
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
4041
|
+
if (!isAborted()) {
|
|
4042
|
+
await stream2.writeSSE({
|
|
4043
|
+
data: "Completed successfully",
|
|
4044
|
+
event: "status"
|
|
4045
|
+
});
|
|
4046
|
+
await stream2.writeSSE({ data: "", event: "done" });
|
|
4047
|
+
}
|
|
3943
4048
|
} catch (error) {
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
4049
|
+
if (!isAborted()) {
|
|
4050
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4051
|
+
const stderr = error instanceof Error && "stderr" in error ? String(error.stderr) : void 0;
|
|
4052
|
+
const fullError = stderr && stderr.trim() ? `${errorMessage}
|
|
4053
|
+
|
|
4054
|
+
stderr:
|
|
4055
|
+
${stderr.trim()}` : errorMessage;
|
|
4056
|
+
await stream2.writeSSE({
|
|
4057
|
+
data: `Error: ${fullError}`,
|
|
4058
|
+
event: "error"
|
|
4059
|
+
});
|
|
4060
|
+
await stream2.writeSSE({ data: "", event: "done" });
|
|
4061
|
+
}
|
|
4062
|
+
} finally {
|
|
4063
|
+
if (sessionId) {
|
|
4064
|
+
abortedSessions.delete(sessionId);
|
|
4065
|
+
}
|
|
3950
4066
|
}
|
|
3951
4067
|
});
|
|
3952
4068
|
});
|
|
4069
|
+
honoApplication.post("/abort/:sessionId", (context) => {
|
|
4070
|
+
const { sessionId } = context.req.param();
|
|
4071
|
+
abortedSessions.add(sessionId);
|
|
4072
|
+
return context.json({ status: "ok" });
|
|
4073
|
+
});
|
|
4074
|
+
honoApplication.post("/undo", async (context) => {
|
|
4075
|
+
if (!lastOpencodeSessionId) {
|
|
4076
|
+
return context.json({ status: "error", message: "No session to undo" });
|
|
4077
|
+
}
|
|
4078
|
+
try {
|
|
4079
|
+
const client2 = await getOpencodeClient();
|
|
4080
|
+
await client2.session.prompt({
|
|
4081
|
+
path: { id: lastOpencodeSessionId },
|
|
4082
|
+
body: {
|
|
4083
|
+
parts: [{ type: "text", text: "/undo" }]
|
|
4084
|
+
}
|
|
4085
|
+
});
|
|
4086
|
+
return context.json({ status: "ok" });
|
|
4087
|
+
} catch (error) {
|
|
4088
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
4089
|
+
return context.json({ status: "error", message: errorMessage });
|
|
4090
|
+
}
|
|
4091
|
+
});
|
|
3953
4092
|
honoApplication.get("/health", (context) => {
|
|
3954
4093
|
return context.json({ status: "ok", provider: "opencode" });
|
|
3955
4094
|
});
|
|
3956
4095
|
return honoApplication;
|
|
3957
4096
|
};
|
|
3958
|
-
var isPortInUse = (port) => new Promise((resolve) => {
|
|
3959
|
-
const netServer = net.createServer();
|
|
3960
|
-
netServer.once("error", () => resolve(true));
|
|
3961
|
-
netServer.once("listening", () => {
|
|
3962
|
-
netServer.close();
|
|
3963
|
-
resolve(false);
|
|
3964
|
-
});
|
|
3965
|
-
netServer.listen(port);
|
|
3966
|
-
});
|
|
3967
4097
|
var startServer = async (port = DEFAULT_PORT) => {
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
}
|
|
4098
|
+
await (0, import_kill_port.default)(port).catch(() => {
|
|
4099
|
+
});
|
|
3971
4100
|
const honoApplication = createServer();
|
|
3972
4101
|
serve({ fetch: honoApplication.fetch, port });
|
|
3973
4102
|
console.log(`${import_picocolors.default.magenta("\u269B")} ${import_picocolors.default.bold("React Grab")} ${import_picocolors.default.gray(VERSION)} ${import_picocolors.default.dim("(Opencode)")}`);
|