open-agents-ai 0.13.5 → 0.14.1
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/index.js +1149 -237
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1170,7 +1170,7 @@ var init_shell = __esm({
|
|
|
1170
1170
|
const timeout = args["timeout"] ?? this.defaultTimeout;
|
|
1171
1171
|
const stdinInput = args["stdin"];
|
|
1172
1172
|
const start = performance.now();
|
|
1173
|
-
return new Promise((
|
|
1173
|
+
return new Promise((resolve16) => {
|
|
1174
1174
|
const child = spawn("bash", ["-c", command], {
|
|
1175
1175
|
cwd: this.workingDir,
|
|
1176
1176
|
env: {
|
|
@@ -1223,7 +1223,7 @@ var init_shell = __esm({
|
|
|
1223
1223
|
const combined = stdout + stderr;
|
|
1224
1224
|
const looksInteractive = /\? .+[›>]|y\/n|yes\/no|\(Y\/n\)|\[y\/N\]/i.test(combined);
|
|
1225
1225
|
const hint = looksInteractive ? " The command appears to be waiting for interactive input. Use non-interactive flags (e.g., --yes, --no-input) or provide input via the stdin parameter." : "";
|
|
1226
|
-
|
|
1226
|
+
resolve16({
|
|
1227
1227
|
success: false,
|
|
1228
1228
|
output: stdout,
|
|
1229
1229
|
error: `Command timed out after ${timeout}ms.${hint}`,
|
|
@@ -1232,7 +1232,7 @@ var init_shell = __esm({
|
|
|
1232
1232
|
return;
|
|
1233
1233
|
}
|
|
1234
1234
|
const success = code === 0;
|
|
1235
|
-
|
|
1235
|
+
resolve16({
|
|
1236
1236
|
success,
|
|
1237
1237
|
output: stdout + (stderr && success ? `
|
|
1238
1238
|
STDERR:
|
|
@@ -1243,7 +1243,7 @@ ${stderr}` : ""),
|
|
|
1243
1243
|
});
|
|
1244
1244
|
child.on("error", (err) => {
|
|
1245
1245
|
clearTimeout(timer);
|
|
1246
|
-
|
|
1246
|
+
resolve16({
|
|
1247
1247
|
success: false,
|
|
1248
1248
|
output: stdout,
|
|
1249
1249
|
error: err.message,
|
|
@@ -4031,8 +4031,8 @@ function deleteCustomToolDefinition(name, scope, repoRoot) {
|
|
|
4031
4031
|
const dir = scope === "project" && repoRoot ? projectToolsDir(repoRoot) : globalToolsDir();
|
|
4032
4032
|
const filePath = join11(dir, `${name}.json`);
|
|
4033
4033
|
if (existsSync8(filePath)) {
|
|
4034
|
-
const { unlinkSync:
|
|
4035
|
-
|
|
4034
|
+
const { unlinkSync: unlinkSync4 } = __require("node:fs");
|
|
4035
|
+
unlinkSync4(filePath);
|
|
4036
4036
|
return true;
|
|
4037
4037
|
}
|
|
4038
4038
|
return false;
|
|
@@ -4150,7 +4150,7 @@ var init_custom_tool = __esm({
|
|
|
4150
4150
|
}
|
|
4151
4151
|
/** Execute a single shell command and return output */
|
|
4152
4152
|
runCommand(command) {
|
|
4153
|
-
return new Promise((
|
|
4153
|
+
return new Promise((resolve16) => {
|
|
4154
4154
|
const child = spawn3("bash", ["-c", command], {
|
|
4155
4155
|
cwd: this.workingDir,
|
|
4156
4156
|
env: { ...process.env, CI: "true", NO_COLOR: "1" },
|
|
@@ -4175,11 +4175,11 @@ var init_custom_tool = __esm({
|
|
|
4175
4175
|
child.kill("SIGTERM");
|
|
4176
4176
|
} catch {
|
|
4177
4177
|
}
|
|
4178
|
-
|
|
4178
|
+
resolve16({ success: false, output: stdout, error: "Command timed out after 60s" });
|
|
4179
4179
|
}, 6e4);
|
|
4180
4180
|
child.on("close", (code) => {
|
|
4181
4181
|
clearTimeout(timer);
|
|
4182
|
-
|
|
4182
|
+
resolve16({
|
|
4183
4183
|
success: code === 0,
|
|
4184
4184
|
output: stdout + (stderr && code === 0 ? `
|
|
4185
4185
|
STDERR:
|
|
@@ -4189,7 +4189,7 @@ ${stderr}` : ""),
|
|
|
4189
4189
|
});
|
|
4190
4190
|
child.on("error", (err) => {
|
|
4191
4191
|
clearTimeout(timer);
|
|
4192
|
-
|
|
4192
|
+
resolve16({ success: false, output: stdout, error: err.message });
|
|
4193
4193
|
});
|
|
4194
4194
|
});
|
|
4195
4195
|
}
|
|
@@ -4840,6 +4840,300 @@ ${content}`,
|
|
|
4840
4840
|
}
|
|
4841
4841
|
});
|
|
4842
4842
|
|
|
4843
|
+
// packages/execution/dist/tools/transcribe-tool.js
|
|
4844
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, readFileSync as readFileSync9, unlinkSync } from "node:fs";
|
|
4845
|
+
import { join as join13, basename as basename3, extname as extname3, resolve as resolve12 } from "node:path";
|
|
4846
|
+
import { homedir as homedir5 } from "node:os";
|
|
4847
|
+
import { execSync as execSync9, spawn as spawn4 } from "node:child_process";
|
|
4848
|
+
function isTranscribable(path) {
|
|
4849
|
+
const ext = extname3(path).toLowerCase();
|
|
4850
|
+
return AUDIO_EXTS.has(ext) || VIDEO_EXTS.has(ext);
|
|
4851
|
+
}
|
|
4852
|
+
async function loadTranscribeCli() {
|
|
4853
|
+
if (_tcChecked)
|
|
4854
|
+
return _tcModule;
|
|
4855
|
+
_tcChecked = true;
|
|
4856
|
+
try {
|
|
4857
|
+
const globalRoot = execSync9("npm root -g", {
|
|
4858
|
+
encoding: "utf-8",
|
|
4859
|
+
timeout: 5e3,
|
|
4860
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4861
|
+
}).trim();
|
|
4862
|
+
const tcPath = join13(globalRoot, "transcribe-cli");
|
|
4863
|
+
if (existsSync10(join13(tcPath, "dist", "index.js"))) {
|
|
4864
|
+
const { createRequire: createRequire4 } = await import("node:module");
|
|
4865
|
+
const req = createRequire4(import.meta.url);
|
|
4866
|
+
_tcModule = req(join13(tcPath, "dist", "index.js"));
|
|
4867
|
+
return _tcModule;
|
|
4868
|
+
}
|
|
4869
|
+
} catch {
|
|
4870
|
+
}
|
|
4871
|
+
const nvmBase = join13(homedir5(), ".nvm", "versions", "node");
|
|
4872
|
+
if (existsSync10(nvmBase)) {
|
|
4873
|
+
try {
|
|
4874
|
+
const { readdirSync: readdirSync9 } = await import("node:fs");
|
|
4875
|
+
for (const ver of readdirSync9(nvmBase)) {
|
|
4876
|
+
const tcPath = join13(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
|
|
4877
|
+
if (existsSync10(join13(tcPath, "dist", "index.js"))) {
|
|
4878
|
+
const { createRequire: createRequire4 } = await import("node:module");
|
|
4879
|
+
const req = createRequire4(import.meta.url);
|
|
4880
|
+
_tcModule = req(join13(tcPath, "dist", "index.js"));
|
|
4881
|
+
return _tcModule;
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
} catch {
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
return null;
|
|
4888
|
+
}
|
|
4889
|
+
function formatTime(seconds) {
|
|
4890
|
+
const m = Math.floor(seconds / 60);
|
|
4891
|
+
const s = Math.floor(seconds % 60);
|
|
4892
|
+
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
4893
|
+
}
|
|
4894
|
+
var AUDIO_EXTS, VIDEO_EXTS, _tcModule, _tcChecked, TranscribeFileTool, TranscribeUrlTool;
|
|
4895
|
+
var init_transcribe_tool = __esm({
|
|
4896
|
+
"packages/execution/dist/tools/transcribe-tool.js"() {
|
|
4897
|
+
"use strict";
|
|
4898
|
+
AUDIO_EXTS = /* @__PURE__ */ new Set([
|
|
4899
|
+
".mp3",
|
|
4900
|
+
".wav",
|
|
4901
|
+
".flac",
|
|
4902
|
+
".aac",
|
|
4903
|
+
".m4a",
|
|
4904
|
+
".ogg",
|
|
4905
|
+
".wma",
|
|
4906
|
+
".opus"
|
|
4907
|
+
]);
|
|
4908
|
+
VIDEO_EXTS = /* @__PURE__ */ new Set([
|
|
4909
|
+
".mp4",
|
|
4910
|
+
".mkv",
|
|
4911
|
+
".avi",
|
|
4912
|
+
".mov",
|
|
4913
|
+
".webm",
|
|
4914
|
+
".flv",
|
|
4915
|
+
".wmv",
|
|
4916
|
+
".m4v",
|
|
4917
|
+
".ts"
|
|
4918
|
+
]);
|
|
4919
|
+
_tcModule = null;
|
|
4920
|
+
_tcChecked = false;
|
|
4921
|
+
TranscribeFileTool = class {
|
|
4922
|
+
name = "transcribe_file";
|
|
4923
|
+
description = "Transcribe a local audio or video file to text using Whisper (faster-whisper). Supports MP3, WAV, FLAC, AAC, M4A, OGG, MP4, MKV, AVI, MOV, WebM. Returns the full transcription text with optional speaker diarization. Transcription is 100% local \u2014 no API keys needed.";
|
|
4924
|
+
parameters = {
|
|
4925
|
+
type: "object",
|
|
4926
|
+
properties: {
|
|
4927
|
+
path: {
|
|
4928
|
+
type: "string",
|
|
4929
|
+
description: "Path to the audio or video file to transcribe"
|
|
4930
|
+
},
|
|
4931
|
+
model: {
|
|
4932
|
+
type: "string",
|
|
4933
|
+
description: "Whisper model size: tiny, base, small, medium, large-v3 (default: base)",
|
|
4934
|
+
enum: ["tiny", "base", "small", "medium", "large-v3"]
|
|
4935
|
+
},
|
|
4936
|
+
diarize: {
|
|
4937
|
+
type: "boolean",
|
|
4938
|
+
description: "Enable speaker diarization (identify who said what). Default: false"
|
|
4939
|
+
}
|
|
4940
|
+
},
|
|
4941
|
+
required: ["path"]
|
|
4942
|
+
};
|
|
4943
|
+
workingDir;
|
|
4944
|
+
constructor(workingDir) {
|
|
4945
|
+
this.workingDir = workingDir;
|
|
4946
|
+
}
|
|
4947
|
+
async execute(args) {
|
|
4948
|
+
const start = performance.now();
|
|
4949
|
+
const filePath = resolve12(this.workingDir, String(args["path"] ?? ""));
|
|
4950
|
+
const model = String(args["model"] ?? "base");
|
|
4951
|
+
const diarize = Boolean(args["diarize"] ?? false);
|
|
4952
|
+
if (!existsSync10(filePath)) {
|
|
4953
|
+
return {
|
|
4954
|
+
success: false,
|
|
4955
|
+
output: "",
|
|
4956
|
+
error: `File not found: ${filePath}`,
|
|
4957
|
+
durationMs: performance.now() - start
|
|
4958
|
+
};
|
|
4959
|
+
}
|
|
4960
|
+
if (!isTranscribable(filePath)) {
|
|
4961
|
+
return {
|
|
4962
|
+
success: false,
|
|
4963
|
+
output: "",
|
|
4964
|
+
error: `Unsupported file type: ${extname3(filePath)}. Supported: ${[...AUDIO_EXTS, ...VIDEO_EXTS].join(", ")}`,
|
|
4965
|
+
durationMs: performance.now() - start
|
|
4966
|
+
};
|
|
4967
|
+
}
|
|
4968
|
+
const tc = await loadTranscribeCli();
|
|
4969
|
+
if (!tc) {
|
|
4970
|
+
return this.execViaCli(filePath, model, diarize, start);
|
|
4971
|
+
}
|
|
4972
|
+
try {
|
|
4973
|
+
const result = await tc.transcribe(filePath, {
|
|
4974
|
+
model,
|
|
4975
|
+
format: "json",
|
|
4976
|
+
diarize,
|
|
4977
|
+
wordTimestamps: false
|
|
4978
|
+
});
|
|
4979
|
+
const transcriptDir = join13(this.workingDir, ".oa", "transcripts");
|
|
4980
|
+
mkdirSync4(transcriptDir, { recursive: true });
|
|
4981
|
+
const outFile = join13(transcriptDir, `${basename3(filePath)}.txt`);
|
|
4982
|
+
writeFileSync4(outFile, result.text, "utf-8");
|
|
4983
|
+
const lines = [
|
|
4984
|
+
`Transcription of: ${basename3(filePath)}`,
|
|
4985
|
+
`Model: ${model} | Language: ${result.language} | Duration: ${result.duration ? `${result.duration.toFixed(1)}s` : "unknown"}`,
|
|
4986
|
+
`Words: ${result.wordCount} | Saved to: ${outFile}`,
|
|
4987
|
+
""
|
|
4988
|
+
];
|
|
4989
|
+
if (result.speakers.length > 1) {
|
|
4990
|
+
lines.push(`Speakers: ${result.speakers.join(", ")}`);
|
|
4991
|
+
lines.push("");
|
|
4992
|
+
}
|
|
4993
|
+
if (diarize && result.segments.length > 0) {
|
|
4994
|
+
for (const seg of result.segments) {
|
|
4995
|
+
const ts = `[${formatTime(seg.start)} \u2192 ${formatTime(seg.end)}]`;
|
|
4996
|
+
const speaker = seg.speaker ? `${seg.speaker}: ` : "";
|
|
4997
|
+
lines.push(`${ts} ${speaker}${seg.text}`);
|
|
4998
|
+
}
|
|
4999
|
+
} else {
|
|
5000
|
+
lines.push(result.text);
|
|
5001
|
+
}
|
|
5002
|
+
return {
|
|
5003
|
+
success: true,
|
|
5004
|
+
output: lines.join("\n"),
|
|
5005
|
+
durationMs: performance.now() - start
|
|
5006
|
+
};
|
|
5007
|
+
} catch (err) {
|
|
5008
|
+
return {
|
|
5009
|
+
success: false,
|
|
5010
|
+
output: "",
|
|
5011
|
+
error: `Transcription failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
5012
|
+
durationMs: performance.now() - start
|
|
5013
|
+
};
|
|
5014
|
+
}
|
|
5015
|
+
}
|
|
5016
|
+
/** Fallback: invoke transcribe-cli as a subprocess. */
|
|
5017
|
+
async execViaCli(filePath, model, diarize, start) {
|
|
5018
|
+
try {
|
|
5019
|
+
const args = [filePath, "-m", model, "-f", "txt"];
|
|
5020
|
+
if (diarize)
|
|
5021
|
+
args.push("--diarize");
|
|
5022
|
+
const output = execSync9(`transcribe-cli ${args.join(" ")}`, {
|
|
5023
|
+
encoding: "utf-8",
|
|
5024
|
+
timeout: 3e5,
|
|
5025
|
+
// 5 min max
|
|
5026
|
+
cwd: this.workingDir,
|
|
5027
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5028
|
+
});
|
|
5029
|
+
return {
|
|
5030
|
+
success: true,
|
|
5031
|
+
output: output.trim() || "(empty transcription)",
|
|
5032
|
+
durationMs: performance.now() - start
|
|
5033
|
+
};
|
|
5034
|
+
} catch (err) {
|
|
5035
|
+
const stderr = err.stderr || "";
|
|
5036
|
+
return {
|
|
5037
|
+
success: false,
|
|
5038
|
+
output: err.stdout || "",
|
|
5039
|
+
error: `transcribe-cli failed: ${stderr || err.message}`,
|
|
5040
|
+
durationMs: performance.now() - start
|
|
5041
|
+
};
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
};
|
|
5045
|
+
TranscribeUrlTool = class {
|
|
5046
|
+
name = "transcribe_url";
|
|
5047
|
+
description = "Download an audio or video file from a URL and transcribe it to text. Supports direct links to MP3, WAV, MP4, and other media files. The file is downloaded to a temp location, transcribed locally, then cleaned up.";
|
|
5048
|
+
parameters = {
|
|
5049
|
+
type: "object",
|
|
5050
|
+
properties: {
|
|
5051
|
+
url: {
|
|
5052
|
+
type: "string",
|
|
5053
|
+
description: "URL of the audio or video file to download and transcribe"
|
|
5054
|
+
},
|
|
5055
|
+
model: {
|
|
5056
|
+
type: "string",
|
|
5057
|
+
description: "Whisper model size: tiny, base, small, medium, large-v3 (default: base)",
|
|
5058
|
+
enum: ["tiny", "base", "small", "medium", "large-v3"]
|
|
5059
|
+
}
|
|
5060
|
+
},
|
|
5061
|
+
required: ["url"]
|
|
5062
|
+
};
|
|
5063
|
+
workingDir;
|
|
5064
|
+
constructor(workingDir) {
|
|
5065
|
+
this.workingDir = workingDir;
|
|
5066
|
+
}
|
|
5067
|
+
async execute(args) {
|
|
5068
|
+
const start = performance.now();
|
|
5069
|
+
const url = String(args["url"] ?? "");
|
|
5070
|
+
const model = String(args["model"] ?? "base");
|
|
5071
|
+
if (!url) {
|
|
5072
|
+
return {
|
|
5073
|
+
success: false,
|
|
5074
|
+
output: "",
|
|
5075
|
+
error: "URL is required.",
|
|
5076
|
+
durationMs: performance.now() - start
|
|
5077
|
+
};
|
|
5078
|
+
}
|
|
5079
|
+
const tmpDir = join13(this.workingDir, ".oa", "tmp");
|
|
5080
|
+
mkdirSync4(tmpDir, { recursive: true });
|
|
5081
|
+
const urlPath = new URL(url).pathname;
|
|
5082
|
+
let ext = extname3(urlPath).toLowerCase();
|
|
5083
|
+
if (!ext || !AUDIO_EXTS.has(ext) && !VIDEO_EXTS.has(ext)) {
|
|
5084
|
+
ext = ".mp3";
|
|
5085
|
+
}
|
|
5086
|
+
const tmpFile = join13(tmpDir, `download-${Date.now()}${ext}`);
|
|
5087
|
+
try {
|
|
5088
|
+
try {
|
|
5089
|
+
execSync9(`curl -sL -o "${tmpFile}" "${url}"`, {
|
|
5090
|
+
timeout: 12e4,
|
|
5091
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5092
|
+
});
|
|
5093
|
+
} catch {
|
|
5094
|
+
execSync9(`wget -q -O "${tmpFile}" "${url}"`, {
|
|
5095
|
+
timeout: 12e4,
|
|
5096
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5097
|
+
});
|
|
5098
|
+
}
|
|
5099
|
+
if (!existsSync10(tmpFile)) {
|
|
5100
|
+
return {
|
|
5101
|
+
success: false,
|
|
5102
|
+
output: "",
|
|
5103
|
+
error: "Download failed \u2014 file not created.",
|
|
5104
|
+
durationMs: performance.now() - start
|
|
5105
|
+
};
|
|
5106
|
+
}
|
|
5107
|
+
const fileTool = new TranscribeFileTool(this.workingDir);
|
|
5108
|
+
const result = await fileTool.execute({ path: tmpFile, model });
|
|
5109
|
+
try {
|
|
5110
|
+
unlinkSync(tmpFile);
|
|
5111
|
+
} catch {
|
|
5112
|
+
}
|
|
5113
|
+
return {
|
|
5114
|
+
success: result.success,
|
|
5115
|
+
output: `Source: ${url}
|
|
5116
|
+
${result.output}`,
|
|
5117
|
+
error: result.error,
|
|
5118
|
+
durationMs: performance.now() - start
|
|
5119
|
+
};
|
|
5120
|
+
} catch (err) {
|
|
5121
|
+
try {
|
|
5122
|
+
unlinkSync(tmpFile);
|
|
5123
|
+
} catch {
|
|
5124
|
+
}
|
|
5125
|
+
return {
|
|
5126
|
+
success: false,
|
|
5127
|
+
output: "",
|
|
5128
|
+
error: `Failed to download/transcribe: ${err instanceof Error ? err.message : String(err)}`,
|
|
5129
|
+
durationMs: performance.now() - start
|
|
5130
|
+
};
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
};
|
|
5134
|
+
}
|
|
5135
|
+
});
|
|
5136
|
+
|
|
4843
5137
|
// packages/execution/dist/shellRunner.js
|
|
4844
5138
|
var init_shellRunner = __esm({
|
|
4845
5139
|
"packages/execution/dist/shellRunner.js"() {
|
|
@@ -4943,6 +5237,7 @@ var init_dist2 = __esm({
|
|
|
4943
5237
|
init_custom_tool();
|
|
4944
5238
|
init_tool_creator();
|
|
4945
5239
|
init_skill_tools();
|
|
5240
|
+
init_transcribe_tool();
|
|
4946
5241
|
init_shellRunner();
|
|
4947
5242
|
init_gitWorktree();
|
|
4948
5243
|
init_patchApplier();
|
|
@@ -6074,7 +6369,7 @@ var init_code_retriever = __esm({
|
|
|
6074
6369
|
import { execFile as execFile4 } from "node:child_process";
|
|
6075
6370
|
import { promisify as promisify4 } from "node:util";
|
|
6076
6371
|
import { readFile as readFile7, readdir, stat } from "node:fs/promises";
|
|
6077
|
-
import { join as
|
|
6372
|
+
import { join as join14, extname as extname4 } from "node:path";
|
|
6078
6373
|
async function searchByPath(pathPattern, options) {
|
|
6079
6374
|
const allFiles = await collectFiles(options.rootDir, options.includeGlobs ?? DEFAULT_INCLUDE_GLOBS, options.excludeGlobs ?? DEFAULT_EXCLUDE_GLOBS);
|
|
6080
6375
|
const pattern = options.caseInsensitive ? pathPattern.toLowerCase() : pathPattern;
|
|
@@ -6216,11 +6511,11 @@ async function walkForFiles(rootDir, dir, excludeGlobs, results) {
|
|
|
6216
6511
|
continue;
|
|
6217
6512
|
if (excludeGlobs.some((g) => entry.name === g || matchesGlob(entry.name, g)))
|
|
6218
6513
|
continue;
|
|
6219
|
-
const absPath =
|
|
6514
|
+
const absPath = join14(dir, entry.name);
|
|
6220
6515
|
if (entry.isDirectory()) {
|
|
6221
6516
|
await walkForFiles(rootDir, absPath, excludeGlobs, results);
|
|
6222
6517
|
} else if (entry.isFile()) {
|
|
6223
|
-
const ext =
|
|
6518
|
+
const ext = extname4(entry.name);
|
|
6224
6519
|
if (!ALL_CODE_EXTS.has(ext))
|
|
6225
6520
|
continue;
|
|
6226
6521
|
const relativePath = absPath.startsWith(rootDir) ? absPath.slice(rootDir.length).replace(/^\//, "") : absPath;
|
|
@@ -6391,7 +6686,7 @@ var init_graphExpand = __esm({
|
|
|
6391
6686
|
|
|
6392
6687
|
// packages/retrieval/dist/snippetPacker.js
|
|
6393
6688
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
6394
|
-
import { join as
|
|
6689
|
+
import { join as join15 } from "node:path";
|
|
6395
6690
|
async function packSnippets(requests, opts = {}) {
|
|
6396
6691
|
const maxTokens = opts.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
6397
6692
|
const contextLines = opts.contextLines ?? DEFAULT_CONTEXT_LINES;
|
|
@@ -6417,7 +6712,7 @@ async function packSnippets(requests, opts = {}) {
|
|
|
6417
6712
|
return { packed, dropped, totalTokens };
|
|
6418
6713
|
}
|
|
6419
6714
|
async function extractSnippet(req, repoRoot, contextLines = DEFAULT_CONTEXT_LINES) {
|
|
6420
|
-
const absPath = req.filePath.startsWith("/") ? req.filePath :
|
|
6715
|
+
const absPath = req.filePath.startsWith("/") ? req.filePath : join15(repoRoot, req.filePath);
|
|
6421
6716
|
let content;
|
|
6422
6717
|
try {
|
|
6423
6718
|
content = await readFile8(absPath, "utf-8");
|
|
@@ -8726,6 +9021,425 @@ var init_dist5 = __esm({
|
|
|
8726
9021
|
}
|
|
8727
9022
|
});
|
|
8728
9023
|
|
|
9024
|
+
// packages/cli/dist/tui/listen.js
|
|
9025
|
+
import { spawn as spawn5, execSync as execSync10 } from "node:child_process";
|
|
9026
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
9027
|
+
import { join as join16 } from "node:path";
|
|
9028
|
+
import { homedir as homedir6 } from "node:os";
|
|
9029
|
+
import { EventEmitter } from "node:events";
|
|
9030
|
+
function isAudioPath(path) {
|
|
9031
|
+
const ext = path.toLowerCase().split(".").pop();
|
|
9032
|
+
return ext ? AUDIO_EXTENSIONS.has(`.${ext}`) : false;
|
|
9033
|
+
}
|
|
9034
|
+
function isVideoPath(path) {
|
|
9035
|
+
const ext = path.toLowerCase().split(".").pop();
|
|
9036
|
+
return ext ? VIDEO_EXTENSIONS.has(`.${ext}`) : false;
|
|
9037
|
+
}
|
|
9038
|
+
function isTranscribablePath(path) {
|
|
9039
|
+
return isAudioPath(path) || isVideoPath(path);
|
|
9040
|
+
}
|
|
9041
|
+
function findMicCaptureCommand() {
|
|
9042
|
+
const platform3 = process.platform;
|
|
9043
|
+
if (platform3 === "linux") {
|
|
9044
|
+
try {
|
|
9045
|
+
execSync10("which arecord", { stdio: "pipe" });
|
|
9046
|
+
return {
|
|
9047
|
+
cmd: "arecord",
|
|
9048
|
+
args: ["-f", "S16_LE", "-r", "16000", "-c", "1", "-t", "raw", "-q", "-"]
|
|
9049
|
+
};
|
|
9050
|
+
} catch {
|
|
9051
|
+
}
|
|
9052
|
+
}
|
|
9053
|
+
if (platform3 === "darwin") {
|
|
9054
|
+
try {
|
|
9055
|
+
execSync10("which sox", { stdio: "pipe" });
|
|
9056
|
+
return {
|
|
9057
|
+
cmd: "sox",
|
|
9058
|
+
args: ["-d", "-t", "raw", "-r", "16000", "-c", "1", "-b", "16", "-e", "signed-integer", "-"]
|
|
9059
|
+
};
|
|
9060
|
+
} catch {
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
try {
|
|
9064
|
+
execSync10("which ffmpeg", { stdio: "pipe" });
|
|
9065
|
+
if (platform3 === "linux") {
|
|
9066
|
+
return {
|
|
9067
|
+
cmd: "ffmpeg",
|
|
9068
|
+
args: [
|
|
9069
|
+
"-f",
|
|
9070
|
+
"pulse",
|
|
9071
|
+
"-i",
|
|
9072
|
+
"default",
|
|
9073
|
+
"-ar",
|
|
9074
|
+
"16000",
|
|
9075
|
+
"-ac",
|
|
9076
|
+
"1",
|
|
9077
|
+
"-f",
|
|
9078
|
+
"s16le",
|
|
9079
|
+
"-loglevel",
|
|
9080
|
+
"quiet",
|
|
9081
|
+
"pipe:1"
|
|
9082
|
+
]
|
|
9083
|
+
};
|
|
9084
|
+
} else if (platform3 === "darwin") {
|
|
9085
|
+
return {
|
|
9086
|
+
cmd: "ffmpeg",
|
|
9087
|
+
args: [
|
|
9088
|
+
"-f",
|
|
9089
|
+
"avfoundation",
|
|
9090
|
+
"-i",
|
|
9091
|
+
":0",
|
|
9092
|
+
"-ar",
|
|
9093
|
+
"16000",
|
|
9094
|
+
"-ac",
|
|
9095
|
+
"1",
|
|
9096
|
+
"-f",
|
|
9097
|
+
"s16le",
|
|
9098
|
+
"-loglevel",
|
|
9099
|
+
"quiet",
|
|
9100
|
+
"pipe:1"
|
|
9101
|
+
]
|
|
9102
|
+
};
|
|
9103
|
+
}
|
|
9104
|
+
} catch {
|
|
9105
|
+
}
|
|
9106
|
+
return null;
|
|
9107
|
+
}
|
|
9108
|
+
function getListenEngine(config) {
|
|
9109
|
+
if (!_engine) {
|
|
9110
|
+
_engine = new ListenEngine(config);
|
|
9111
|
+
}
|
|
9112
|
+
return _engine;
|
|
9113
|
+
}
|
|
9114
|
+
var AUDIO_EXTENSIONS, VIDEO_EXTENSIONS, ListenEngine, _engine;
|
|
9115
|
+
var init_listen = __esm({
|
|
9116
|
+
"packages/cli/dist/tui/listen.js"() {
|
|
9117
|
+
"use strict";
|
|
9118
|
+
AUDIO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
9119
|
+
".mp3",
|
|
9120
|
+
".wav",
|
|
9121
|
+
".flac",
|
|
9122
|
+
".aac",
|
|
9123
|
+
".m4a",
|
|
9124
|
+
".ogg",
|
|
9125
|
+
".wma",
|
|
9126
|
+
".opus"
|
|
9127
|
+
]);
|
|
9128
|
+
VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
9129
|
+
".mp4",
|
|
9130
|
+
".mkv",
|
|
9131
|
+
".avi",
|
|
9132
|
+
".mov",
|
|
9133
|
+
".webm",
|
|
9134
|
+
".flv",
|
|
9135
|
+
".wmv",
|
|
9136
|
+
".m4v",
|
|
9137
|
+
".ts"
|
|
9138
|
+
]);
|
|
9139
|
+
ListenEngine = class extends EventEmitter {
|
|
9140
|
+
config;
|
|
9141
|
+
micProcess = null;
|
|
9142
|
+
liveTranscriber = null;
|
|
9143
|
+
// TranscribeLive from transcribe-cli
|
|
9144
|
+
active = false;
|
|
9145
|
+
silenceTimer = null;
|
|
9146
|
+
countdownInterval = null;
|
|
9147
|
+
lastTranscriptTime = 0;
|
|
9148
|
+
pendingText = "";
|
|
9149
|
+
blinkTimer = null;
|
|
9150
|
+
blinkState = false;
|
|
9151
|
+
transcribeCliAvailable = null;
|
|
9152
|
+
constructor(config) {
|
|
9153
|
+
super();
|
|
9154
|
+
this.config = {
|
|
9155
|
+
model: config?.model ?? "base",
|
|
9156
|
+
mode: config?.mode ?? "confirm",
|
|
9157
|
+
silenceTimeoutMs: config?.silenceTimeoutMs ?? 3e3
|
|
9158
|
+
};
|
|
9159
|
+
}
|
|
9160
|
+
get isActive() {
|
|
9161
|
+
return this.active;
|
|
9162
|
+
}
|
|
9163
|
+
get isBlinking() {
|
|
9164
|
+
return this.active && this.blinkState;
|
|
9165
|
+
}
|
|
9166
|
+
get currentModel() {
|
|
9167
|
+
return this.config.model;
|
|
9168
|
+
}
|
|
9169
|
+
get currentMode() {
|
|
9170
|
+
return this.config.mode;
|
|
9171
|
+
}
|
|
9172
|
+
get pendingTranscript() {
|
|
9173
|
+
return this.pendingText;
|
|
9174
|
+
}
|
|
9175
|
+
setModel(model) {
|
|
9176
|
+
this.config.model = model;
|
|
9177
|
+
}
|
|
9178
|
+
setMode(mode) {
|
|
9179
|
+
this.config.mode = mode;
|
|
9180
|
+
}
|
|
9181
|
+
/** Check if transcribe-cli is available. */
|
|
9182
|
+
async isAvailable() {
|
|
9183
|
+
if (this.transcribeCliAvailable !== null)
|
|
9184
|
+
return this.transcribeCliAvailable;
|
|
9185
|
+
try {
|
|
9186
|
+
const mod = await this.loadTranscribeCli();
|
|
9187
|
+
this.transcribeCliAvailable = mod !== null;
|
|
9188
|
+
} catch {
|
|
9189
|
+
this.transcribeCliAvailable = false;
|
|
9190
|
+
}
|
|
9191
|
+
if (!this.transcribeCliAvailable) {
|
|
9192
|
+
try {
|
|
9193
|
+
execSync10("which transcribe-cli", { stdio: "pipe" });
|
|
9194
|
+
this.transcribeCliAvailable = true;
|
|
9195
|
+
} catch {
|
|
9196
|
+
this.transcribeCliAvailable = false;
|
|
9197
|
+
}
|
|
9198
|
+
}
|
|
9199
|
+
return this.transcribeCliAvailable;
|
|
9200
|
+
}
|
|
9201
|
+
/** Load transcribe-cli dynamically (it may not be installed). */
|
|
9202
|
+
async loadTranscribeCli() {
|
|
9203
|
+
try {
|
|
9204
|
+
const globalRoot = execSync10("npm root -g", {
|
|
9205
|
+
encoding: "utf-8",
|
|
9206
|
+
timeout: 5e3,
|
|
9207
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9208
|
+
}).trim();
|
|
9209
|
+
const tcPath = join16(globalRoot, "transcribe-cli");
|
|
9210
|
+
if (existsSync11(join16(tcPath, "dist", "index.js"))) {
|
|
9211
|
+
const { createRequire: createRequire4 } = await import("node:module");
|
|
9212
|
+
const req = createRequire4(import.meta.url);
|
|
9213
|
+
return req(join16(tcPath, "dist", "index.js"));
|
|
9214
|
+
}
|
|
9215
|
+
} catch {
|
|
9216
|
+
}
|
|
9217
|
+
const nvmBase = join16(homedir6(), ".nvm", "versions", "node");
|
|
9218
|
+
if (existsSync11(nvmBase)) {
|
|
9219
|
+
try {
|
|
9220
|
+
const { readdirSync: readdirSync9 } = await import("node:fs");
|
|
9221
|
+
for (const ver of readdirSync9(nvmBase)) {
|
|
9222
|
+
const tcPath = join16(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
|
|
9223
|
+
if (existsSync11(join16(tcPath, "dist", "index.js"))) {
|
|
9224
|
+
const { createRequire: createRequire4 } = await import("node:module");
|
|
9225
|
+
const req = createRequire4(import.meta.url);
|
|
9226
|
+
return req(join16(tcPath, "dist", "index.js"));
|
|
9227
|
+
}
|
|
9228
|
+
}
|
|
9229
|
+
} catch {
|
|
9230
|
+
}
|
|
9231
|
+
}
|
|
9232
|
+
return null;
|
|
9233
|
+
}
|
|
9234
|
+
/**
|
|
9235
|
+
* Start listening — captures microphone audio and feeds to TranscribeLive.
|
|
9236
|
+
*/
|
|
9237
|
+
async start() {
|
|
9238
|
+
if (this.active)
|
|
9239
|
+
return "Already listening.";
|
|
9240
|
+
const micCmd = findMicCaptureCommand();
|
|
9241
|
+
if (!micCmd) {
|
|
9242
|
+
return "No microphone capture tool found. Install arecord (Linux), sox (macOS), or ffmpeg.";
|
|
9243
|
+
}
|
|
9244
|
+
let tc = await this.loadTranscribeCli();
|
|
9245
|
+
if (!tc) {
|
|
9246
|
+
this.emit("info", "transcribe-cli not found. Installing globally...");
|
|
9247
|
+
try {
|
|
9248
|
+
execSync10("npm i -g transcribe-cli", { stdio: "pipe", timeout: 12e4 });
|
|
9249
|
+
this.transcribeCliAvailable = null;
|
|
9250
|
+
tc = await this.loadTranscribeCli();
|
|
9251
|
+
} catch {
|
|
9252
|
+
}
|
|
9253
|
+
if (!tc) {
|
|
9254
|
+
return "Failed to install transcribe-cli. Try manually: npm i -g transcribe-cli";
|
|
9255
|
+
}
|
|
9256
|
+
}
|
|
9257
|
+
const TranscribeLive = tc.TranscribeLive;
|
|
9258
|
+
this.liveTranscriber = new TranscribeLive({
|
|
9259
|
+
model: this.config.model,
|
|
9260
|
+
sampleRate: 16e3,
|
|
9261
|
+
channels: 1,
|
|
9262
|
+
sampleWidth: 2,
|
|
9263
|
+
chunkDuration: 3
|
|
9264
|
+
// 3s chunks for responsive transcription
|
|
9265
|
+
});
|
|
9266
|
+
this.liveTranscriber.on("transcript", (evt) => {
|
|
9267
|
+
if (!evt.text.trim())
|
|
9268
|
+
return;
|
|
9269
|
+
this.lastTranscriptTime = Date.now();
|
|
9270
|
+
this.pendingText = evt.text.trim();
|
|
9271
|
+
this.emit("transcript", this.pendingText, evt.isFinal);
|
|
9272
|
+
if (this.config.mode === "auto") {
|
|
9273
|
+
this.resetSilenceTimer();
|
|
9274
|
+
}
|
|
9275
|
+
});
|
|
9276
|
+
this.liveTranscriber.on("error", (err) => {
|
|
9277
|
+
this.emit("error", err);
|
|
9278
|
+
});
|
|
9279
|
+
await new Promise((resolve16, reject) => {
|
|
9280
|
+
const timeout = setTimeout(() => reject(new Error("Model load timeout (60s)")), 6e4);
|
|
9281
|
+
this.liveTranscriber.on("ready", () => {
|
|
9282
|
+
clearTimeout(timeout);
|
|
9283
|
+
resolve16();
|
|
9284
|
+
});
|
|
9285
|
+
this.liveTranscriber.on("error", (err) => {
|
|
9286
|
+
clearTimeout(timeout);
|
|
9287
|
+
reject(err);
|
|
9288
|
+
});
|
|
9289
|
+
});
|
|
9290
|
+
this.micProcess = spawn5(micCmd.cmd, micCmd.args, {
|
|
9291
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
9292
|
+
env: { ...process.env }
|
|
9293
|
+
});
|
|
9294
|
+
this.micProcess.stdout?.on("data", (chunk) => {
|
|
9295
|
+
if (this.active && this.liveTranscriber) {
|
|
9296
|
+
this.liveTranscriber.write(chunk);
|
|
9297
|
+
}
|
|
9298
|
+
});
|
|
9299
|
+
this.micProcess.stderr?.on("data", () => {
|
|
9300
|
+
});
|
|
9301
|
+
this.micProcess.on("error", (err) => {
|
|
9302
|
+
this.emit("error", new Error(`Mic capture failed: ${err.message}`));
|
|
9303
|
+
this.stop();
|
|
9304
|
+
});
|
|
9305
|
+
this.micProcess.on("close", () => {
|
|
9306
|
+
if (this.active) {
|
|
9307
|
+
this.stop();
|
|
9308
|
+
}
|
|
9309
|
+
});
|
|
9310
|
+
this.active = true;
|
|
9311
|
+
this.blinkState = true;
|
|
9312
|
+
this.blinkTimer = setInterval(() => {
|
|
9313
|
+
this.blinkState = !this.blinkState;
|
|
9314
|
+
this.emit("recording", this.blinkState);
|
|
9315
|
+
}, 500);
|
|
9316
|
+
this.lastTranscriptTime = Date.now();
|
|
9317
|
+
this.emit("started");
|
|
9318
|
+
this.emit("recording", true);
|
|
9319
|
+
const modeDesc = this.config.mode === "auto" ? `auto (${this.config.silenceTimeoutMs / 1e3}s timeout)` : "confirm (press Enter to submit)";
|
|
9320
|
+
return `Listening with ${this.config.model} model (${modeDesc})`;
|
|
9321
|
+
}
|
|
9322
|
+
/**
|
|
9323
|
+
* Stop listening — cleanup mic, transcriber, timers.
|
|
9324
|
+
*/
|
|
9325
|
+
async stop() {
|
|
9326
|
+
if (!this.active)
|
|
9327
|
+
return "Not listening.";
|
|
9328
|
+
this.active = false;
|
|
9329
|
+
this.blinkState = false;
|
|
9330
|
+
if (this.blinkTimer) {
|
|
9331
|
+
clearInterval(this.blinkTimer);
|
|
9332
|
+
this.blinkTimer = null;
|
|
9333
|
+
}
|
|
9334
|
+
if (this.silenceTimer) {
|
|
9335
|
+
clearTimeout(this.silenceTimer);
|
|
9336
|
+
this.silenceTimer = null;
|
|
9337
|
+
}
|
|
9338
|
+
if (this.countdownInterval) {
|
|
9339
|
+
clearInterval(this.countdownInterval);
|
|
9340
|
+
this.countdownInterval = null;
|
|
9341
|
+
}
|
|
9342
|
+
if (this.micProcess) {
|
|
9343
|
+
try {
|
|
9344
|
+
this.micProcess.kill("SIGTERM");
|
|
9345
|
+
} catch {
|
|
9346
|
+
}
|
|
9347
|
+
this.micProcess = null;
|
|
9348
|
+
}
|
|
9349
|
+
if (this.liveTranscriber) {
|
|
9350
|
+
try {
|
|
9351
|
+
this.liveTranscriber.stop();
|
|
9352
|
+
} catch {
|
|
9353
|
+
}
|
|
9354
|
+
this.liveTranscriber = null;
|
|
9355
|
+
}
|
|
9356
|
+
this.emit("recording", false);
|
|
9357
|
+
this.emit("stopped");
|
|
9358
|
+
const result = this.pendingText;
|
|
9359
|
+
this.pendingText = "";
|
|
9360
|
+
return result || "Stopped listening.";
|
|
9361
|
+
}
|
|
9362
|
+
/**
|
|
9363
|
+
* Accept the current pending transcript (for confirm mode).
|
|
9364
|
+
* Returns the text to inject into the input line.
|
|
9365
|
+
*/
|
|
9366
|
+
acceptTranscript() {
|
|
9367
|
+
const text = this.pendingText;
|
|
9368
|
+
this.pendingText = "";
|
|
9369
|
+
return text;
|
|
9370
|
+
}
|
|
9371
|
+
/**
|
|
9372
|
+
* Transcribe a file (audio or video) using transcribe-cli.
|
|
9373
|
+
* Returns the transcription result.
|
|
9374
|
+
*/
|
|
9375
|
+
async transcribeFile(filePath, outputDir) {
|
|
9376
|
+
let tc = await this.loadTranscribeCli();
|
|
9377
|
+
if (!tc) {
|
|
9378
|
+
try {
|
|
9379
|
+
execSync10("npm i -g transcribe-cli", { stdio: "pipe", timeout: 12e4 });
|
|
9380
|
+
this.transcribeCliAvailable = null;
|
|
9381
|
+
tc = await this.loadTranscribeCli();
|
|
9382
|
+
} catch {
|
|
9383
|
+
}
|
|
9384
|
+
}
|
|
9385
|
+
if (!tc)
|
|
9386
|
+
return null;
|
|
9387
|
+
try {
|
|
9388
|
+
const result = await tc.transcribe(filePath, {
|
|
9389
|
+
model: this.config.model,
|
|
9390
|
+
format: "json",
|
|
9391
|
+
diarize: false,
|
|
9392
|
+
wordTimestamps: false
|
|
9393
|
+
});
|
|
9394
|
+
if (outputDir) {
|
|
9395
|
+
const { basename: basename8 } = await import("node:path");
|
|
9396
|
+
const transcriptDir = join16(outputDir, ".oa", "transcripts");
|
|
9397
|
+
mkdirSync5(transcriptDir, { recursive: true });
|
|
9398
|
+
const outFile = join16(transcriptDir, `${basename8(filePath)}.txt`);
|
|
9399
|
+
writeFileSync5(outFile, result.text, "utf-8");
|
|
9400
|
+
}
|
|
9401
|
+
return {
|
|
9402
|
+
text: result.text,
|
|
9403
|
+
duration: result.duration,
|
|
9404
|
+
speakers: result.speakers,
|
|
9405
|
+
segments: result.segments
|
|
9406
|
+
};
|
|
9407
|
+
} catch (err) {
|
|
9408
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
9409
|
+
return null;
|
|
9410
|
+
}
|
|
9411
|
+
}
|
|
9412
|
+
// -------------------------------------------------------------------------
|
|
9413
|
+
// Auto-mode silence detection
|
|
9414
|
+
// -------------------------------------------------------------------------
|
|
9415
|
+
resetSilenceTimer() {
|
|
9416
|
+
if (this.silenceTimer)
|
|
9417
|
+
clearTimeout(this.silenceTimer);
|
|
9418
|
+
if (this.countdownInterval)
|
|
9419
|
+
clearInterval(this.countdownInterval);
|
|
9420
|
+
const timeoutMs = this.config.silenceTimeoutMs;
|
|
9421
|
+
const startTime = Date.now();
|
|
9422
|
+
this.countdownInterval = setInterval(() => {
|
|
9423
|
+
const elapsed = Date.now() - startTime;
|
|
9424
|
+
const remaining = Math.max(0, Math.ceil((timeoutMs - elapsed) / 1e3));
|
|
9425
|
+
this.emit("countdown", remaining);
|
|
9426
|
+
if (remaining <= 0 && this.countdownInterval) {
|
|
9427
|
+
clearInterval(this.countdownInterval);
|
|
9428
|
+
this.countdownInterval = null;
|
|
9429
|
+
}
|
|
9430
|
+
}, 1e3);
|
|
9431
|
+
this.silenceTimer = setTimeout(() => {
|
|
9432
|
+
if (this.active && this.pendingText) {
|
|
9433
|
+
this.emit("countdown", 0);
|
|
9434
|
+
this.emit("transcript", this.pendingText, true);
|
|
9435
|
+
}
|
|
9436
|
+
}, timeoutMs);
|
|
9437
|
+
}
|
|
9438
|
+
};
|
|
9439
|
+
_engine = null;
|
|
9440
|
+
}
|
|
9441
|
+
});
|
|
9442
|
+
|
|
8729
9443
|
// packages/cli/dist/tui/model-picker.js
|
|
8730
9444
|
async function fetchOllamaModels(baseUrl) {
|
|
8731
9445
|
const url = `${baseUrl.replace(/\/$/, "")}/api/tags`;
|
|
@@ -9016,10 +9730,16 @@ function renderSlashHelp() {
|
|
|
9016
9730
|
["/dream deep", "Deep dream \u2014 multi-cycle exploration with sleep architecture"],
|
|
9017
9731
|
["/dream lucid", "Lucid dream \u2014 implements proposals, tests, evaluates in cycles"],
|
|
9018
9732
|
["/dream stop", "Wake up \u2014 stop dreaming"],
|
|
9733
|
+
["/listen", "Toggle live microphone transcription (Whisper)"],
|
|
9734
|
+
["/listen <model>", "Set model: tiny, base, small, medium, large"],
|
|
9735
|
+
["/listen confirm", "Require Enter to submit transcription"],
|
|
9736
|
+
["/listen auto", "Auto-submit after 3s silence (blinking \u25CF indicator)"],
|
|
9737
|
+
["/listen stop", "Stop listening"],
|
|
9019
9738
|
["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
|
|
9020
9739
|
["/tools", "List agent-created custom tools"],
|
|
9021
9740
|
["/skills", "List available AIWG skills"],
|
|
9022
9741
|
["/skills <keyword>", "Filter skills by name or trigger"],
|
|
9742
|
+
["/<skill-name> [args]", "Invoke an AIWG skill directly"],
|
|
9023
9743
|
["/verbose", "Toggle verbose mode"],
|
|
9024
9744
|
["/clear", "Clear the screen"],
|
|
9025
9745
|
["/help", "Show this help"],
|
|
@@ -9149,6 +9869,10 @@ function formatToolArgs(toolName, args) {
|
|
|
9149
9869
|
return String(args["region"] ?? "full screen");
|
|
9150
9870
|
case "ocr":
|
|
9151
9871
|
return `${args["path"] ?? ""}${args["region"] ? ` (${args["region"]})` : ""}`;
|
|
9872
|
+
case "transcribe_file":
|
|
9873
|
+
return `${args["path"] ?? ""}${args["model"] ? ` (${args["model"]})` : ""}`;
|
|
9874
|
+
case "transcribe_url":
|
|
9875
|
+
return truncStr(String(args["url"] ?? ""), 60);
|
|
9152
9876
|
default:
|
|
9153
9877
|
return Object.entries(args).map(([k, v]) => `${k}=${truncStr(String(v), 30)}`).join(", ");
|
|
9154
9878
|
}
|
|
@@ -9214,7 +9938,10 @@ var init_render = __esm({
|
|
|
9214
9938
|
// Image tools
|
|
9215
9939
|
image_read: "\u{1F5BC}\uFE0F",
|
|
9216
9940
|
screenshot: "\u{1F4F8}",
|
|
9217
|
-
ocr: "\u{1F441}\uFE0F"
|
|
9941
|
+
ocr: "\u{1F441}\uFE0F",
|
|
9942
|
+
// Transcription tools
|
|
9943
|
+
transcribe_file: "\u{1F399}\uFE0F",
|
|
9944
|
+
transcribe_url: "\u{1F399}\uFE0F"
|
|
9218
9945
|
};
|
|
9219
9946
|
TOOL_LABELS = {
|
|
9220
9947
|
file_read: "Read",
|
|
@@ -9245,7 +9972,10 @@ var init_render = __esm({
|
|
|
9245
9972
|
// Image tools
|
|
9246
9973
|
image_read: "Image read",
|
|
9247
9974
|
screenshot: "Screenshot",
|
|
9248
|
-
ocr: "OCR"
|
|
9975
|
+
ocr: "OCR",
|
|
9976
|
+
// Transcription tools
|
|
9977
|
+
transcribe_file: "Transcribe",
|
|
9978
|
+
transcribe_url: "Transcribe URL"
|
|
9249
9979
|
};
|
|
9250
9980
|
_contentWriteHook = null;
|
|
9251
9981
|
HINTS = [
|
|
@@ -9287,6 +10017,8 @@ var init_render = __esm({
|
|
|
9287
10017
|
"image_read",
|
|
9288
10018
|
"screenshot",
|
|
9289
10019
|
"ocr",
|
|
10020
|
+
"transcribe_file",
|
|
10021
|
+
"transcribe_url",
|
|
9290
10022
|
"aiwg_setup",
|
|
9291
10023
|
"aiwg_health",
|
|
9292
10024
|
"aiwg_workflow",
|
|
@@ -9301,11 +10033,13 @@ var init_render = __esm({
|
|
|
9301
10033
|
"/config",
|
|
9302
10034
|
"/update",
|
|
9303
10035
|
"/voice",
|
|
10036
|
+
"/listen",
|
|
9304
10037
|
"/stream",
|
|
9305
10038
|
"/verbose",
|
|
9306
10039
|
"/dream",
|
|
9307
10040
|
"/bruteforce",
|
|
9308
10041
|
"/tools",
|
|
10042
|
+
"/skills",
|
|
9309
10043
|
"/clear",
|
|
9310
10044
|
"/help",
|
|
9311
10045
|
"/quit"
|
|
@@ -9314,52 +10048,52 @@ var init_render = __esm({
|
|
|
9314
10048
|
});
|
|
9315
10049
|
|
|
9316
10050
|
// packages/cli/dist/tui/oa-directory.js
|
|
9317
|
-
import { existsSync as
|
|
9318
|
-
import { join as
|
|
9319
|
-
import { homedir as
|
|
10051
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6, readdirSync as readdirSync6, statSync as statSync5, unlinkSync as unlinkSync2 } from "node:fs";
|
|
10052
|
+
import { join as join17, relative as relative2, basename as basename4, extname as extname5 } from "node:path";
|
|
10053
|
+
import { homedir as homedir7 } from "node:os";
|
|
9320
10054
|
function initOaDirectory(repoRoot) {
|
|
9321
|
-
const oaPath =
|
|
10055
|
+
const oaPath = join17(repoRoot, OA_DIR);
|
|
9322
10056
|
for (const sub of SUBDIRS) {
|
|
9323
|
-
|
|
10057
|
+
mkdirSync6(join17(oaPath, sub), { recursive: true });
|
|
9324
10058
|
}
|
|
9325
10059
|
return oaPath;
|
|
9326
10060
|
}
|
|
9327
10061
|
function hasOaDirectory(repoRoot) {
|
|
9328
|
-
return
|
|
10062
|
+
return existsSync12(join17(repoRoot, OA_DIR, "index"));
|
|
9329
10063
|
}
|
|
9330
10064
|
function loadProjectSettings(repoRoot) {
|
|
9331
|
-
const settingsPath =
|
|
10065
|
+
const settingsPath = join17(repoRoot, OA_DIR, "settings.json");
|
|
9332
10066
|
try {
|
|
9333
|
-
if (
|
|
9334
|
-
return JSON.parse(
|
|
10067
|
+
if (existsSync12(settingsPath)) {
|
|
10068
|
+
return JSON.parse(readFileSync10(settingsPath, "utf-8"));
|
|
9335
10069
|
}
|
|
9336
10070
|
} catch {
|
|
9337
10071
|
}
|
|
9338
10072
|
return {};
|
|
9339
10073
|
}
|
|
9340
10074
|
function saveProjectSettings(repoRoot, settings) {
|
|
9341
|
-
const oaPath =
|
|
9342
|
-
|
|
10075
|
+
const oaPath = join17(repoRoot, OA_DIR);
|
|
10076
|
+
mkdirSync6(oaPath, { recursive: true });
|
|
9343
10077
|
const existing = loadProjectSettings(repoRoot);
|
|
9344
10078
|
const merged = { ...existing, ...settings };
|
|
9345
|
-
|
|
10079
|
+
writeFileSync6(join17(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
9346
10080
|
}
|
|
9347
10081
|
function loadGlobalSettings() {
|
|
9348
|
-
const settingsPath =
|
|
10082
|
+
const settingsPath = join17(homedir7(), ".open-agents", "settings.json");
|
|
9349
10083
|
try {
|
|
9350
|
-
if (
|
|
9351
|
-
return JSON.parse(
|
|
10084
|
+
if (existsSync12(settingsPath)) {
|
|
10085
|
+
return JSON.parse(readFileSync10(settingsPath, "utf-8"));
|
|
9352
10086
|
}
|
|
9353
10087
|
} catch {
|
|
9354
10088
|
}
|
|
9355
10089
|
return {};
|
|
9356
10090
|
}
|
|
9357
10091
|
function saveGlobalSettings(settings) {
|
|
9358
|
-
const dir =
|
|
9359
|
-
|
|
10092
|
+
const dir = join17(homedir7(), ".open-agents");
|
|
10093
|
+
mkdirSync6(dir, { recursive: true });
|
|
9360
10094
|
const existing = loadGlobalSettings();
|
|
9361
10095
|
const merged = { ...existing, ...settings };
|
|
9362
|
-
|
|
10096
|
+
writeFileSync6(join17(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
9363
10097
|
}
|
|
9364
10098
|
function resolveSettings(repoRoot) {
|
|
9365
10099
|
const global = loadGlobalSettings();
|
|
@@ -9374,12 +10108,12 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
9374
10108
|
while (dir && !visited.has(dir)) {
|
|
9375
10109
|
visited.add(dir);
|
|
9376
10110
|
for (const name of CONTEXT_FILES) {
|
|
9377
|
-
const filePath =
|
|
10111
|
+
const filePath = join17(dir, name);
|
|
9378
10112
|
const normalizedName = name.toLowerCase();
|
|
9379
|
-
if (
|
|
10113
|
+
if (existsSync12(filePath) && !seen.has(filePath)) {
|
|
9380
10114
|
seen.add(filePath);
|
|
9381
10115
|
try {
|
|
9382
|
-
let content =
|
|
10116
|
+
let content = readFileSync10(filePath, "utf-8");
|
|
9383
10117
|
if (content.length > maxContentLen) {
|
|
9384
10118
|
content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
|
|
9385
10119
|
}
|
|
@@ -9393,11 +10127,11 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
9393
10127
|
}
|
|
9394
10128
|
}
|
|
9395
10129
|
}
|
|
9396
|
-
const projectMap =
|
|
9397
|
-
if (
|
|
10130
|
+
const projectMap = join17(dir, OA_DIR, "context", "project-map.md");
|
|
10131
|
+
if (existsSync12(projectMap) && !seen.has(projectMap)) {
|
|
9398
10132
|
seen.add(projectMap);
|
|
9399
10133
|
try {
|
|
9400
|
-
let content =
|
|
10134
|
+
let content = readFileSync10(projectMap, "utf-8");
|
|
9401
10135
|
if (content.length > maxContentLen) {
|
|
9402
10136
|
content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
|
|
9403
10137
|
}
|
|
@@ -9409,7 +10143,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
9409
10143
|
} catch {
|
|
9410
10144
|
}
|
|
9411
10145
|
}
|
|
9412
|
-
const parent =
|
|
10146
|
+
const parent = join17(dir, "..");
|
|
9413
10147
|
if (parent === dir)
|
|
9414
10148
|
break;
|
|
9415
10149
|
dir = parent;
|
|
@@ -9427,16 +10161,16 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
9427
10161
|
return found;
|
|
9428
10162
|
}
|
|
9429
10163
|
function readIndexMeta(repoRoot) {
|
|
9430
|
-
const metaPath =
|
|
10164
|
+
const metaPath = join17(repoRoot, OA_DIR, "index", "meta.json");
|
|
9431
10165
|
try {
|
|
9432
|
-
return JSON.parse(
|
|
10166
|
+
return JSON.parse(readFileSync10(metaPath, "utf-8"));
|
|
9433
10167
|
} catch {
|
|
9434
10168
|
return null;
|
|
9435
10169
|
}
|
|
9436
10170
|
}
|
|
9437
10171
|
function generateProjectMap(repoRoot) {
|
|
9438
10172
|
const sections = [];
|
|
9439
|
-
const repoName2 =
|
|
10173
|
+
const repoName2 = basename4(repoRoot);
|
|
9440
10174
|
sections.push(`# Project Map: ${repoName2}
|
|
9441
10175
|
`);
|
|
9442
10176
|
sections.push(`> Auto-generated by open-agents. Updated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
@@ -9480,28 +10214,28 @@ ${tree}\`\`\`
|
|
|
9480
10214
|
sections.push("");
|
|
9481
10215
|
}
|
|
9482
10216
|
const content = sections.join("\n");
|
|
9483
|
-
const contextDir =
|
|
9484
|
-
|
|
9485
|
-
|
|
10217
|
+
const contextDir = join17(repoRoot, OA_DIR, "context");
|
|
10218
|
+
mkdirSync6(contextDir, { recursive: true });
|
|
10219
|
+
writeFileSync6(join17(contextDir, "project-map.md"), content, "utf-8");
|
|
9486
10220
|
return content;
|
|
9487
10221
|
}
|
|
9488
10222
|
function saveSession(repoRoot, session) {
|
|
9489
|
-
const historyDir =
|
|
9490
|
-
|
|
9491
|
-
|
|
10223
|
+
const historyDir = join17(repoRoot, OA_DIR, "history");
|
|
10224
|
+
mkdirSync6(historyDir, { recursive: true });
|
|
10225
|
+
writeFileSync6(join17(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
|
|
9492
10226
|
}
|
|
9493
10227
|
function loadRecentSessions(repoRoot, limit = 5) {
|
|
9494
|
-
const historyDir =
|
|
9495
|
-
if (!
|
|
10228
|
+
const historyDir = join17(repoRoot, OA_DIR, "history");
|
|
10229
|
+
if (!existsSync12(historyDir))
|
|
9496
10230
|
return [];
|
|
9497
10231
|
try {
|
|
9498
10232
|
const files = readdirSync6(historyDir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
9499
|
-
const stat3 = statSync5(
|
|
10233
|
+
const stat3 = statSync5(join17(historyDir, f));
|
|
9500
10234
|
return { file: f, mtime: stat3.mtimeMs };
|
|
9501
10235
|
}).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
|
|
9502
10236
|
return files.map((f) => {
|
|
9503
10237
|
try {
|
|
9504
|
-
return JSON.parse(
|
|
10238
|
+
return JSON.parse(readFileSync10(join17(historyDir, f.file), "utf-8"));
|
|
9505
10239
|
} catch {
|
|
9506
10240
|
return null;
|
|
9507
10241
|
}
|
|
@@ -9511,18 +10245,18 @@ function loadRecentSessions(repoRoot, limit = 5) {
|
|
|
9511
10245
|
}
|
|
9512
10246
|
}
|
|
9513
10247
|
function savePendingTask(repoRoot, task) {
|
|
9514
|
-
const historyDir =
|
|
9515
|
-
|
|
9516
|
-
|
|
10248
|
+
const historyDir = join17(repoRoot, OA_DIR, "history");
|
|
10249
|
+
mkdirSync6(historyDir, { recursive: true });
|
|
10250
|
+
writeFileSync6(join17(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
|
|
9517
10251
|
}
|
|
9518
10252
|
function loadPendingTask(repoRoot) {
|
|
9519
|
-
const filePath =
|
|
10253
|
+
const filePath = join17(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
|
|
9520
10254
|
try {
|
|
9521
|
-
if (!
|
|
10255
|
+
if (!existsSync12(filePath))
|
|
9522
10256
|
return null;
|
|
9523
|
-
const data = JSON.parse(
|
|
10257
|
+
const data = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
9524
10258
|
try {
|
|
9525
|
-
|
|
10259
|
+
unlinkSync2(filePath);
|
|
9526
10260
|
} catch {
|
|
9527
10261
|
}
|
|
9528
10262
|
return data;
|
|
@@ -9548,12 +10282,12 @@ function detectManifests(repoRoot) {
|
|
|
9548
10282
|
{ file: "docker-compose.yaml", type: "Docker Compose" }
|
|
9549
10283
|
];
|
|
9550
10284
|
for (const check of checks) {
|
|
9551
|
-
const filePath =
|
|
9552
|
-
if (
|
|
10285
|
+
const filePath = join17(repoRoot, check.file);
|
|
10286
|
+
if (existsSync12(filePath)) {
|
|
9553
10287
|
let name;
|
|
9554
10288
|
if (check.nameField) {
|
|
9555
10289
|
try {
|
|
9556
|
-
const data = JSON.parse(
|
|
10290
|
+
const data = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
9557
10291
|
name = data[check.nameField];
|
|
9558
10292
|
} catch {
|
|
9559
10293
|
}
|
|
@@ -9582,7 +10316,7 @@ function findKeyFiles(repoRoot) {
|
|
|
9582
10316
|
{ pattern: "CLAUDE.md", description: "Claude Code context" }
|
|
9583
10317
|
];
|
|
9584
10318
|
for (const check of checks) {
|
|
9585
|
-
if (
|
|
10319
|
+
if (existsSync12(join17(repoRoot, check.pattern))) {
|
|
9586
10320
|
keyFiles.push({ path: check.pattern, description: check.description });
|
|
9587
10321
|
}
|
|
9588
10322
|
}
|
|
@@ -9608,12 +10342,12 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
|
|
|
9608
10342
|
if (entry.isDirectory()) {
|
|
9609
10343
|
let fileCount = 0;
|
|
9610
10344
|
try {
|
|
9611
|
-
fileCount = readdirSync6(
|
|
10345
|
+
fileCount = readdirSync6(join17(root, entry.name)).filter((f) => !f.startsWith(".")).length;
|
|
9612
10346
|
} catch {
|
|
9613
10347
|
}
|
|
9614
10348
|
result += `${prefix}${connector}${entry.name}/ (${fileCount})
|
|
9615
10349
|
`;
|
|
9616
|
-
result += buildDirTree(
|
|
10350
|
+
result += buildDirTree(join17(root, entry.name), maxDepth, childPrefix, depth + 1);
|
|
9617
10351
|
} else if (depth < maxDepth) {
|
|
9618
10352
|
result += `${prefix}${connector}${entry.name}
|
|
9619
10353
|
`;
|
|
@@ -9808,7 +10542,7 @@ async function handleSlashCommand(input, ctx) {
|
|
|
9808
10542
|
}
|
|
9809
10543
|
}
|
|
9810
10544
|
process.stdout.write("\n");
|
|
9811
|
-
renderInfo(
|
|
10545
|
+
renderInfo('Invoke directly: /<skill-name> [args] (e.g. /ralph "fix tests" --completion "npm test passes")');
|
|
9812
10546
|
renderInfo("Filter with: /skills <keyword>");
|
|
9813
10547
|
}
|
|
9814
10548
|
return "handled";
|
|
@@ -9829,6 +10563,38 @@ async function handleSlashCommand(input, ctx) {
|
|
|
9829
10563
|
}
|
|
9830
10564
|
return "handled";
|
|
9831
10565
|
}
|
|
10566
|
+
case "listen":
|
|
10567
|
+
case "mic": {
|
|
10568
|
+
if (!ctx.listenToggle) {
|
|
10569
|
+
renderWarning("Listen mode not available in this context.");
|
|
10570
|
+
return "handled";
|
|
10571
|
+
}
|
|
10572
|
+
if (arg === "stop" || arg === "off") {
|
|
10573
|
+
const msg2 = await (ctx.listenStop?.() ?? Promise.resolve("Not listening."));
|
|
10574
|
+
renderInfo(msg2);
|
|
10575
|
+
return "handled";
|
|
10576
|
+
}
|
|
10577
|
+
if (arg === "confirm") {
|
|
10578
|
+
const msg2 = ctx.listenSetMode?.("confirm") ?? "Confirm mode set.";
|
|
10579
|
+
renderInfo(msg2);
|
|
10580
|
+
return "handled";
|
|
10581
|
+
}
|
|
10582
|
+
if (arg === "auto") {
|
|
10583
|
+
const msg2 = ctx.listenSetMode?.("auto") ?? "Auto mode set.";
|
|
10584
|
+
renderInfo(msg2);
|
|
10585
|
+
return "handled";
|
|
10586
|
+
}
|
|
10587
|
+
const modelSizes = ["tiny", "base", "small", "medium", "large", "large-v3"];
|
|
10588
|
+
if (arg && modelSizes.includes(arg.toLowerCase())) {
|
|
10589
|
+
const model = arg.toLowerCase() === "large" ? "large-v3" : arg.toLowerCase();
|
|
10590
|
+
const msg2 = await (ctx.listenSetModel?.(model) ?? Promise.resolve(`Model set to ${model}.`));
|
|
10591
|
+
renderInfo(msg2);
|
|
10592
|
+
return "handled";
|
|
10593
|
+
}
|
|
10594
|
+
const msg = await ctx.listenToggle();
|
|
10595
|
+
renderInfo(msg);
|
|
10596
|
+
return "handled";
|
|
10597
|
+
}
|
|
9832
10598
|
case "bruteforce":
|
|
9833
10599
|
case "brute": {
|
|
9834
10600
|
const isOn = ctx.bruteForceToggle();
|
|
@@ -9837,9 +10603,19 @@ async function handleSlashCommand(input, ctx) {
|
|
|
9837
10603
|
renderInfo(`Brute-force mode: ${isOn ? "on" : "off"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 agent will auto re-engage when turn limit is hit, reassess and try creative strategies" : ""));
|
|
9838
10604
|
return "handled";
|
|
9839
10605
|
}
|
|
9840
|
-
default:
|
|
10606
|
+
default: {
|
|
10607
|
+
const skills = discoverSkills(ctx.repoRoot);
|
|
10608
|
+
const skill = skills.find((s) => s.name === cmd || s.name === cmd.replace(/_/g, "-"));
|
|
10609
|
+
if (skill) {
|
|
10610
|
+
const content = loadSkillContent(skill.filePath);
|
|
10611
|
+
if (content) {
|
|
10612
|
+
renderInfo(`Loading skill: ${c2.bold(skill.name)} (${skill.source})`);
|
|
10613
|
+
return { type: "skill", name: skill.name, content, args: arg };
|
|
10614
|
+
}
|
|
10615
|
+
}
|
|
9841
10616
|
renderWarning(`Unknown command: /${cmd}. Type /help for available commands.`);
|
|
9842
10617
|
return "handled";
|
|
10618
|
+
}
|
|
9843
10619
|
}
|
|
9844
10620
|
}
|
|
9845
10621
|
async function listModels(ctx) {
|
|
@@ -9971,17 +10747,17 @@ async function handleUpdate(subcommand, repoRoot, savePendingTaskState) {
|
|
|
9971
10747
|
try {
|
|
9972
10748
|
const { createRequire: createRequire4 } = await import("node:module");
|
|
9973
10749
|
const { fileURLToPath: fileURLToPath3 } = await import("node:url");
|
|
9974
|
-
const { dirname: dirname5, join:
|
|
9975
|
-
const { existsSync:
|
|
10750
|
+
const { dirname: dirname5, join: join28 } = await import("node:path");
|
|
10751
|
+
const { existsSync: existsSync19 } = await import("node:fs");
|
|
9976
10752
|
const req = createRequire4(import.meta.url);
|
|
9977
10753
|
const thisDir = dirname5(fileURLToPath3(import.meta.url));
|
|
9978
10754
|
const candidates = [
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
10755
|
+
join28(thisDir, "..", "package.json"),
|
|
10756
|
+
join28(thisDir, "..", "..", "package.json"),
|
|
10757
|
+
join28(thisDir, "..", "..", "..", "package.json")
|
|
9982
10758
|
];
|
|
9983
10759
|
for (const pkgPath of candidates) {
|
|
9984
|
-
if (
|
|
10760
|
+
if (existsSync19(pkgPath)) {
|
|
9985
10761
|
const pkg = req(pkgPath);
|
|
9986
10762
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
9987
10763
|
currentVersion = pkg.version ?? "0.0.0";
|
|
@@ -10065,17 +10841,17 @@ var init_commands = __esm({
|
|
|
10065
10841
|
|
|
10066
10842
|
// packages/cli/dist/tui/setup.js
|
|
10067
10843
|
import * as readline from "node:readline";
|
|
10068
|
-
import { execSync as
|
|
10069
|
-
import { existsSync as
|
|
10070
|
-
import { join as
|
|
10071
|
-
import { homedir as
|
|
10844
|
+
import { execSync as execSync11 } from "node:child_process";
|
|
10845
|
+
import { existsSync as existsSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "node:fs";
|
|
10846
|
+
import { join as join18 } from "node:path";
|
|
10847
|
+
import { homedir as homedir8 } from "node:os";
|
|
10072
10848
|
function detectSystemSpecs() {
|
|
10073
10849
|
let totalRamGB = 0;
|
|
10074
10850
|
let availableRamGB = 0;
|
|
10075
10851
|
let gpuVramGB = 0;
|
|
10076
10852
|
let gpuName = "";
|
|
10077
10853
|
try {
|
|
10078
|
-
const memInfo =
|
|
10854
|
+
const memInfo = execSync11("free -b 2>/dev/null || sysctl -n hw.memsize 2>/dev/null", {
|
|
10079
10855
|
encoding: "utf8",
|
|
10080
10856
|
timeout: 5e3
|
|
10081
10857
|
});
|
|
@@ -10095,7 +10871,7 @@ function detectSystemSpecs() {
|
|
|
10095
10871
|
} catch {
|
|
10096
10872
|
}
|
|
10097
10873
|
try {
|
|
10098
|
-
const nvidiaSmi =
|
|
10874
|
+
const nvidiaSmi = execSync11("nvidia-smi --query-gpu=memory.total,name --format=csv,noheader,nounits 2>/dev/null", { encoding: "utf8", timeout: 5e3 });
|
|
10099
10875
|
const lines = nvidiaSmi.trim().split("\n");
|
|
10100
10876
|
if (lines.length > 0) {
|
|
10101
10877
|
for (const line of lines) {
|
|
@@ -10151,13 +10927,13 @@ function modelSupportsToolCalling(modelName) {
|
|
|
10151
10927
|
return false;
|
|
10152
10928
|
}
|
|
10153
10929
|
function ask(rl, question) {
|
|
10154
|
-
return new Promise((
|
|
10155
|
-
rl.question(question, (answer) =>
|
|
10930
|
+
return new Promise((resolve16) => {
|
|
10931
|
+
rl.question(question, (answer) => resolve16(answer.trim()));
|
|
10156
10932
|
});
|
|
10157
10933
|
}
|
|
10158
10934
|
function pullModelWithAutoUpdate(tag) {
|
|
10159
10935
|
try {
|
|
10160
|
-
|
|
10936
|
+
execSync11(`ollama pull ${tag}`, {
|
|
10161
10937
|
stdio: "inherit",
|
|
10162
10938
|
timeout: 36e5
|
|
10163
10939
|
// 1 hour max
|
|
@@ -10174,7 +10950,7 @@ function pullModelWithAutoUpdate(tag) {
|
|
|
10174
10950
|
|
|
10175
10951
|
`);
|
|
10176
10952
|
try {
|
|
10177
|
-
|
|
10953
|
+
execSync11("curl -fsSL https://ollama.com/install.sh | sh", {
|
|
10178
10954
|
stdio: "inherit",
|
|
10179
10955
|
timeout: 3e5
|
|
10180
10956
|
// 5 min max for install
|
|
@@ -10185,7 +10961,7 @@ function pullModelWithAutoUpdate(tag) {
|
|
|
10185
10961
|
process.stdout.write(` ${c2.cyan("\u25CF")} Retrying pull of ${c2.bold(tag)}...
|
|
10186
10962
|
|
|
10187
10963
|
`);
|
|
10188
|
-
|
|
10964
|
+
execSync11(`ollama pull ${tag}`, {
|
|
10189
10965
|
stdio: "inherit",
|
|
10190
10966
|
timeout: 36e5
|
|
10191
10967
|
});
|
|
@@ -10363,12 +11139,12 @@ async function doSetup(config, rl) {
|
|
|
10363
11139
|
`PARAMETER num_predict 16384`,
|
|
10364
11140
|
`PARAMETER stop "<|endoftext|>"`
|
|
10365
11141
|
].join("\n");
|
|
10366
|
-
const modelDir2 =
|
|
10367
|
-
|
|
10368
|
-
const modelfilePath =
|
|
10369
|
-
|
|
11142
|
+
const modelDir2 = join18(homedir8(), ".open-agents", "models");
|
|
11143
|
+
mkdirSync7(modelDir2, { recursive: true });
|
|
11144
|
+
const modelfilePath = join18(modelDir2, `Modelfile.${customName}`);
|
|
11145
|
+
writeFileSync7(modelfilePath, modelfileContent + "\n", "utf8");
|
|
10370
11146
|
process.stdout.write(` ${c2.dim("Creating model...")} `);
|
|
10371
|
-
|
|
11147
|
+
execSync11(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
10372
11148
|
stdio: "pipe",
|
|
10373
11149
|
timeout: 12e4
|
|
10374
11150
|
});
|
|
@@ -10411,7 +11187,7 @@ async function isModelAvailable(config) {
|
|
|
10411
11187
|
}
|
|
10412
11188
|
function isFirstRun() {
|
|
10413
11189
|
try {
|
|
10414
|
-
return !
|
|
11190
|
+
return !existsSync13(join18(homedir8(), ".open-agents", "config.json"));
|
|
10415
11191
|
} catch {
|
|
10416
11192
|
return true;
|
|
10417
11193
|
}
|
|
@@ -10451,10 +11227,10 @@ var init_setup = __esm({
|
|
|
10451
11227
|
});
|
|
10452
11228
|
|
|
10453
11229
|
// packages/cli/dist/tui/project-context.js
|
|
10454
|
-
import { existsSync as
|
|
10455
|
-
import { join as
|
|
10456
|
-
import { execSync as
|
|
10457
|
-
import { homedir as
|
|
11230
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, readdirSync as readdirSync7 } from "node:fs";
|
|
11231
|
+
import { join as join19, basename as basename5 } from "node:path";
|
|
11232
|
+
import { execSync as execSync12 } from "node:child_process";
|
|
11233
|
+
import { homedir as homedir9, platform, release } from "node:os";
|
|
10458
11234
|
function loadProjectFiles(repoRoot) {
|
|
10459
11235
|
const discovered = discoverContextFiles(repoRoot);
|
|
10460
11236
|
if (discovered.length === 0)
|
|
@@ -10472,10 +11248,10 @@ function loadProjectMap(repoRoot) {
|
|
|
10472
11248
|
if (!hasOaDirectory(repoRoot)) {
|
|
10473
11249
|
initOaDirectory(repoRoot);
|
|
10474
11250
|
}
|
|
10475
|
-
const mapPath =
|
|
10476
|
-
if (
|
|
11251
|
+
const mapPath = join19(repoRoot, OA_DIR, "context", "project-map.md");
|
|
11252
|
+
if (existsSync14(mapPath)) {
|
|
10477
11253
|
try {
|
|
10478
|
-
const content =
|
|
11254
|
+
const content = readFileSync11(mapPath, "utf-8");
|
|
10479
11255
|
return content;
|
|
10480
11256
|
} catch {
|
|
10481
11257
|
}
|
|
@@ -10484,19 +11260,19 @@ function loadProjectMap(repoRoot) {
|
|
|
10484
11260
|
}
|
|
10485
11261
|
function getGitInfo(repoRoot) {
|
|
10486
11262
|
try {
|
|
10487
|
-
|
|
11263
|
+
execSync12("git rev-parse --is-inside-work-tree", { cwd: repoRoot, stdio: "pipe" });
|
|
10488
11264
|
} catch {
|
|
10489
11265
|
return "";
|
|
10490
11266
|
}
|
|
10491
11267
|
const lines = [];
|
|
10492
11268
|
try {
|
|
10493
|
-
const branch =
|
|
11269
|
+
const branch = execSync12("git branch --show-current", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
10494
11270
|
if (branch)
|
|
10495
11271
|
lines.push(`Branch: ${branch}`);
|
|
10496
11272
|
} catch {
|
|
10497
11273
|
}
|
|
10498
11274
|
try {
|
|
10499
|
-
const status =
|
|
11275
|
+
const status = execSync12("git status --porcelain", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
10500
11276
|
if (status) {
|
|
10501
11277
|
const changed = status.split("\n").length;
|
|
10502
11278
|
lines.push(`Working tree: ${changed} changed file(s)`);
|
|
@@ -10506,7 +11282,7 @@ function getGitInfo(repoRoot) {
|
|
|
10506
11282
|
} catch {
|
|
10507
11283
|
}
|
|
10508
11284
|
try {
|
|
10509
|
-
const log =
|
|
11285
|
+
const log = execSync12("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
10510
11286
|
if (log)
|
|
10511
11287
|
lines.push(`Recent commits:
|
|
10512
11288
|
${log}`);
|
|
@@ -10516,33 +11292,33 @@ ${log}`);
|
|
|
10516
11292
|
}
|
|
10517
11293
|
function loadMemoryContext(repoRoot) {
|
|
10518
11294
|
const sections = [];
|
|
10519
|
-
const oaMemDir =
|
|
11295
|
+
const oaMemDir = join19(repoRoot, OA_DIR, "memory");
|
|
10520
11296
|
const oaEntries = loadMemoryDir(oaMemDir, "project");
|
|
10521
11297
|
if (oaEntries)
|
|
10522
11298
|
sections.push(oaEntries);
|
|
10523
|
-
const legacyMemDir =
|
|
10524
|
-
if (legacyMemDir !== oaMemDir &&
|
|
11299
|
+
const legacyMemDir = join19(repoRoot, ".open-agents", "memory");
|
|
11300
|
+
if (legacyMemDir !== oaMemDir && existsSync14(legacyMemDir)) {
|
|
10525
11301
|
const legacyEntries = loadMemoryDir(legacyMemDir, "project/legacy");
|
|
10526
11302
|
if (legacyEntries)
|
|
10527
11303
|
sections.push(legacyEntries);
|
|
10528
11304
|
}
|
|
10529
|
-
const globalMemDir =
|
|
11305
|
+
const globalMemDir = join19(homedir9(), ".open-agents", "memory");
|
|
10530
11306
|
const globalEntries = loadMemoryDir(globalMemDir, "global");
|
|
10531
11307
|
if (globalEntries)
|
|
10532
11308
|
sections.push(globalEntries);
|
|
10533
11309
|
return sections.join("\n\n");
|
|
10534
11310
|
}
|
|
10535
11311
|
function loadMemoryDir(memDir, scope) {
|
|
10536
|
-
if (!
|
|
11312
|
+
if (!existsSync14(memDir))
|
|
10537
11313
|
return "";
|
|
10538
11314
|
const lines = [];
|
|
10539
11315
|
try {
|
|
10540
11316
|
const files = readdirSync7(memDir).filter((f) => f.endsWith(".json"));
|
|
10541
11317
|
for (const file of files.slice(0, 10)) {
|
|
10542
11318
|
try {
|
|
10543
|
-
const raw =
|
|
11319
|
+
const raw = readFileSync11(join19(memDir, file), "utf-8");
|
|
10544
11320
|
const entries = JSON.parse(raw);
|
|
10545
|
-
const topic =
|
|
11321
|
+
const topic = basename5(file, ".json");
|
|
10546
11322
|
const keys = Object.keys(entries);
|
|
10547
11323
|
if (keys.length === 0)
|
|
10548
11324
|
continue;
|
|
@@ -11551,25 +12327,25 @@ var init_carousel = __esm({
|
|
|
11551
12327
|
});
|
|
11552
12328
|
|
|
11553
12329
|
// packages/cli/dist/tui/voice.js
|
|
11554
|
-
import { existsSync as
|
|
11555
|
-
import { join as
|
|
11556
|
-
import { homedir as
|
|
11557
|
-
import { execSync as
|
|
12330
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, writeFileSync as writeFileSync8, readFileSync as readFileSync12, unlinkSync as unlinkSync3 } from "node:fs";
|
|
12331
|
+
import { join as join20 } from "node:path";
|
|
12332
|
+
import { homedir as homedir10, tmpdir as tmpdir2, platform as platform2 } from "node:os";
|
|
12333
|
+
import { execSync as execSync13, spawn as nodeSpawn } from "node:child_process";
|
|
11558
12334
|
import { createRequire } from "node:module";
|
|
11559
12335
|
function voiceDir() {
|
|
11560
|
-
return
|
|
12336
|
+
return join20(homedir10(), ".open-agents", "voice");
|
|
11561
12337
|
}
|
|
11562
12338
|
function modelsDir() {
|
|
11563
|
-
return
|
|
12339
|
+
return join20(voiceDir(), "models");
|
|
11564
12340
|
}
|
|
11565
12341
|
function modelDir(id) {
|
|
11566
|
-
return
|
|
12342
|
+
return join20(modelsDir(), id);
|
|
11567
12343
|
}
|
|
11568
12344
|
function modelOnnxPath(id) {
|
|
11569
|
-
return
|
|
12345
|
+
return join20(modelDir(id), "model.onnx");
|
|
11570
12346
|
}
|
|
11571
12347
|
function modelConfigPath(id) {
|
|
11572
|
-
return
|
|
12348
|
+
return join20(modelDir(id), "config.json");
|
|
11573
12349
|
}
|
|
11574
12350
|
function describeToolCall(toolName, args) {
|
|
11575
12351
|
const path = args["path"];
|
|
@@ -11842,11 +12618,11 @@ var init_voice = __esm({
|
|
|
11842
12618
|
const audioData = result["output"].data;
|
|
11843
12619
|
if (audioData.length === 0)
|
|
11844
12620
|
return;
|
|
11845
|
-
const wavPath =
|
|
12621
|
+
const wavPath = join20(tmpdir2(), `oa-voice-${Date.now()}.wav`);
|
|
11846
12622
|
this.writeWav(audioData, this.config.audio.sample_rate, wavPath);
|
|
11847
12623
|
await this.playWav(wavPath);
|
|
11848
12624
|
try {
|
|
11849
|
-
|
|
12625
|
+
unlinkSync3(wavPath);
|
|
11850
12626
|
} catch {
|
|
11851
12627
|
}
|
|
11852
12628
|
}
|
|
@@ -11922,7 +12698,7 @@ var init_voice = __esm({
|
|
|
11922
12698
|
buffer.write("data", 36);
|
|
11923
12699
|
buffer.writeUInt32LE(dataSize, 40);
|
|
11924
12700
|
Buffer.from(int16.buffer, int16.byteOffset, int16.byteLength).copy(buffer, 44);
|
|
11925
|
-
|
|
12701
|
+
writeFileSync8(path, buffer);
|
|
11926
12702
|
}
|
|
11927
12703
|
// -------------------------------------------------------------------------
|
|
11928
12704
|
// Audio playback (system default speakers)
|
|
@@ -11931,7 +12707,7 @@ var init_voice = __esm({
|
|
|
11931
12707
|
const cmd = this.getPlayCommand(path);
|
|
11932
12708
|
if (!cmd)
|
|
11933
12709
|
return;
|
|
11934
|
-
return new Promise((
|
|
12710
|
+
return new Promise((resolve16) => {
|
|
11935
12711
|
const child = nodeSpawn(cmd[0], cmd.slice(1), {
|
|
11936
12712
|
stdio: "ignore",
|
|
11937
12713
|
detached: false
|
|
@@ -11940,12 +12716,12 @@ var init_voice = __esm({
|
|
|
11940
12716
|
child.on("close", () => {
|
|
11941
12717
|
if (this.currentPlayback === child)
|
|
11942
12718
|
this.currentPlayback = null;
|
|
11943
|
-
|
|
12719
|
+
resolve16();
|
|
11944
12720
|
});
|
|
11945
12721
|
child.on("error", () => {
|
|
11946
12722
|
if (this.currentPlayback === child)
|
|
11947
12723
|
this.currentPlayback = null;
|
|
11948
|
-
|
|
12724
|
+
resolve16();
|
|
11949
12725
|
});
|
|
11950
12726
|
setTimeout(() => {
|
|
11951
12727
|
if (this.currentPlayback === child) {
|
|
@@ -11955,7 +12731,7 @@ var init_voice = __esm({
|
|
|
11955
12731
|
}
|
|
11956
12732
|
this.currentPlayback = null;
|
|
11957
12733
|
}
|
|
11958
|
-
|
|
12734
|
+
resolve16();
|
|
11959
12735
|
}, 15e3);
|
|
11960
12736
|
});
|
|
11961
12737
|
}
|
|
@@ -11972,7 +12748,7 @@ var init_voice = __esm({
|
|
|
11972
12748
|
}
|
|
11973
12749
|
for (const player of ["paplay", "pw-play", "aplay"]) {
|
|
11974
12750
|
try {
|
|
11975
|
-
|
|
12751
|
+
execSync13(`which ${player}`, { stdio: "pipe" });
|
|
11976
12752
|
return [player, path];
|
|
11977
12753
|
} catch {
|
|
11978
12754
|
}
|
|
@@ -11994,36 +12770,36 @@ var init_voice = __esm({
|
|
|
11994
12770
|
async ensureRuntime() {
|
|
11995
12771
|
if (this.ort)
|
|
11996
12772
|
return;
|
|
11997
|
-
|
|
11998
|
-
const pkgPath =
|
|
12773
|
+
mkdirSync8(voiceDir(), { recursive: true });
|
|
12774
|
+
const pkgPath = join20(voiceDir(), "package.json");
|
|
11999
12775
|
const expectedDeps = {
|
|
12000
12776
|
"onnxruntime-node": "^1.21.0",
|
|
12001
12777
|
"phonemizer": "^1.2.1"
|
|
12002
12778
|
};
|
|
12003
|
-
if (
|
|
12779
|
+
if (existsSync15(pkgPath)) {
|
|
12004
12780
|
try {
|
|
12005
|
-
const existing = JSON.parse(
|
|
12781
|
+
const existing = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
12006
12782
|
if (!existing.dependencies?.["phonemizer"]) {
|
|
12007
12783
|
existing.dependencies = { ...existing.dependencies, ...expectedDeps };
|
|
12008
|
-
|
|
12784
|
+
writeFileSync8(pkgPath, JSON.stringify(existing, null, 2));
|
|
12009
12785
|
}
|
|
12010
12786
|
} catch {
|
|
12011
12787
|
}
|
|
12012
12788
|
}
|
|
12013
|
-
if (!
|
|
12014
|
-
|
|
12789
|
+
if (!existsSync15(pkgPath)) {
|
|
12790
|
+
writeFileSync8(pkgPath, JSON.stringify({
|
|
12015
12791
|
name: "open-agents-voice",
|
|
12016
12792
|
private: true,
|
|
12017
12793
|
dependencies: expectedDeps
|
|
12018
12794
|
}, null, 2));
|
|
12019
12795
|
}
|
|
12020
|
-
const voiceRequire = createRequire(
|
|
12796
|
+
const voiceRequire = createRequire(join20(voiceDir(), "index.js"));
|
|
12021
12797
|
try {
|
|
12022
12798
|
this.ort = voiceRequire("onnxruntime-node");
|
|
12023
12799
|
} catch {
|
|
12024
12800
|
renderInfo("Installing ONNX runtime for voice synthesis...");
|
|
12025
12801
|
try {
|
|
12026
|
-
|
|
12802
|
+
execSync13("npm install --no-audit --no-fund", {
|
|
12027
12803
|
cwd: voiceDir(),
|
|
12028
12804
|
stdio: "pipe",
|
|
12029
12805
|
timeout: 12e4
|
|
@@ -12040,7 +12816,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
12040
12816
|
} catch {
|
|
12041
12817
|
renderInfo("Installing phonemizer for voice synthesis...");
|
|
12042
12818
|
try {
|
|
12043
|
-
|
|
12819
|
+
execSync13("npm install --no-audit --no-fund", {
|
|
12044
12820
|
cwd: voiceDir(),
|
|
12045
12821
|
stdio: "pipe",
|
|
12046
12822
|
timeout: 12e4
|
|
@@ -12063,18 +12839,18 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
12063
12839
|
const dir = modelDir(id);
|
|
12064
12840
|
const onnxPath = modelOnnxPath(id);
|
|
12065
12841
|
const configPath = modelConfigPath(id);
|
|
12066
|
-
if (
|
|
12842
|
+
if (existsSync15(onnxPath) && existsSync15(configPath))
|
|
12067
12843
|
return;
|
|
12068
|
-
|
|
12069
|
-
if (!
|
|
12844
|
+
mkdirSync8(dir, { recursive: true });
|
|
12845
|
+
if (!existsSync15(configPath)) {
|
|
12070
12846
|
renderInfo(`Downloading ${model.label} voice config...`);
|
|
12071
12847
|
const configResp = await fetch(model.configUrl);
|
|
12072
12848
|
if (!configResp.ok)
|
|
12073
12849
|
throw new Error(`Failed to download config: HTTP ${configResp.status}`);
|
|
12074
12850
|
const configText = await configResp.text();
|
|
12075
|
-
|
|
12851
|
+
writeFileSync8(configPath, configText);
|
|
12076
12852
|
}
|
|
12077
|
-
if (!
|
|
12853
|
+
if (!existsSync15(onnxPath)) {
|
|
12078
12854
|
renderInfo(`Downloading ${model.label} voice model (this may take a minute)...`);
|
|
12079
12855
|
const onnxResp = await fetch(model.onnxUrl);
|
|
12080
12856
|
if (!onnxResp.ok)
|
|
@@ -12098,7 +12874,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
12098
12874
|
}
|
|
12099
12875
|
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
12100
12876
|
const fullBuffer = Buffer.concat(chunks);
|
|
12101
|
-
|
|
12877
|
+
writeFileSync8(onnxPath, fullBuffer);
|
|
12102
12878
|
renderInfo(`${model.label} model downloaded (${formatBytes2(fullBuffer.length)}).`);
|
|
12103
12879
|
}
|
|
12104
12880
|
}
|
|
@@ -12110,10 +12886,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
12110
12886
|
throw new Error("ONNX runtime not loaded");
|
|
12111
12887
|
const onnxPath = modelOnnxPath(this.modelId);
|
|
12112
12888
|
const configPath = modelConfigPath(this.modelId);
|
|
12113
|
-
if (!
|
|
12889
|
+
if (!existsSync15(onnxPath) || !existsSync15(configPath)) {
|
|
12114
12890
|
throw new Error(`Model files not found for ${this.modelId}`);
|
|
12115
12891
|
}
|
|
12116
|
-
this.config = JSON.parse(
|
|
12892
|
+
this.config = JSON.parse(readFileSync12(configPath, "utf8"));
|
|
12117
12893
|
renderInfo("Loading voice model...");
|
|
12118
12894
|
this.session = await this.ort.InferenceSession.create(onnxPath, {
|
|
12119
12895
|
executionProviders: ["cpu"],
|
|
@@ -12570,13 +13346,13 @@ var init_stream_renderer = __esm({
|
|
|
12570
13346
|
});
|
|
12571
13347
|
|
|
12572
13348
|
// packages/cli/dist/tui/edit-history.js
|
|
12573
|
-
import { appendFileSync, mkdirSync as
|
|
12574
|
-
import { join as
|
|
13349
|
+
import { appendFileSync, mkdirSync as mkdirSync9 } from "node:fs";
|
|
13350
|
+
import { join as join21 } from "node:path";
|
|
12575
13351
|
function createEditHistoryLogger(repoRoot, sessionId) {
|
|
12576
|
-
const historyDir =
|
|
12577
|
-
const logPath =
|
|
13352
|
+
const historyDir = join21(repoRoot, ".oa", "history");
|
|
13353
|
+
const logPath = join21(historyDir, "edits.jsonl");
|
|
12578
13354
|
try {
|
|
12579
|
-
|
|
13355
|
+
mkdirSync9(historyDir, { recursive: true });
|
|
12580
13356
|
} catch {
|
|
12581
13357
|
}
|
|
12582
13358
|
function logToolCall(toolName, toolArgs, success) {
|
|
@@ -12685,9 +13461,9 @@ var init_edit_history = __esm({
|
|
|
12685
13461
|
});
|
|
12686
13462
|
|
|
12687
13463
|
// packages/cli/dist/tui/dream-engine.js
|
|
12688
|
-
import { mkdirSync as
|
|
12689
|
-
import { join as
|
|
12690
|
-
import { execSync as
|
|
13464
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync13, existsSync as existsSync16, cpSync, rmSync, readdirSync as readdirSync8 } from "node:fs";
|
|
13465
|
+
import { join as join22, basename as basename6 } from "node:path";
|
|
13466
|
+
import { execSync as execSync14 } from "node:child_process";
|
|
12691
13467
|
function adaptTool(tool) {
|
|
12692
13468
|
return {
|
|
12693
13469
|
name: tool.name,
|
|
@@ -12861,14 +13637,14 @@ var init_dream_engine = __esm({
|
|
|
12861
13637
|
const content = String(args["content"] ?? "");
|
|
12862
13638
|
if (!rawPath)
|
|
12863
13639
|
return { success: false, output: "", error: "path is required", durationMs: Date.now() - start };
|
|
12864
|
-
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ?
|
|
13640
|
+
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join22(this.dreamsDir, basename6(rawPath)) : join22(this.dreamsDir, rawPath);
|
|
12865
13641
|
if (!targetPath.startsWith(this.dreamsDir)) {
|
|
12866
13642
|
return { success: false, output: "", error: "Dream mode: writes are confined to .oa/dreams/", durationMs: Date.now() - start };
|
|
12867
13643
|
}
|
|
12868
13644
|
try {
|
|
12869
|
-
const dir =
|
|
12870
|
-
|
|
12871
|
-
|
|
13645
|
+
const dir = join22(targetPath, "..");
|
|
13646
|
+
mkdirSync10(dir, { recursive: true });
|
|
13647
|
+
writeFileSync9(targetPath, content, "utf-8");
|
|
12872
13648
|
return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
|
|
12873
13649
|
} catch (err) {
|
|
12874
13650
|
return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
|
|
@@ -12896,20 +13672,20 @@ var init_dream_engine = __esm({
|
|
|
12896
13672
|
const rawPath = String(args["path"] ?? "");
|
|
12897
13673
|
const oldStr = String(args["old_string"] ?? "");
|
|
12898
13674
|
const newStr = String(args["new_string"] ?? "");
|
|
12899
|
-
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ?
|
|
13675
|
+
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join22(this.dreamsDir, basename6(rawPath)) : join22(this.dreamsDir, rawPath);
|
|
12900
13676
|
if (!targetPath.startsWith(this.dreamsDir)) {
|
|
12901
13677
|
return { success: false, output: "", error: "Dream mode: edits are confined to .oa/dreams/", durationMs: Date.now() - start };
|
|
12902
13678
|
}
|
|
12903
13679
|
try {
|
|
12904
|
-
if (!
|
|
13680
|
+
if (!existsSync16(targetPath)) {
|
|
12905
13681
|
return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
|
|
12906
13682
|
}
|
|
12907
|
-
let content =
|
|
13683
|
+
let content = readFileSync13(targetPath, "utf-8");
|
|
12908
13684
|
if (!content.includes(oldStr)) {
|
|
12909
13685
|
return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
|
|
12910
13686
|
}
|
|
12911
13687
|
content = content.replace(oldStr, newStr);
|
|
12912
|
-
|
|
13688
|
+
writeFileSync9(targetPath, content, "utf-8");
|
|
12913
13689
|
return { success: true, output: `Edited ${rawPath}`, durationMs: Date.now() - start };
|
|
12914
13690
|
} catch (err) {
|
|
12915
13691
|
return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
|
|
@@ -12940,7 +13716,7 @@ var init_dream_engine = __esm({
|
|
|
12940
13716
|
}
|
|
12941
13717
|
}
|
|
12942
13718
|
try {
|
|
12943
|
-
const output =
|
|
13719
|
+
const output = execSync14(cmd, {
|
|
12944
13720
|
cwd: this.repoRoot,
|
|
12945
13721
|
timeout: 3e4,
|
|
12946
13722
|
encoding: "utf-8",
|
|
@@ -12963,7 +13739,7 @@ var init_dream_engine = __esm({
|
|
|
12963
13739
|
constructor(config, repoRoot) {
|
|
12964
13740
|
this.config = config;
|
|
12965
13741
|
this.repoRoot = repoRoot;
|
|
12966
|
-
this.dreamsDir =
|
|
13742
|
+
this.dreamsDir = join22(repoRoot, ".oa", "dreams");
|
|
12967
13743
|
this.state = {
|
|
12968
13744
|
mode: "default",
|
|
12969
13745
|
active: false,
|
|
@@ -12994,7 +13770,7 @@ var init_dream_engine = __esm({
|
|
|
12994
13770
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12995
13771
|
results: []
|
|
12996
13772
|
};
|
|
12997
|
-
|
|
13773
|
+
mkdirSync10(this.dreamsDir, { recursive: true });
|
|
12998
13774
|
this.saveDreamState();
|
|
12999
13775
|
try {
|
|
13000
13776
|
for (let cycle = 1; cycle <= totalCycles; cycle++) {
|
|
@@ -13035,8 +13811,8 @@ ${result.summary}`;
|
|
|
13035
13811
|
if (mode !== "default" || cycle === totalCycles) {
|
|
13036
13812
|
renderDreamContraction(cycle);
|
|
13037
13813
|
const cycleSummary = this.buildCycleSummary(cycle, previousFindings);
|
|
13038
|
-
const summaryPath =
|
|
13039
|
-
|
|
13814
|
+
const summaryPath = join22(this.dreamsDir, `cycle-${cycle}-summary.md`);
|
|
13815
|
+
writeFileSync9(summaryPath, cycleSummary, "utf-8");
|
|
13040
13816
|
}
|
|
13041
13817
|
if (mode === "lucid" && !this.abortController.signal.aborted) {
|
|
13042
13818
|
this.saveVersionCheckpoint(cycle);
|
|
@@ -13156,29 +13932,29 @@ Dreams directory: ${this.dreamsDir}`);
|
|
|
13156
13932
|
}
|
|
13157
13933
|
/** Save workspace backup for lucid mode */
|
|
13158
13934
|
saveVersionCheckpoint(cycle) {
|
|
13159
|
-
const checkpointDir =
|
|
13935
|
+
const checkpointDir = join22(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
|
|
13160
13936
|
try {
|
|
13161
|
-
|
|
13937
|
+
mkdirSync10(checkpointDir, { recursive: true });
|
|
13162
13938
|
try {
|
|
13163
|
-
const gitStatus =
|
|
13939
|
+
const gitStatus = execSync14("git status --porcelain", {
|
|
13164
13940
|
cwd: this.repoRoot,
|
|
13165
13941
|
encoding: "utf-8",
|
|
13166
13942
|
timeout: 1e4
|
|
13167
13943
|
});
|
|
13168
|
-
const gitDiff =
|
|
13944
|
+
const gitDiff = execSync14("git diff", {
|
|
13169
13945
|
cwd: this.repoRoot,
|
|
13170
13946
|
encoding: "utf-8",
|
|
13171
13947
|
timeout: 1e4
|
|
13172
13948
|
});
|
|
13173
|
-
const gitHash =
|
|
13949
|
+
const gitHash = execSync14("git rev-parse HEAD 2>/dev/null || echo 'no-git'", {
|
|
13174
13950
|
cwd: this.repoRoot,
|
|
13175
13951
|
encoding: "utf-8",
|
|
13176
13952
|
timeout: 5e3
|
|
13177
13953
|
}).trim();
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13954
|
+
writeFileSync9(join22(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
|
|
13955
|
+
writeFileSync9(join22(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
|
|
13956
|
+
writeFileSync9(join22(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
|
|
13957
|
+
writeFileSync9(join22(checkpointDir, "checkpoint.json"), JSON.stringify({
|
|
13182
13958
|
cycle,
|
|
13183
13959
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13184
13960
|
gitHash,
|
|
@@ -13186,7 +13962,7 @@ Dreams directory: ${this.dreamsDir}`);
|
|
|
13186
13962
|
}, null, 2), "utf-8");
|
|
13187
13963
|
renderInfo(`Checkpoint saved: cycle ${cycle} (${gitHash.slice(0, 8)})`);
|
|
13188
13964
|
} catch {
|
|
13189
|
-
|
|
13965
|
+
writeFileSync9(join22(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
|
|
13190
13966
|
renderInfo(`Checkpoint saved: cycle ${cycle} (no git)`);
|
|
13191
13967
|
}
|
|
13192
13968
|
} catch (err) {
|
|
@@ -13244,14 +14020,14 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
|
|
|
13244
14020
|
---
|
|
13245
14021
|
*Auto-generated by open-agents dream engine*
|
|
13246
14022
|
`;
|
|
13247
|
-
|
|
14023
|
+
writeFileSync9(join22(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
|
|
13248
14024
|
} catch {
|
|
13249
14025
|
}
|
|
13250
14026
|
}
|
|
13251
14027
|
/** Save dream state for resume/inspection */
|
|
13252
14028
|
saveDreamState() {
|
|
13253
14029
|
try {
|
|
13254
|
-
|
|
14030
|
+
writeFileSync9(join22(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
|
|
13255
14031
|
} catch {
|
|
13256
14032
|
}
|
|
13257
14033
|
}
|
|
@@ -13297,6 +14073,10 @@ var init_status_bar = __esm({
|
|
|
13297
14073
|
* own output is suppressed.
|
|
13298
14074
|
*/
|
|
13299
14075
|
inputStateProvider = null;
|
|
14076
|
+
/** Whether microphone is recording (blinking indicator) */
|
|
14077
|
+
_recording = false;
|
|
14078
|
+
/** Countdown seconds for auto-mode silence timeout (0 = not counting down) */
|
|
14079
|
+
_countdown = 0;
|
|
13300
14080
|
/**
|
|
13301
14081
|
* Provide a callback that returns readline's current input state.
|
|
13302
14082
|
* StatusBar uses this to render typed text and position the cursor
|
|
@@ -13305,6 +14085,20 @@ var init_status_bar = __esm({
|
|
|
13305
14085
|
setInputStateProvider(provider) {
|
|
13306
14086
|
this.inputStateProvider = provider;
|
|
13307
14087
|
}
|
|
14088
|
+
/** Set recording indicator state (blinking red ●) */
|
|
14089
|
+
setRecording(active) {
|
|
14090
|
+
this._recording = active;
|
|
14091
|
+
if (!active)
|
|
14092
|
+
this._countdown = 0;
|
|
14093
|
+
if (this.active)
|
|
14094
|
+
this.renderFooterPreserveCursor();
|
|
14095
|
+
}
|
|
14096
|
+
/** Set countdown seconds for auto-mode silence timeout */
|
|
14097
|
+
setCountdown(seconds) {
|
|
14098
|
+
this._countdown = seconds;
|
|
14099
|
+
if (this.active)
|
|
14100
|
+
this.renderFooterPreserveCursor();
|
|
14101
|
+
}
|
|
13308
14102
|
/** Context window size to display. Can be updated if model changes. */
|
|
13309
14103
|
setContextWindowSize(size) {
|
|
13310
14104
|
this.metrics.contextWindowSize = size;
|
|
@@ -13453,7 +14247,13 @@ var init_status_bar = __esm({
|
|
|
13453
14247
|
const ctxPct = ctxTotal > 0 ? Math.max(0, Math.min(100, Math.round((1 - ctxUsed / ctxTotal) * 100))) : 100;
|
|
13454
14248
|
const ctxColor = ctxPct > 50 ? c2.green : ctxPct > 20 ? c2.yellow : c2.red;
|
|
13455
14249
|
const ctxLabel = c2.blue("Ctx: ") + c2.bold(`${ctxUsed.toLocaleString()}/${ctxTotal.toLocaleString()}`) + ` ${ctxColor(`${ctxPct}%`)}`;
|
|
13456
|
-
|
|
14250
|
+
let recordingLabel = "";
|
|
14251
|
+
if (this._recording) {
|
|
14252
|
+
const dot = c2.red("\u25CF");
|
|
14253
|
+
const countdown = this._countdown > 0 ? c2.dim(` ${this._countdown}s`) : "";
|
|
14254
|
+
recordingLabel = pipe + dot + c2.red(" REC") + countdown;
|
|
14255
|
+
}
|
|
14256
|
+
return ` ${tokInLabel}${pipe}${tokOutLabel}${pipe}${ctxLabel}${recordingLabel}`;
|
|
13457
14257
|
}
|
|
13458
14258
|
// -------------------------------------------------------------------------
|
|
13459
14259
|
// Private
|
|
@@ -13580,23 +14380,22 @@ var init_status_bar = __esm({
|
|
|
13580
14380
|
import * as readline2 from "node:readline";
|
|
13581
14381
|
import { Writable } from "node:stream";
|
|
13582
14382
|
import { cwd } from "node:process";
|
|
13583
|
-
import { resolve as
|
|
14383
|
+
import { resolve as resolve13, join as join23, dirname as dirname3, extname as extname6 } from "node:path";
|
|
13584
14384
|
import { createRequire as createRequire2 } from "node:module";
|
|
13585
14385
|
import { fileURLToPath } from "node:url";
|
|
13586
|
-
import { readFileSync as
|
|
13587
|
-
import { existsSync as
|
|
13588
|
-
import { extname as extname5 } from "node:path";
|
|
14386
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
14387
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
13589
14388
|
function getVersion() {
|
|
13590
14389
|
try {
|
|
13591
14390
|
const require2 = createRequire2(import.meta.url);
|
|
13592
14391
|
const thisDir = dirname3(fileURLToPath(import.meta.url));
|
|
13593
14392
|
const candidates = [
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
14393
|
+
join23(thisDir, "..", "package.json"),
|
|
14394
|
+
join23(thisDir, "..", "..", "package.json"),
|
|
14395
|
+
join23(thisDir, "..", "..", "..", "package.json")
|
|
13597
14396
|
];
|
|
13598
14397
|
for (const pkgPath of candidates) {
|
|
13599
|
-
if (
|
|
14398
|
+
if (existsSync17(pkgPath)) {
|
|
13600
14399
|
const pkg = require2(pkgPath);
|
|
13601
14400
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
13602
14401
|
return pkg.version ?? "0.0.0";
|
|
@@ -13673,7 +14472,10 @@ function buildTools(repoRoot, config) {
|
|
|
13673
14472
|
...buildCustomTools(repoRoot),
|
|
13674
14473
|
// Skill system (AIWG skills — discovery and execution)
|
|
13675
14474
|
new SkillListTool(repoRoot),
|
|
13676
|
-
new SkillExecuteTool(repoRoot)
|
|
14475
|
+
new SkillExecuteTool(repoRoot),
|
|
14476
|
+
// Transcription tools (transcribe-cli / faster-whisper)
|
|
14477
|
+
new TranscribeFileTool(repoRoot),
|
|
14478
|
+
new TranscribeUrlTool(repoRoot)
|
|
13677
14479
|
];
|
|
13678
14480
|
return [
|
|
13679
14481
|
...executionTools.map(adaptTool2),
|
|
@@ -13926,7 +14728,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
|
|
|
13926
14728
|
} };
|
|
13927
14729
|
}
|
|
13928
14730
|
async function startInteractive(config, repoPath) {
|
|
13929
|
-
const repoRoot =
|
|
14731
|
+
const repoRoot = resolve13(repoPath ?? cwd());
|
|
13930
14732
|
const isResumed = !!process.env.__OA_RESUMED;
|
|
13931
14733
|
if (isResumed) {
|
|
13932
14734
|
delete process.env.__OA_RESUMED;
|
|
@@ -14153,7 +14955,7 @@ async function startInteractive(config, repoPath) {
|
|
|
14153
14955
|
}
|
|
14154
14956
|
},
|
|
14155
14957
|
savePendingTaskState() {
|
|
14156
|
-
if (lastSubmittedPrompt) {
|
|
14958
|
+
if (lastSubmittedPrompt && activeTask) {
|
|
14157
14959
|
savePendingTask(repoRoot, {
|
|
14158
14960
|
prompt: lastSubmittedPrompt,
|
|
14159
14961
|
progressSummary: `Manual /update triggered. ${sessionToolCallCount} tool calls completed in last task.`,
|
|
@@ -14195,6 +14997,59 @@ async function startInteractive(config, repoPath) {
|
|
|
14195
14997
|
},
|
|
14196
14998
|
isDreaming() {
|
|
14197
14999
|
return dreamEngine?.isActive ?? false;
|
|
15000
|
+
},
|
|
15001
|
+
// Listen mode (transcribe-cli integration)
|
|
15002
|
+
async listenToggle() {
|
|
15003
|
+
const engine = getListenEngine();
|
|
15004
|
+
if (engine.isActive) {
|
|
15005
|
+
const text = await engine.stop();
|
|
15006
|
+
statusBar.setRecording(false);
|
|
15007
|
+
return text ? `Stopped. Last transcript: "${text}"` : "Stopped listening.";
|
|
15008
|
+
}
|
|
15009
|
+
const available = await engine.isAvailable();
|
|
15010
|
+
if (!available) {
|
|
15011
|
+
return "transcribe-cli not installed. Run: npm i -g transcribe-cli";
|
|
15012
|
+
}
|
|
15013
|
+
engine.on("transcript", (text, isFinal) => {
|
|
15014
|
+
if (engine.currentMode === "confirm") {
|
|
15015
|
+
writeContent(() => renderInfo(`Heard: "${text}" ${c2.dim("(press Enter to submit)")}`));
|
|
15016
|
+
} else if (isFinal) {
|
|
15017
|
+
writeContent(() => renderInfo(`Auto-submitting: "${text}"`));
|
|
15018
|
+
rl.write(text + "\n");
|
|
15019
|
+
} else {
|
|
15020
|
+
writeContent(() => renderInfo(`Hearing: "${text}"`));
|
|
15021
|
+
}
|
|
15022
|
+
});
|
|
15023
|
+
engine.on("recording", (active) => {
|
|
15024
|
+
statusBar.setRecording(active);
|
|
15025
|
+
});
|
|
15026
|
+
engine.on("countdown", (seconds) => {
|
|
15027
|
+
statusBar.setCountdown(seconds);
|
|
15028
|
+
});
|
|
15029
|
+
engine.on("error", (err) => {
|
|
15030
|
+
writeContent(() => renderWarning(`Listen error: ${err.message}`));
|
|
15031
|
+
});
|
|
15032
|
+
engine.on("info", (msg2) => {
|
|
15033
|
+
writeContent(() => renderInfo(msg2));
|
|
15034
|
+
});
|
|
15035
|
+
const msg = await engine.start();
|
|
15036
|
+
return msg;
|
|
15037
|
+
},
|
|
15038
|
+
async listenSetModel(model) {
|
|
15039
|
+
const engine = getListenEngine();
|
|
15040
|
+
engine.setModel(model);
|
|
15041
|
+
return `Whisper model set to: ${model}`;
|
|
15042
|
+
},
|
|
15043
|
+
listenSetMode(mode) {
|
|
15044
|
+
const engine = getListenEngine();
|
|
15045
|
+
engine.setMode(mode);
|
|
15046
|
+
return mode === "auto" ? "Auto mode: transcriptions auto-submit after 3s silence" : "Confirm mode: press Enter to submit transcriptions";
|
|
15047
|
+
},
|
|
15048
|
+
async listenStop() {
|
|
15049
|
+
const engine = getListenEngine();
|
|
15050
|
+
const text = await engine.stop();
|
|
15051
|
+
statusBar.setRecording(false);
|
|
15052
|
+
return text ? `Stopped. Last transcript: "${text}"` : "Stopped listening.";
|
|
14198
15053
|
}
|
|
14199
15054
|
};
|
|
14200
15055
|
showPrompt();
|
|
@@ -14242,16 +15097,56 @@ ${c2.dim("Goodbye!")}
|
|
|
14242
15097
|
showPrompt();
|
|
14243
15098
|
return;
|
|
14244
15099
|
}
|
|
15100
|
+
if (typeof cmdResult === "object" && cmdResult.type === "skill") {
|
|
15101
|
+
const skillPrompt = cmdResult.args ? `<skill-context name="${cmdResult.name}">
|
|
15102
|
+
${cmdResult.content}
|
|
15103
|
+
</skill-context>
|
|
15104
|
+
|
|
15105
|
+
ARGUMENTS: ${cmdResult.args}` : `<skill-context name="${cmdResult.name}">
|
|
15106
|
+
${cmdResult.content}
|
|
15107
|
+
</skill-context>
|
|
15108
|
+
|
|
15109
|
+
Execute this skill now. Follow the behavioral guidance above.`;
|
|
15110
|
+
if (!carouselRetired && carousel.isRunning) {
|
|
15111
|
+
carousel.stop();
|
|
15112
|
+
carouselRetired = true;
|
|
15113
|
+
if (statusBar.isActive)
|
|
15114
|
+
statusBar.setScrollRegionTop(1);
|
|
15115
|
+
}
|
|
15116
|
+
writeContent(() => renderUserMessage(`/${cmdResult.name}${cmdResult.args ? " " + cmdResult.args : ""}`));
|
|
15117
|
+
lastSubmittedPrompt = skillPrompt;
|
|
15118
|
+
try {
|
|
15119
|
+
const task = startTask(skillPrompt, currentConfig, repoRoot, voiceEngine, {
|
|
15120
|
+
enabled: streamEnabled,
|
|
15121
|
+
renderer: streamRenderer
|
|
15122
|
+
}, {
|
|
15123
|
+
contextStores,
|
|
15124
|
+
taskMemoryStore: taskMemoryStore ?? void 0,
|
|
15125
|
+
failureStore: failureStore ?? void 0,
|
|
15126
|
+
toolPatternStore: toolPatternStore ?? void 0
|
|
15127
|
+
}, bruteForceEnabled, statusBar);
|
|
15128
|
+
activeTask = task;
|
|
15129
|
+
showPrompt();
|
|
15130
|
+
await task.promise;
|
|
15131
|
+
} catch (err) {
|
|
15132
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15133
|
+
writeContent(() => renderError(errMsg));
|
|
15134
|
+
}
|
|
15135
|
+
activeTask = null;
|
|
15136
|
+
showPrompt();
|
|
15137
|
+
return;
|
|
15138
|
+
}
|
|
14245
15139
|
}
|
|
14246
15140
|
const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
|
|
14247
|
-
const isImage = isImagePath(cleanPath) &&
|
|
15141
|
+
const isImage = isImagePath(cleanPath) && existsSync17(resolve13(repoRoot, cleanPath));
|
|
15142
|
+
const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync17(resolve13(repoRoot, cleanPath));
|
|
14248
15143
|
if (activeTask) {
|
|
14249
15144
|
if (isImage) {
|
|
14250
15145
|
try {
|
|
14251
|
-
const imgPath =
|
|
14252
|
-
const imgBuffer =
|
|
15146
|
+
const imgPath = resolve13(repoRoot, cleanPath);
|
|
15147
|
+
const imgBuffer = readFileSync14(imgPath);
|
|
14253
15148
|
const base64 = imgBuffer.toString("base64");
|
|
14254
|
-
const ext =
|
|
15149
|
+
const ext = extname6(cleanPath).toLowerCase();
|
|
14255
15150
|
const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
14256
15151
|
activeTask.runner.injectImage(base64, mime, `User shared image: ${cleanPath}`);
|
|
14257
15152
|
writeContent(() => renderUserInterrupt(`[Image: ${cleanPath}]`));
|
|
@@ -14259,6 +15154,19 @@ ${c2.dim("Goodbye!")}
|
|
|
14259
15154
|
activeTask.runner.injectUserMessage(input);
|
|
14260
15155
|
writeContent(() => renderUserInterrupt(input));
|
|
14261
15156
|
}
|
|
15157
|
+
} else if (isMedia) {
|
|
15158
|
+
writeContent(() => renderInfo(`Transcribing: ${cleanPath}...`));
|
|
15159
|
+
const engine = getListenEngine();
|
|
15160
|
+
const result = await engine.transcribeFile(resolve13(repoRoot, cleanPath), repoRoot);
|
|
15161
|
+
if (result) {
|
|
15162
|
+
const transcript = `[Transcription of ${cleanPath}]
|
|
15163
|
+
${result.text}`;
|
|
15164
|
+
activeTask.runner.injectUserMessage(transcript);
|
|
15165
|
+
writeContent(() => renderUserInterrupt(`[Audio: ${cleanPath}] (${result.text.split(" ").length} words)`));
|
|
15166
|
+
} else {
|
|
15167
|
+
activeTask.runner.injectUserMessage(`User dropped audio/video file: ${cleanPath}. Use transcribe_file tool to transcribe it.`);
|
|
15168
|
+
writeContent(() => renderUserInterrupt(`[Media: ${cleanPath}]`));
|
|
15169
|
+
}
|
|
14262
15170
|
} else {
|
|
14263
15171
|
activeTask.runner.injectUserMessage(input);
|
|
14264
15172
|
writeContent(() => renderUserInterrupt(input));
|
|
@@ -14274,6 +15182,22 @@ ${c2.dim("Goodbye!")}
|
|
|
14274
15182
|
if (isImage && fullInput === input) {
|
|
14275
15183
|
fullInput = `The user has provided an image file: ${cleanPath}. Read it with image_read and describe what you see. Extract any text with OCR if relevant.`;
|
|
14276
15184
|
}
|
|
15185
|
+
if (isMedia && fullInput === input) {
|
|
15186
|
+
writeContent(() => renderInfo(`Transcribing: ${cleanPath}...`));
|
|
15187
|
+
const engine = getListenEngine();
|
|
15188
|
+
const result = await engine.transcribeFile(resolve13(repoRoot, cleanPath), repoRoot);
|
|
15189
|
+
if (result) {
|
|
15190
|
+
fullInput = `The user has provided an audio/video file: ${cleanPath}.
|
|
15191
|
+
|
|
15192
|
+
Transcription (${result.text.split(" ").length} words, ${result.duration ? result.duration.toFixed(1) + "s" : "unknown duration"}):
|
|
15193
|
+
|
|
15194
|
+
${result.text}
|
|
15195
|
+
|
|
15196
|
+
Summarize or analyze this transcription as appropriate.`;
|
|
15197
|
+
} else {
|
|
15198
|
+
fullInput = `The user has provided an audio/video file: ${cleanPath}. Use the transcribe_file tool to transcribe it and then summarize the content.`;
|
|
15199
|
+
}
|
|
15200
|
+
}
|
|
14277
15201
|
if (!carouselRetired && carousel.isRunning) {
|
|
14278
15202
|
carousel.stop();
|
|
14279
15203
|
carouselRetired = true;
|
|
@@ -14332,19 +15256,6 @@ ${c2.dim("Goodbye!")}
|
|
|
14332
15256
|
writeContent(() => renderInfo(`Update available: v${version} \u2192 v${updateInfo.latestVersion}. Installing...`));
|
|
14333
15257
|
const ok = performSilentUpdate();
|
|
14334
15258
|
if (ok) {
|
|
14335
|
-
if (lastSubmittedPrompt) {
|
|
14336
|
-
try {
|
|
14337
|
-
savePendingTask(repoRoot, {
|
|
14338
|
-
prompt: lastSubmittedPrompt,
|
|
14339
|
-
progressSummary: `Task completed ${sessionToolCallCount} tool calls before update. Last task finished normally.`,
|
|
14340
|
-
filesModified: sessionFilesTouched,
|
|
14341
|
-
bruteForce: bruteForceEnabled,
|
|
14342
|
-
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14343
|
-
toolCallCount: sessionToolCallCount
|
|
14344
|
-
});
|
|
14345
|
-
} catch {
|
|
14346
|
-
}
|
|
14347
|
-
}
|
|
14348
15259
|
writeContent(() => renderInfo(`Updated to v${updateInfo.latestVersion}. Reloading...
|
|
14349
15260
|
`));
|
|
14350
15261
|
process.env.__OA_RESUMED = "1";
|
|
@@ -14384,7 +15295,7 @@ ${c2.dim("(Use /quit to exit)")}
|
|
|
14384
15295
|
});
|
|
14385
15296
|
}
|
|
14386
15297
|
async function runWithTUI(task, config, repoPath) {
|
|
14387
|
-
const repoRoot =
|
|
15298
|
+
const repoRoot = resolve13(repoPath ?? cwd());
|
|
14388
15299
|
const needsSetup = isFirstRun() || !await isModelAvailable(config);
|
|
14389
15300
|
if (needsSetup && config.backendType === "ollama") {
|
|
14390
15301
|
const setupModel = await runSetupWizard(config);
|
|
@@ -14421,6 +15332,7 @@ var init_interactive = __esm({
|
|
|
14421
15332
|
"use strict";
|
|
14422
15333
|
init_dist5();
|
|
14423
15334
|
init_dist2();
|
|
15335
|
+
init_listen();
|
|
14424
15336
|
init_updater();
|
|
14425
15337
|
init_commands();
|
|
14426
15338
|
init_setup();
|
|
@@ -14469,7 +15381,7 @@ import { glob } from "glob";
|
|
|
14469
15381
|
import ignore from "ignore";
|
|
14470
15382
|
import { readFile as readFile9, stat as stat2 } from "node:fs/promises";
|
|
14471
15383
|
import { createHash } from "node:crypto";
|
|
14472
|
-
import { join as
|
|
15384
|
+
import { join as join24, relative as relative3, extname as extname7, basename as basename7 } from "node:path";
|
|
14473
15385
|
var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
|
|
14474
15386
|
var init_codebase_indexer = __esm({
|
|
14475
15387
|
"packages/indexer/dist/codebase-indexer.js"() {
|
|
@@ -14513,7 +15425,7 @@ var init_codebase_indexer = __esm({
|
|
|
14513
15425
|
const ig = ignore.default();
|
|
14514
15426
|
if (this.config.respectGitignore) {
|
|
14515
15427
|
try {
|
|
14516
|
-
const gitignoreContent = await readFile9(
|
|
15428
|
+
const gitignoreContent = await readFile9(join24(this.config.rootDir, ".gitignore"), "utf-8");
|
|
14517
15429
|
ig.add(gitignoreContent);
|
|
14518
15430
|
} catch {
|
|
14519
15431
|
}
|
|
@@ -14528,14 +15440,14 @@ var init_codebase_indexer = __esm({
|
|
|
14528
15440
|
for (const relativePath of files) {
|
|
14529
15441
|
if (ig.ignores(relativePath))
|
|
14530
15442
|
continue;
|
|
14531
|
-
const fullPath =
|
|
15443
|
+
const fullPath = join24(this.config.rootDir, relativePath);
|
|
14532
15444
|
try {
|
|
14533
15445
|
const fileStat = await stat2(fullPath);
|
|
14534
15446
|
if (fileStat.size > this.config.maxFileSize)
|
|
14535
15447
|
continue;
|
|
14536
15448
|
const content = await readFile9(fullPath);
|
|
14537
15449
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
14538
|
-
const ext =
|
|
15450
|
+
const ext = extname7(relativePath);
|
|
14539
15451
|
indexed.push({
|
|
14540
15452
|
path: fullPath,
|
|
14541
15453
|
relativePath,
|
|
@@ -14551,7 +15463,7 @@ var init_codebase_indexer = __esm({
|
|
|
14551
15463
|
}
|
|
14552
15464
|
buildTree(files) {
|
|
14553
15465
|
const root = {
|
|
14554
|
-
name:
|
|
15466
|
+
name: basename7(this.config.rootDir),
|
|
14555
15467
|
path: this.config.rootDir,
|
|
14556
15468
|
type: "directory",
|
|
14557
15469
|
children: []
|
|
@@ -14574,7 +15486,7 @@ var init_codebase_indexer = __esm({
|
|
|
14574
15486
|
if (!child) {
|
|
14575
15487
|
child = {
|
|
14576
15488
|
name: part,
|
|
14577
|
-
path:
|
|
15489
|
+
path: join24(current.path, part),
|
|
14578
15490
|
type: "directory",
|
|
14579
15491
|
children: []
|
|
14580
15492
|
};
|
|
@@ -14648,14 +15560,14 @@ var index_repo_exports = {};
|
|
|
14648
15560
|
__export(index_repo_exports, {
|
|
14649
15561
|
indexRepoCommand: () => indexRepoCommand
|
|
14650
15562
|
});
|
|
14651
|
-
import { resolve as
|
|
14652
|
-
import { existsSync as
|
|
15563
|
+
import { resolve as resolve14 } from "node:path";
|
|
15564
|
+
import { existsSync as existsSync18, statSync as statSync6 } from "node:fs";
|
|
14653
15565
|
import { cwd as cwd2 } from "node:process";
|
|
14654
15566
|
async function indexRepoCommand(opts, _config) {
|
|
14655
|
-
const repoRoot =
|
|
15567
|
+
const repoRoot = resolve14(opts.repoPath ?? cwd2());
|
|
14656
15568
|
printHeader("Index Repository");
|
|
14657
15569
|
printInfo(`Indexing: ${repoRoot}`);
|
|
14658
|
-
if (!
|
|
15570
|
+
if (!existsSync18(repoRoot)) {
|
|
14659
15571
|
printError(`Path does not exist: ${repoRoot}`);
|
|
14660
15572
|
process.exit(1);
|
|
14661
15573
|
}
|
|
@@ -14901,8 +15813,8 @@ var config_exports = {};
|
|
|
14901
15813
|
__export(config_exports, {
|
|
14902
15814
|
configCommand: () => configCommand
|
|
14903
15815
|
});
|
|
14904
|
-
import { join as
|
|
14905
|
-
import { homedir as
|
|
15816
|
+
import { join as join25, resolve as resolve15 } from "node:path";
|
|
15817
|
+
import { homedir as homedir11 } from "node:os";
|
|
14906
15818
|
import { cwd as cwd3 } from "node:process";
|
|
14907
15819
|
function coerceForSettings(key, value) {
|
|
14908
15820
|
if (INT_KEYS.has(key))
|
|
@@ -14922,7 +15834,7 @@ async function configCommand(opts, config) {
|
|
|
14922
15834
|
return handleShow(opts, config);
|
|
14923
15835
|
}
|
|
14924
15836
|
function handleShow(opts, config) {
|
|
14925
|
-
const repoRoot =
|
|
15837
|
+
const repoRoot = resolve15(opts.repoPath ?? cwd3());
|
|
14926
15838
|
printHeader("Configuration");
|
|
14927
15839
|
printSection("Active Settings (merged)");
|
|
14928
15840
|
printKeyValue("backendUrl", config.backendUrl, 2);
|
|
@@ -14954,7 +15866,7 @@ function handleShow(opts, config) {
|
|
|
14954
15866
|
}
|
|
14955
15867
|
}
|
|
14956
15868
|
printSection("Config File");
|
|
14957
|
-
printInfo(`~/.open-agents/config.json (${
|
|
15869
|
+
printInfo(`~/.open-agents/config.json (${join25(homedir11(), ".open-agents", "config.json")})`);
|
|
14958
15870
|
printSection("Priority Chain");
|
|
14959
15871
|
printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
|
|
14960
15872
|
printInfo(" 2. Project .oa/settings.json (--local)");
|
|
@@ -14987,13 +15899,13 @@ function handleSet(opts, _config) {
|
|
|
14987
15899
|
process.exit(1);
|
|
14988
15900
|
}
|
|
14989
15901
|
if (opts.local) {
|
|
14990
|
-
const repoRoot =
|
|
15902
|
+
const repoRoot = resolve15(opts.repoPath ?? cwd3());
|
|
14991
15903
|
try {
|
|
14992
15904
|
initOaDirectory(repoRoot);
|
|
14993
15905
|
const coerced = coerceForSettings(key, value);
|
|
14994
15906
|
saveProjectSettings(repoRoot, { [key]: coerced });
|
|
14995
15907
|
printSuccess(`Project override set: ${key} = ${value}`);
|
|
14996
|
-
printInfo(`Saved to ${
|
|
15908
|
+
printInfo(`Saved to ${join25(repoRoot, ".oa", "settings.json")}`);
|
|
14997
15909
|
printInfo("This override applies only when running in this workspace.");
|
|
14998
15910
|
} catch (err) {
|
|
14999
15911
|
printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -15053,7 +15965,7 @@ var serve_exports = {};
|
|
|
15053
15965
|
__export(serve_exports, {
|
|
15054
15966
|
serveCommand: () => serveCommand
|
|
15055
15967
|
});
|
|
15056
|
-
import { spawn as
|
|
15968
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
15057
15969
|
async function serveCommand(opts, config) {
|
|
15058
15970
|
const backendType = config.backendType;
|
|
15059
15971
|
if (backendType === "ollama") {
|
|
@@ -15145,8 +16057,8 @@ async function serveVllm(opts, config) {
|
|
|
15145
16057
|
await runVllmServer(args, opts.verbose ?? false);
|
|
15146
16058
|
}
|
|
15147
16059
|
async function runVllmServer(args, verbose) {
|
|
15148
|
-
return new Promise((
|
|
15149
|
-
const child =
|
|
16060
|
+
return new Promise((resolve16, reject) => {
|
|
16061
|
+
const child = spawn6("python", args, {
|
|
15150
16062
|
stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
15151
16063
|
env: { ...process.env }
|
|
15152
16064
|
});
|
|
@@ -15180,10 +16092,10 @@ async function runVllmServer(args, verbose) {
|
|
|
15180
16092
|
child.once("exit", (code, signal) => {
|
|
15181
16093
|
if (signal) {
|
|
15182
16094
|
printInfo(`vLLM server stopped by signal ${signal}`);
|
|
15183
|
-
|
|
16095
|
+
resolve16();
|
|
15184
16096
|
} else if (code === 0) {
|
|
15185
16097
|
printSuccess("vLLM server exited cleanly");
|
|
15186
|
-
|
|
16098
|
+
resolve16();
|
|
15187
16099
|
} else {
|
|
15188
16100
|
printError(`vLLM server exited with code ${code}`);
|
|
15189
16101
|
reject(new Error(`vLLM exited with code ${code}`));
|
|
@@ -15211,8 +16123,8 @@ __export(eval_exports, {
|
|
|
15211
16123
|
evalCommand: () => evalCommand
|
|
15212
16124
|
});
|
|
15213
16125
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
15214
|
-
import { mkdirSync as
|
|
15215
|
-
import { join as
|
|
16126
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "node:fs";
|
|
16127
|
+
import { join as join26 } from "node:path";
|
|
15216
16128
|
async function evalCommand(opts, config) {
|
|
15217
16129
|
const suiteName = opts.suite ?? "basic";
|
|
15218
16130
|
const suite = SUITES[suiteName];
|
|
@@ -15333,9 +16245,9 @@ async function evalCommand(opts, config) {
|
|
|
15333
16245
|
process.exit(failed > 0 ? 1 : 0);
|
|
15334
16246
|
}
|
|
15335
16247
|
function createTempEvalRepo() {
|
|
15336
|
-
const dir =
|
|
15337
|
-
|
|
15338
|
-
|
|
16248
|
+
const dir = join26(tmpdir3(), `open-agents-eval-${Date.now()}`);
|
|
16249
|
+
mkdirSync11(dir, { recursive: true });
|
|
16250
|
+
writeFileSync10(join26(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
|
|
15339
16251
|
return dir;
|
|
15340
16252
|
}
|
|
15341
16253
|
var BASIC_SUITE, FULL_SUITE, SUITES;
|
|
@@ -15395,7 +16307,7 @@ init_updater();
|
|
|
15395
16307
|
import { parseArgs as nodeParseArgs2 } from "node:util";
|
|
15396
16308
|
import { createRequire as createRequire3 } from "node:module";
|
|
15397
16309
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15398
|
-
import { dirname as dirname4, join as
|
|
16310
|
+
import { dirname as dirname4, join as join27 } from "node:path";
|
|
15399
16311
|
|
|
15400
16312
|
// packages/cli/dist/cli.js
|
|
15401
16313
|
import { createInterface } from "node:readline";
|
|
@@ -15502,7 +16414,7 @@ init_output();
|
|
|
15502
16414
|
function getVersion2() {
|
|
15503
16415
|
try {
|
|
15504
16416
|
const require2 = createRequire3(import.meta.url);
|
|
15505
|
-
const pkgPath =
|
|
16417
|
+
const pkgPath = join27(dirname4(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
15506
16418
|
const pkg = require2(pkgPath);
|
|
15507
16419
|
return pkg.version;
|
|
15508
16420
|
} catch {
|