@menteeai/menteeswe 0.1.5 → 0.1.9
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.js +401 -227
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -96,6 +96,102 @@ var init_base = __esm({
|
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
// src/tools/exec.ts
|
|
100
|
+
import { spawn } from "child_process";
|
|
101
|
+
function killTree(child) {
|
|
102
|
+
if (child.pid === void 0) return;
|
|
103
|
+
if (process.platform === "win32") {
|
|
104
|
+
spawn(`taskkill /pid ${String(child.pid)} /T /F`, { stdio: "ignore", shell: true });
|
|
105
|
+
} else {
|
|
106
|
+
child.kill("SIGKILL");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function killActiveChild() {
|
|
110
|
+
if (!activeChild) return;
|
|
111
|
+
try {
|
|
112
|
+
activeChild.kill("SIGKILL");
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
killTree(activeChild);
|
|
116
|
+
}
|
|
117
|
+
async function runShellCommand(command, cwd, timeoutMs = 12e4, maxChars = 1e5) {
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
const started = Date.now();
|
|
120
|
+
const child = spawn(command, {
|
|
121
|
+
shell: true,
|
|
122
|
+
cwd,
|
|
123
|
+
env: { ...process.env, MENTEE: "1" },
|
|
124
|
+
windowsHide: true
|
|
125
|
+
});
|
|
126
|
+
activeChild = child;
|
|
127
|
+
let stdout = "";
|
|
128
|
+
let stderr = "";
|
|
129
|
+
let killed = false;
|
|
130
|
+
const timer = setTimeout(() => {
|
|
131
|
+
killed = true;
|
|
132
|
+
killTree(child);
|
|
133
|
+
}, timeoutMs);
|
|
134
|
+
child.stdout?.on("data", (chunk) => {
|
|
135
|
+
if (stdout.length < maxChars * 2) stdout += chunk.toString("utf8");
|
|
136
|
+
});
|
|
137
|
+
child.stderr?.on("data", (chunk) => {
|
|
138
|
+
if (stderr.length < maxChars * 2) stderr += chunk.toString("utf8");
|
|
139
|
+
});
|
|
140
|
+
child.on("error", (error) => {
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
if (activeChild === child) activeChild = null;
|
|
143
|
+
resolve({
|
|
144
|
+
exitCode: -1,
|
|
145
|
+
stdout,
|
|
146
|
+
stderr: `${stderr}
|
|
147
|
+
${error.message}`.trim(),
|
|
148
|
+
durationMs: Date.now() - started
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
child.on("close", (code) => {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
if (activeChild === child) activeChild = null;
|
|
154
|
+
resolve({
|
|
155
|
+
exitCode: killed ? "timeout" : code ?? -1,
|
|
156
|
+
stdout,
|
|
157
|
+
stderr,
|
|
158
|
+
durationMs: Date.now() - started
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function formatOutcome(outcome, maxChars = 1e5) {
|
|
164
|
+
const cut = (text, limit) => {
|
|
165
|
+
if (text.length <= limit) return { text, truncated: false };
|
|
166
|
+
const head = Math.floor(limit * 0.7);
|
|
167
|
+
return {
|
|
168
|
+
text: `${text.slice(0, head)}
|
|
169
|
+
...[truncated, ${text.length - head} chars removed]...`,
|
|
170
|
+
truncated: true
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
const out = cut(outcome.stdout.trim(), maxChars);
|
|
174
|
+
const err = cut(outcome.stderr.trim(), Math.floor(maxChars / 2));
|
|
175
|
+
const sections = [`exit_code: ${outcome.exitCode}`, `duration_ms: ${outcome.durationMs}`];
|
|
176
|
+
if (out.text) sections.push(`stdout:
|
|
177
|
+
${out.text}`);
|
|
178
|
+
if (err.text) sections.push(`stderr:
|
|
179
|
+
${err.text}`);
|
|
180
|
+
if (!out.text && !err.text) sections.push("(no output)");
|
|
181
|
+
return {
|
|
182
|
+
output: sections.join("\n"),
|
|
183
|
+
success: outcome.exitCode === 0,
|
|
184
|
+
truncated: out.truncated || err.truncated
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
var activeChild;
|
|
188
|
+
var init_exec = __esm({
|
|
189
|
+
"src/tools/exec.ts"() {
|
|
190
|
+
"use strict";
|
|
191
|
+
activeChild = null;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
99
195
|
// src/events.ts
|
|
100
196
|
var EventBus;
|
|
101
197
|
var init_events = __esm({
|
|
@@ -155,17 +251,17 @@ function friendlyToolName(name) {
|
|
|
155
251
|
function formatEvent(event) {
|
|
156
252
|
switch (event.type) {
|
|
157
253
|
case "task_started":
|
|
158
|
-
return chalk.bold.magenta(`\u25B8
|
|
254
|
+
return "\n" + chalk.bold.magenta(`\u25B8 Task: ${event.message ?? ""}`);
|
|
159
255
|
case "model_request":
|
|
160
|
-
return chalk.gray("\u25CF
|
|
256
|
+
return chalk.gray("\u25CF thinking...");
|
|
161
257
|
case "info":
|
|
162
|
-
return chalk.cyan(
|
|
258
|
+
return chalk.cyan.bold(`\u{1F4AD} ${event.message ?? ""}`);
|
|
163
259
|
case "tool_started": {
|
|
164
260
|
const tool = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
165
261
|
const color = CATEGORY_COLOR[TOOL_CATEGORY[tool] ?? "other"];
|
|
166
262
|
const friendly = friendlyToolName(tool);
|
|
167
263
|
const preview = tool && event.message?.startsWith(tool) ? event.message.slice(tool.length).trim() : event.message ?? "";
|
|
168
|
-
return color(`\u2699
|
|
264
|
+
return color(`\u2699 ${friendly} (${preview})...`);
|
|
169
265
|
}
|
|
170
266
|
case "tool_completed": {
|
|
171
267
|
const tool = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
@@ -173,14 +269,32 @@ function formatEvent(event) {
|
|
|
173
269
|
const output = typeof event.data?.output === "string" ? event.data.output.trim() : "";
|
|
174
270
|
const firstLine = (output.split("\n")[0] ?? event.message ?? "").slice(0, 160);
|
|
175
271
|
const color = success ? CATEGORY_COLOR[TOOL_CATEGORY[tool] ?? "other"] : chalk.red;
|
|
176
|
-
return color(` ${success ? "\u2713" : "\u2717"}
|
|
272
|
+
return color(` ${success ? "\u2713" : "\u2717"} ${firstLine}`);
|
|
177
273
|
}
|
|
178
274
|
case "approval":
|
|
179
|
-
return chalk.magenta(`\u{1F510}
|
|
275
|
+
return chalk.magenta(`\u{1F510} Asking permission to use ${event.message ?? "tool"}...`);
|
|
180
276
|
case "warning":
|
|
181
|
-
return chalk.yellow(`\u26A0
|
|
277
|
+
return chalk.yellow(`\u26A0 ${event.message ?? ""}`);
|
|
182
278
|
case "error":
|
|
183
|
-
return chalk.red(`\u2716
|
|
279
|
+
return chalk.red(`\u2716 ${event.message ?? ""}`);
|
|
280
|
+
case "patch": {
|
|
281
|
+
const filePath = typeof event.data?.path === "string" ? event.data.path : "file";
|
|
282
|
+
const oldStr = typeof event.data?.old_string === "string" ? event.data.old_string : "";
|
|
283
|
+
const newStr = typeof event.data?.new_string === "string" ? event.data.new_string : "";
|
|
284
|
+
const MAX = 60;
|
|
285
|
+
const oldLines = oldStr.split(/\r?\n/);
|
|
286
|
+
const newLines = newStr.split(/\r?\n/);
|
|
287
|
+
const out = [chalk.bold.magenta(`\u270E ${filePath}`)];
|
|
288
|
+
for (const line of oldLines.slice(0, MAX)) {
|
|
289
|
+
out.push(chalk.bgRed.white(` - ${line}`));
|
|
290
|
+
}
|
|
291
|
+
if (oldLines.length > MAX) out.push(chalk.bgRed.white(` \u2026 ${oldLines.length - MAX} more removed line(s)`));
|
|
292
|
+
for (const line of newLines.slice(0, MAX)) {
|
|
293
|
+
out.push(chalk.bgGreen.black(` + ${line}`));
|
|
294
|
+
}
|
|
295
|
+
if (newLines.length > MAX) out.push(chalk.bgGreen.black(` \u2026 ${newLines.length - MAX} more added line(s)`));
|
|
296
|
+
return out.join("\n");
|
|
297
|
+
}
|
|
184
298
|
case "task_completed": {
|
|
185
299
|
const success = event.data?.success === true;
|
|
186
300
|
const usage = event.data?.usage;
|
|
@@ -201,9 +315,13 @@ function formatEvent(event) {
|
|
|
201
315
|
if (modifiedFiles.length > 0) stats.push(`files ${modifiedFiles.length}`);
|
|
202
316
|
const sep = chalk.dim("\u2500".repeat(48));
|
|
203
317
|
const statsLine = chalk.cyan(` ${stats.join(" \xB7 ")}`);
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
318
|
+
const cancelled = event.data?.cancelled === true;
|
|
319
|
+
const statusLine = cancelled ? chalk.yellow.bold("\u2716 Task cancelled") : success ? chalk.green.bold("\u2714 Task completed") : chalk.red.bold("\u2716 Task failed");
|
|
320
|
+
const filesLine = modifiedFiles.length > 0 ? chalk.cyan.dim(` \u{1F4DD} ${modifiedFiles.length} file(s): ${modifiedFiles.join(", ")}`) : "";
|
|
321
|
+
const tipLine = success ? chalk.dim(" Next: type a new task, or /help for commands") : chalk.yellow.dim(" The task did not finish \u2014 check the steps above and retry");
|
|
322
|
+
const detailsLine = toolCalls > 0 ? chalk.dim(` \u{1F50D} press d to toggle ${toolCalls} tool call(s)`) : "";
|
|
323
|
+
const block = [sep, statusLine, statsLine, filesLine, tipLine, detailsLine, sep, ""];
|
|
324
|
+
return block.filter((line) => line !== "").join("\n");
|
|
207
325
|
}
|
|
208
326
|
default:
|
|
209
327
|
return null;
|
|
@@ -366,6 +484,9 @@ function buildSystemPrompt(cwd) {
|
|
|
366
484
|
const tree = projectTree(cwd).trim() || "(empty)";
|
|
367
485
|
return `You are MenteE, an autonomous software-engineering agent working inside a repository on the user's machine.
|
|
368
486
|
|
|
487
|
+
# Your design philosophy
|
|
488
|
+
You are built around a tool harness: every meaningful action (reading, searching, editing, running, git) goes through a tool. Use the harness decisively and efficiently. Prefer precise tool calls over long prose so token usage stays very low while your engineering stays sharp and intelligent. A good session does more with fewer tokens \u2014 call exactly the tool needed, read only what you must, and let the harness do the heavy lifting.
|
|
489
|
+
|
|
369
490
|
# Environment
|
|
370
491
|
- Workspace: ${cwd}
|
|
371
492
|
- Platform: ${os2.platform()} (${os2.release()}), shell commands run with the system shell
|
|
@@ -386,6 +507,8 @@ ${tree}
|
|
|
386
507
|
# Tool usage notes
|
|
387
508
|
- Call only the tools needed for the immediate next step. Do NOT read files you already understand, and do NOT read build/config files (e.g. *.config.ts, tsconfig.json) unless the task specifically needs them.
|
|
388
509
|
- Prefer search_code over reading many files.
|
|
510
|
+
- NEVER start long-running servers, dev servers, or watchers (e.g. 'npm run dev', 'npm start', 'vite', 'npm run watch', 'python -m http.server') yourself. These block and never exit. If the user needs to run the app, give them the exact command to run in their OWN terminal. Only run commands that exit on their own.
|
|
511
|
+
- If a task result requires running the app to verify, say so and provide the command \u2014 do not run it for them.
|
|
389
512
|
|
|
390
513
|
# Research strategy (IMPORTANT \u2014 HARD RULE)
|
|
391
514
|
- The file tree above already shows the whole structure. Do NOT read files just to "see what's there".
|
|
@@ -538,11 +661,11 @@ function isRateLimitError(error) {
|
|
|
538
661
|
}
|
|
539
662
|
return /\b429\b|max rpm|rate limit|too many requests/i.test(error.message);
|
|
540
663
|
}
|
|
541
|
-
async function generateWithRetry(provider, model, system, messages, tools, bus) {
|
|
664
|
+
async function generateWithRetry(provider, model, system, messages, tools, bus, onToken) {
|
|
542
665
|
let lastError = null;
|
|
543
666
|
for (let attempt = 0; attempt <= RATE_LIMIT_MAX_ATTEMPTS; attempt++) {
|
|
544
667
|
try {
|
|
545
|
-
return await provider.generate({ system, messages, tools: tools.schemas() }, model);
|
|
668
|
+
return await provider.generate({ system, messages, tools: tools.schemas(), onToken }, model);
|
|
546
669
|
} catch (error) {
|
|
547
670
|
lastError = error;
|
|
548
671
|
if (attempt >= RATE_LIMIT_MAX_ATTEMPTS || !isRateLimitError(lastError)) {
|
|
@@ -593,7 +716,8 @@ async function runAgent(options) {
|
|
|
593
716
|
bus,
|
|
594
717
|
approval,
|
|
595
718
|
maxIterations = 40,
|
|
596
|
-
systemExtra
|
|
719
|
+
systemExtra,
|
|
720
|
+
signal
|
|
597
721
|
} = options;
|
|
598
722
|
const state = createAgentState(task, maxIterations);
|
|
599
723
|
const prior = formatConversationContext(cwd);
|
|
@@ -618,13 +742,31 @@ ${systemExtra}` : "");
|
|
|
618
742
|
let finalText = "";
|
|
619
743
|
let sawFinish = false;
|
|
620
744
|
while (state.iteration < maxIterations) {
|
|
745
|
+
if (signal?.aborted) {
|
|
746
|
+
finalText = "Task cancelled.";
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
621
749
|
state.iteration += 1;
|
|
622
750
|
state.usage.modelRequests += 1;
|
|
623
751
|
bus.emit("model_request", `iteration ${state.iteration}/${maxIterations}`);
|
|
752
|
+
let thinkBuf = "";
|
|
753
|
+
let thinkTimer = null;
|
|
754
|
+
const flushThink = () => {
|
|
755
|
+
if (thinkBuf) {
|
|
756
|
+
bus.emit("thinking", thinkBuf);
|
|
757
|
+
thinkBuf = "";
|
|
758
|
+
}
|
|
759
|
+
thinkTimer = null;
|
|
760
|
+
};
|
|
761
|
+
const onToken = (delta) => {
|
|
762
|
+
thinkBuf += delta;
|
|
763
|
+
if (thinkTimer == null) thinkTimer = setTimeout(flushThink, 80);
|
|
764
|
+
};
|
|
624
765
|
let response;
|
|
625
766
|
try {
|
|
626
|
-
response = await generateWithRetry(provider, model, system, messages, tools, bus);
|
|
767
|
+
response = await generateWithRetry(provider, model, system, messages, tools, bus, onToken);
|
|
627
768
|
} catch (error) {
|
|
769
|
+
flushThink();
|
|
628
770
|
const message = error.message;
|
|
629
771
|
let hint = "";
|
|
630
772
|
if (/insufficient balance|no resource package|recharge/i.test(message)) {
|
|
@@ -646,6 +788,7 @@ ${systemExtra}` : "");
|
|
|
646
788
|
});
|
|
647
789
|
return { success: false, finalText, state };
|
|
648
790
|
}
|
|
791
|
+
flushThink();
|
|
649
792
|
bus.emit(
|
|
650
793
|
"model_response",
|
|
651
794
|
void 0,
|
|
@@ -674,7 +817,7 @@ ${systemExtra}` : "");
|
|
|
674
817
|
content: response.content,
|
|
675
818
|
tool_calls: response.toolCalls
|
|
676
819
|
});
|
|
677
|
-
if (response.content && response.content.trim()) {
|
|
820
|
+
if (!onToken && response.content && response.content.trim()) {
|
|
678
821
|
bus.emit("info", response.content.trim().slice(0, 300));
|
|
679
822
|
}
|
|
680
823
|
for (const call of response.toolCalls) {
|
|
@@ -768,12 +911,23 @@ ${systemExtra}` : "");
|
|
|
768
911
|
success: result.success,
|
|
769
912
|
output: result.output.slice(0, 2e3)
|
|
770
913
|
});
|
|
914
|
+
if (tool.name === "apply_patch" && result.success && result.data?.kind === "patch") {
|
|
915
|
+
bus.emit("patch", void 0, {
|
|
916
|
+
path: result.data.path,
|
|
917
|
+
old_string: result.data.old_string,
|
|
918
|
+
new_string: result.data.new_string
|
|
919
|
+
});
|
|
920
|
+
}
|
|
771
921
|
messages.push({
|
|
772
922
|
role: "tool",
|
|
773
923
|
tool_call_id: call.id,
|
|
774
924
|
content: result.output
|
|
775
925
|
});
|
|
776
926
|
}
|
|
927
|
+
if (signal?.aborted) {
|
|
928
|
+
finalText = "Task cancelled.";
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
777
931
|
if (estimateContextChars(messages, system) > MAX_CONTEXT_CHARS) {
|
|
778
932
|
bus.emit("warning", "Context is large; trimming oldest tool outputs.");
|
|
779
933
|
trimOldToolResults(messages);
|
|
@@ -784,7 +938,7 @@ ${systemExtra}` : "");
|
|
|
784
938
|
bus.emit("warning", finalText);
|
|
785
939
|
}
|
|
786
940
|
const success = sawFinish && state.errors.length === 0;
|
|
787
|
-
if (finalText) appendTurn(cwd, task, finalText);
|
|
941
|
+
if (finalText && !signal?.aborted) appendTurn(cwd, task, finalText);
|
|
788
942
|
bus.emit("task_completed", finalText.slice(0, 400), {
|
|
789
943
|
success,
|
|
790
944
|
finalText,
|
|
@@ -792,7 +946,8 @@ ${systemExtra}` : "");
|
|
|
792
946
|
toolCalls: state.totalToolCalls,
|
|
793
947
|
modifiedFiles: state.modifiedFiles,
|
|
794
948
|
usage: state.usage,
|
|
795
|
-
durationMs: Date.now() - state.startedAt
|
|
949
|
+
durationMs: Date.now() - state.startedAt,
|
|
950
|
+
cancelled: signal?.aborted === true
|
|
796
951
|
});
|
|
797
952
|
return { success, finalText, state };
|
|
798
953
|
}
|
|
@@ -977,7 +1132,7 @@ var init_KeyDialog = __esm({
|
|
|
977
1132
|
var version;
|
|
978
1133
|
var init_package = __esm({
|
|
979
1134
|
"package.json"() {
|
|
980
|
-
version = "0.1.
|
|
1135
|
+
version = "0.1.9";
|
|
981
1136
|
}
|
|
982
1137
|
});
|
|
983
1138
|
|
|
@@ -987,11 +1142,12 @@ __export(App_exports, {
|
|
|
987
1142
|
App: () => App
|
|
988
1143
|
});
|
|
989
1144
|
import { useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState3 } from "react";
|
|
990
|
-
import { Box as Box4, Static, Text as Text4, useApp, useInput as useInput4 } from "ink";
|
|
1145
|
+
import { Box as Box4, Static, Text as Text4, useApp, useInput as useInput4, useStdout } from "ink";
|
|
991
1146
|
import TextInput2 from "ink-text-input";
|
|
992
1147
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
993
1148
|
function App(props) {
|
|
994
1149
|
const { exit } = useApp();
|
|
1150
|
+
const { stdout } = useStdout();
|
|
995
1151
|
const bus = useMemo2(() => new EventBus(), []);
|
|
996
1152
|
const bridge = useMemo2(() => new ApprovalBridge(), []);
|
|
997
1153
|
const [log, setLog] = useState3([]);
|
|
@@ -1014,23 +1170,31 @@ function App(props) {
|
|
|
1014
1170
|
const [toolHistory, setToolHistory] = useState3([]);
|
|
1015
1171
|
const [inspectorIndex, setInspectorIndex] = useState3(null);
|
|
1016
1172
|
const [detailsOpen, setDetailsOpen] = useState3(false);
|
|
1017
|
-
const [
|
|
1173
|
+
const [showDetails, setShowDetails] = useState3(false);
|
|
1174
|
+
const [thinkingText, setThinkingText] = useState3("");
|
|
1018
1175
|
const pendingTool = useRef(null);
|
|
1019
1176
|
const counter = useRef(0);
|
|
1020
1177
|
const stateRef = useRef({ running, approval, dialog, factoryResult, providerName, model });
|
|
1021
1178
|
stateRef.current = { running, approval, dialog, factoryResult, providerName, model };
|
|
1022
|
-
const
|
|
1179
|
+
const abortRef = useRef(null);
|
|
1180
|
+
const appendLog = (text, noise = false) => {
|
|
1023
1181
|
if (!text) return;
|
|
1024
1182
|
counter.current += 1;
|
|
1025
|
-
const line = { id: counter.current, text };
|
|
1183
|
+
const line = { id: counter.current, text, noise };
|
|
1026
1184
|
setLog((prev) => [...prev, line]);
|
|
1027
1185
|
};
|
|
1028
1186
|
useEffect2(() => {
|
|
1029
|
-
const
|
|
1187
|
+
const TOOL_NOISE = /* @__PURE__ */ new Set(["tool_started", "tool_completed", "model_request"]);
|
|
1030
1188
|
const unsubscribeLog = bus.subscribe((event) => {
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1189
|
+
if (event.type === "thinking") {
|
|
1190
|
+
const delta = (event.message ?? "").replace(/[\r\n]+/g, " ");
|
|
1191
|
+
if (delta) setThinkingText((prev) => (prev + delta).slice(-4e3));
|
|
1192
|
+
return;
|
|
1033
1193
|
}
|
|
1194
|
+
if (event.type === "model_request" || event.type === "task_completed") {
|
|
1195
|
+
setThinkingText("");
|
|
1196
|
+
}
|
|
1197
|
+
appendLog(formatEvent(event), TOOL_NOISE.has(event.type));
|
|
1034
1198
|
if (event.type === "tool_started") {
|
|
1035
1199
|
const name = typeof event.data?.tool === "string" ? event.data.tool : "";
|
|
1036
1200
|
const target = typeof event.message === "string" ? event.message : "";
|
|
@@ -1063,12 +1227,18 @@ function App(props) {
|
|
|
1063
1227
|
unsubscribeLog();
|
|
1064
1228
|
unsubscribeLog2();
|
|
1065
1229
|
};
|
|
1066
|
-
}, [bus, bridge,
|
|
1230
|
+
}, [bus, bridge, showDetails]);
|
|
1067
1231
|
useEffect2(() => {
|
|
1068
1232
|
if (!running) return;
|
|
1069
1233
|
const timer = setInterval(() => setFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
|
|
1070
1234
|
return () => clearInterval(timer);
|
|
1071
1235
|
}, [running]);
|
|
1236
|
+
useEffect2(() => {
|
|
1237
|
+
const turns = loadConversation(props.cwd);
|
|
1238
|
+
if (turns.length > 0) {
|
|
1239
|
+
appendLog(`Resumed previous session \u2014 ${turns.length} prior turn(s) loaded as context.`);
|
|
1240
|
+
}
|
|
1241
|
+
}, []);
|
|
1072
1242
|
useEffect2(() => {
|
|
1073
1243
|
if (!typing || finalPending == null) return;
|
|
1074
1244
|
if (typedText.length >= finalPending.length) {
|
|
@@ -1076,8 +1246,11 @@ function App(props) {
|
|
|
1076
1246
|
return;
|
|
1077
1247
|
}
|
|
1078
1248
|
const timer = setTimeout(() => {
|
|
1079
|
-
|
|
1080
|
-
|
|
1249
|
+
const rest = finalPending.slice(typedText.length);
|
|
1250
|
+
const match = /^(\S+\s*)/.exec(rest);
|
|
1251
|
+
const step = match ? match[0].length : 1;
|
|
1252
|
+
setTypedText(finalPending.slice(0, typedText.length + step));
|
|
1253
|
+
}, 55);
|
|
1081
1254
|
return () => clearTimeout(timer);
|
|
1082
1255
|
}, [typing, finalPending, typedText]);
|
|
1083
1256
|
const updateConfig = (mutate) => {
|
|
@@ -1222,7 +1395,10 @@ function App(props) {
|
|
|
1222
1395
|
setToolHistory([]);
|
|
1223
1396
|
setInspectorIndex(null);
|
|
1224
1397
|
setDetailsOpen(false);
|
|
1398
|
+
setShowDetails(false);
|
|
1225
1399
|
setRunning(true);
|
|
1400
|
+
const ac = new AbortController();
|
|
1401
|
+
abortRef.current = ac;
|
|
1226
1402
|
try {
|
|
1227
1403
|
await runAgent({
|
|
1228
1404
|
task,
|
|
@@ -1233,7 +1409,8 @@ function App(props) {
|
|
|
1233
1409
|
bus,
|
|
1234
1410
|
approval: props.yes ? async () => "allow" : bridge.handler,
|
|
1235
1411
|
maxIterations: props.maxIterations,
|
|
1236
|
-
systemExtra: props.systemExtra
|
|
1412
|
+
systemExtra: props.systemExtra,
|
|
1413
|
+
signal: ac.signal
|
|
1237
1414
|
});
|
|
1238
1415
|
} catch (error) {
|
|
1239
1416
|
bus.emit("error", error.message);
|
|
@@ -1270,16 +1447,25 @@ function App(props) {
|
|
|
1270
1447
|
}
|
|
1271
1448
|
};
|
|
1272
1449
|
useInput4((input, key) => {
|
|
1450
|
+
if (key.ctrl && input === "c") {
|
|
1451
|
+
if (stateRef.current.running) {
|
|
1452
|
+
appendLog("\u23F9 Interrupted by you \u2014 stopping the current task and returning to the prompt.");
|
|
1453
|
+
killActiveChild();
|
|
1454
|
+
abortRef.current?.abort();
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
exit();
|
|
1458
|
+
setTimeout(() => process.exit(0), 50);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (key.meta && (input === "d" || input === "D")) {
|
|
1462
|
+
setShowDetails((v) => !v);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1273
1465
|
if (stateRef.current.dialog || stateRef.current.approval || stateRef.current.running) return;
|
|
1274
1466
|
if (key.meta && (input === "m" || input === "M")) openModelDialog();
|
|
1275
1467
|
else if (key.ctrl && input === "p") setDialog({ kind: "provider" });
|
|
1276
1468
|
else if (key.ctrl && input === "k") setDialog({ kind: "key" });
|
|
1277
|
-
else if (input === "d" || input === "D") cycleInspector();
|
|
1278
|
-
else if (input === "t" || input === "T") setShowTools((v) => !v);
|
|
1279
|
-
else if (key.ctrl && input === "c") {
|
|
1280
|
-
exit();
|
|
1281
|
-
setTimeout(() => process.exit(0), 50);
|
|
1282
|
-
}
|
|
1283
1469
|
});
|
|
1284
1470
|
const modelLabel = model ?? factoryResult.provider?.defaultModel ?? (factoryResult.error ? "no key" : "");
|
|
1285
1471
|
const header = /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginBottom: 1, children: [
|
|
@@ -1290,114 +1476,117 @@ function App(props) {
|
|
|
1290
1476
|
" \xB7 ",
|
|
1291
1477
|
providerName,
|
|
1292
1478
|
modelLabel ? `:${modelLabel}` : "",
|
|
1293
|
-
" \xB7
|
|
1294
|
-
|
|
1479
|
+
" \xB7 details ",
|
|
1480
|
+
showDetails ? "open" : "collapsed",
|
|
1295
1481
|
" \xB7 ",
|
|
1296
1482
|
props.cwd
|
|
1297
1483
|
] }),
|
|
1298
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7
|
|
1484
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "Alt+M model \xB7 Ctrl+P provider \xB7 Ctrl+K key \xB7 Alt+D details \xB7 /help" })
|
|
1299
1485
|
] }, "header");
|
|
1486
|
+
const cols = stdout.columns || 80;
|
|
1487
|
+
const marquee = thinkingText.slice(-Math.max(1, cols - 4));
|
|
1300
1488
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1301
|
-
/* @__PURE__ */ jsx4(
|
|
1302
|
-
|
|
1303
|
-
{
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
" "
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
error: modelError,
|
|
1345
|
-
current: model ?? factoryResult.provider?.defaultModel,
|
|
1346
|
-
onSelect: (id) => {
|
|
1347
|
-
setDialog(null);
|
|
1348
|
-
setModelAndPersist(id);
|
|
1349
|
-
},
|
|
1350
|
-
onClose: () => setDialog(null)
|
|
1351
|
-
}
|
|
1352
|
-
) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx4(
|
|
1353
|
-
SelectDialog,
|
|
1354
|
-
{
|
|
1355
|
-
title: "Provider",
|
|
1356
|
-
items: PROVIDER_NAMES,
|
|
1357
|
-
descriptions: PROVIDER_DESCRIPTIONS,
|
|
1358
|
-
current: providerName,
|
|
1359
|
-
onSelect: (name) => {
|
|
1360
|
-
setDialog(null);
|
|
1361
|
-
switchProvider(name);
|
|
1362
|
-
},
|
|
1363
|
-
onClose: () => setDialog(null)
|
|
1364
|
-
}
|
|
1365
|
-
) : dialog?.kind === "key" ? /* @__PURE__ */ jsx4(
|
|
1366
|
-
KeyDialog,
|
|
1367
|
-
{
|
|
1368
|
-
providerName,
|
|
1369
|
-
onSubmit: (value) => {
|
|
1370
|
-
setDialog(null);
|
|
1371
|
-
saveKey(value);
|
|
1372
|
-
},
|
|
1373
|
-
onClose: () => setDialog(null)
|
|
1374
|
-
}
|
|
1375
|
-
) : approval ? /* @__PURE__ */ jsx4(
|
|
1376
|
-
ApprovalPrompt,
|
|
1377
|
-
{
|
|
1378
|
-
request: approval,
|
|
1379
|
-
onDecision: (decision) => {
|
|
1380
|
-
bridge.resolve(decision);
|
|
1381
|
-
if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
|
|
1489
|
+
/* @__PURE__ */ jsx4(Static, { items: [{ key: "header", text: header }], children: (item) => /* @__PURE__ */ jsx4(Box4, { children: item.text }, item.key) }),
|
|
1490
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1491
|
+
log.filter((l) => showDetails || !l.noise).map((line) => /* @__PURE__ */ jsx4(Text4, { children: line.text }, line.id)),
|
|
1492
|
+
finalPending !== null ? /* @__PURE__ */ jsxs4(Box4, { marginTop: 1, children: [
|
|
1493
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", children: typedText }),
|
|
1494
|
+
typing ? /* @__PURE__ */ jsx4(Text4, { color: "green", children: "\u258C" }) : null
|
|
1495
|
+
] }) : null,
|
|
1496
|
+
detailsOpen && inspectorIndex !== null && toolHistory[inspectorIndex] ? (() => {
|
|
1497
|
+
const rec = toolHistory[inspectorIndex];
|
|
1498
|
+
const outLines = rec.output.split("\n").slice(0, 24).join("\n");
|
|
1499
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
|
|
1500
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
1501
|
+
" tool details (",
|
|
1502
|
+
inspectorIndex + 1,
|
|
1503
|
+
"/",
|
|
1504
|
+
toolHistory.length,
|
|
1505
|
+
") \xB7 press d to cycle "
|
|
1506
|
+
] }),
|
|
1507
|
+
/* @__PURE__ */ jsxs4(Text4, { color: toolColorName(rec.name), bold: true, children: [
|
|
1508
|
+
rec.name,
|
|
1509
|
+
" ",
|
|
1510
|
+
rec.target
|
|
1511
|
+
] }),
|
|
1512
|
+
/* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
|
|
1513
|
+
"args: ",
|
|
1514
|
+
rec.args.slice(0, 400) || "(none)"
|
|
1515
|
+
] }),
|
|
1516
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: outLines || "(no output)" })
|
|
1517
|
+
] });
|
|
1518
|
+
})() : null,
|
|
1519
|
+
dialog?.kind === "model" ? /* @__PURE__ */ jsx4(
|
|
1520
|
+
SelectDialog,
|
|
1521
|
+
{
|
|
1522
|
+
title: `Model \u2014 ${providerName}`,
|
|
1523
|
+
items: modelItems,
|
|
1524
|
+
loading: modelLoading,
|
|
1525
|
+
error: modelError,
|
|
1526
|
+
current: model ?? factoryResult.provider?.defaultModel,
|
|
1527
|
+
onSelect: (id) => {
|
|
1528
|
+
setDialog(null);
|
|
1529
|
+
setModelAndPersist(id);
|
|
1530
|
+
},
|
|
1531
|
+
onClose: () => setDialog(null)
|
|
1382
1532
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
/* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
|
|
1386
|
-
SPINNER_FRAMES[frame],
|
|
1387
|
-
" "
|
|
1388
|
-
] }),
|
|
1389
|
-
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
|
|
1390
|
-
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1391
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u25B8 " }),
|
|
1392
|
-
/* @__PURE__ */ jsx4(
|
|
1393
|
-
TextInput2,
|
|
1533
|
+
) : dialog?.kind === "provider" ? /* @__PURE__ */ jsx4(
|
|
1534
|
+
SelectDialog,
|
|
1394
1535
|
{
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1536
|
+
title: "Provider",
|
|
1537
|
+
items: PROVIDER_NAMES,
|
|
1538
|
+
descriptions: PROVIDER_DESCRIPTIONS,
|
|
1539
|
+
current: providerName,
|
|
1540
|
+
onSelect: (name) => {
|
|
1541
|
+
setDialog(null);
|
|
1542
|
+
switchProvider(name);
|
|
1543
|
+
},
|
|
1544
|
+
onClose: () => setDialog(null)
|
|
1399
1545
|
}
|
|
1400
|
-
)
|
|
1546
|
+
) : dialog?.kind === "key" ? /* @__PURE__ */ jsx4(
|
|
1547
|
+
KeyDialog,
|
|
1548
|
+
{
|
|
1549
|
+
providerName,
|
|
1550
|
+
onSubmit: (value) => {
|
|
1551
|
+
setDialog(null);
|
|
1552
|
+
saveKey(value);
|
|
1553
|
+
},
|
|
1554
|
+
onClose: () => setDialog(null)
|
|
1555
|
+
}
|
|
1556
|
+
) : approval ? /* @__PURE__ */ jsx4(
|
|
1557
|
+
ApprovalPrompt,
|
|
1558
|
+
{
|
|
1559
|
+
request: approval,
|
|
1560
|
+
onDecision: (decision) => {
|
|
1561
|
+
bridge.resolve(decision);
|
|
1562
|
+
if (decision === "always") bus.emit("info", "Approved for the rest of this session.");
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
) : running ? /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1567
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "yellow", children: [
|
|
1568
|
+
SPINNER_FRAMES[frame],
|
|
1569
|
+
" "
|
|
1570
|
+
] }),
|
|
1571
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: true, children: "agent working... (Ctrl+C to exit)" })
|
|
1572
|
+
] }),
|
|
1573
|
+
thinkingText ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1574
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u{1F4AD} " }),
|
|
1575
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", dimColor: true, children: marquee }),
|
|
1576
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "\u258C" })
|
|
1577
|
+
] }) : null
|
|
1578
|
+
] }) : /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
1579
|
+
/* @__PURE__ */ jsx4(Text4, { color: "cyan", bold: true, children: "\u25B8 " }),
|
|
1580
|
+
/* @__PURE__ */ jsx4(
|
|
1581
|
+
TextInput2,
|
|
1582
|
+
{
|
|
1583
|
+
value: taskInput,
|
|
1584
|
+
onChange: setTaskInput,
|
|
1585
|
+
onSubmit: handleSubmit,
|
|
1586
|
+
placeholder: "Describe a task, or /help"
|
|
1587
|
+
}
|
|
1588
|
+
)
|
|
1589
|
+
] })
|
|
1401
1590
|
] })
|
|
1402
1591
|
] });
|
|
1403
1592
|
}
|
|
@@ -1410,6 +1599,8 @@ var init_App = __esm({
|
|
|
1410
1599
|
init_render();
|
|
1411
1600
|
init_approval();
|
|
1412
1601
|
init_loop();
|
|
1602
|
+
init_conversation();
|
|
1603
|
+
init_exec();
|
|
1413
1604
|
init_config();
|
|
1414
1605
|
init_Approval();
|
|
1415
1606
|
init_SelectDialog();
|
|
@@ -1466,6 +1657,20 @@ function toWireTools(tools) {
|
|
|
1466
1657
|
}
|
|
1467
1658
|
}));
|
|
1468
1659
|
}
|
|
1660
|
+
function parseResponse(response) {
|
|
1661
|
+
const choice = response.choices?.[0];
|
|
1662
|
+
const message = choice?.message;
|
|
1663
|
+
const usage = response.usage ? {
|
|
1664
|
+
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
1665
|
+
outputTokens: response.usage.completion_tokens ?? 0
|
|
1666
|
+
} : void 0;
|
|
1667
|
+
return {
|
|
1668
|
+
content: message?.content ?? null,
|
|
1669
|
+
toolCalls: message?.tool_calls ?? [],
|
|
1670
|
+
finishReason: mapFinishReason(choice?.finish_reason),
|
|
1671
|
+
usage
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1469
1674
|
var OpenAICompatProvider = class {
|
|
1470
1675
|
name;
|
|
1471
1676
|
defaultModel;
|
|
@@ -1483,26 +1688,71 @@ var OpenAICompatProvider = class {
|
|
|
1483
1688
|
{ role: "system", content: request.system },
|
|
1484
1689
|
...request.messages
|
|
1485
1690
|
];
|
|
1486
|
-
const
|
|
1691
|
+
const baseParams = {
|
|
1487
1692
|
model: model ?? this.defaultModel,
|
|
1488
1693
|
// the wire format matches our internal shape; keep loose typing at the boundary
|
|
1489
1694
|
messages,
|
|
1490
1695
|
...request.tools.length > 0 ? { tools: toWireTools(request.tools) } : {},
|
|
1491
1696
|
...request.temperature !== void 0 ? { temperature: request.temperature } : {},
|
|
1492
1697
|
...request.maxTokens ? { max_tokens: request.maxTokens } : {}
|
|
1493
|
-
});
|
|
1494
|
-
const choice = response.choices[0];
|
|
1495
|
-
const message = choice?.message;
|
|
1496
|
-
const usage = response.usage ? {
|
|
1497
|
-
inputTokens: response.usage.prompt_tokens ?? 0,
|
|
1498
|
-
outputTokens: response.usage.completion_tokens ?? 0
|
|
1499
|
-
} : void 0;
|
|
1500
|
-
return {
|
|
1501
|
-
content: message?.content ?? null,
|
|
1502
|
-
toolCalls: message?.tool_calls ?? [],
|
|
1503
|
-
finishReason: mapFinishReason(choice?.finish_reason),
|
|
1504
|
-
usage
|
|
1505
1698
|
};
|
|
1699
|
+
if (!request.onToken) {
|
|
1700
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
1701
|
+
return parseResponse(response);
|
|
1702
|
+
}
|
|
1703
|
+
try {
|
|
1704
|
+
const stream = await this.client.chat.completions.create({
|
|
1705
|
+
...baseParams,
|
|
1706
|
+
stream: true,
|
|
1707
|
+
stream_options: { include_usage: true }
|
|
1708
|
+
});
|
|
1709
|
+
let content = "";
|
|
1710
|
+
let finishReason = "stop";
|
|
1711
|
+
let usage;
|
|
1712
|
+
const tcMap = /* @__PURE__ */ new Map();
|
|
1713
|
+
for await (const chunk of stream) {
|
|
1714
|
+
const choice = chunk.choices?.[0];
|
|
1715
|
+
const delta = choice?.delta;
|
|
1716
|
+
if (delta?.content) {
|
|
1717
|
+
content += delta.content;
|
|
1718
|
+
request.onToken(delta.content);
|
|
1719
|
+
}
|
|
1720
|
+
if (delta?.tool_calls) {
|
|
1721
|
+
for (const tc of delta.tool_calls) {
|
|
1722
|
+
const idx = tc.index ?? 0;
|
|
1723
|
+
let acc = tcMap.get(idx);
|
|
1724
|
+
if (!acc) {
|
|
1725
|
+
acc = { args: "" };
|
|
1726
|
+
tcMap.set(idx, acc);
|
|
1727
|
+
}
|
|
1728
|
+
if (tc.id) acc.id = tc.id;
|
|
1729
|
+
if (tc.function?.name) acc.name = tc.function.name;
|
|
1730
|
+
if (tc.function?.arguments) acc.args += tc.function.arguments;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (choice?.finish_reason) finishReason = choice.finish_reason;
|
|
1734
|
+
if (chunk.usage) {
|
|
1735
|
+
usage = {
|
|
1736
|
+
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
1737
|
+
outputTokens: chunk.usage.completion_tokens ?? 0
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
const toolCalls = [...tcMap.entries()].sort((a, b) => a[0] - b[0]).map(([idx, acc]) => ({
|
|
1742
|
+
id: acc.id ?? `call_${idx}`,
|
|
1743
|
+
type: "function",
|
|
1744
|
+
function: { name: acc.name ?? "", arguments: acc.args }
|
|
1745
|
+
}));
|
|
1746
|
+
return {
|
|
1747
|
+
content,
|
|
1748
|
+
toolCalls,
|
|
1749
|
+
finishReason: mapFinishReason(finishReason),
|
|
1750
|
+
usage
|
|
1751
|
+
};
|
|
1752
|
+
} catch {
|
|
1753
|
+
const response = await this.client.chat.completions.create(baseParams);
|
|
1754
|
+
return parseResponse(response);
|
|
1755
|
+
}
|
|
1506
1756
|
}
|
|
1507
1757
|
async listModels() {
|
|
1508
1758
|
const page = await this.client.models.list();
|
|
@@ -2374,85 +2624,8 @@ function classifyCommand(command) {
|
|
|
2374
2624
|
return overall;
|
|
2375
2625
|
}
|
|
2376
2626
|
|
|
2377
|
-
// src/tools/exec.ts
|
|
2378
|
-
import { spawn } from "child_process";
|
|
2379
|
-
function killTree(child) {
|
|
2380
|
-
if (child.pid === void 0) return;
|
|
2381
|
-
if (process.platform === "win32") {
|
|
2382
|
-
spawn(`taskkill /pid ${String(child.pid)} /T /F`, { stdio: "ignore", shell: true });
|
|
2383
|
-
} else {
|
|
2384
|
-
child.kill("SIGKILL");
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
async function runShellCommand(command, cwd, timeoutMs = 12e4, maxChars = 1e5) {
|
|
2388
|
-
return new Promise((resolve) => {
|
|
2389
|
-
const started = Date.now();
|
|
2390
|
-
const child = spawn(command, {
|
|
2391
|
-
shell: true,
|
|
2392
|
-
cwd,
|
|
2393
|
-
env: { ...process.env, MENTEE: "1" },
|
|
2394
|
-
windowsHide: true
|
|
2395
|
-
});
|
|
2396
|
-
let stdout = "";
|
|
2397
|
-
let stderr = "";
|
|
2398
|
-
let killed = false;
|
|
2399
|
-
const timer = setTimeout(() => {
|
|
2400
|
-
killed = true;
|
|
2401
|
-
killTree(child);
|
|
2402
|
-
}, timeoutMs);
|
|
2403
|
-
child.stdout?.on("data", (chunk) => {
|
|
2404
|
-
if (stdout.length < maxChars * 2) stdout += chunk.toString("utf8");
|
|
2405
|
-
});
|
|
2406
|
-
child.stderr?.on("data", (chunk) => {
|
|
2407
|
-
if (stderr.length < maxChars * 2) stderr += chunk.toString("utf8");
|
|
2408
|
-
});
|
|
2409
|
-
child.on("error", (error) => {
|
|
2410
|
-
clearTimeout(timer);
|
|
2411
|
-
resolve({
|
|
2412
|
-
exitCode: -1,
|
|
2413
|
-
stdout,
|
|
2414
|
-
stderr: `${stderr}
|
|
2415
|
-
${error.message}`.trim(),
|
|
2416
|
-
durationMs: Date.now() - started
|
|
2417
|
-
});
|
|
2418
|
-
});
|
|
2419
|
-
child.on("close", (code) => {
|
|
2420
|
-
clearTimeout(timer);
|
|
2421
|
-
resolve({
|
|
2422
|
-
exitCode: killed ? "timeout" : code ?? -1,
|
|
2423
|
-
stdout,
|
|
2424
|
-
stderr,
|
|
2425
|
-
durationMs: Date.now() - started
|
|
2426
|
-
});
|
|
2427
|
-
});
|
|
2428
|
-
});
|
|
2429
|
-
}
|
|
2430
|
-
function formatOutcome(outcome, maxChars = 1e5) {
|
|
2431
|
-
const cut = (text, limit) => {
|
|
2432
|
-
if (text.length <= limit) return { text, truncated: false };
|
|
2433
|
-
const head = Math.floor(limit * 0.7);
|
|
2434
|
-
return {
|
|
2435
|
-
text: `${text.slice(0, head)}
|
|
2436
|
-
...[truncated, ${text.length - head} chars removed]...`,
|
|
2437
|
-
truncated: true
|
|
2438
|
-
};
|
|
2439
|
-
};
|
|
2440
|
-
const out = cut(outcome.stdout.trim(), maxChars);
|
|
2441
|
-
const err = cut(outcome.stderr.trim(), Math.floor(maxChars / 2));
|
|
2442
|
-
const sections = [`exit_code: ${outcome.exitCode}`, `duration_ms: ${outcome.durationMs}`];
|
|
2443
|
-
if (out.text) sections.push(`stdout:
|
|
2444
|
-
${out.text}`);
|
|
2445
|
-
if (err.text) sections.push(`stderr:
|
|
2446
|
-
${err.text}`);
|
|
2447
|
-
if (!out.text && !err.text) sections.push("(no output)");
|
|
2448
|
-
return {
|
|
2449
|
-
output: sections.join("\n"),
|
|
2450
|
-
success: outcome.exitCode === 0,
|
|
2451
|
-
truncated: out.truncated || err.truncated
|
|
2452
|
-
};
|
|
2453
|
-
}
|
|
2454
|
-
|
|
2455
2627
|
// src/tools/terminal.ts
|
|
2628
|
+
init_exec();
|
|
2456
2629
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
2457
2630
|
var MAX_OUTPUT_CHARS = 1e5;
|
|
2458
2631
|
var executeCommand = {
|
|
@@ -2488,6 +2661,7 @@ var executeCommand = {
|
|
|
2488
2661
|
|
|
2489
2662
|
// src/tools/testing.ts
|
|
2490
2663
|
init_base();
|
|
2664
|
+
init_exec();
|
|
2491
2665
|
import fs4 from "fs";
|
|
2492
2666
|
import path4 from "path";
|
|
2493
2667
|
var DEFAULT_TIMEOUT_MS2 = 18e4;
|
package/package.json
CHANGED