open-agents-ai 0.185.72 → 0.185.74
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 +1199 -437
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2129,10 +2129,10 @@ function loadLog(workingDir) {
|
|
|
2129
2129
|
return { version: 1, entries: [] };
|
|
2130
2130
|
}
|
|
2131
2131
|
}
|
|
2132
|
-
function saveLog(workingDir,
|
|
2132
|
+
function saveLog(workingDir, log2) {
|
|
2133
2133
|
const dir = join4(workingDir, ".oa", "index");
|
|
2134
2134
|
mkdirSync4(dir, { recursive: true });
|
|
2135
|
-
writeFileSync4(logPath(workingDir), JSON.stringify(
|
|
2135
|
+
writeFileSync4(logPath(workingDir), JSON.stringify(log2, null, 2), "utf-8");
|
|
2136
2136
|
}
|
|
2137
2137
|
function setChangeLogSession(sessionId, taskGoal) {
|
|
2138
2138
|
_sessionId = sessionId;
|
|
@@ -2140,7 +2140,7 @@ function setChangeLogSession(sessionId, taskGoal) {
|
|
|
2140
2140
|
}
|
|
2141
2141
|
function recordChange(workingDir, opts) {
|
|
2142
2142
|
try {
|
|
2143
|
-
const
|
|
2143
|
+
const log2 = loadLog(workingDir);
|
|
2144
2144
|
const entry = {
|
|
2145
2145
|
id: `chg-${randomBytes2(4).toString("hex")}`,
|
|
2146
2146
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2155,11 +2155,11 @@ function recordChange(workingDir, opts) {
|
|
|
2155
2155
|
relatedNoteIds: opts.relatedNoteIds,
|
|
2156
2156
|
taskGoal: _taskGoal || void 0
|
|
2157
2157
|
};
|
|
2158
|
-
|
|
2159
|
-
if (
|
|
2160
|
-
|
|
2158
|
+
log2.entries.push(entry);
|
|
2159
|
+
if (log2.entries.length > 500) {
|
|
2160
|
+
log2.entries = log2.entries.slice(-500);
|
|
2161
2161
|
}
|
|
2162
|
-
saveLog(workingDir,
|
|
2162
|
+
saveLog(workingDir, log2);
|
|
2163
2163
|
return entry.id;
|
|
2164
2164
|
} catch {
|
|
2165
2165
|
return null;
|
|
@@ -2167,16 +2167,16 @@ function recordChange(workingDir, opts) {
|
|
|
2167
2167
|
}
|
|
2168
2168
|
function markSessionValidated(workingDir) {
|
|
2169
2169
|
try {
|
|
2170
|
-
const
|
|
2170
|
+
const log2 = loadLog(workingDir);
|
|
2171
2171
|
let count = 0;
|
|
2172
|
-
for (const entry of
|
|
2172
|
+
for (const entry of log2.entries) {
|
|
2173
2173
|
if (entry.sessionId === _sessionId && entry.outcome === "untested") {
|
|
2174
2174
|
entry.outcome = "success";
|
|
2175
2175
|
count++;
|
|
2176
2176
|
}
|
|
2177
2177
|
}
|
|
2178
2178
|
if (count > 0)
|
|
2179
|
-
saveLog(workingDir,
|
|
2179
|
+
saveLog(workingDir, log2);
|
|
2180
2180
|
return count;
|
|
2181
2181
|
} catch {
|
|
2182
2182
|
return 0;
|
|
@@ -2184,12 +2184,12 @@ function markSessionValidated(workingDir) {
|
|
|
2184
2184
|
}
|
|
2185
2185
|
function markReverted(workingDir, changeId) {
|
|
2186
2186
|
try {
|
|
2187
|
-
const
|
|
2188
|
-
const entry =
|
|
2187
|
+
const log2 = loadLog(workingDir);
|
|
2188
|
+
const entry = log2.entries.find((e) => e.id === changeId);
|
|
2189
2189
|
if (!entry)
|
|
2190
2190
|
return false;
|
|
2191
2191
|
entry.outcome = "reverted";
|
|
2192
|
-
saveLog(workingDir,
|
|
2192
|
+
saveLog(workingDir, log2);
|
|
2193
2193
|
return true;
|
|
2194
2194
|
} catch {
|
|
2195
2195
|
return false;
|
|
@@ -2197,24 +2197,24 @@ function markReverted(workingDir, changeId) {
|
|
|
2197
2197
|
}
|
|
2198
2198
|
function getFileChanges(workingDir, filePath, limit = 10) {
|
|
2199
2199
|
try {
|
|
2200
|
-
const
|
|
2201
|
-
return
|
|
2200
|
+
const log2 = loadLog(workingDir);
|
|
2201
|
+
return log2.entries.filter((e) => e.file === filePath).slice(-limit);
|
|
2202
2202
|
} catch {
|
|
2203
2203
|
return [];
|
|
2204
2204
|
}
|
|
2205
2205
|
}
|
|
2206
2206
|
function getSessionChanges(workingDir) {
|
|
2207
2207
|
try {
|
|
2208
|
-
const
|
|
2209
|
-
return
|
|
2208
|
+
const log2 = loadLog(workingDir);
|
|
2209
|
+
return log2.entries.filter((e) => e.sessionId === _sessionId);
|
|
2210
2210
|
} catch {
|
|
2211
2211
|
return [];
|
|
2212
2212
|
}
|
|
2213
2213
|
}
|
|
2214
2214
|
function getRecentChangesSummary(workingDir, limit = 10) {
|
|
2215
2215
|
try {
|
|
2216
|
-
const
|
|
2217
|
-
const recent =
|
|
2216
|
+
const log2 = loadLog(workingDir);
|
|
2217
|
+
const recent = log2.entries.slice(-limit);
|
|
2218
2218
|
if (recent.length === 0)
|
|
2219
2219
|
return "";
|
|
2220
2220
|
const lines = [`Recent changes (${recent.length}):`];
|
|
@@ -5261,9 +5261,9 @@ var init_git_info = __esm({
|
|
|
5261
5261
|
}
|
|
5262
5262
|
report.push("");
|
|
5263
5263
|
report.push("## Recent Commits");
|
|
5264
|
-
const
|
|
5265
|
-
if (
|
|
5266
|
-
for (const line of
|
|
5264
|
+
const log2 = this.git(repoDir, `log --oneline -${logCount} --no-decorate`);
|
|
5265
|
+
if (log2) {
|
|
5266
|
+
for (const line of log2.split("\n").filter((l) => l.trim())) {
|
|
5267
5267
|
report.push(` ${line}`);
|
|
5268
5268
|
}
|
|
5269
5269
|
} else {
|
|
@@ -13782,9 +13782,9 @@ print("${sentinel}")
|
|
|
13782
13782
|
if (!this.proc || this.proc.killed) {
|
|
13783
13783
|
return { success: false, path: "" };
|
|
13784
13784
|
}
|
|
13785
|
-
const { mkdirSync:
|
|
13785
|
+
const { mkdirSync: mkdirSync32, writeFileSync: writeFileSync30 } = await import("node:fs");
|
|
13786
13786
|
const sessionDir = join22(this.cwd, ".oa", "rlm");
|
|
13787
|
-
|
|
13787
|
+
mkdirSync32(sessionDir, { recursive: true });
|
|
13788
13788
|
const sessionPath = join22(sessionDir, "session.json");
|
|
13789
13789
|
try {
|
|
13790
13790
|
const inspectCode = `
|
|
@@ -13820,11 +13820,11 @@ print("__SESSION__" + json.dumps(_session) + "__SESSION__")
|
|
|
13820
13820
|
* what was previously computed. */
|
|
13821
13821
|
async loadSessionInfo() {
|
|
13822
13822
|
try {
|
|
13823
|
-
const { readFileSync:
|
|
13823
|
+
const { readFileSync: readFileSync46, existsSync: existsSync58 } = await import("node:fs");
|
|
13824
13824
|
const sessionPath = join22(this.cwd, ".oa", "rlm", "session.json");
|
|
13825
|
-
if (!
|
|
13825
|
+
if (!existsSync58(sessionPath))
|
|
13826
13826
|
return null;
|
|
13827
|
-
return JSON.parse(
|
|
13827
|
+
return JSON.parse(readFileSync46(sessionPath, "utf8"));
|
|
13828
13828
|
} catch {
|
|
13829
13829
|
return null;
|
|
13830
13830
|
}
|
|
@@ -14001,10 +14001,10 @@ var init_memory_metabolism = __esm({
|
|
|
14001
14001
|
const trajDir = join23(this.cwd, ".oa", "rlm-trajectories");
|
|
14002
14002
|
let lessons = [];
|
|
14003
14003
|
try {
|
|
14004
|
-
const { readdirSync: readdirSync24, readFileSync:
|
|
14004
|
+
const { readdirSync: readdirSync24, readFileSync: readFileSync46 } = await import("node:fs");
|
|
14005
14005
|
const files = readdirSync24(trajDir).filter((f) => f.endsWith(".jsonl")).sort().reverse().slice(0, 3);
|
|
14006
14006
|
for (const file of files) {
|
|
14007
|
-
const lines =
|
|
14007
|
+
const lines = readFileSync46(join23(trajDir, file), "utf8").split("\n").filter((l) => l.trim());
|
|
14008
14008
|
for (const line of lines) {
|
|
14009
14009
|
try {
|
|
14010
14010
|
const entry = JSON.parse(line);
|
|
@@ -14388,14 +14388,14 @@ ${issues.map((i) => ` - ${i}`).join("\n")}` : " No issues found."),
|
|
|
14388
14388
|
* Optionally filter by task type for phase-aware context (FSM paper insight).
|
|
14389
14389
|
*/
|
|
14390
14390
|
getTopMemoriesSync(k = 5, taskType) {
|
|
14391
|
-
const { readFileSync:
|
|
14391
|
+
const { readFileSync: readFileSync46, existsSync: existsSync58 } = __require("node:fs");
|
|
14392
14392
|
const metaDir = join23(this.cwd, ".oa", "memory", "metabolism");
|
|
14393
14393
|
const storeFile = join23(metaDir, "store.json");
|
|
14394
|
-
if (!
|
|
14394
|
+
if (!existsSync58(storeFile))
|
|
14395
14395
|
return "";
|
|
14396
14396
|
let store = [];
|
|
14397
14397
|
try {
|
|
14398
|
-
store = JSON.parse(
|
|
14398
|
+
store = JSON.parse(readFileSync46(storeFile, "utf8"));
|
|
14399
14399
|
} catch {
|
|
14400
14400
|
return "";
|
|
14401
14401
|
}
|
|
@@ -14417,14 +14417,14 @@ ${issues.map((i) => ` - ${i}`).join("\n")}` : " No issues found."),
|
|
|
14417
14417
|
/** Update memory scores based on task outcome. Called after task completion.
|
|
14418
14418
|
* Memories used in successful tasks get boosted. Memories present during failures get decayed. */
|
|
14419
14419
|
updateFromOutcomeSync(surfacedMemoryText, succeeded) {
|
|
14420
|
-
const { readFileSync:
|
|
14420
|
+
const { readFileSync: readFileSync46, writeFileSync: writeFileSync30, existsSync: existsSync58, mkdirSync: mkdirSync32 } = __require("node:fs");
|
|
14421
14421
|
const metaDir = join23(this.cwd, ".oa", "memory", "metabolism");
|
|
14422
14422
|
const storeFile = join23(metaDir, "store.json");
|
|
14423
|
-
if (!
|
|
14423
|
+
if (!existsSync58(storeFile))
|
|
14424
14424
|
return;
|
|
14425
14425
|
let store = [];
|
|
14426
14426
|
try {
|
|
14427
|
-
store = JSON.parse(
|
|
14427
|
+
store = JSON.parse(readFileSync46(storeFile, "utf8"));
|
|
14428
14428
|
} catch {
|
|
14429
14429
|
return;
|
|
14430
14430
|
}
|
|
@@ -14448,7 +14448,7 @@ ${issues.map((i) => ` - ${i}`).join("\n")}` : " No issues found."),
|
|
|
14448
14448
|
updated = true;
|
|
14449
14449
|
}
|
|
14450
14450
|
if (updated) {
|
|
14451
|
-
|
|
14451
|
+
mkdirSync32(metaDir, { recursive: true });
|
|
14452
14452
|
writeFileSync30(storeFile, JSON.stringify(store, null, 2));
|
|
14453
14453
|
}
|
|
14454
14454
|
}
|
|
@@ -14871,13 +14871,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
|
|
|
14871
14871
|
// Per EvoSkill (arXiv:2603.02766): retrieve relevant strategies from archive.
|
|
14872
14872
|
/** Retrieve top-K strategies for context injection. Returns "" if none. */
|
|
14873
14873
|
getRelevantStrategiesSync(k = 3, taskType) {
|
|
14874
|
-
const { readFileSync:
|
|
14874
|
+
const { readFileSync: readFileSync46, existsSync: existsSync58 } = __require("node:fs");
|
|
14875
14875
|
const archiveFile = join25(this.cwd, ".oa", "arche", "variants.json");
|
|
14876
|
-
if (!
|
|
14876
|
+
if (!existsSync58(archiveFile))
|
|
14877
14877
|
return "";
|
|
14878
14878
|
let variants = [];
|
|
14879
14879
|
try {
|
|
14880
|
-
variants = JSON.parse(
|
|
14880
|
+
variants = JSON.parse(readFileSync46(archiveFile, "utf8"));
|
|
14881
14881
|
} catch {
|
|
14882
14882
|
return "";
|
|
14883
14883
|
}
|
|
@@ -14895,13 +14895,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
|
|
|
14895
14895
|
}
|
|
14896
14896
|
/** Archive a strategy variant synchronously (for task completion path) */
|
|
14897
14897
|
archiveVariantSync(strategy, outcome, tags = []) {
|
|
14898
|
-
const { readFileSync:
|
|
14898
|
+
const { readFileSync: readFileSync46, writeFileSync: writeFileSync30, existsSync: existsSync58, mkdirSync: mkdirSync32 } = __require("node:fs");
|
|
14899
14899
|
const dir = join25(this.cwd, ".oa", "arche");
|
|
14900
14900
|
const archiveFile = join25(dir, "variants.json");
|
|
14901
14901
|
let variants = [];
|
|
14902
14902
|
try {
|
|
14903
|
-
if (
|
|
14904
|
-
variants = JSON.parse(
|
|
14903
|
+
if (existsSync58(archiveFile))
|
|
14904
|
+
variants = JSON.parse(readFileSync46(archiveFile, "utf8"));
|
|
14905
14905
|
} catch {
|
|
14906
14906
|
}
|
|
14907
14907
|
variants.push({
|
|
@@ -14916,7 +14916,7 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
|
|
|
14916
14916
|
});
|
|
14917
14917
|
if (variants.length > 50)
|
|
14918
14918
|
variants = variants.slice(-50);
|
|
14919
|
-
|
|
14919
|
+
mkdirSync32(dir, { recursive: true });
|
|
14920
14920
|
writeFileSync30(archiveFile, JSON.stringify(variants, null, 2));
|
|
14921
14921
|
}
|
|
14922
14922
|
async saveArchive(variants) {
|
|
@@ -27422,10 +27422,10 @@ ${marker}` : marker);
|
|
|
27422
27422
|
if (!this._workingDirectory)
|
|
27423
27423
|
return;
|
|
27424
27424
|
try {
|
|
27425
|
-
const { mkdirSync:
|
|
27426
|
-
const { join:
|
|
27427
|
-
const sessionDir =
|
|
27428
|
-
|
|
27425
|
+
const { mkdirSync: mkdirSync32, writeFileSync: writeFileSync30 } = __require("node:fs");
|
|
27426
|
+
const { join: join78 } = __require("node:path");
|
|
27427
|
+
const sessionDir = join78(this._workingDirectory, ".oa", "session", this._sessionId);
|
|
27428
|
+
mkdirSync32(sessionDir, { recursive: true });
|
|
27429
27429
|
const checkpoint = {
|
|
27430
27430
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27431
27431
|
sessionId: this._sessionId,
|
|
@@ -27437,7 +27437,7 @@ ${marker}` : marker);
|
|
|
27437
27437
|
memexEntryCount: this._memexArchive.size,
|
|
27438
27438
|
fileRegistrySize: this._fileRegistry.size
|
|
27439
27439
|
};
|
|
27440
|
-
writeFileSync30(
|
|
27440
|
+
writeFileSync30(join78(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
|
|
27441
27441
|
} catch {
|
|
27442
27442
|
}
|
|
27443
27443
|
}
|
|
@@ -28575,17 +28575,17 @@ ${transcript}`
|
|
|
28575
28575
|
let resizedBase64 = null;
|
|
28576
28576
|
try {
|
|
28577
28577
|
const { execSync: execSync36 } = await import("node:child_process");
|
|
28578
|
-
const { writeFileSync: writeFileSync30, readFileSync:
|
|
28579
|
-
const { join:
|
|
28578
|
+
const { writeFileSync: writeFileSync30, readFileSync: readFileSync46, unlinkSync: unlinkSync13 } = await import("node:fs");
|
|
28579
|
+
const { join: join78 } = await import("node:path");
|
|
28580
28580
|
const { tmpdir: tmpdir11 } = await import("node:os");
|
|
28581
|
-
const tmpIn =
|
|
28582
|
-
const tmpOut =
|
|
28581
|
+
const tmpIn = join78(tmpdir11(), `oa_img_in_${Date.now()}.png`);
|
|
28582
|
+
const tmpOut = join78(tmpdir11(), `oa_img_out_${Date.now()}.jpg`);
|
|
28583
28583
|
writeFileSync30(tmpIn, buffer);
|
|
28584
28584
|
const pyBin = process.platform === "win32" ? "python" : "python3";
|
|
28585
28585
|
const escapedIn = tmpIn.replace(/\\/g, "\\\\");
|
|
28586
28586
|
const escapedOut = tmpOut.replace(/\\/g, "\\\\");
|
|
28587
28587
|
execSync36(`${pyBin} -c "from PIL import Image; img = Image.open('${escapedIn}'); img.thumbnail((512, 512), Image.LANCZOS); img = img.convert('RGB'); img.save('${escapedOut}', 'JPEG', quality=75)"`, { timeout: 1e4, stdio: "pipe" });
|
|
28588
|
-
const resizedBuf =
|
|
28588
|
+
const resizedBuf = readFileSync46(tmpOut);
|
|
28589
28589
|
resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
|
|
28590
28590
|
try {
|
|
28591
28591
|
unlinkSync13(tmpIn);
|
|
@@ -39557,26 +39557,26 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
|
|
|
39557
39557
|
async function fetchPeerModels(peerId, authKey) {
|
|
39558
39558
|
try {
|
|
39559
39559
|
const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
39560
|
-
const { existsSync:
|
|
39561
|
-
const { join:
|
|
39560
|
+
const { existsSync: existsSync58, readFileSync: readFileSync46 } = await import("node:fs");
|
|
39561
|
+
const { join: join78 } = await import("node:path");
|
|
39562
39562
|
const cwd4 = process.cwd();
|
|
39563
39563
|
const nexusTool = new NexusTool2(cwd4);
|
|
39564
39564
|
const nexusDir = nexusTool.getNexusDir();
|
|
39565
39565
|
let isLocalPeer = false;
|
|
39566
39566
|
try {
|
|
39567
|
-
const statusPath =
|
|
39568
|
-
if (
|
|
39569
|
-
const status = JSON.parse(
|
|
39567
|
+
const statusPath = join78(nexusDir, "status.json");
|
|
39568
|
+
if (existsSync58(statusPath)) {
|
|
39569
|
+
const status = JSON.parse(readFileSync46(statusPath, "utf8"));
|
|
39570
39570
|
if (status.peerId === peerId)
|
|
39571
39571
|
isLocalPeer = true;
|
|
39572
39572
|
}
|
|
39573
39573
|
} catch {
|
|
39574
39574
|
}
|
|
39575
39575
|
if (isLocalPeer) {
|
|
39576
|
-
const pricingPath =
|
|
39577
|
-
if (
|
|
39576
|
+
const pricingPath = join78(nexusDir, "pricing.json");
|
|
39577
|
+
if (existsSync58(pricingPath)) {
|
|
39578
39578
|
try {
|
|
39579
|
-
const pricing = JSON.parse(
|
|
39579
|
+
const pricing = JSON.parse(readFileSync46(pricingPath, "utf8"));
|
|
39580
39580
|
const localModels = (pricing.models || []).map((m) => ({
|
|
39581
39581
|
name: m.model || "unknown",
|
|
39582
39582
|
size: m.parameterSize || "",
|
|
@@ -39590,10 +39590,10 @@ async function fetchPeerModels(peerId, authKey) {
|
|
|
39590
39590
|
}
|
|
39591
39591
|
}
|
|
39592
39592
|
}
|
|
39593
|
-
const cachePath =
|
|
39594
|
-
if (
|
|
39593
|
+
const cachePath = join78(nexusDir, "peer-models-cache.json");
|
|
39594
|
+
if (existsSync58(cachePath)) {
|
|
39595
39595
|
try {
|
|
39596
|
-
const cache4 = JSON.parse(
|
|
39596
|
+
const cache4 = JSON.parse(readFileSync46(cachePath, "utf8"));
|
|
39597
39597
|
if (cache4.peerId === peerId && cache4.models?.length > 0) {
|
|
39598
39598
|
const age = Date.now() - new Date(cache4.cachedAt).getTime();
|
|
39599
39599
|
if (age < 5 * 60 * 1e3) {
|
|
@@ -39708,10 +39708,10 @@ async function fetchPeerModels(peerId, authKey) {
|
|
|
39708
39708
|
} catch {
|
|
39709
39709
|
}
|
|
39710
39710
|
if (isLocalPeer) {
|
|
39711
|
-
const pricingPath =
|
|
39712
|
-
if (
|
|
39711
|
+
const pricingPath = join78(nexusDir, "pricing.json");
|
|
39712
|
+
if (existsSync58(pricingPath)) {
|
|
39713
39713
|
try {
|
|
39714
|
-
const pricing = JSON.parse(
|
|
39714
|
+
const pricing = JSON.parse(readFileSync46(pricingPath, "utf8"));
|
|
39715
39715
|
return (pricing.models || []).map((m) => ({
|
|
39716
39716
|
name: m.model || "unknown",
|
|
39717
39717
|
size: m.parameterSize || "",
|
|
@@ -41666,7 +41666,7 @@ function getWeightRepoInfo(tier) {
|
|
|
41666
41666
|
return WEIGHT_REPOS[tier];
|
|
41667
41667
|
}
|
|
41668
41668
|
async function installPersonaPlex(onInfo, weightTier) {
|
|
41669
|
-
const
|
|
41669
|
+
const log2 = onInfo ?? (() => {
|
|
41670
41670
|
});
|
|
41671
41671
|
mkdirSync15(PERSONAPLEX_DIR, { recursive: true });
|
|
41672
41672
|
let arch2 = "";
|
|
@@ -41676,21 +41676,21 @@ async function installPersonaPlex(onInfo, weightTier) {
|
|
|
41676
41676
|
}
|
|
41677
41677
|
const isAarch64 = arch2 === "aarch64" || arch2 === "arm64";
|
|
41678
41678
|
if (isAarch64)
|
|
41679
|
-
|
|
41679
|
+
log2(`Detected ARM64 platform (${arch2}) \u2014 Jetson/ARM install path`);
|
|
41680
41680
|
const venvDir = join54(PERSONAPLEX_DIR, "venv");
|
|
41681
41681
|
if (!existsSync37(venvDir)) {
|
|
41682
|
-
|
|
41682
|
+
log2("Creating Python virtual environment...");
|
|
41683
41683
|
try {
|
|
41684
41684
|
const ssp = isAarch64 ? " --system-site-packages" : "";
|
|
41685
41685
|
await execAsync(`python3 -m venv${ssp} "${venvDir}"`, { timeout: 6e4 });
|
|
41686
41686
|
} catch (err) {
|
|
41687
|
-
|
|
41687
|
+
log2(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
|
|
41688
41688
|
return false;
|
|
41689
41689
|
}
|
|
41690
41690
|
}
|
|
41691
41691
|
const pip = process.platform === "win32" ? join54(venvDir, "Scripts", "pip.exe") : join54(venvDir, "bin", "pip");
|
|
41692
41692
|
const python = process.platform === "win32" ? join54(venvDir, "Scripts", "python.exe") : join54(venvDir, "bin", "python3");
|
|
41693
|
-
|
|
41693
|
+
log2("Checking system dependencies (libopus)...");
|
|
41694
41694
|
try {
|
|
41695
41695
|
if (process.platform === "linux") {
|
|
41696
41696
|
execSync28("dpkg -l libopus-dev 2>/dev/null || sudo apt-get install -y libopus-dev", { timeout: 3e4, stdio: "pipe" });
|
|
@@ -41700,16 +41700,16 @@ async function installPersonaPlex(onInfo, weightTier) {
|
|
|
41700
41700
|
} catch {
|
|
41701
41701
|
}
|
|
41702
41702
|
if (isAarch64) {
|
|
41703
|
-
|
|
41703
|
+
log2("ARM64: Checking Rust toolchain for sphn build...");
|
|
41704
41704
|
try {
|
|
41705
41705
|
execSync28("rustc --version", { timeout: 5e3, stdio: "pipe" });
|
|
41706
41706
|
} catch {
|
|
41707
|
-
|
|
41707
|
+
log2("ARM64: Installing Rust toolchain (needed for sphn audio codec)...");
|
|
41708
41708
|
try {
|
|
41709
41709
|
await execAsync("curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y", { timeout: 12e4 });
|
|
41710
41710
|
} catch (e) {
|
|
41711
|
-
|
|
41712
|
-
|
|
41711
|
+
log2(`Rust install failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
41712
|
+
log2("Install Rust manually: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh");
|
|
41713
41713
|
return false;
|
|
41714
41714
|
}
|
|
41715
41715
|
}
|
|
@@ -41718,38 +41718,38 @@ async function installPersonaPlex(onInfo, weightTier) {
|
|
|
41718
41718
|
} catch {
|
|
41719
41719
|
}
|
|
41720
41720
|
}
|
|
41721
|
-
|
|
41721
|
+
log2("Installing PersonaPlex (moshi package)...");
|
|
41722
41722
|
const repoDir = join54(PERSONAPLEX_DIR, "personaplex-repo");
|
|
41723
41723
|
try {
|
|
41724
41724
|
if (!existsSync37(repoDir)) {
|
|
41725
41725
|
await execAsync(`git clone https://github.com/NVIDIA/personaplex.git "${repoDir}"`, { timeout: 12e4 });
|
|
41726
41726
|
}
|
|
41727
41727
|
if (isAarch64) {
|
|
41728
|
-
|
|
41728
|
+
log2("ARM64: Building sphn from source (Opus codec bindings)...");
|
|
41729
41729
|
try {
|
|
41730
41730
|
const rustEnv = `export PATH="$HOME/.cargo/bin:$PATH" &&`;
|
|
41731
41731
|
await execAsync(`${rustEnv} "${pip}" install --quiet --no-binary sphn sphn`, { timeout: 3e5 });
|
|
41732
|
-
|
|
41732
|
+
log2("ARM64: sphn built successfully");
|
|
41733
41733
|
} catch (e) {
|
|
41734
|
-
|
|
41735
|
-
|
|
41734
|
+
log2(`ARM64: sphn build failed \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
41735
|
+
log2("Ensure Rust, libopus-dev, and cmake are installed.");
|
|
41736
41736
|
return false;
|
|
41737
41737
|
}
|
|
41738
41738
|
}
|
|
41739
41739
|
if (isAarch64) {
|
|
41740
|
-
|
|
41740
|
+
log2("ARM64: Installing moshi (--no-deps to preserve JetPack torch)...");
|
|
41741
41741
|
await execAsync(`"${pip}" install --quiet --no-deps "${join54(repoDir, "moshi")}/."`, { timeout: 3e5 });
|
|
41742
|
-
|
|
41742
|
+
log2("ARM64: Installing remaining moshi dependencies...");
|
|
41743
41743
|
await execAsync(`"${pip}" install --quiet "numpy>=1.26,<2.2" "safetensors>=0.4.0,<0.5" "huggingface-hub>=0.24,<0.25" "einops==0.7" "sentencepiece==0.2" "sounddevice==0.5" "aiohttp>=3.10.5,<3.11"`, { timeout: 3e5 });
|
|
41744
41744
|
} else {
|
|
41745
41745
|
await execAsync(`"${pip}" install --quiet "${join54(repoDir, "moshi")}/."`, { timeout: 6e5 });
|
|
41746
41746
|
}
|
|
41747
41747
|
} catch (err) {
|
|
41748
|
-
|
|
41748
|
+
log2(`Moshi install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
41749
41749
|
if (isAarch64) {
|
|
41750
|
-
|
|
41751
|
-
|
|
41752
|
-
|
|
41750
|
+
log2("ARM64: This often means the pip process was OOM-killed.");
|
|
41751
|
+
log2("Check: dmesg | grep -i 'oom\\|killed' | tail -5");
|
|
41752
|
+
log2("Ensure JetPack PyTorch is installed: pip3 show torch");
|
|
41753
41753
|
}
|
|
41754
41754
|
try {
|
|
41755
41755
|
await execAsync(`"${pip}" install --quiet torch torchaudio websockets soundfile huggingface_hub`, { timeout: 3e5, stdio: "pipe" });
|
|
@@ -41770,7 +41770,7 @@ async function installPersonaPlex(onInfo, weightTier) {
|
|
|
41770
41770
|
if (src.includes('int(request["seed"])')) {
|
|
41771
41771
|
src = src.replace('int(request["seed"])', 'int(request.query["seed"])');
|
|
41772
41772
|
writeFileSync16(serverFile, src);
|
|
41773
|
-
|
|
41773
|
+
log2("Applied seed parameter bug fix to moshi server.");
|
|
41774
41774
|
}
|
|
41775
41775
|
}
|
|
41776
41776
|
} catch {
|
|
@@ -41868,7 +41868,7 @@ $2if filename.endswith(".safetensors"):`);
|
|
|
41868
41868
|
${patchPoint}`);
|
|
41869
41869
|
}
|
|
41870
41870
|
writeFileSync16(loadersFile, src);
|
|
41871
|
-
|
|
41871
|
+
log2("Patched loaders.py with 2-bit TurboQuant native dequant support.");
|
|
41872
41872
|
}
|
|
41873
41873
|
}
|
|
41874
41874
|
} catch {
|
|
@@ -41882,31 +41882,31 @@ $2if filename.endswith(".safetensors"):`);
|
|
|
41882
41882
|
const hybridDest = join54(sitePackages2, "hybrid_agent.py");
|
|
41883
41883
|
const serverDest = join54(sitePackages2, "server.py");
|
|
41884
41884
|
if (!existsSync37(hybridDest) || !readFileSync28(hybridDest, "utf8").includes("OA_API_BASE")) {
|
|
41885
|
-
|
|
41885
|
+
log2("Deploying hybrid_agent.py (OA API integration)...");
|
|
41886
41886
|
try {
|
|
41887
41887
|
await execAsync(`curl -sL "https://raw.githubusercontent.com/robit-man/personaplex/main/personaplex-setup/moshi/moshi/hybrid_agent.py" -o "${hybridDest}"`, { timeout: 3e4 });
|
|
41888
41888
|
if (existsSync37(hybridDest) && readFileSync28(hybridDest, "utf8").includes("OA_API_BASE")) {
|
|
41889
|
-
|
|
41889
|
+
log2("hybrid_agent.py deployed (OA API + Ollama fallback).");
|
|
41890
41890
|
}
|
|
41891
41891
|
} catch {
|
|
41892
|
-
|
|
41892
|
+
log2("hybrid_agent.py download failed \u2014 hybrid mode will be disabled.");
|
|
41893
41893
|
}
|
|
41894
41894
|
}
|
|
41895
41895
|
if (!readFileSync28(serverDest, "utf8").includes("hybrid_agent")) {
|
|
41896
|
-
|
|
41896
|
+
log2("Deploying patched server.py (hybrid mode + API endpoints)...");
|
|
41897
41897
|
try {
|
|
41898
41898
|
await execAsync(`curl -sL "https://raw.githubusercontent.com/robit-man/personaplex/main/personaplex-setup/moshi/moshi/server.py" -o "${serverDest}"`, { timeout: 3e4 });
|
|
41899
41899
|
if (readFileSync28(serverDest, "utf8").includes("hybrid_agent")) {
|
|
41900
|
-
|
|
41900
|
+
log2("server.py patched with hybrid intercept + REST APIs.");
|
|
41901
41901
|
}
|
|
41902
41902
|
} catch {
|
|
41903
|
-
|
|
41903
|
+
log2("server.py download failed \u2014 will use upstream version.");
|
|
41904
41904
|
}
|
|
41905
41905
|
}
|
|
41906
41906
|
} catch {
|
|
41907
41907
|
}
|
|
41908
41908
|
if (isAarch64) {
|
|
41909
|
-
|
|
41909
|
+
log2("ARM64: Installing bitsandbytes for INT4 inference...");
|
|
41910
41910
|
try {
|
|
41911
41911
|
await execAsync(`"${pip}" install --quiet bitsandbytes`, { timeout: 12e4, stdio: "pipe" });
|
|
41912
41912
|
} catch {
|
|
@@ -41922,15 +41922,15 @@ $2if filename.endswith(".safetensors"):`);
|
|
|
41922
41922
|
}
|
|
41923
41923
|
const tier = weightTier ?? detectPersonaPlexCapability().weightTier;
|
|
41924
41924
|
const repoInfo = WEIGHT_REPOS[tier];
|
|
41925
|
-
|
|
41926
|
-
|
|
41925
|
+
log2(`Weight tier: ${tier} (${repoInfo.sizeGB}GB) \u2014 ${repoInfo.needsToken ? "requires HF_TOKEN" : "public, no token needed"}`);
|
|
41926
|
+
log2(`Downloading PersonaPlex weights (${repoInfo.sizeGB}GB)...`);
|
|
41927
41927
|
try {
|
|
41928
41928
|
const tokenArg = repoInfo.needsToken ? "" : "--token ''";
|
|
41929
41929
|
const dlCmd = `"${python}" -c "from huggingface_hub import hf_hub_download; f=hf_hub_download('${repoInfo.repo}', '${repoInfo.file}'${repoInfo.needsToken ? "" : ", token=False"}); print(f)"`;
|
|
41930
41930
|
const weightPath = (await execAsync(dlCmd, { timeout: 6e5 })).trim();
|
|
41931
|
-
|
|
41931
|
+
log2(`Weights downloaded: ${repoInfo.file}`);
|
|
41932
41932
|
if (tier !== "original") {
|
|
41933
|
-
|
|
41933
|
+
log2("Downloading Mimi codec + tokenizer (no token needed)...");
|
|
41934
41934
|
const supportFiles = ["tokenizer-e351c8d8-checkpoint125.safetensors", "tokenizer_spm_32k_3.model", "config.json"];
|
|
41935
41935
|
for (const sf of supportFiles) {
|
|
41936
41936
|
try {
|
|
@@ -41946,49 +41946,49 @@ $2if filename.endswith(".safetensors"):`);
|
|
|
41946
41946
|
}
|
|
41947
41947
|
}
|
|
41948
41948
|
}
|
|
41949
|
-
|
|
41949
|
+
log2("Codec + tokenizer downloaded.");
|
|
41950
41950
|
}
|
|
41951
41951
|
} catch (err) {
|
|
41952
41952
|
const msg = err instanceof Error ? err.message : String(err);
|
|
41953
41953
|
if (repoInfo.needsToken && /401|403|gated|unauthorized/i.test(msg)) {
|
|
41954
|
-
|
|
41954
|
+
log2(`HF_TOKEN required for ${tier} weights. Set HF_TOKEN or accept license at https://huggingface.co/${repoInfo.repo}`);
|
|
41955
41955
|
if (tier === "original") {
|
|
41956
|
-
|
|
41956
|
+
log2("Auto-downgrading to INT4 weights (no token required)...");
|
|
41957
41957
|
const nf4 = WEIGHT_REPOS["nf4"];
|
|
41958
41958
|
try {
|
|
41959
41959
|
await execAsync(`"${python}" -c "from huggingface_hub import hf_hub_download; hf_hub_download('${nf4.repo}', '${nf4.file}', token=False)"`, {
|
|
41960
41960
|
timeout: 6e5
|
|
41961
41961
|
});
|
|
41962
41962
|
writeFileSync16(join54(PERSONAPLEX_DIR, "weight_tier"), "nf4");
|
|
41963
|
-
|
|
41963
|
+
log2(`Downloaded INT4 weights instead (${nf4.sizeGB}GB, public).`);
|
|
41964
41964
|
} catch {
|
|
41965
|
-
|
|
41965
|
+
log2("Weight download failed.");
|
|
41966
41966
|
return false;
|
|
41967
41967
|
}
|
|
41968
41968
|
}
|
|
41969
41969
|
} else {
|
|
41970
|
-
|
|
41971
|
-
|
|
41970
|
+
log2(`Weight download failed: ${msg}`);
|
|
41971
|
+
log2("Weights will download on first server launch.");
|
|
41972
41972
|
}
|
|
41973
41973
|
}
|
|
41974
41974
|
writeFileSync16(join54(PERSONAPLEX_DIR, "weight_tier"), tier);
|
|
41975
41975
|
writeFileSync16(join54(PERSONAPLEX_DIR, "model_ready"), (/* @__PURE__ */ new Date()).toISOString());
|
|
41976
|
-
|
|
41976
|
+
log2(`PersonaPlex installed (${tier} tier). Use /call to start voice session.`);
|
|
41977
41977
|
return true;
|
|
41978
41978
|
}
|
|
41979
41979
|
async function startPersonaPlexDaemon(onInfo) {
|
|
41980
|
-
const
|
|
41980
|
+
const log2 = onInfo ?? (() => {
|
|
41981
41981
|
});
|
|
41982
41982
|
const PORT = 8998;
|
|
41983
41983
|
if (isPersonaPlexRunning()) {
|
|
41984
41984
|
const url = getPersonaPlexWSUrl();
|
|
41985
41985
|
if (url) {
|
|
41986
|
-
|
|
41986
|
+
log2(`PersonaPlex already running at ${url}`);
|
|
41987
41987
|
return url;
|
|
41988
41988
|
}
|
|
41989
41989
|
}
|
|
41990
41990
|
if (!isPersonaPlexInstalled()) {
|
|
41991
|
-
|
|
41991
|
+
log2("PersonaPlex not installed. Run /voice personaplex to set up.");
|
|
41992
41992
|
return null;
|
|
41993
41993
|
}
|
|
41994
41994
|
mkdirSync15(PERSONAPLEX_DIR, { recursive: true });
|
|
@@ -42001,12 +42001,12 @@ async function startPersonaPlexDaemon(onInfo) {
|
|
|
42001
42001
|
if (tier !== "original") {
|
|
42002
42002
|
const cachedBf16 = join54(PERSONAPLEX_DIR, "model-bf16-cache.safetensors");
|
|
42003
42003
|
if (tier === "nf4-distilled") {
|
|
42004
|
-
|
|
42004
|
+
log2(`Weight tier: ${tier} \u2014 distilled NF4 (90% token match, ${repoInfo.sizeGB}GB)...`);
|
|
42005
42005
|
try {
|
|
42006
42006
|
const weightPath = execSync28(`"${venvPython2}" -c "from huggingface_hub import hf_hub_download; print(hf_hub_download('${repoInfo.repo}', '${repoInfo.file}', token=False))"`, { encoding: "utf8", timeout: 6e4, stdio: "pipe" }).trim();
|
|
42007
42007
|
if (existsSync37(weightPath)) {
|
|
42008
42008
|
if (!existsSync37(cachedBf16)) {
|
|
42009
|
-
|
|
42009
|
+
log2("Converting .pt checkpoint to safetensors (one-time)...");
|
|
42010
42010
|
execSync28(`"${venvPython2}" -c "
|
|
42011
42011
|
import torch; from safetensors.torch import save_file
|
|
42012
42012
|
state = torch.load('${weightPath}', map_location='cpu', weights_only=True)
|
|
@@ -42017,16 +42017,16 @@ print('Converted')
|
|
|
42017
42017
|
}
|
|
42018
42018
|
if (existsSync37(cachedBf16)) {
|
|
42019
42019
|
extraArgs.push("--moshi-weight", cachedBf16);
|
|
42020
|
-
|
|
42020
|
+
log2(`Using distilled weights: ${(statSync13(cachedBf16).size / 1024 ** 3).toFixed(1)}GB`);
|
|
42021
42021
|
} else {
|
|
42022
42022
|
extraArgs.push("--moshi-weight", weightPath);
|
|
42023
42023
|
}
|
|
42024
42024
|
}
|
|
42025
42025
|
} catch (e) {
|
|
42026
|
-
|
|
42026
|
+
log2(`Failed to load distilled weights \u2014 falling back to standard NF4`);
|
|
42027
42027
|
}
|
|
42028
42028
|
} else {
|
|
42029
|
-
|
|
42029
|
+
log2(`Weight tier: ${tier} (${repoInfo.sizeGB}GB) \u2014 dequantizing to bf16 cache...`);
|
|
42030
42030
|
const dequantScript = join54(PERSONAPLEX_DIR, "dequant-loader.py");
|
|
42031
42031
|
if (!existsSync37(dequantScript)) {
|
|
42032
42032
|
const shipped = getShippedVoicesDir();
|
|
@@ -42043,10 +42043,10 @@ print('Converted')
|
|
|
42043
42043
|
execSync28(`"${venvPython2}" "${dequantScript}" --input "${weightPath}" --output "${cachedBf16}"`, { timeout: 3e5, stdio: "pipe" });
|
|
42044
42044
|
if (existsSync37(cachedBf16)) {
|
|
42045
42045
|
extraArgs.push("--moshi-weight", cachedBf16);
|
|
42046
|
-
|
|
42046
|
+
log2(`Using dequantized cache: ${(statSync13(cachedBf16).size / 1024 ** 3).toFixed(1)}GB`);
|
|
42047
42047
|
}
|
|
42048
42048
|
} catch (e) {
|
|
42049
|
-
|
|
42049
|
+
log2(`Dequantization failed \u2014 server will try to load original weights`);
|
|
42050
42050
|
}
|
|
42051
42051
|
}
|
|
42052
42052
|
try {
|
|
@@ -42062,7 +42062,7 @@ print('Converted')
|
|
|
42062
42062
|
} catch {
|
|
42063
42063
|
}
|
|
42064
42064
|
} catch {
|
|
42065
|
-
|
|
42065
|
+
log2(`Weight file not found \u2014 server will download on first run`);
|
|
42066
42066
|
}
|
|
42067
42067
|
extraArgs.push("--hf-repo", repoInfo.repo);
|
|
42068
42068
|
}
|
|
@@ -42087,17 +42087,17 @@ print('Converted')
|
|
|
42087
42087
|
});
|
|
42088
42088
|
if (ollamaCheck.includes("models")) {
|
|
42089
42089
|
hybridEnabled = true;
|
|
42090
|
-
|
|
42090
|
+
log2(`Hybrid mode: PersonaPlex voice + ${ollamaModel} reasoning`);
|
|
42091
42091
|
}
|
|
42092
42092
|
} catch {
|
|
42093
|
-
|
|
42093
|
+
log2("Ollama not detected \u2014 running PersonaPlex standalone (no hybrid)");
|
|
42094
42094
|
}
|
|
42095
42095
|
const caps = detectPersonaPlexCapability();
|
|
42096
42096
|
const needsOffload = caps.vramGB > 0 && caps.vramGB < 24;
|
|
42097
42097
|
if (needsOffload) {
|
|
42098
|
-
|
|
42098
|
+
log2(`GPU has ${caps.vramGB.toFixed(0)}GB VRAM \u2014 enabling CPU offload (model needs ~19GB)`);
|
|
42099
42099
|
}
|
|
42100
|
-
|
|
42100
|
+
log2(`Starting PersonaPlex daemon (${tier} tier${hybridEnabled ? ", hybrid" : ""}${needsOffload ? ", cpu-offload" : ""})...`);
|
|
42101
42101
|
const serverArgs = [
|
|
42102
42102
|
"-m",
|
|
42103
42103
|
"moshi.server",
|
|
@@ -42153,7 +42153,7 @@ print('Converted')
|
|
|
42153
42153
|
if (child.pid)
|
|
42154
42154
|
process.kill(child.pid, 0);
|
|
42155
42155
|
} catch {
|
|
42156
|
-
|
|
42156
|
+
log2(`PersonaPlex daemon exited unexpectedly. Check ${fileLink2(LOG_FILE, "daemon.log")}`);
|
|
42157
42157
|
return null;
|
|
42158
42158
|
}
|
|
42159
42159
|
try {
|
|
@@ -42163,16 +42163,16 @@ print('Converted')
|
|
|
42163
42163
|
encoding: "utf8"
|
|
42164
42164
|
});
|
|
42165
42165
|
const url = `wss://127.0.0.1:${PORT}`;
|
|
42166
|
-
|
|
42166
|
+
log2(`PersonaPlex ready at ${url}`);
|
|
42167
42167
|
return url;
|
|
42168
42168
|
} catch {
|
|
42169
42169
|
}
|
|
42170
42170
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
42171
42171
|
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
42172
42172
|
if (elapsed % 10 === 0)
|
|
42173
|
-
|
|
42173
|
+
log2(`Still loading... (${elapsed}s)`);
|
|
42174
42174
|
}
|
|
42175
|
-
|
|
42175
|
+
log2(`PersonaPlex daemon failed to start within 120s. Check ${fileLink2(LOG_FILE, "daemon.log")}`);
|
|
42176
42176
|
return null;
|
|
42177
42177
|
}
|
|
42178
42178
|
function stopPersonaPlex() {
|
|
@@ -42229,30 +42229,30 @@ function listPersonaPlexVoices() {
|
|
|
42229
42229
|
return voices;
|
|
42230
42230
|
}
|
|
42231
42231
|
async function clonePersonaPlexVoice(inputWav, voiceName, onInfo) {
|
|
42232
|
-
const
|
|
42232
|
+
const log2 = onInfo ?? (() => {
|
|
42233
42233
|
});
|
|
42234
42234
|
if (!isPersonaPlexInstalled()) {
|
|
42235
|
-
|
|
42235
|
+
log2("PersonaPlex not installed. Run /voice personaplex first.");
|
|
42236
42236
|
return null;
|
|
42237
42237
|
}
|
|
42238
42238
|
if (!existsSync37(inputWav)) {
|
|
42239
|
-
|
|
42239
|
+
log2(`Input WAV not found: ${inputWav}`);
|
|
42240
42240
|
return null;
|
|
42241
42241
|
}
|
|
42242
42242
|
mkdirSync15(CUSTOM_VOICES_DIR, { recursive: true });
|
|
42243
42243
|
const outputPt = join54(CUSTOM_VOICES_DIR, `${voiceName}.pt`);
|
|
42244
42244
|
if (existsSync37(outputPt)) {
|
|
42245
|
-
|
|
42245
|
+
log2(`Voice "${voiceName}" already exists. Delete ${outputPt} to re-clone.`);
|
|
42246
42246
|
return outputPt;
|
|
42247
42247
|
}
|
|
42248
42248
|
const venvPython2 = process.platform === "win32" ? join54(PERSONAPLEX_DIR, "venv", "Scripts", "python.exe") : join54(PERSONAPLEX_DIR, "venv", "bin", "python3");
|
|
42249
42249
|
const cloneScript = join54(PERSONAPLEX_DIR, "clone-voice.py");
|
|
42250
42250
|
if (!existsSync37(cloneScript)) {
|
|
42251
|
-
|
|
42251
|
+
log2("clone-voice.py not found. Reinstall PersonaPlex.");
|
|
42252
42252
|
return null;
|
|
42253
42253
|
}
|
|
42254
|
-
|
|
42255
|
-
|
|
42254
|
+
log2(`Cloning voice "${voiceName}" from ${inputWav}...`);
|
|
42255
|
+
log2("This requires loading the full 7B model \u2014 may take 30-60s...");
|
|
42256
42256
|
return new Promise((resolve36) => {
|
|
42257
42257
|
const child = spawn19(venvPython2, [
|
|
42258
42258
|
cloneScript,
|
|
@@ -42272,7 +42272,7 @@ async function clonePersonaPlexVoice(inputWav, voiceName, onInfo) {
|
|
|
42272
42272
|
const line = d.toString();
|
|
42273
42273
|
output += line;
|
|
42274
42274
|
for (const l of line.split("\n").filter((s) => s.trim())) {
|
|
42275
|
-
|
|
42275
|
+
log2(l.trim());
|
|
42276
42276
|
}
|
|
42277
42277
|
});
|
|
42278
42278
|
child.stderr?.on("data", (d) => {
|
|
@@ -42280,10 +42280,10 @@ async function clonePersonaPlexVoice(inputWav, voiceName, onInfo) {
|
|
|
42280
42280
|
});
|
|
42281
42281
|
child.on("close", (code) => {
|
|
42282
42282
|
if (code === 0 && existsSync37(outputPt)) {
|
|
42283
|
-
|
|
42283
|
+
log2(`Voice "${voiceName}" cloned successfully.`);
|
|
42284
42284
|
resolve36(outputPt);
|
|
42285
42285
|
} else {
|
|
42286
|
-
|
|
42286
|
+
log2(`Voice cloning failed (exit ${code}).`);
|
|
42287
42287
|
resolve36(null);
|
|
42288
42288
|
}
|
|
42289
42289
|
});
|
|
@@ -42315,7 +42315,7 @@ function getShippedVoicesDir() {
|
|
|
42315
42315
|
return null;
|
|
42316
42316
|
}
|
|
42317
42317
|
function provisionShippedVoices(onInfo) {
|
|
42318
|
-
const
|
|
42318
|
+
const log2 = onInfo ?? (() => {
|
|
42319
42319
|
});
|
|
42320
42320
|
const shippedDir = getShippedVoicesDir();
|
|
42321
42321
|
if (!shippedDir)
|
|
@@ -42335,7 +42335,7 @@ function provisionShippedVoices(onInfo) {
|
|
|
42335
42335
|
const hfDst = join54(hfVoicesDir, f);
|
|
42336
42336
|
if (!existsSync37(hfDst)) {
|
|
42337
42337
|
copyFileSync2(join54(shippedDir, f), hfDst);
|
|
42338
|
-
|
|
42338
|
+
log2(`Deployed voice: ${f.replace(".pt", "")}`);
|
|
42339
42339
|
deployed++;
|
|
42340
42340
|
}
|
|
42341
42341
|
}
|
|
@@ -42370,7 +42370,7 @@ function getHFVoicesDir() {
|
|
|
42370
42370
|
return null;
|
|
42371
42371
|
}
|
|
42372
42372
|
function patchFrontendVoiceList(onInfo) {
|
|
42373
|
-
const
|
|
42373
|
+
const log2 = onInfo ?? (() => {
|
|
42374
42374
|
});
|
|
42375
42375
|
const hfBase = join54(homedir13(), ".cache", "huggingface", "hub", "models--nvidia--personaplex-7b-v1");
|
|
42376
42376
|
if (!existsSync37(hfBase))
|
|
@@ -42404,7 +42404,7 @@ function patchFrontendVoiceList(onInfo) {
|
|
|
42404
42404
|
const additions = customVoices.map((v) => `"${v}"`).join(", ");
|
|
42405
42405
|
js = js.replace(needle, `"VARM4.pt", ${additions}]`);
|
|
42406
42406
|
writeFileSync16(jsPath, js);
|
|
42407
|
-
|
|
42407
|
+
log2(`Added ${customVoices.length} custom voice(s) to frontend: ${customVoices.map((v) => v.replace(".pt", "")).join(", ")}`);
|
|
42408
42408
|
}
|
|
42409
42409
|
}
|
|
42410
42410
|
}
|
|
@@ -42412,36 +42412,36 @@ function patchFrontendVoiceList(onInfo) {
|
|
|
42412
42412
|
}
|
|
42413
42413
|
}
|
|
42414
42414
|
async function autoSetupPersonaPlex(onInfo) {
|
|
42415
|
-
const
|
|
42415
|
+
const log2 = onInfo ?? (() => {
|
|
42416
42416
|
});
|
|
42417
42417
|
const caps = detectPersonaPlexCapability();
|
|
42418
42418
|
if (!caps.supported) {
|
|
42419
|
-
|
|
42419
|
+
log2(`PersonaPlex not available: ${caps.reason}`);
|
|
42420
42420
|
return null;
|
|
42421
42421
|
}
|
|
42422
42422
|
const tierInfo = WEIGHT_REPOS[caps.weightTier];
|
|
42423
|
-
|
|
42423
|
+
log2(`GPU: ${caps.gpuName} (${caps.vramGB.toFixed(0)}GB) \u2192 ${caps.weightTier} weights (${tierInfo.sizeGB}GB${caps.needsHfToken ? "" : ", no HF token needed"})`);
|
|
42424
42424
|
if (!isPersonaPlexInstalled()) {
|
|
42425
|
-
|
|
42426
|
-
const ok = await installPersonaPlex(
|
|
42425
|
+
log2("Installing PersonaPlex (first time setup)...");
|
|
42426
|
+
const ok = await installPersonaPlex(log2, caps.weightTier);
|
|
42427
42427
|
if (!ok) {
|
|
42428
|
-
|
|
42428
|
+
log2("PersonaPlex installation failed.");
|
|
42429
42429
|
return null;
|
|
42430
42430
|
}
|
|
42431
42431
|
}
|
|
42432
|
-
const deployed = provisionShippedVoices(
|
|
42432
|
+
const deployed = provisionShippedVoices(log2);
|
|
42433
42433
|
if (deployed > 0) {
|
|
42434
|
-
|
|
42434
|
+
log2(`Provisioned ${deployed} shipped voice(s)`);
|
|
42435
42435
|
}
|
|
42436
|
-
patchFrontendVoiceList(
|
|
42436
|
+
patchFrontendVoiceList(log2);
|
|
42437
42437
|
if (isPersonaPlexRunning()) {
|
|
42438
42438
|
const url = getPersonaPlexWSUrl();
|
|
42439
42439
|
if (url) {
|
|
42440
|
-
|
|
42440
|
+
log2(`PersonaPlex already running at ${url}`);
|
|
42441
42441
|
return url;
|
|
42442
42442
|
}
|
|
42443
42443
|
}
|
|
42444
|
-
return await startPersonaPlexDaemon(
|
|
42444
|
+
return await startPersonaPlexDaemon(log2);
|
|
42445
42445
|
}
|
|
42446
42446
|
var WEIGHT_REPOS, PERSONAPLEX_DIR, PID_FILE, PORT_FILE, LOG_FILE, CUSTOM_VOICES_DIR;
|
|
42447
42447
|
var init_personaplex = __esm({
|
|
@@ -43624,20 +43624,20 @@ function hasVenvModule() {
|
|
|
43624
43624
|
return false;
|
|
43625
43625
|
}
|
|
43626
43626
|
}
|
|
43627
|
-
function ensureVenv(
|
|
43627
|
+
function ensureVenv(log2) {
|
|
43628
43628
|
const venvDir = getVenvDir();
|
|
43629
43629
|
const isWin2 = process.platform === "win32";
|
|
43630
43630
|
const pipPath = isWin2 ? join55(venvDir, "Scripts", "pip.exe") : join55(venvDir, "bin", "pip");
|
|
43631
43631
|
const pythonCmd = isWin2 ? "python" : "python3";
|
|
43632
43632
|
if (existsSync38(pipPath))
|
|
43633
43633
|
return venvDir;
|
|
43634
|
-
|
|
43634
|
+
log2("Creating Python venv for vision deps...");
|
|
43635
43635
|
if (!hasCmd(pythonCmd) && !hasCmd("python3")) {
|
|
43636
|
-
|
|
43636
|
+
log2(`${pythonCmd} not found \u2014 cannot create venv.`);
|
|
43637
43637
|
return null;
|
|
43638
43638
|
}
|
|
43639
43639
|
if (!isWin2 && !hasVenvModule()) {
|
|
43640
|
-
|
|
43640
|
+
log2("python3 venv module not available \u2014 venv creation skipped.");
|
|
43641
43641
|
return null;
|
|
43642
43642
|
}
|
|
43643
43643
|
try {
|
|
@@ -43648,10 +43648,10 @@ function ensureVenv(log) {
|
|
|
43648
43648
|
stdio: "pipe",
|
|
43649
43649
|
timeout: 6e4
|
|
43650
43650
|
});
|
|
43651
|
-
|
|
43651
|
+
log2("Python venv created at ~/.open-agents/venv");
|
|
43652
43652
|
return venvDir;
|
|
43653
43653
|
} catch (err) {
|
|
43654
|
-
|
|
43654
|
+
log2(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
|
|
43655
43655
|
return null;
|
|
43656
43656
|
}
|
|
43657
43657
|
}
|
|
@@ -43688,7 +43688,7 @@ function runWithSudo(cmd, password, timeoutMs = 12e4) {
|
|
|
43688
43688
|
function validateSudoPassword(password) {
|
|
43689
43689
|
return runWithSudo("true", password, 1e4) === "ok";
|
|
43690
43690
|
}
|
|
43691
|
-
async function acquireSudoPassword(getSudoPassword,
|
|
43691
|
+
async function acquireSudoPassword(getSudoPassword, log2, cachedPasswordRef) {
|
|
43692
43692
|
if (cachedPasswordRef.value && validateSudoPassword(cachedPasswordRef.value)) {
|
|
43693
43693
|
return cachedPasswordRef.value;
|
|
43694
43694
|
}
|
|
@@ -43697,7 +43697,7 @@ async function acquireSudoPassword(getSudoPassword, log, cachedPasswordRef) {
|
|
|
43697
43697
|
}
|
|
43698
43698
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
43699
43699
|
if (attempt > 0)
|
|
43700
|
-
|
|
43700
|
+
log2("Authentication failed \u2014 please re-enter password.");
|
|
43701
43701
|
const pw = await getSudoPassword();
|
|
43702
43702
|
if (!pw)
|
|
43703
43703
|
return null;
|
|
@@ -43708,10 +43708,10 @@ async function acquireSudoPassword(getSudoPassword, log, cachedPasswordRef) {
|
|
|
43708
43708
|
}
|
|
43709
43709
|
return null;
|
|
43710
43710
|
}
|
|
43711
|
-
async function sudoInstall(cmd, getSudoPassword,
|
|
43711
|
+
async function sudoInstall(cmd, getSudoPassword, log2, cachedPasswordRef, timeoutMs = 12e4) {
|
|
43712
43712
|
if (trySudoPasswordless(cmd, timeoutMs))
|
|
43713
43713
|
return true;
|
|
43714
|
-
const pw = await acquireSudoPassword(getSudoPassword,
|
|
43714
|
+
const pw = await acquireSudoPassword(getSudoPassword, log2, cachedPasswordRef);
|
|
43715
43715
|
if (pw === null)
|
|
43716
43716
|
return false;
|
|
43717
43717
|
if (pw === "")
|
|
@@ -43720,12 +43720,12 @@ async function sudoInstall(cmd, getSudoPassword, log, cachedPasswordRef, timeout
|
|
|
43720
43720
|
if (result === "ok")
|
|
43721
43721
|
return true;
|
|
43722
43722
|
if (result === "cmd_fail") {
|
|
43723
|
-
|
|
43723
|
+
log2(`Install command failed (not an auth issue) \u2014 package may not be available for this OS.`);
|
|
43724
43724
|
}
|
|
43725
43725
|
return false;
|
|
43726
43726
|
}
|
|
43727
43727
|
async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
43728
|
-
const
|
|
43728
|
+
const log2 = onInfo ?? (() => {
|
|
43729
43729
|
});
|
|
43730
43730
|
const cachedPasswordRef = { value: null };
|
|
43731
43731
|
const getPassword = getSudoPassword ?? (() => Promise.resolve(null));
|
|
@@ -43751,7 +43751,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43751
43751
|
if (missing.length === 0) {
|
|
43752
43752
|
} else if (!pm2) {
|
|
43753
43753
|
for (const d of missing)
|
|
43754
|
-
|
|
43754
|
+
log2(`No supported package manager (choco/winget/apt/brew) \u2014 ${d.label} unavailable. Install manually or install Chocolatey: https://chocolatey.org/install`);
|
|
43755
43755
|
} else {
|
|
43756
43756
|
const labels = missing.map((d) => d.label).join(", ");
|
|
43757
43757
|
let winNeedsElevation = false;
|
|
@@ -43760,19 +43760,19 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43760
43760
|
execSync29("net session", { stdio: "pipe", timeout: 3e3 });
|
|
43761
43761
|
} catch {
|
|
43762
43762
|
winNeedsElevation = true;
|
|
43763
|
-
|
|
43763
|
+
log2(`Installing ${labels} via ${pm2} (requires admin \u2014 UAC prompt will appear)...`);
|
|
43764
43764
|
}
|
|
43765
43765
|
if (!winNeedsElevation)
|
|
43766
|
-
|
|
43766
|
+
log2(`Installing ${labels} via ${pm2}...`);
|
|
43767
43767
|
} else {
|
|
43768
|
-
|
|
43768
|
+
log2(`Installing ${labels} via ${pm2}...`);
|
|
43769
43769
|
}
|
|
43770
43770
|
const needsSudo = pm2 !== "brew" && pm2 !== "choco" && pm2 !== "winget";
|
|
43771
43771
|
for (const d of missing) {
|
|
43772
43772
|
const pkgName = d.pkgs[pm2];
|
|
43773
43773
|
const pipPkg = d.pkgs.pip;
|
|
43774
43774
|
if (!pkgName && !pipPkg) {
|
|
43775
|
-
|
|
43775
|
+
log2(`${d.label} not available for ${pm2}.`);
|
|
43776
43776
|
continue;
|
|
43777
43777
|
}
|
|
43778
43778
|
let lastError = "";
|
|
@@ -43803,7 +43803,7 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43803
43803
|
}
|
|
43804
43804
|
try {
|
|
43805
43805
|
if (needsSudo) {
|
|
43806
|
-
await sudoInstall(installCmd, getPassword,
|
|
43806
|
+
await sudoInstall(installCmd, getPassword, log2, cachedPasswordRef, 18e4);
|
|
43807
43807
|
} else if (winNeedsElevation) {
|
|
43808
43808
|
execSync29(`powershell -NoProfile -Command "Start-Process -FilePath 'cmd.exe' -ArgumentList '/c ${installCmd.replace(/'/g, "''")}' -Verb RunAs -Wait"`, { stdio: "pipe", timeout: 18e4 });
|
|
43809
43809
|
} else {
|
|
@@ -43840,10 +43840,10 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43840
43840
|
}
|
|
43841
43841
|
}
|
|
43842
43842
|
if (hasCmd(d.binary)) {
|
|
43843
|
-
|
|
43843
|
+
log2(`${d.label} installed.`);
|
|
43844
43844
|
} else {
|
|
43845
43845
|
const reason = lastError ? ` Reason: ${lastError}` : " (binary not found in PATH after install \u2014 may need terminal restart or admin elevation)";
|
|
43846
|
-
|
|
43846
|
+
log2(`${d.label} could not be installed \u2014 features will be limited.${reason}`);
|
|
43847
43847
|
}
|
|
43848
43848
|
}
|
|
43849
43849
|
}
|
|
@@ -43856,10 +43856,10 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43856
43856
|
};
|
|
43857
43857
|
const pipCmd = pipCmds[pm];
|
|
43858
43858
|
if (pipCmd) {
|
|
43859
|
-
|
|
43860
|
-
const ok = await sudoInstall(pipCmd, getPassword,
|
|
43859
|
+
log2("Installing python3-pip...");
|
|
43860
|
+
const ok = await sudoInstall(pipCmd, getPassword, log2, cachedPasswordRef);
|
|
43861
43861
|
if (!ok) {
|
|
43862
|
-
|
|
43862
|
+
log2("python3-pip could not be installed \u2014 moondream-station may be unavailable.");
|
|
43863
43863
|
}
|
|
43864
43864
|
}
|
|
43865
43865
|
}
|
|
@@ -43879,10 +43879,10 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43879
43879
|
if (cmdFn) {
|
|
43880
43880
|
const cmd = cmdFn();
|
|
43881
43881
|
if (cmd) {
|
|
43882
|
-
|
|
43883
|
-
const ok = await sudoInstall(cmd, getPassword,
|
|
43882
|
+
log2("Installing python3-venv...");
|
|
43883
|
+
const ok = await sudoInstall(cmd, getPassword, log2, cachedPasswordRef);
|
|
43884
43884
|
if (!ok) {
|
|
43885
|
-
|
|
43885
|
+
log2("python3-venv could not be installed \u2014 moondream-station may be unavailable.");
|
|
43886
43886
|
}
|
|
43887
43887
|
}
|
|
43888
43888
|
}
|
|
@@ -43891,26 +43891,26 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43891
43891
|
const isWin2 = process.platform === "win32";
|
|
43892
43892
|
const venvBin = join55(venvDir, isWin2 ? "Scripts" : "bin");
|
|
43893
43893
|
const venvMoondream = join55(venvBin, isWin2 ? "moondream-station.exe" : "moondream-station");
|
|
43894
|
-
const venv = ensureVenv(
|
|
43894
|
+
const venv = ensureVenv(log2);
|
|
43895
43895
|
if (venv && !hasCmd("moondream-station") && !existsSync38(venvMoondream)) {
|
|
43896
43896
|
const venvPip2 = join55(venvBin, "pip");
|
|
43897
|
-
|
|
43897
|
+
log2("Installing moondream-station in ~/.open-agents/venv...");
|
|
43898
43898
|
try {
|
|
43899
43899
|
execSync29(`"${venvPip2}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
|
|
43900
43900
|
if (existsSync38(venvMoondream)) {
|
|
43901
|
-
|
|
43901
|
+
log2("moondream-station installed successfully.");
|
|
43902
43902
|
} else {
|
|
43903
43903
|
try {
|
|
43904
43904
|
const check = execSync29(`"${venvPip2}" show moondream-station`, { encoding: "utf8", stdio: "pipe", timeout: 5e3 });
|
|
43905
43905
|
if (check.includes("moondream")) {
|
|
43906
|
-
|
|
43906
|
+
log2("moondream-station package installed.");
|
|
43907
43907
|
}
|
|
43908
43908
|
} catch {
|
|
43909
|
-
|
|
43909
|
+
log2("moondream-station install completed.");
|
|
43910
43910
|
}
|
|
43911
43911
|
}
|
|
43912
43912
|
} catch (err) {
|
|
43913
|
-
|
|
43913
|
+
log2(`moondream-station install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
43914
43914
|
}
|
|
43915
43915
|
}
|
|
43916
43916
|
if (venv) {
|
|
@@ -43924,21 +43924,21 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
43924
43924
|
}
|
|
43925
43925
|
if (!ocrStackInstalled) {
|
|
43926
43926
|
const ocrPackages = "pytesseract Pillow opencv-python-headless numpy";
|
|
43927
|
-
|
|
43927
|
+
log2("Installing OCR Python stack (pytesseract, OpenCV, Pillow, numpy)...");
|
|
43928
43928
|
try {
|
|
43929
43929
|
execSync29(`"${venvPip2}" install ${ocrPackages}`, { stdio: "pipe", timeout: 3e5 });
|
|
43930
43930
|
try {
|
|
43931
43931
|
execSync29(`"${venvPython2}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
|
|
43932
|
-
|
|
43932
|
+
log2("OCR Python stack installed successfully.");
|
|
43933
43933
|
} catch {
|
|
43934
|
-
|
|
43934
|
+
log2("OCR Python stack install completed but import verification failed.");
|
|
43935
43935
|
}
|
|
43936
43936
|
} catch (err) {
|
|
43937
|
-
|
|
43937
|
+
log2(`OCR Python stack install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
43938
43938
|
}
|
|
43939
43939
|
}
|
|
43940
43940
|
} else {
|
|
43941
|
-
|
|
43941
|
+
log2("Python venv unavailable \u2014 advanced OCR pipeline will fall back to basic tesseract.");
|
|
43942
43942
|
}
|
|
43943
43943
|
}
|
|
43944
43944
|
function ensureCloudflaredBackground(onInfo) {
|
|
@@ -43948,14 +43948,14 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
43948
43948
|
_cloudflaredInstallPromise = Promise.resolve(true);
|
|
43949
43949
|
return;
|
|
43950
43950
|
}
|
|
43951
|
-
const
|
|
43951
|
+
const log2 = onInfo ?? (() => {
|
|
43952
43952
|
});
|
|
43953
|
-
|
|
43953
|
+
log2("Installing cloudflared for live voice sessions...");
|
|
43954
43954
|
_cloudflaredInstallPromise = (async () => {
|
|
43955
43955
|
const arch2 = process.arch;
|
|
43956
43956
|
const os = platform2();
|
|
43957
43957
|
if (os !== "win32" && !ensureCurl()) {
|
|
43958
|
-
|
|
43958
|
+
log2("curl not available \u2014 cannot install cloudflared.");
|
|
43959
43959
|
return false;
|
|
43960
43960
|
}
|
|
43961
43961
|
if (os === "linux") {
|
|
@@ -43967,7 +43967,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
43967
43967
|
process.env.PATH = `${homedir14()}/.local/bin:${process.env.PATH}`;
|
|
43968
43968
|
}
|
|
43969
43969
|
if (hasCmd("cloudflared")) {
|
|
43970
|
-
|
|
43970
|
+
log2("cloudflared installed.");
|
|
43971
43971
|
return true;
|
|
43972
43972
|
}
|
|
43973
43973
|
} catch {
|
|
@@ -43975,7 +43975,7 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
43975
43975
|
try {
|
|
43976
43976
|
execSync29(`curl -fsSL "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${cfArch}" -o /tmp/cloudflared && chmod +x /tmp/cloudflared && sudo mv /tmp/cloudflared /usr/local/bin/cloudflared 2>/dev/null`, { stdio: "pipe", timeout: 6e4 });
|
|
43977
43977
|
if (hasCmd("cloudflared")) {
|
|
43978
|
-
|
|
43978
|
+
log2("cloudflared installed.");
|
|
43979
43979
|
return true;
|
|
43980
43980
|
}
|
|
43981
43981
|
} catch {
|
|
@@ -43984,13 +43984,13 @@ function ensureCloudflaredBackground(onInfo) {
|
|
|
43984
43984
|
try {
|
|
43985
43985
|
execSync29("brew install cloudflared", { stdio: "pipe", timeout: 12e4 });
|
|
43986
43986
|
if (hasCmd("cloudflared")) {
|
|
43987
|
-
|
|
43987
|
+
log2("cloudflared installed via Homebrew.");
|
|
43988
43988
|
return true;
|
|
43989
43989
|
}
|
|
43990
43990
|
} catch {
|
|
43991
43991
|
}
|
|
43992
43992
|
}
|
|
43993
|
-
|
|
43993
|
+
log2("cloudflared not available \u2014 live voice sessions disabled.");
|
|
43994
43994
|
return false;
|
|
43995
43995
|
})();
|
|
43996
43996
|
}
|
|
@@ -52121,12 +52121,12 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
|
|
|
52121
52121
|
continue;
|
|
52122
52122
|
}
|
|
52123
52123
|
const { basename: basename18, join: pathJoin } = await import("node:path");
|
|
52124
|
-
const { copyFileSync: copyFileSync3, mkdirSync:
|
|
52124
|
+
const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync32, existsSync: exists } = await import("node:fs");
|
|
52125
52125
|
const { homedir: homedir21 } = await import("node:os");
|
|
52126
52126
|
const modelName = basename18(onnxDrop.path, ".onnx").replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
52127
52127
|
const destDir = pathJoin(homedir21(), ".open-agents", "voice", "models", modelName);
|
|
52128
52128
|
if (!exists(destDir))
|
|
52129
|
-
|
|
52129
|
+
mkdirSync32(destDir, { recursive: true });
|
|
52130
52130
|
copyFileSync3(onnxDrop.path, pathJoin(destDir, "model.onnx"));
|
|
52131
52131
|
copyFileSync3(jsonDrop.path, pathJoin(destDir, "config.json"));
|
|
52132
52132
|
const { registerCustomOnnxModel: registerCustomOnnxModel2 } = await Promise.resolve().then(() => (init_voice(), voice_exports));
|
|
@@ -52965,10 +52965,10 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
|
|
|
52965
52965
|
const models = await fetchModels(peerUrl, authKey);
|
|
52966
52966
|
if (models.length > 0) {
|
|
52967
52967
|
try {
|
|
52968
|
-
const { writeFileSync: writeFileSync30, mkdirSync:
|
|
52969
|
-
const { join:
|
|
52970
|
-
const cachePath =
|
|
52971
|
-
|
|
52968
|
+
const { writeFileSync: writeFileSync30, mkdirSync: mkdirSync32 } = await import("node:fs");
|
|
52969
|
+
const { join: join78, dirname: dirname24 } = await import("node:path");
|
|
52970
|
+
const cachePath = join78(ctx.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
|
|
52971
|
+
mkdirSync32(dirname24(cachePath), { recursive: true });
|
|
52972
52972
|
writeFileSync30(cachePath, JSON.stringify({
|
|
52973
52973
|
peerId,
|
|
52974
52974
|
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -53168,17 +53168,17 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
53168
53168
|
try {
|
|
53169
53169
|
const { createRequire: createRequire6 } = await import("node:module");
|
|
53170
53170
|
const { fileURLToPath: fileURLToPath16 } = await import("node:url");
|
|
53171
|
-
const { dirname: dirname24, join:
|
|
53172
|
-
const { existsSync:
|
|
53171
|
+
const { dirname: dirname24, join: join78 } = await import("node:path");
|
|
53172
|
+
const { existsSync: existsSync58 } = await import("node:fs");
|
|
53173
53173
|
const req = createRequire6(import.meta.url);
|
|
53174
53174
|
const thisDir = dirname24(fileURLToPath16(import.meta.url));
|
|
53175
53175
|
const candidates = [
|
|
53176
|
-
|
|
53177
|
-
|
|
53178
|
-
|
|
53176
|
+
join78(thisDir, "..", "package.json"),
|
|
53177
|
+
join78(thisDir, "..", "..", "package.json"),
|
|
53178
|
+
join78(thisDir, "..", "..", "..", "package.json")
|
|
53179
53179
|
];
|
|
53180
53180
|
for (const pkgPath of candidates) {
|
|
53181
|
-
if (
|
|
53181
|
+
if (existsSync58(pkgPath)) {
|
|
53182
53182
|
const pkg = req(pkgPath);
|
|
53183
53183
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
53184
53184
|
currentVersion = pkg.version ?? "0.0.0";
|
|
@@ -54096,10 +54096,10 @@ function getGitInfo(repoRoot) {
|
|
|
54096
54096
|
} catch {
|
|
54097
54097
|
}
|
|
54098
54098
|
try {
|
|
54099
|
-
const
|
|
54100
|
-
if (
|
|
54099
|
+
const log2 = execSync32("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
|
|
54100
|
+
if (log2)
|
|
54101
54101
|
lines.push(`Recent commits:
|
|
54102
|
-
${
|
|
54102
|
+
${log2}`);
|
|
54103
54103
|
} catch {
|
|
54104
54104
|
}
|
|
54105
54105
|
return lines.join("\n");
|
|
@@ -65238,25 +65238,728 @@ var init_direct_input = __esm({
|
|
|
65238
65238
|
}
|
|
65239
65239
|
});
|
|
65240
65240
|
|
|
65241
|
-
// packages/cli/dist/api/
|
|
65242
|
-
import {
|
|
65241
|
+
// packages/cli/dist/api/audit-log.js
|
|
65242
|
+
import { mkdirSync as mkdirSync26, appendFileSync as appendFileSync4, readFileSync as readFileSync41, existsSync as existsSync52 } from "node:fs";
|
|
65243
65243
|
import { join as join69 } from "node:path";
|
|
65244
|
+
function initAuditLog(oaDir) {
|
|
65245
|
+
auditDir = join69(oaDir, "audit");
|
|
65246
|
+
auditFile = join69(auditDir, "audit.jsonl");
|
|
65247
|
+
try {
|
|
65248
|
+
mkdirSync26(auditDir, { recursive: true });
|
|
65249
|
+
initialized = true;
|
|
65250
|
+
} catch {
|
|
65251
|
+
}
|
|
65252
|
+
}
|
|
65253
|
+
function recordAudit(record) {
|
|
65254
|
+
if (!initialized)
|
|
65255
|
+
return;
|
|
65256
|
+
try {
|
|
65257
|
+
const line = JSON.stringify(record) + "\n";
|
|
65258
|
+
appendFileSync4(auditFile, line, "utf-8");
|
|
65259
|
+
} catch {
|
|
65260
|
+
}
|
|
65261
|
+
}
|
|
65262
|
+
function queryAudit(opts) {
|
|
65263
|
+
if (!initialized || !existsSync52(auditFile))
|
|
65264
|
+
return [];
|
|
65265
|
+
try {
|
|
65266
|
+
const raw = readFileSync41(auditFile, "utf-8");
|
|
65267
|
+
const lines = raw.split("\n").filter(Boolean);
|
|
65268
|
+
let records = lines.map((l) => {
|
|
65269
|
+
try {
|
|
65270
|
+
return JSON.parse(l);
|
|
65271
|
+
} catch {
|
|
65272
|
+
return null;
|
|
65273
|
+
}
|
|
65274
|
+
}).filter(Boolean);
|
|
65275
|
+
if (opts.since) {
|
|
65276
|
+
const sinceTs = new Date(opts.since).getTime();
|
|
65277
|
+
records = records.filter((r) => new Date(r.ts).getTime() >= sinceTs);
|
|
65278
|
+
}
|
|
65279
|
+
if (opts.user) {
|
|
65280
|
+
records = records.filter((r) => r.user === opts.user);
|
|
65281
|
+
}
|
|
65282
|
+
records.reverse();
|
|
65283
|
+
if (opts.limit && opts.limit > 0) {
|
|
65284
|
+
records = records.slice(0, opts.limit);
|
|
65285
|
+
}
|
|
65286
|
+
return records;
|
|
65287
|
+
} catch {
|
|
65288
|
+
return [];
|
|
65289
|
+
}
|
|
65290
|
+
}
|
|
65291
|
+
var auditDir, auditFile, initialized;
|
|
65292
|
+
var init_audit_log = __esm({
|
|
65293
|
+
"packages/cli/dist/api/audit-log.js"() {
|
|
65294
|
+
"use strict";
|
|
65295
|
+
auditDir = "";
|
|
65296
|
+
auditFile = "";
|
|
65297
|
+
initialized = false;
|
|
65298
|
+
}
|
|
65299
|
+
});
|
|
65300
|
+
|
|
65301
|
+
// packages/cli/dist/api/web-ui.js
|
|
65302
|
+
function getWebUI() {
|
|
65303
|
+
return `<!DOCTYPE html>
|
|
65304
|
+
<html lang="en">
|
|
65305
|
+
<head>
|
|
65306
|
+
<meta charset="UTF-8">
|
|
65307
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
65308
|
+
<title>Open Agents</title>
|
|
65309
|
+
<style>
|
|
65310
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
65311
|
+
body {
|
|
65312
|
+
font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
|
|
65313
|
+
background: #1a1a1e;
|
|
65314
|
+
color: #b0b0b0;
|
|
65315
|
+
display: flex;
|
|
65316
|
+
flex-direction: column;
|
|
65317
|
+
height: 100vh;
|
|
65318
|
+
overflow: hidden;
|
|
65319
|
+
}
|
|
65320
|
+
#header {
|
|
65321
|
+
display: flex;
|
|
65322
|
+
align-items: center;
|
|
65323
|
+
gap: 12px;
|
|
65324
|
+
padding: 8px 16px;
|
|
65325
|
+
background: #1e1e22;
|
|
65326
|
+
border-bottom: 1px solid #2a2a30;
|
|
65327
|
+
flex-shrink: 0;
|
|
65328
|
+
}
|
|
65329
|
+
#header .accent { color: #b2920a; font-weight: bold; font-size: 0.8rem; }
|
|
65330
|
+
#header .status { font-size: 0.7rem; color: #555; }
|
|
65331
|
+
#header .status.live { color: #b2920a; }
|
|
65332
|
+
#header select {
|
|
65333
|
+
background: #2a2a30;
|
|
65334
|
+
border: 1px solid #3a3a42;
|
|
65335
|
+
color: #b0b0b0;
|
|
65336
|
+
padding: 4px 8px;
|
|
65337
|
+
border-radius: 3px;
|
|
65338
|
+
font-family: inherit;
|
|
65339
|
+
font-size: 0.7rem;
|
|
65340
|
+
margin-left: auto;
|
|
65341
|
+
}
|
|
65342
|
+
#header .key-btn {
|
|
65343
|
+
background: #2a2a30;
|
|
65344
|
+
border: 1px solid #3a3a42;
|
|
65345
|
+
color: #b2920a;
|
|
65346
|
+
padding: 4px 10px;
|
|
65347
|
+
border-radius: 3px;
|
|
65348
|
+
font-family: inherit;
|
|
65349
|
+
font-size: 0.7rem;
|
|
65350
|
+
cursor: pointer;
|
|
65351
|
+
transition: background 0.15s;
|
|
65352
|
+
}
|
|
65353
|
+
#header .key-btn:hover { background: #3a3a42; }
|
|
65354
|
+
#conversation {
|
|
65355
|
+
flex: 1;
|
|
65356
|
+
overflow-y: auto;
|
|
65357
|
+
padding: 12px 16px;
|
|
65358
|
+
display: flex;
|
|
65359
|
+
flex-direction: column;
|
|
65360
|
+
gap: 4px;
|
|
65361
|
+
}
|
|
65362
|
+
.msg { padding: 6px 0; font-size: 0.82rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
|
|
65363
|
+
.msg.user { color: #888; }
|
|
65364
|
+
.msg.user::before { content: '\\25B8 '; color: #555; }
|
|
65365
|
+
.msg.assistant { color: #b2920a; }
|
|
65366
|
+
.msg.assistant::before { content: '\\25B9 '; color: #b2920a; }
|
|
65367
|
+
.msg.system { color: #555; font-size: 0.7rem; }
|
|
65368
|
+
.msg code {
|
|
65369
|
+
background: #2a2a30;
|
|
65370
|
+
padding: 1px 4px;
|
|
65371
|
+
border-radius: 2px;
|
|
65372
|
+
font-size: 0.78rem;
|
|
65373
|
+
}
|
|
65374
|
+
.msg pre {
|
|
65375
|
+
background: #1e1e22;
|
|
65376
|
+
border: 1px solid #2a2a30;
|
|
65377
|
+
border-radius: 3px;
|
|
65378
|
+
padding: 8px 12px;
|
|
65379
|
+
margin: 6px 0;
|
|
65380
|
+
overflow-x: auto;
|
|
65381
|
+
font-size: 0.78rem;
|
|
65382
|
+
line-height: 1.4;
|
|
65383
|
+
position: relative;
|
|
65384
|
+
}
|
|
65385
|
+
.msg pre .copy-btn {
|
|
65386
|
+
position: absolute;
|
|
65387
|
+
top: 4px;
|
|
65388
|
+
right: 4px;
|
|
65389
|
+
background: #2a2a30;
|
|
65390
|
+
border: 1px solid #3a3a42;
|
|
65391
|
+
color: #b2920a;
|
|
65392
|
+
padding: 2px 8px;
|
|
65393
|
+
border-radius: 2px;
|
|
65394
|
+
font-family: inherit;
|
|
65395
|
+
font-size: 0.6rem;
|
|
65396
|
+
cursor: pointer;
|
|
65397
|
+
opacity: 0.5;
|
|
65398
|
+
transition: opacity 0.15s;
|
|
65399
|
+
}
|
|
65400
|
+
.msg pre:hover .copy-btn { opacity: 1; }
|
|
65401
|
+
.msg-actions {
|
|
65402
|
+
display: flex;
|
|
65403
|
+
gap: 8px;
|
|
65404
|
+
margin-top: 2px;
|
|
65405
|
+
}
|
|
65406
|
+
.msg-actions button {
|
|
65407
|
+
background: none;
|
|
65408
|
+
border: none;
|
|
65409
|
+
color: #555;
|
|
65410
|
+
font-family: inherit;
|
|
65411
|
+
font-size: 0.6rem;
|
|
65412
|
+
cursor: pointer;
|
|
65413
|
+
padding: 0;
|
|
65414
|
+
}
|
|
65415
|
+
.msg-actions button:hover { color: #b2920a; }
|
|
65416
|
+
#footer {
|
|
65417
|
+
display: flex;
|
|
65418
|
+
align-items: flex-end;
|
|
65419
|
+
gap: 10px;
|
|
65420
|
+
padding: 8px 16px;
|
|
65421
|
+
background: #1e1e22;
|
|
65422
|
+
border-top: 1px solid #2a2a30;
|
|
65423
|
+
flex-shrink: 0;
|
|
65424
|
+
}
|
|
65425
|
+
#input-area {
|
|
65426
|
+
flex: 1;
|
|
65427
|
+
background: #2a2a30;
|
|
65428
|
+
border: 1px solid #3a3a42;
|
|
65429
|
+
border-radius: 3px;
|
|
65430
|
+
padding: 8px 12px;
|
|
65431
|
+
color: #b0b0b0;
|
|
65432
|
+
font-family: inherit;
|
|
65433
|
+
font-size: 0.82rem;
|
|
65434
|
+
resize: none;
|
|
65435
|
+
min-height: 36px;
|
|
65436
|
+
max-height: 120px;
|
|
65437
|
+
line-height: 1.4;
|
|
65438
|
+
outline: none;
|
|
65439
|
+
}
|
|
65440
|
+
#input-area:focus { border-color: #b2920a; }
|
|
65441
|
+
#send-btn {
|
|
65442
|
+
background: #2a2a30;
|
|
65443
|
+
border: 1px solid #3a3a42;
|
|
65444
|
+
color: #b2920a;
|
|
65445
|
+
padding: 8px 16px;
|
|
65446
|
+
border-radius: 3px;
|
|
65447
|
+
font-family: inherit;
|
|
65448
|
+
font-size: 0.75rem;
|
|
65449
|
+
cursor: pointer;
|
|
65450
|
+
transition: background 0.15s;
|
|
65451
|
+
flex-shrink: 0;
|
|
65452
|
+
}
|
|
65453
|
+
#send-btn:hover { background: #3a3a42; }
|
|
65454
|
+
#send-btn:disabled { opacity: 0.3; cursor: default; }
|
|
65455
|
+
#system-prompt-toggle {
|
|
65456
|
+
font-size: 0.65rem;
|
|
65457
|
+
color: #444;
|
|
65458
|
+
cursor: pointer;
|
|
65459
|
+
padding: 4px 0;
|
|
65460
|
+
}
|
|
65461
|
+
#system-prompt-toggle:hover { color: #b2920a; }
|
|
65462
|
+
#system-prompt-area {
|
|
65463
|
+
display: none;
|
|
65464
|
+
padding: 0 16px 8px;
|
|
65465
|
+
background: #1e1e22;
|
|
65466
|
+
}
|
|
65467
|
+
#system-prompt-area textarea {
|
|
65468
|
+
width: 100%;
|
|
65469
|
+
background: #2a2a30;
|
|
65470
|
+
border: 1px solid #3a3a42;
|
|
65471
|
+
border-radius: 3px;
|
|
65472
|
+
padding: 6px 10px;
|
|
65473
|
+
color: #b0b0b0;
|
|
65474
|
+
font-family: inherit;
|
|
65475
|
+
font-size: 0.7rem;
|
|
65476
|
+
resize: vertical;
|
|
65477
|
+
min-height: 48px;
|
|
65478
|
+
outline: none;
|
|
65479
|
+
}
|
|
65480
|
+
#key-modal {
|
|
65481
|
+
display: none;
|
|
65482
|
+
position: fixed;
|
|
65483
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
65484
|
+
background: rgba(0,0,0,0.7);
|
|
65485
|
+
z-index: 100;
|
|
65486
|
+
align-items: center;
|
|
65487
|
+
justify-content: center;
|
|
65488
|
+
}
|
|
65489
|
+
#key-modal.visible { display: flex; }
|
|
65490
|
+
#key-modal .modal {
|
|
65491
|
+
background: #1e1e22;
|
|
65492
|
+
border: 1px solid #2a2a30;
|
|
65493
|
+
border-radius: 6px;
|
|
65494
|
+
padding: 20px;
|
|
65495
|
+
width: 360px;
|
|
65496
|
+
max-width: 90vw;
|
|
65497
|
+
}
|
|
65498
|
+
#key-modal .modal h3 { color: #b2920a; font-size: 0.8rem; margin-bottom: 12px; }
|
|
65499
|
+
#key-modal .modal input {
|
|
65500
|
+
width: 100%;
|
|
65501
|
+
background: #2a2a30;
|
|
65502
|
+
border: 1px solid #3a3a42;
|
|
65503
|
+
border-radius: 3px;
|
|
65504
|
+
padding: 8px 10px;
|
|
65505
|
+
color: #b0b0b0;
|
|
65506
|
+
font-family: inherit;
|
|
65507
|
+
font-size: 0.75rem;
|
|
65508
|
+
outline: none;
|
|
65509
|
+
margin-bottom: 12px;
|
|
65510
|
+
}
|
|
65511
|
+
#key-modal .modal button {
|
|
65512
|
+
background: #2a2a30;
|
|
65513
|
+
border: 1px solid #b2920a;
|
|
65514
|
+
color: #b2920a;
|
|
65515
|
+
padding: 6px 16px;
|
|
65516
|
+
border-radius: 3px;
|
|
65517
|
+
font-family: inherit;
|
|
65518
|
+
font-size: 0.75rem;
|
|
65519
|
+
cursor: pointer;
|
|
65520
|
+
margin-right: 8px;
|
|
65521
|
+
}
|
|
65522
|
+
</style>
|
|
65523
|
+
</head>
|
|
65524
|
+
<body>
|
|
65525
|
+
|
|
65526
|
+
<div id="header">
|
|
65527
|
+
<span class="accent">OA</span>
|
|
65528
|
+
<span class="status" id="status">connecting...</span>
|
|
65529
|
+
<select id="model-select"><option>loading...</option></select>
|
|
65530
|
+
<button class="key-btn" id="key-btn" title="Set API key">key</button>
|
|
65531
|
+
</div>
|
|
65532
|
+
|
|
65533
|
+
<div id="system-prompt-area">
|
|
65534
|
+
<textarea id="system-prompt" placeholder="System prompt (optional)"></textarea>
|
|
65535
|
+
</div>
|
|
65536
|
+
|
|
65537
|
+
<div id="tabs" style="display:flex;gap:0;background:#1e1e22;border-bottom:1px solid #2a2a30;padding:0 16px;flex-shrink:0">
|
|
65538
|
+
<button class="tab active" onclick="switchTab('chat')" id="tab-chat" style="background:none;border:none;border-bottom:2px solid #b2920a;color:#b2920a;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">chat</button>
|
|
65539
|
+
<button class="tab" onclick="switchTab('agent')" id="tab-agent" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">agent</button>
|
|
65540
|
+
<button class="tab" onclick="switchTab('jobs')" id="tab-jobs" style="background:none;border:none;border-bottom:2px solid transparent;color:#555;padding:6px 16px;font-family:inherit;font-size:0.7rem;cursor:pointer">jobs</button>
|
|
65541
|
+
</div>
|
|
65542
|
+
<div id="conversation"></div>
|
|
65543
|
+
<div id="agent-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
|
|
65544
|
+
<textarea id="agent-task" placeholder="Describe the task for the agent..." style="width:100%;min-height:80px;background:#2a2a30;border:1px solid #3a3a42;border-radius:3px;padding:8px 12px;color:#b0b0b0;font-family:inherit;font-size:0.82rem;resize:vertical;outline:none;margin-bottom:8px"></textarea>
|
|
65545
|
+
<div style="display:flex;gap:8px;margin-bottom:12px;align-items:center">
|
|
65546
|
+
<select id="agent-profile" style="background:#2a2a30;border:1px solid #3a3a42;color:#b0b0b0;padding:4px 8px;border-radius:3px;font-family:inherit;font-size:0.7rem"><option value="">no profile</option></select>
|
|
65547
|
+
<button onclick="submitAgentTask()" id="agent-submit" style="background:#2a2a30;border:1px solid #3a3a42;color:#b2920a;padding:6px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer">run task</button>
|
|
65548
|
+
<button onclick="abortAgentTask()" id="agent-abort" style="display:none;background:#2a2a30;border:1px solid #ff4444;color:#ff4444;padding:6px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer">abort</button>
|
|
65549
|
+
</div>
|
|
65550
|
+
<div id="agent-events" style="font-size:0.78rem;line-height:1.5"></div>
|
|
65551
|
+
</div>
|
|
65552
|
+
<div id="jobs-panel" style="display:none;flex:1;overflow-y:auto;padding:12px 16px">
|
|
65553
|
+
<div id="jobs-list" style="font-size:0.78rem"></div>
|
|
65554
|
+
</div>
|
|
65555
|
+
|
|
65556
|
+
<div id="footer">
|
|
65557
|
+
<span id="system-prompt-toggle" onclick="toggleSystemPrompt()">sys</span>
|
|
65558
|
+
<textarea id="input-area" placeholder="Type a message..." rows="1"></textarea>
|
|
65559
|
+
<button id="send-btn" onclick="sendMessage()">send</button>
|
|
65560
|
+
</div>
|
|
65561
|
+
|
|
65562
|
+
<div id="key-modal">
|
|
65563
|
+
<div class="modal">
|
|
65564
|
+
<h3>API Key</h3>
|
|
65565
|
+
<input id="key-input" type="password" placeholder="Bearer token (leave empty if auth disabled)">
|
|
65566
|
+
<div>
|
|
65567
|
+
<button onclick="saveKey()">save</button>
|
|
65568
|
+
<button onclick="clearKey()">clear</button>
|
|
65569
|
+
<button onclick="closeKeyModal()">cancel</button>
|
|
65570
|
+
</div>
|
|
65571
|
+
</div>
|
|
65572
|
+
</div>
|
|
65573
|
+
|
|
65574
|
+
<script>
|
|
65575
|
+
const conv = document.getElementById('conversation');
|
|
65576
|
+
const input = document.getElementById('input-area');
|
|
65577
|
+
const sendBtn = document.getElementById('send-btn');
|
|
65578
|
+
const modelSelect = document.getElementById('model-select');
|
|
65579
|
+
const statusEl = document.getElementById('status');
|
|
65580
|
+
let apiKey = localStorage.getItem('oa-api-key') || '';
|
|
65581
|
+
let streaming = false;
|
|
65582
|
+
let messages = [];
|
|
65583
|
+
|
|
65584
|
+
// Auto-resize textarea
|
|
65585
|
+
input.addEventListener('input', () => {
|
|
65586
|
+
input.style.height = 'auto';
|
|
65587
|
+
input.style.height = Math.min(input.scrollHeight, 120) + 'px';
|
|
65588
|
+
});
|
|
65589
|
+
input.addEventListener('keydown', (e) => {
|
|
65590
|
+
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
|
|
65591
|
+
});
|
|
65592
|
+
|
|
65593
|
+
function headers() {
|
|
65594
|
+
const h = { 'Content-Type': 'application/json' };
|
|
65595
|
+
if (apiKey) h['Authorization'] = 'Bearer ' + apiKey;
|
|
65596
|
+
return h;
|
|
65597
|
+
}
|
|
65598
|
+
|
|
65599
|
+
// Health check
|
|
65600
|
+
async function checkHealth() {
|
|
65601
|
+
try {
|
|
65602
|
+
const r = await fetch('/health');
|
|
65603
|
+
const d = await r.json();
|
|
65604
|
+
statusEl.textContent = 'connected (' + d.version + ')';
|
|
65605
|
+
statusEl.className = 'status live';
|
|
65606
|
+
} catch {
|
|
65607
|
+
statusEl.textContent = 'disconnected';
|
|
65608
|
+
statusEl.className = 'status';
|
|
65609
|
+
}
|
|
65610
|
+
}
|
|
65611
|
+
|
|
65612
|
+
// Load models
|
|
65613
|
+
async function loadModels() {
|
|
65614
|
+
try {
|
|
65615
|
+
const r = await fetch('/v1/models', { headers: headers() });
|
|
65616
|
+
const d = await r.json();
|
|
65617
|
+
modelSelect.innerHTML = '';
|
|
65618
|
+
for (const m of (d.data || [])) {
|
|
65619
|
+
const opt = document.createElement('option');
|
|
65620
|
+
// Strip "local/" prefix for cleaner display
|
|
65621
|
+
opt.value = m.id.replace(/^local\\//, '');
|
|
65622
|
+
opt.textContent = m.id.replace(/^local\\//, '');
|
|
65623
|
+
modelSelect.appendChild(opt);
|
|
65624
|
+
}
|
|
65625
|
+
// Default to first model with "9b" in name, or first overall
|
|
65626
|
+
const preferred = Array.from(modelSelect.options).find(o => /9b/i.test(o.value));
|
|
65627
|
+
if (preferred) modelSelect.value = preferred.value;
|
|
65628
|
+
} catch { modelSelect.innerHTML = '<option>error loading models</option>'; }
|
|
65629
|
+
}
|
|
65630
|
+
|
|
65631
|
+
function addMessage(role, content) {
|
|
65632
|
+
const div = document.createElement('div');
|
|
65633
|
+
div.className = 'msg ' + role;
|
|
65634
|
+
div.innerHTML = renderMarkdown(content);
|
|
65635
|
+
// Copy button
|
|
65636
|
+
if (role === 'assistant') {
|
|
65637
|
+
const actions = document.createElement('div');
|
|
65638
|
+
actions.className = 'msg-actions';
|
|
65639
|
+
const copyBtn = document.createElement('button');
|
|
65640
|
+
copyBtn.textContent = 'copy';
|
|
65641
|
+
copyBtn.onclick = () => { navigator.clipboard.writeText(content); copyBtn.textContent = 'copied'; setTimeout(() => copyBtn.textContent = 'copy', 1500); };
|
|
65642
|
+
actions.appendChild(copyBtn);
|
|
65643
|
+
div.appendChild(actions);
|
|
65644
|
+
}
|
|
65645
|
+
conv.appendChild(div);
|
|
65646
|
+
conv.scrollTop = conv.scrollHeight;
|
|
65647
|
+
return div;
|
|
65648
|
+
}
|
|
65649
|
+
|
|
65650
|
+
function renderMarkdown(text) {
|
|
65651
|
+
// Code blocks
|
|
65652
|
+
text = text.replace(/\`\`\`(\\w*)\\n([\\s\\S]*?)\`\`\`/g, (_, lang, code) => {
|
|
65653
|
+
const id = 'cb' + Math.random().toString(36).slice(2,8);
|
|
65654
|
+
return '<pre><button class="copy-btn" onclick="navigator.clipboard.writeText(document.getElementById(\\'' + id + '\\').textContent);this.textContent=\\'copied\\';setTimeout(()=>this.textContent=\\'copy\\',1500)">copy</button><code id="' + id + '">' + escHtml(code) + '</code></pre>';
|
|
65655
|
+
});
|
|
65656
|
+
// Inline code
|
|
65657
|
+
text = text.replace(/\`([^\`]+)\`/g, '<code>$1</code>');
|
|
65658
|
+
// Bold
|
|
65659
|
+
text = text.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>');
|
|
65660
|
+
return text;
|
|
65661
|
+
}
|
|
65662
|
+
|
|
65663
|
+
function escHtml(s) {
|
|
65664
|
+
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
65665
|
+
}
|
|
65666
|
+
|
|
65667
|
+
async function sendMessage() {
|
|
65668
|
+
const text = input.value.trim();
|
|
65669
|
+
if (!text || streaming) return;
|
|
65670
|
+
input.value = '';
|
|
65671
|
+
input.style.height = 'auto';
|
|
65672
|
+
|
|
65673
|
+
// Add user message
|
|
65674
|
+
messages.push({ role: 'user', content: text });
|
|
65675
|
+
addMessage('user', text);
|
|
65676
|
+
|
|
65677
|
+
// System prompt
|
|
65678
|
+
const sysPrompt = document.getElementById('system-prompt').value.trim();
|
|
65679
|
+
|
|
65680
|
+
streaming = true;
|
|
65681
|
+
sendBtn.disabled = true;
|
|
65682
|
+
sendBtn.textContent = '...';
|
|
65683
|
+
|
|
65684
|
+
const msgDiv = addMessage('assistant', '');
|
|
65685
|
+
let fullContent = '';
|
|
65686
|
+
|
|
65687
|
+
try {
|
|
65688
|
+
const body = {
|
|
65689
|
+
model: modelSelect.value,
|
|
65690
|
+
messages: [
|
|
65691
|
+
...(sysPrompt ? [{ role: 'system', content: sysPrompt }] : []),
|
|
65692
|
+
...messages,
|
|
65693
|
+
],
|
|
65694
|
+
stream: true,
|
|
65695
|
+
max_tokens: 4096,
|
|
65696
|
+
};
|
|
65697
|
+
|
|
65698
|
+
const response = await fetch('/v1/chat/completions', {
|
|
65699
|
+
method: 'POST',
|
|
65700
|
+
headers: headers(),
|
|
65701
|
+
body: JSON.stringify(body),
|
|
65702
|
+
});
|
|
65703
|
+
|
|
65704
|
+
const reader = response.body.getReader();
|
|
65705
|
+
const decoder = new TextDecoder();
|
|
65706
|
+
let buffer = '';
|
|
65707
|
+
|
|
65708
|
+
while (true) {
|
|
65709
|
+
const { done, value } = await reader.read();
|
|
65710
|
+
if (done) break;
|
|
65711
|
+
buffer += decoder.decode(value, { stream: true });
|
|
65712
|
+
|
|
65713
|
+
const lines = buffer.split('\\n');
|
|
65714
|
+
buffer = lines.pop() || '';
|
|
65715
|
+
|
|
65716
|
+
for (const line of lines) {
|
|
65717
|
+
if (!line.startsWith('data: ')) continue;
|
|
65718
|
+
const data = line.slice(6);
|
|
65719
|
+
if (data === '[DONE]') continue;
|
|
65720
|
+
try {
|
|
65721
|
+
const chunk = JSON.parse(data);
|
|
65722
|
+
const delta = chunk.choices?.[0]?.delta?.content || '';
|
|
65723
|
+
if (delta) {
|
|
65724
|
+
fullContent += delta;
|
|
65725
|
+
msgDiv.innerHTML = renderMarkdown(fullContent);
|
|
65726
|
+
conv.scrollTop = conv.scrollHeight;
|
|
65727
|
+
}
|
|
65728
|
+
} catch {}
|
|
65729
|
+
}
|
|
65730
|
+
}
|
|
65731
|
+
|
|
65732
|
+
messages.push({ role: 'assistant', content: fullContent });
|
|
65733
|
+
// Re-render with copy button
|
|
65734
|
+
msgDiv.innerHTML = renderMarkdown(fullContent);
|
|
65735
|
+
const actions = document.createElement('div');
|
|
65736
|
+
actions.className = 'msg-actions';
|
|
65737
|
+
const copyBtn = document.createElement('button');
|
|
65738
|
+
copyBtn.textContent = 'copy';
|
|
65739
|
+
copyBtn.onclick = () => { navigator.clipboard.writeText(fullContent); copyBtn.textContent = 'copied'; setTimeout(() => copyBtn.textContent = 'copy', 1500); };
|
|
65740
|
+
actions.appendChild(copyBtn);
|
|
65741
|
+
msgDiv.appendChild(actions);
|
|
65742
|
+
} catch (err) {
|
|
65743
|
+
msgDiv.innerHTML = '<span style="color:#ff4444">Error: ' + escHtml(err.message) + '</span>';
|
|
65744
|
+
}
|
|
65745
|
+
|
|
65746
|
+
streaming = false;
|
|
65747
|
+
sendBtn.disabled = false;
|
|
65748
|
+
sendBtn.textContent = 'send';
|
|
65749
|
+
conv.scrollTop = conv.scrollHeight;
|
|
65750
|
+
}
|
|
65751
|
+
|
|
65752
|
+
function toggleSystemPrompt() {
|
|
65753
|
+
const area = document.getElementById('system-prompt-area');
|
|
65754
|
+
area.style.display = area.style.display === 'none' ? 'block' : 'none';
|
|
65755
|
+
}
|
|
65756
|
+
|
|
65757
|
+
// Key modal
|
|
65758
|
+
document.getElementById('key-btn').onclick = () => {
|
|
65759
|
+
document.getElementById('key-modal').classList.add('visible');
|
|
65760
|
+
document.getElementById('key-input').value = apiKey;
|
|
65761
|
+
};
|
|
65762
|
+
function saveKey() {
|
|
65763
|
+
apiKey = document.getElementById('key-input').value;
|
|
65764
|
+
localStorage.setItem('oa-api-key', apiKey);
|
|
65765
|
+
closeKeyModal();
|
|
65766
|
+
loadModels();
|
|
65767
|
+
}
|
|
65768
|
+
function clearKey() {
|
|
65769
|
+
apiKey = '';
|
|
65770
|
+
localStorage.removeItem('oa-api-key');
|
|
65771
|
+
document.getElementById('key-input').value = '';
|
|
65772
|
+
closeKeyModal();
|
|
65773
|
+
loadModels();
|
|
65774
|
+
}
|
|
65775
|
+
function closeKeyModal() {
|
|
65776
|
+
document.getElementById('key-modal').classList.remove('visible');
|
|
65777
|
+
}
|
|
65778
|
+
|
|
65779
|
+
// Tab switching
|
|
65780
|
+
function switchTab(tab) {
|
|
65781
|
+
document.getElementById('conversation').style.display = tab === 'chat' ? 'flex' : 'none';
|
|
65782
|
+
document.getElementById('agent-panel').style.display = tab === 'agent' ? 'block' : 'none';
|
|
65783
|
+
document.getElementById('jobs-panel').style.display = tab === 'jobs' ? 'block' : 'none';
|
|
65784
|
+
document.getElementById('footer').style.display = tab === 'chat' ? 'flex' : 'none';
|
|
65785
|
+
document.querySelectorAll('.tab').forEach(t => { t.style.borderBottomColor = 'transparent'; t.style.color = '#555'; });
|
|
65786
|
+
const active = document.getElementById('tab-' + tab);
|
|
65787
|
+
if (active) { active.style.borderBottomColor = '#b2920a'; active.style.color = '#b2920a'; }
|
|
65788
|
+
if (tab === 'jobs') loadJobs();
|
|
65789
|
+
if (tab === 'agent') loadProfiles();
|
|
65790
|
+
}
|
|
65791
|
+
|
|
65792
|
+
// Agent task
|
|
65793
|
+
let currentRunId = null;
|
|
65794
|
+
async function loadProfiles() {
|
|
65795
|
+
try {
|
|
65796
|
+
const r = await fetch('/v1/profiles', { headers: headers() });
|
|
65797
|
+
const d = await r.json();
|
|
65798
|
+
const sel = document.getElementById('agent-profile');
|
|
65799
|
+
sel.innerHTML = '<option value="">no profile</option>';
|
|
65800
|
+
for (const p of (d.profiles || [])) {
|
|
65801
|
+
const opt = document.createElement('option');
|
|
65802
|
+
opt.value = p.name; opt.textContent = p.name + (p.encrypted ? ' (encrypted)' : '');
|
|
65803
|
+
sel.appendChild(opt);
|
|
65804
|
+
}
|
|
65805
|
+
} catch {}
|
|
65806
|
+
}
|
|
65807
|
+
|
|
65808
|
+
async function submitAgentTask() {
|
|
65809
|
+
const task = document.getElementById('agent-task').value.trim();
|
|
65810
|
+
if (!task) return;
|
|
65811
|
+
const eventsDiv = document.getElementById('agent-events');
|
|
65812
|
+
eventsDiv.innerHTML = '';
|
|
65813
|
+
document.getElementById('agent-submit').style.display = 'none';
|
|
65814
|
+
document.getElementById('agent-abort').style.display = 'inline-block';
|
|
65815
|
+
|
|
65816
|
+
const profile = document.getElementById('agent-profile').value;
|
|
65817
|
+
try {
|
|
65818
|
+
const resp = await fetch('/v1/run', {
|
|
65819
|
+
method: 'POST',
|
|
65820
|
+
headers: headers(),
|
|
65821
|
+
body: JSON.stringify({
|
|
65822
|
+
task, model: modelSelect.value, stream: true,
|
|
65823
|
+
...(profile ? { profile } : {}),
|
|
65824
|
+
}),
|
|
65825
|
+
});
|
|
65826
|
+
|
|
65827
|
+
const reader = resp.body.getReader();
|
|
65828
|
+
const decoder = new TextDecoder();
|
|
65829
|
+
let buffer = '';
|
|
65830
|
+
|
|
65831
|
+
while (true) {
|
|
65832
|
+
const { done, value } = await reader.read();
|
|
65833
|
+
if (done) break;
|
|
65834
|
+
buffer += decoder.decode(value, { stream: true });
|
|
65835
|
+
const lines = buffer.split('\\n');
|
|
65836
|
+
buffer = lines.pop() || '';
|
|
65837
|
+
|
|
65838
|
+
for (const line of lines) {
|
|
65839
|
+
if (!line.startsWith('data: ')) continue;
|
|
65840
|
+
const data = line.slice(6);
|
|
65841
|
+
if (data === '[DONE]') continue;
|
|
65842
|
+
try {
|
|
65843
|
+
const evt = JSON.parse(data);
|
|
65844
|
+
const div = document.createElement('div');
|
|
65845
|
+
div.style.padding = '2px 0';
|
|
65846
|
+
if (evt.type === 'run_started') {
|
|
65847
|
+
currentRunId = evt.run_id;
|
|
65848
|
+
div.innerHTML = '<span style="color:#b2920a">Task started</span> \u2014 run_id: ' + evt.run_id;
|
|
65849
|
+
} else if (evt.type === 'run_completed') {
|
|
65850
|
+
div.innerHTML = '<span style="color:' + (evt.exit_code === 0 ? '#4ec94e' : '#ff4444') + '">Task ' + (evt.exit_code === 0 ? 'completed' : 'failed') + '</span> \u2014 exit: ' + evt.exit_code;
|
|
65851
|
+
document.getElementById('agent-submit').style.display = 'inline-block';
|
|
65852
|
+
document.getElementById('agent-abort').style.display = 'none';
|
|
65853
|
+
currentRunId = null;
|
|
65854
|
+
} else if (evt.type === 'stdout') {
|
|
65855
|
+
div.style.color = '#888';
|
|
65856
|
+
div.style.fontFamily = 'inherit';
|
|
65857
|
+
div.textContent = evt.data?.slice?.(0, 200) || '';
|
|
65858
|
+
}
|
|
65859
|
+
eventsDiv.appendChild(div);
|
|
65860
|
+
eventsDiv.scrollTop = eventsDiv.scrollHeight;
|
|
65861
|
+
} catch {}
|
|
65862
|
+
}
|
|
65863
|
+
}
|
|
65864
|
+
} catch (err) {
|
|
65865
|
+
eventsDiv.innerHTML += '<div style="color:#ff4444">Error: ' + escHtml(err.message) + '</div>';
|
|
65866
|
+
}
|
|
65867
|
+
document.getElementById('agent-submit').style.display = 'inline-block';
|
|
65868
|
+
document.getElementById('agent-abort').style.display = 'none';
|
|
65869
|
+
}
|
|
65870
|
+
|
|
65871
|
+
async function abortAgentTask() {
|
|
65872
|
+
if (!currentRunId) return;
|
|
65873
|
+
try { await fetch('/v1/runs/' + currentRunId, { method: 'DELETE', headers: headers() }); } catch {}
|
|
65874
|
+
}
|
|
65875
|
+
|
|
65876
|
+
async function loadJobs() {
|
|
65877
|
+
const list = document.getElementById('jobs-list');
|
|
65878
|
+
try {
|
|
65879
|
+
const r = await fetch('/v1/runs', { headers: headers() });
|
|
65880
|
+
const d = await r.json();
|
|
65881
|
+
if (!d.runs?.length) { list.innerHTML = '<div style="color:#555">No jobs yet</div>'; return; }
|
|
65882
|
+
let html = '<table style="width:100%;border-collapse:collapse">';
|
|
65883
|
+
html += '<tr style="color:#b2920a;font-size:0.65rem"><th style="text-align:left;padding:4px">ID</th><th>Status</th><th>Task</th><th>Duration</th></tr>';
|
|
65884
|
+
for (const j of d.runs.slice(0, 20)) {
|
|
65885
|
+
const color = j.status === 'completed' ? '#4ec94e' : j.status === 'running' ? '#b2920a' : '#ff4444';
|
|
65886
|
+
const dur = j.durationMs ? (j.durationMs / 1000).toFixed(1) + 's' : '\u2014';
|
|
65887
|
+
html += '<tr style="border-top:1px solid #2a2a30"><td style="padding:4px;color:#888">' + (j.id||'').slice(0,12) + '</td>';
|
|
65888
|
+
html += '<td style="color:' + color + '">' + (j.status||'?') + '</td>';
|
|
65889
|
+
html += '<td style="color:#b0b0b0;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml((j.task||'').slice(0,60)) + '</td>';
|
|
65890
|
+
html += '<td style="color:#555">' + dur + '</td></tr>';
|
|
65891
|
+
}
|
|
65892
|
+
html += '</table>';
|
|
65893
|
+
list.innerHTML = html;
|
|
65894
|
+
} catch { list.innerHTML = '<div style="color:#ff4444">Failed to load jobs</div>'; }
|
|
65895
|
+
}
|
|
65896
|
+
|
|
65897
|
+
// Init
|
|
65898
|
+
checkHealth();
|
|
65899
|
+
loadModels();
|
|
65900
|
+
setInterval(checkHealth, 30000);
|
|
65901
|
+
input.focus();
|
|
65902
|
+
</script>
|
|
65903
|
+
</body>
|
|
65904
|
+
</html>`;
|
|
65905
|
+
}
|
|
65906
|
+
var init_web_ui = __esm({
|
|
65907
|
+
"packages/cli/dist/api/web-ui.js"() {
|
|
65908
|
+
"use strict";
|
|
65909
|
+
}
|
|
65910
|
+
});
|
|
65911
|
+
|
|
65912
|
+
// packages/cli/dist/api/logger.js
|
|
65913
|
+
function log(level, fields) {
|
|
65914
|
+
if (LEVEL_NUM[level] > LEVEL_NUM[configuredLevel])
|
|
65915
|
+
return;
|
|
65916
|
+
if (useJson) {
|
|
65917
|
+
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, ...fields };
|
|
65918
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
65919
|
+
} else {
|
|
65920
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().split("T")[1]?.slice(0, 8) ?? "";
|
|
65921
|
+
const parts = Object.entries(fields).map(([k, v]) => `${k}=${v}`).join(" ");
|
|
65922
|
+
process.stderr.write(`[${ts}] ${level.toUpperCase()} ${parts}
|
|
65923
|
+
`);
|
|
65924
|
+
}
|
|
65925
|
+
}
|
|
65926
|
+
function logRequest(fields) {
|
|
65927
|
+
log("info", fields);
|
|
65928
|
+
}
|
|
65929
|
+
var LEVEL_NUM, configuredLevel, useJson;
|
|
65930
|
+
var init_logger = __esm({
|
|
65931
|
+
"packages/cli/dist/api/logger.js"() {
|
|
65932
|
+
"use strict";
|
|
65933
|
+
LEVEL_NUM = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
65934
|
+
configuredLevel = (() => {
|
|
65935
|
+
const env = (process.env["OA_LOG_LEVEL"] || "info").toLowerCase();
|
|
65936
|
+
if (env in LEVEL_NUM)
|
|
65937
|
+
return env;
|
|
65938
|
+
return "info";
|
|
65939
|
+
})();
|
|
65940
|
+
useJson = (process.env["OA_LOG_FORMAT"] || "json").toLowerCase() !== "text";
|
|
65941
|
+
}
|
|
65942
|
+
});
|
|
65943
|
+
|
|
65944
|
+
// packages/cli/dist/api/profiles.js
|
|
65945
|
+
import { existsSync as existsSync53, readFileSync as readFileSync42, writeFileSync as writeFileSync25, mkdirSync as mkdirSync27, readdirSync as readdirSync20, unlinkSync as unlinkSync12 } from "node:fs";
|
|
65946
|
+
import { join as join70 } from "node:path";
|
|
65244
65947
|
import { homedir as homedir18 } from "node:os";
|
|
65245
65948
|
import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes15, scryptSync as scryptSync3, createHash as createHash5 } from "node:crypto";
|
|
65246
65949
|
function globalProfileDir() {
|
|
65247
|
-
return
|
|
65950
|
+
return join70(homedir18(), ".open-agents", "profiles");
|
|
65248
65951
|
}
|
|
65249
65952
|
function projectProfileDir(projectDir) {
|
|
65250
|
-
return
|
|
65953
|
+
return join70(projectDir || process.cwd(), ".oa", "profiles");
|
|
65251
65954
|
}
|
|
65252
65955
|
function listProfiles(projectDir) {
|
|
65253
65956
|
const result = [];
|
|
65254
65957
|
const seen = /* @__PURE__ */ new Set();
|
|
65255
65958
|
const projDir = projectProfileDir(projectDir);
|
|
65256
|
-
if (
|
|
65959
|
+
if (existsSync53(projDir)) {
|
|
65257
65960
|
for (const f of readdirSync20(projDir).filter((f2) => f2.endsWith(".json"))) {
|
|
65258
65961
|
try {
|
|
65259
|
-
const raw = JSON.parse(
|
|
65962
|
+
const raw = JSON.parse(readFileSync42(join70(projDir, f), "utf8"));
|
|
65260
65963
|
const name = f.replace(".json", "");
|
|
65261
65964
|
seen.add(name);
|
|
65262
65965
|
result.push({
|
|
@@ -65270,13 +65973,13 @@ function listProfiles(projectDir) {
|
|
|
65270
65973
|
}
|
|
65271
65974
|
}
|
|
65272
65975
|
const globDir = globalProfileDir();
|
|
65273
|
-
if (
|
|
65976
|
+
if (existsSync53(globDir)) {
|
|
65274
65977
|
for (const f of readdirSync20(globDir).filter((f2) => f2.endsWith(".json"))) {
|
|
65275
65978
|
const name = f.replace(".json", "");
|
|
65276
65979
|
if (seen.has(name))
|
|
65277
65980
|
continue;
|
|
65278
65981
|
try {
|
|
65279
|
-
const raw = JSON.parse(
|
|
65982
|
+
const raw = JSON.parse(readFileSync42(join70(globDir, f), "utf8"));
|
|
65280
65983
|
result.push({
|
|
65281
65984
|
name,
|
|
65282
65985
|
description: raw.description || "",
|
|
@@ -65291,12 +65994,12 @@ function listProfiles(projectDir) {
|
|
|
65291
65994
|
}
|
|
65292
65995
|
function loadProfile(name, password, projectDir) {
|
|
65293
65996
|
const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
65294
|
-
const projPath =
|
|
65295
|
-
const globPath =
|
|
65296
|
-
const filePath =
|
|
65997
|
+
const projPath = join70(projectProfileDir(projectDir), `${sanitized}.json`);
|
|
65998
|
+
const globPath = join70(globalProfileDir(), `${sanitized}.json`);
|
|
65999
|
+
const filePath = existsSync53(projPath) ? projPath : existsSync53(globPath) ? globPath : null;
|
|
65297
66000
|
if (!filePath)
|
|
65298
66001
|
return null;
|
|
65299
|
-
const raw = JSON.parse(
|
|
66002
|
+
const raw = JSON.parse(readFileSync42(filePath, "utf8"));
|
|
65300
66003
|
if (raw.encrypted === true) {
|
|
65301
66004
|
if (!password)
|
|
65302
66005
|
return null;
|
|
@@ -65306,9 +66009,9 @@ function loadProfile(name, password, projectDir) {
|
|
|
65306
66009
|
}
|
|
65307
66010
|
function saveProfile(profile, password, scope = "global", projectDir) {
|
|
65308
66011
|
const dir = scope === "project" ? projectProfileDir(projectDir) : globalProfileDir();
|
|
65309
|
-
|
|
66012
|
+
mkdirSync27(dir, { recursive: true });
|
|
65310
66013
|
const sanitized = profile.name.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
65311
|
-
const filePath =
|
|
66014
|
+
const filePath = join70(dir, `${sanitized}.json`);
|
|
65312
66015
|
profile.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
65313
66016
|
if (password) {
|
|
65314
66017
|
const encrypted = encryptProfile(profile, password);
|
|
@@ -65321,8 +66024,8 @@ function saveProfile(profile, password, scope = "global", projectDir) {
|
|
|
65321
66024
|
function deleteProfile(name, scope = "global", projectDir) {
|
|
65322
66025
|
const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
65323
66026
|
const dir = scope === "project" ? projectProfileDir(projectDir) : globalProfileDir();
|
|
65324
|
-
const filePath =
|
|
65325
|
-
if (
|
|
66027
|
+
const filePath = join70(dir, `${sanitized}.json`);
|
|
66028
|
+
if (existsSync53(filePath)) {
|
|
65326
66029
|
unlinkSync12(filePath);
|
|
65327
66030
|
return true;
|
|
65328
66031
|
}
|
|
@@ -65415,22 +66118,22 @@ import * as http from "node:http";
|
|
|
65415
66118
|
import * as https from "node:https";
|
|
65416
66119
|
import { createRequire as createRequire3 } from "node:module";
|
|
65417
66120
|
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
65418
|
-
import { dirname as dirname21, join as
|
|
66121
|
+
import { dirname as dirname21, join as join71, resolve as resolve31 } from "node:path";
|
|
65419
66122
|
import { spawn as spawn21 } from "node:child_process";
|
|
65420
|
-
import { mkdirSync as
|
|
65421
|
-
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
66123
|
+
import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync26, readFileSync as readFileSync43, readdirSync as readdirSync21, existsSync as existsSync54 } from "node:fs";
|
|
66124
|
+
import { randomBytes as randomBytes16, randomUUID as randomUUID4 } from "node:crypto";
|
|
65422
66125
|
function getVersion3() {
|
|
65423
66126
|
try {
|
|
65424
66127
|
const require2 = createRequire3(import.meta.url);
|
|
65425
66128
|
const thisDir = dirname21(fileURLToPath13(import.meta.url));
|
|
65426
66129
|
const candidates = [
|
|
65427
|
-
|
|
65428
|
-
|
|
65429
|
-
|
|
66130
|
+
join71(thisDir, "..", "package.json"),
|
|
66131
|
+
join71(thisDir, "..", "..", "package.json"),
|
|
66132
|
+
join71(thisDir, "..", "..", "..", "package.json")
|
|
65430
66133
|
];
|
|
65431
66134
|
for (const pkgPath of candidates) {
|
|
65432
66135
|
try {
|
|
65433
|
-
if (!
|
|
66136
|
+
if (!existsSync54(pkgPath))
|
|
65434
66137
|
continue;
|
|
65435
66138
|
const pkg = require2(pkgPath);
|
|
65436
66139
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
|
|
@@ -65613,27 +66316,33 @@ function formatMetrics() {
|
|
|
65613
66316
|
}
|
|
65614
66317
|
function jsonResponse(res, status, body) {
|
|
65615
66318
|
const payload = JSON.stringify(body);
|
|
65616
|
-
res.writeHead(status, {
|
|
65617
|
-
"Content-Type": "application/json",
|
|
65618
|
-
"Access-Control-Allow-Origin": "*",
|
|
65619
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
65620
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
65621
|
-
});
|
|
66319
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
65622
66320
|
res.end(payload);
|
|
65623
66321
|
}
|
|
65624
66322
|
function textResponse(res, status, body, contentType = "text/plain") {
|
|
65625
|
-
res.writeHead(status, {
|
|
65626
|
-
"Content-Type": contentType,
|
|
65627
|
-
"Access-Control-Allow-Origin": "*",
|
|
65628
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
65629
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
65630
|
-
});
|
|
66323
|
+
res.writeHead(status, { "Content-Type": contentType });
|
|
65631
66324
|
res.end(body);
|
|
65632
66325
|
}
|
|
65633
|
-
function
|
|
65634
|
-
|
|
66326
|
+
function isOriginAllowed(origin) {
|
|
66327
|
+
if (!origin)
|
|
66328
|
+
return true;
|
|
66329
|
+
if (_corsOrigins.includes("*"))
|
|
66330
|
+
return true;
|
|
66331
|
+
if (_corsLocalOnly)
|
|
66332
|
+
return /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin);
|
|
66333
|
+
return _corsOrigins.some((p) => origin === p || p.includes("*") && new RegExp("^" + p.replace(/\*/g, ".*") + "$").test(origin));
|
|
66334
|
+
}
|
|
66335
|
+
function corsHeaders(req, res) {
|
|
66336
|
+
const origin = req.headers["origin"];
|
|
66337
|
+
if (!isOriginAllowed(origin)) {
|
|
66338
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
66339
|
+
res.end(JSON.stringify({ error: "Forbidden", message: `Origin '${origin}' not allowed. Set OA_CORS_ORIGINS to allow.` }));
|
|
66340
|
+
return false;
|
|
66341
|
+
}
|
|
66342
|
+
res.setHeader("Access-Control-Allow-Origin", origin || "*");
|
|
65635
66343
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
65636
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
66344
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Request-ID, X-Working-Directory, X-Tool-Profile, X-Profile-Password");
|
|
66345
|
+
return true;
|
|
65637
66346
|
}
|
|
65638
66347
|
function readBody(req) {
|
|
65639
66348
|
return new Promise((resolve36, reject) => {
|
|
@@ -65725,29 +66434,29 @@ function ollamaStream(ollamaUrl, path, method, body, onData, onEnd, onError) {
|
|
|
65725
66434
|
}
|
|
65726
66435
|
function jobsDir() {
|
|
65727
66436
|
const root = resolve31(process.cwd());
|
|
65728
|
-
const dir =
|
|
65729
|
-
|
|
66437
|
+
const dir = join71(root, ".oa", "jobs");
|
|
66438
|
+
mkdirSync28(dir, { recursive: true });
|
|
65730
66439
|
return dir;
|
|
65731
66440
|
}
|
|
65732
66441
|
function loadJob(id) {
|
|
65733
|
-
const file =
|
|
65734
|
-
if (!
|
|
66442
|
+
const file = join71(jobsDir(), `${id}.json`);
|
|
66443
|
+
if (!existsSync54(file))
|
|
65735
66444
|
return null;
|
|
65736
66445
|
try {
|
|
65737
|
-
return JSON.parse(
|
|
66446
|
+
return JSON.parse(readFileSync43(file, "utf-8"));
|
|
65738
66447
|
} catch {
|
|
65739
66448
|
return null;
|
|
65740
66449
|
}
|
|
65741
66450
|
}
|
|
65742
66451
|
function listJobs() {
|
|
65743
66452
|
const dir = jobsDir();
|
|
65744
|
-
if (!
|
|
66453
|
+
if (!existsSync54(dir))
|
|
65745
66454
|
return [];
|
|
65746
66455
|
const files = readdirSync21(dir).filter((f) => f.endsWith(".json")).sort();
|
|
65747
66456
|
const jobs = [];
|
|
65748
66457
|
for (const file of files) {
|
|
65749
66458
|
try {
|
|
65750
|
-
jobs.push(JSON.parse(
|
|
66459
|
+
jobs.push(JSON.parse(readFileSync43(join71(dir, file), "utf-8")));
|
|
65751
66460
|
} catch {
|
|
65752
66461
|
}
|
|
65753
66462
|
}
|
|
@@ -65874,10 +66583,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
|
|
|
65874
66583
|
res.writeHead(200, {
|
|
65875
66584
|
"Content-Type": "text/event-stream",
|
|
65876
66585
|
"Cache-Control": "no-cache",
|
|
65877
|
-
"Connection": "keep-alive"
|
|
65878
|
-
"Access-Control-Allow-Origin": "*",
|
|
65879
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
65880
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
66586
|
+
"Connection": "keep-alive"
|
|
65881
66587
|
});
|
|
65882
66588
|
ollamaStream(targetUrl, "/v1/chat/completions", "POST", payload, (chunk) => {
|
|
65883
66589
|
res.write(chunk);
|
|
@@ -65923,10 +66629,7 @@ async function handleV1ChatCompletions(req, res, ollamaUrl) {
|
|
|
65923
66629
|
res.writeHead(200, {
|
|
65924
66630
|
"Content-Type": "text/event-stream",
|
|
65925
66631
|
"Cache-Control": "no-cache",
|
|
65926
|
-
"Connection": "keep-alive"
|
|
65927
|
-
"Access-Control-Allow-Origin": "*",
|
|
65928
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
65929
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
66632
|
+
"Connection": "keep-alive"
|
|
65930
66633
|
});
|
|
65931
66634
|
const chatId = `chatcmpl-${randomBytes16(12).toString("hex")}`;
|
|
65932
66635
|
let buffer = "";
|
|
@@ -66140,8 +66843,8 @@ async function handleV1Run(req, res) {
|
|
|
66140
66843
|
if (workingDir) {
|
|
66141
66844
|
cwd4 = resolve31(workingDir);
|
|
66142
66845
|
} else if (isolate) {
|
|
66143
|
-
const wsDir =
|
|
66144
|
-
|
|
66846
|
+
const wsDir = join71(dir, "..", "workspaces", id);
|
|
66847
|
+
mkdirSync28(wsDir, { recursive: true });
|
|
66145
66848
|
cwd4 = wsDir;
|
|
66146
66849
|
} else {
|
|
66147
66850
|
cwd4 = resolve31(process.cwd());
|
|
@@ -66213,16 +66916,13 @@ async function handleV1Run(req, res) {
|
|
|
66213
66916
|
});
|
|
66214
66917
|
child.unref();
|
|
66215
66918
|
job.pid = child.pid ?? 0;
|
|
66216
|
-
writeFileSync26(
|
|
66919
|
+
writeFileSync26(join71(dir, `${id}.json`), JSON.stringify(job, null, 2));
|
|
66217
66920
|
runningProcesses.set(id, child);
|
|
66218
66921
|
if (streamMode) {
|
|
66219
66922
|
res.writeHead(200, {
|
|
66220
66923
|
"Content-Type": "text/event-stream",
|
|
66221
66924
|
"Cache-Control": "no-cache",
|
|
66222
|
-
"Connection": "keep-alive"
|
|
66223
|
-
"Access-Control-Allow-Origin": "*",
|
|
66224
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
66225
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
66925
|
+
"Connection": "keep-alive"
|
|
66226
66926
|
});
|
|
66227
66927
|
res.write(`data: ${JSON.stringify({ type: "run_started", run_id: id, pid: job.pid })}
|
|
66228
66928
|
|
|
@@ -66243,7 +66943,7 @@ async function handleV1Run(req, res) {
|
|
|
66243
66943
|
job.status = code === 0 ? "completed" : "failed";
|
|
66244
66944
|
job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
66245
66945
|
try {
|
|
66246
|
-
writeFileSync26(
|
|
66946
|
+
writeFileSync26(join71(dir, `${id}.json`), JSON.stringify(job, null, 2));
|
|
66247
66947
|
} catch {
|
|
66248
66948
|
}
|
|
66249
66949
|
runningProcesses.delete(id);
|
|
@@ -66274,7 +66974,7 @@ async function handleV1Run(req, res) {
|
|
|
66274
66974
|
job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
66275
66975
|
}
|
|
66276
66976
|
try {
|
|
66277
|
-
writeFileSync26(
|
|
66977
|
+
writeFileSync26(join71(dir, `${id}.json`), JSON.stringify(job, null, 2));
|
|
66278
66978
|
} catch {
|
|
66279
66979
|
}
|
|
66280
66980
|
runningProcesses.delete(id);
|
|
@@ -66315,7 +67015,7 @@ function handleV1RunsDelete(res, id) {
|
|
|
66315
67015
|
job.error = "Aborted via API";
|
|
66316
67016
|
const dir = jobsDir();
|
|
66317
67017
|
try {
|
|
66318
|
-
writeFileSync26(
|
|
67018
|
+
writeFileSync26(join71(dir, `${id}.json`), JSON.stringify(job, null, 2));
|
|
66319
67019
|
} catch {
|
|
66320
67020
|
}
|
|
66321
67021
|
runningProcesses.delete(id);
|
|
@@ -66459,17 +67159,18 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
|
|
|
66459
67159
|
const method = req.method ?? "GET";
|
|
66460
67160
|
const urlObj = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
66461
67161
|
const pathname = urlObj.pathname;
|
|
66462
|
-
|
|
66463
|
-
|
|
66464
|
-
|
|
66465
|
-
`);
|
|
66466
|
-
}
|
|
67162
|
+
const startMs = performance.now();
|
|
67163
|
+
const requestId = req.headers["x-request-id"] || randomUUID4();
|
|
67164
|
+
res.setHeader("X-Request-ID", requestId);
|
|
66467
67165
|
if (method === "OPTIONS") {
|
|
66468
|
-
corsHeaders(res)
|
|
67166
|
+
if (!corsHeaders(req, res))
|
|
67167
|
+
return;
|
|
66469
67168
|
res.writeHead(204);
|
|
66470
67169
|
res.end();
|
|
66471
67170
|
return;
|
|
66472
67171
|
}
|
|
67172
|
+
if (!corsHeaders(req, res))
|
|
67173
|
+
return;
|
|
66473
67174
|
let status = 200;
|
|
66474
67175
|
try {
|
|
66475
67176
|
if (pathname === "/health" && method === "GET") {
|
|
@@ -66492,6 +67193,11 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
|
|
|
66492
67193
|
handleMetrics(res);
|
|
66493
67194
|
return;
|
|
66494
67195
|
}
|
|
67196
|
+
if (pathname === "/" && method === "GET" && req.headers.accept?.includes("text/html")) {
|
|
67197
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
67198
|
+
res.end(getWebUI());
|
|
67199
|
+
return;
|
|
67200
|
+
}
|
|
66495
67201
|
if (pathname.startsWith("/v1/")) {
|
|
66496
67202
|
const isWrite = method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
|
|
66497
67203
|
const isRun = pathname === "/v1/run" || pathname.startsWith("/v1/runs");
|
|
@@ -66650,6 +67356,15 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
|
|
|
66650
67356
|
jsonResponse(res, deleted ? 200 : 404, deleted ? { status: "deleted", name } : { error: "Profile not found", name });
|
|
66651
67357
|
return;
|
|
66652
67358
|
}
|
|
67359
|
+
if (pathname === "/v1/audit" && method === "GET") {
|
|
67360
|
+
if (!checkAuth(req, res, "read"))
|
|
67361
|
+
return;
|
|
67362
|
+
const since = urlObj.searchParams.get("since") ?? void 0;
|
|
67363
|
+
const user = urlObj.searchParams.get("user") ?? void 0;
|
|
67364
|
+
const limit = parseInt(urlObj.searchParams.get("limit") ?? "100", 10);
|
|
67365
|
+
jsonResponse(res, 200, { records: queryAudit({ since, user, limit }) });
|
|
67366
|
+
return;
|
|
67367
|
+
}
|
|
66653
67368
|
status = 404;
|
|
66654
67369
|
jsonResponse(res, 404, { error: "Not found", path: pathname });
|
|
66655
67370
|
} catch (err) {
|
|
@@ -66661,10 +67376,31 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
|
|
|
66661
67376
|
});
|
|
66662
67377
|
} finally {
|
|
66663
67378
|
recordMetric(method, pathname, status);
|
|
67379
|
+
const latencyMs = Math.round(performance.now() - startMs);
|
|
67380
|
+
logRequest({
|
|
67381
|
+
requestId,
|
|
67382
|
+
method,
|
|
67383
|
+
path: pathname,
|
|
67384
|
+
status,
|
|
67385
|
+
latencyMs,
|
|
67386
|
+
user: req._authUser ?? "anonymous",
|
|
67387
|
+
scope: req._authScope ?? "none"
|
|
67388
|
+
});
|
|
67389
|
+
recordAudit({
|
|
67390
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
67391
|
+
requestId,
|
|
67392
|
+
method,
|
|
67393
|
+
path: pathname,
|
|
67394
|
+
status,
|
|
67395
|
+
user: req._authUser ?? "anonymous",
|
|
67396
|
+
scope: req._authScope ?? "none",
|
|
67397
|
+
latencyMs: Math.round(performance.now() - startMs),
|
|
67398
|
+
ip: req.socket?.remoteAddress ?? ""
|
|
67399
|
+
});
|
|
66664
67400
|
}
|
|
66665
67401
|
}
|
|
66666
67402
|
function startApiServer(options = {}) {
|
|
66667
|
-
const
|
|
67403
|
+
const log2 = options.quiet ? (_msg) => {
|
|
66668
67404
|
} : (msg) => process.stderr.write(msg);
|
|
66669
67405
|
let host = "127.0.0.1";
|
|
66670
67406
|
let port = 11435;
|
|
@@ -66687,7 +67423,26 @@ function startApiServer(options = {}) {
|
|
|
66687
67423
|
const verbose = options.verbose ?? false;
|
|
66688
67424
|
const config = loadConfig();
|
|
66689
67425
|
const ollamaUrl = options.ollamaUrl ?? config.backendUrl;
|
|
66690
|
-
const
|
|
67426
|
+
const cwd4 = process.cwd();
|
|
67427
|
+
initAuditLog(join71(cwd4, ".oa"));
|
|
67428
|
+
const tlsCert = process.env["OA_TLS_CERT"];
|
|
67429
|
+
const tlsKey = process.env["OA_TLS_KEY"];
|
|
67430
|
+
const useTls = !!(tlsCert && tlsKey);
|
|
67431
|
+
let tlsOpts = null;
|
|
67432
|
+
if (useTls) {
|
|
67433
|
+
try {
|
|
67434
|
+
tlsOpts = {
|
|
67435
|
+
cert: readFileSync43(resolve31(tlsCert)),
|
|
67436
|
+
key: readFileSync43(resolve31(tlsKey))
|
|
67437
|
+
};
|
|
67438
|
+
} catch (e) {
|
|
67439
|
+
log2(`
|
|
67440
|
+
ERROR: TLS cert/key could not be loaded: ${e.message}
|
|
67441
|
+
`);
|
|
67442
|
+
process.exit(1);
|
|
67443
|
+
}
|
|
67444
|
+
}
|
|
67445
|
+
const handler = (req, res) => {
|
|
66691
67446
|
handleRequest(req, res, ollamaUrl, verbose).catch((err) => {
|
|
66692
67447
|
metrics.totalErrors++;
|
|
66693
67448
|
try {
|
|
@@ -66698,12 +67453,13 @@ function startApiServer(options = {}) {
|
|
|
66698
67453
|
} catch {
|
|
66699
67454
|
}
|
|
66700
67455
|
});
|
|
66701
|
-
}
|
|
67456
|
+
};
|
|
67457
|
+
const server = useTls ? https.createServer(tlsOpts, handler) : http.createServer(handler);
|
|
66702
67458
|
let retried = false;
|
|
66703
67459
|
server.on("error", (err) => {
|
|
66704
67460
|
if (err.code === "EADDRINUSE" && !retried) {
|
|
66705
67461
|
retried = true;
|
|
66706
|
-
|
|
67462
|
+
log2(` Port ${port} in use \u2014 reclaiming...
|
|
66707
67463
|
`);
|
|
66708
67464
|
try {
|
|
66709
67465
|
const { execSync: es } = __require("node:child_process");
|
|
@@ -66729,10 +67485,10 @@ function startApiServer(options = {}) {
|
|
|
66729
67485
|
server.listen(port, host);
|
|
66730
67486
|
}, 2e3);
|
|
66731
67487
|
} else if (err.code === "EADDRINUSE" && retried) {
|
|
66732
|
-
|
|
67488
|
+
log2(` Port ${port} still in use after reclaim attempt \u2014 API server skipped (non-fatal).
|
|
66733
67489
|
`);
|
|
66734
67490
|
} else {
|
|
66735
|
-
|
|
67491
|
+
log2(` API server error (non-fatal): ${err.message}
|
|
66736
67492
|
`);
|
|
66737
67493
|
}
|
|
66738
67494
|
});
|
|
@@ -66753,43 +67509,44 @@ function startApiServer(options = {}) {
|
|
|
66753
67509
|
});
|
|
66754
67510
|
server.listen(port, host, () => {
|
|
66755
67511
|
const version = getVersion3();
|
|
66756
|
-
|
|
67512
|
+
log2(`
|
|
66757
67513
|
open-agents API server v${version}
|
|
66758
67514
|
`);
|
|
66759
|
-
|
|
67515
|
+
const proto = useTls ? "https" : "http";
|
|
67516
|
+
log2(` Listening on ${proto}://${host}:${port}
|
|
66760
67517
|
`);
|
|
66761
|
-
|
|
67518
|
+
log2(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
|
|
66762
67519
|
`);
|
|
66763
67520
|
if (process.env["OA_API_KEYS"]) {
|
|
66764
67521
|
const keyCount = process.env["OA_API_KEYS"].split(",").length;
|
|
66765
|
-
|
|
67522
|
+
log2(` Auth: ${keyCount} scoped key(s) (read/run/admin)
|
|
66766
67523
|
`);
|
|
66767
67524
|
} else if (process.env["OA_API_KEY"]) {
|
|
66768
|
-
|
|
67525
|
+
log2(` Auth: single Bearer token (admin scope)
|
|
66769
67526
|
`);
|
|
66770
67527
|
} else {
|
|
66771
|
-
|
|
67528
|
+
log2(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
|
|
66772
67529
|
`);
|
|
66773
67530
|
}
|
|
66774
|
-
|
|
67531
|
+
log2(` Discovering sponsors in background...
|
|
66775
67532
|
|
|
66776
67533
|
`);
|
|
66777
67534
|
refreshEndpointRegistry().then(() => {
|
|
66778
67535
|
if (endpointRegistry.length > 1) {
|
|
66779
|
-
|
|
67536
|
+
log2(` Sponsors: ${endpointRegistry.length - 1} endpoint(s) discovered
|
|
66780
67537
|
`);
|
|
66781
67538
|
for (const ep of endpointRegistry.slice(1)) {
|
|
66782
|
-
|
|
67539
|
+
log2(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
|
|
66783
67540
|
`);
|
|
66784
67541
|
}
|
|
66785
|
-
|
|
67542
|
+
log2(`
|
|
66786
67543
|
`);
|
|
66787
67544
|
}
|
|
66788
67545
|
}).catch(() => {
|
|
66789
67546
|
});
|
|
66790
67547
|
});
|
|
66791
67548
|
const shutdown = () => {
|
|
66792
|
-
|
|
67549
|
+
log2("\n Shutting down API server...\n");
|
|
66793
67550
|
for (const [id, child] of runningProcesses) {
|
|
66794
67551
|
if (!child.killed) {
|
|
66795
67552
|
child.kill("SIGTERM");
|
|
@@ -66797,7 +67554,7 @@ function startApiServer(options = {}) {
|
|
|
66797
67554
|
runningProcesses.delete(id);
|
|
66798
67555
|
}
|
|
66799
67556
|
server.close(() => {
|
|
66800
|
-
|
|
67557
|
+
log2(" Server stopped.\n");
|
|
66801
67558
|
process.exit(0);
|
|
66802
67559
|
});
|
|
66803
67560
|
setTimeout(() => process.exit(1), 5e3).unref();
|
|
@@ -66816,11 +67573,14 @@ async function apiServeCommand(opts, config) {
|
|
|
66816
67573
|
server.on("close", resolve36);
|
|
66817
67574
|
});
|
|
66818
67575
|
}
|
|
66819
|
-
var endpointRegistry, modelRouteMap, endpointUsage, metrics, startedAt, runningProcesses;
|
|
67576
|
+
var endpointRegistry, modelRouteMap, endpointUsage, metrics, startedAt, _corsOrigins, _corsLocalOnly, runningProcesses;
|
|
66820
67577
|
var init_serve = __esm({
|
|
66821
67578
|
"packages/cli/dist/api/serve.js"() {
|
|
66822
67579
|
"use strict";
|
|
66823
67580
|
init_config();
|
|
67581
|
+
init_audit_log();
|
|
67582
|
+
init_web_ui();
|
|
67583
|
+
init_logger();
|
|
66824
67584
|
init_oa_directory();
|
|
66825
67585
|
init_render();
|
|
66826
67586
|
init_profiles();
|
|
@@ -66834,17 +67594,19 @@ var init_serve = __esm({
|
|
|
66834
67594
|
totalErrors: 0
|
|
66835
67595
|
};
|
|
66836
67596
|
startedAt = Date.now();
|
|
67597
|
+
_corsOrigins = (process.env["OA_CORS_ORIGINS"] || "").split(",").filter(Boolean);
|
|
67598
|
+
_corsLocalOnly = _corsOrigins.length === 0;
|
|
66837
67599
|
runningProcesses = /* @__PURE__ */ new Map();
|
|
66838
67600
|
}
|
|
66839
67601
|
});
|
|
66840
67602
|
|
|
66841
67603
|
// packages/cli/dist/tui/interactive.js
|
|
66842
67604
|
import { cwd } from "node:process";
|
|
66843
|
-
import { resolve as resolve32, join as
|
|
67605
|
+
import { resolve as resolve32, join as join72, dirname as dirname22, extname as extname11 } from "node:path";
|
|
66844
67606
|
import { createRequire as createRequire4 } from "node:module";
|
|
66845
67607
|
import { fileURLToPath as fileURLToPath14 } from "node:url";
|
|
66846
|
-
import { readFileSync as
|
|
66847
|
-
import { existsSync as
|
|
67608
|
+
import { readFileSync as readFileSync44, writeFileSync as writeFileSync27, appendFileSync as appendFileSync5, rmSync as rmSync3, readdirSync as readdirSync22, mkdirSync as mkdirSync29 } from "node:fs";
|
|
67609
|
+
import { existsSync as existsSync55 } from "node:fs";
|
|
66848
67610
|
import { execSync as execSync35 } from "node:child_process";
|
|
66849
67611
|
import { homedir as homedir19 } from "node:os";
|
|
66850
67612
|
function formatTimeAgo(date) {
|
|
@@ -66865,12 +67627,12 @@ function getVersion4() {
|
|
|
66865
67627
|
const require2 = createRequire4(import.meta.url);
|
|
66866
67628
|
const thisDir = dirname22(fileURLToPath14(import.meta.url));
|
|
66867
67629
|
const candidates = [
|
|
66868
|
-
|
|
66869
|
-
|
|
66870
|
-
|
|
67630
|
+
join72(thisDir, "..", "package.json"),
|
|
67631
|
+
join72(thisDir, "..", "..", "package.json"),
|
|
67632
|
+
join72(thisDir, "..", "..", "..", "package.json")
|
|
66871
67633
|
];
|
|
66872
67634
|
for (const pkgPath of candidates) {
|
|
66873
|
-
if (
|
|
67635
|
+
if (existsSync55(pkgPath)) {
|
|
66874
67636
|
const pkg = require2(pkgPath);
|
|
66875
67637
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
|
|
66876
67638
|
return pkg.version ?? "0.0.0";
|
|
@@ -67109,15 +67871,15 @@ Use task_status("${taskId}") or task_output("${taskId}") to check progress.`
|
|
|
67109
67871
|
function gatherMemorySnippets(root) {
|
|
67110
67872
|
const snippets = [];
|
|
67111
67873
|
const dirs = [
|
|
67112
|
-
|
|
67113
|
-
|
|
67874
|
+
join72(root, ".oa", "memory"),
|
|
67875
|
+
join72(root, ".open-agents", "memory")
|
|
67114
67876
|
];
|
|
67115
67877
|
for (const dir of dirs) {
|
|
67116
|
-
if (!
|
|
67878
|
+
if (!existsSync55(dir))
|
|
67117
67879
|
continue;
|
|
67118
67880
|
try {
|
|
67119
67881
|
for (const f of readdirSync22(dir).filter((f2) => f2.endsWith(".json"))) {
|
|
67120
|
-
const data = JSON.parse(
|
|
67882
|
+
const data = JSON.parse(readFileSync44(join72(dir, f), "utf-8"));
|
|
67121
67883
|
for (const val of Object.values(data)) {
|
|
67122
67884
|
const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
|
|
67123
67885
|
if (v.length > 10)
|
|
@@ -67274,9 +68036,9 @@ ${metabolismMemories}
|
|
|
67274
68036
|
} catch {
|
|
67275
68037
|
}
|
|
67276
68038
|
try {
|
|
67277
|
-
const archeFile =
|
|
67278
|
-
if (
|
|
67279
|
-
const variants = JSON.parse(
|
|
68039
|
+
const archeFile = join72(repoRoot, ".oa", "arche", "variants.json");
|
|
68040
|
+
if (existsSync55(archeFile)) {
|
|
68041
|
+
const variants = JSON.parse(readFileSync44(archeFile, "utf8"));
|
|
67280
68042
|
if (variants.length > 0) {
|
|
67281
68043
|
let filtered = variants;
|
|
67282
68044
|
if (taskType) {
|
|
@@ -67445,9 +68207,9 @@ RULES:
|
|
|
67445
68207
|
const compactionThreshold = modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
|
|
67446
68208
|
let identityInjection = "";
|
|
67447
68209
|
try {
|
|
67448
|
-
const ikStateFile =
|
|
67449
|
-
if (
|
|
67450
|
-
const selfState = JSON.parse(
|
|
68210
|
+
const ikStateFile = join72(repoRoot, ".oa", "identity", "self-state.json");
|
|
68211
|
+
if (existsSync55(ikStateFile)) {
|
|
68212
|
+
const selfState = JSON.parse(readFileSync44(ikStateFile, "utf8"));
|
|
67451
68213
|
const lines = [
|
|
67452
68214
|
`[Identity State v${selfState.version}]`,
|
|
67453
68215
|
`Self: ${selfState.narrative_summary}`,
|
|
@@ -68091,13 +68853,13 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
|
|
|
68091
68853
|
});
|
|
68092
68854
|
}
|
|
68093
68855
|
try {
|
|
68094
|
-
const ikDir =
|
|
68095
|
-
const ikFile =
|
|
68856
|
+
const ikDir = join72(repoRoot, ".oa", "identity");
|
|
68857
|
+
const ikFile = join72(ikDir, "self-state.json");
|
|
68096
68858
|
let ikState;
|
|
68097
|
-
if (
|
|
68098
|
-
ikState = JSON.parse(
|
|
68859
|
+
if (existsSync55(ikFile)) {
|
|
68860
|
+
ikState = JSON.parse(readFileSync44(ikFile, "utf8"));
|
|
68099
68861
|
} else {
|
|
68100
|
-
|
|
68862
|
+
mkdirSync29(ikDir, { recursive: true });
|
|
68101
68863
|
const machineId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
68102
68864
|
ikState = {
|
|
68103
68865
|
self_id: `oa-${machineId}`,
|
|
@@ -68138,9 +68900,9 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
|
|
|
68138
68900
|
} else {
|
|
68139
68901
|
renderTaskIncomplete(result.turns, result.toolCalls, result.durationMs, tokens);
|
|
68140
68902
|
try {
|
|
68141
|
-
const ikFile =
|
|
68142
|
-
if (
|
|
68143
|
-
const ikState = JSON.parse(
|
|
68903
|
+
const ikFile = join72(repoRoot, ".oa", "identity", "self-state.json");
|
|
68904
|
+
if (existsSync55(ikFile)) {
|
|
68905
|
+
const ikState = JSON.parse(readFileSync44(ikFile, "utf8"));
|
|
68144
68906
|
ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.1);
|
|
68145
68907
|
ikState.homeostasis.coherence = Math.max(0, ikState.homeostasis.coherence - 0.05);
|
|
68146
68908
|
ikState.session_count = (ikState.session_count || 0) + 1;
|
|
@@ -68242,10 +69004,10 @@ async function startInteractive(config, repoPath) {
|
|
|
68242
69004
|
process.stdin.pause();
|
|
68243
69005
|
}
|
|
68244
69006
|
try {
|
|
68245
|
-
const oaDir =
|
|
68246
|
-
const nexusPidFile =
|
|
68247
|
-
if (
|
|
68248
|
-
const pid = parseInt(
|
|
69007
|
+
const oaDir = join72(repoRoot, ".oa");
|
|
69008
|
+
const nexusPidFile = join72(oaDir, "nexus", "daemon.pid");
|
|
69009
|
+
if (existsSync55(nexusPidFile)) {
|
|
69010
|
+
const pid = parseInt(readFileSync44(nexusPidFile, "utf8").trim(), 10);
|
|
68249
69011
|
if (pid > 0) {
|
|
68250
69012
|
try {
|
|
68251
69013
|
process.kill(pid, 0);
|
|
@@ -68582,7 +69344,7 @@ Review its full output in the [${id}] tab or via full_sub_agent(action='output',
|
|
|
68582
69344
|
let p2pGateway = null;
|
|
68583
69345
|
let peerMesh = null;
|
|
68584
69346
|
let inferenceRouter = null;
|
|
68585
|
-
const secretVault = new SecretVault(
|
|
69347
|
+
const secretVault = new SecretVault(join72(repoRoot, ".oa", "vault.enc"));
|
|
68586
69348
|
let adminSessionKey = null;
|
|
68587
69349
|
const callSubAgents = /* @__PURE__ */ new Map();
|
|
68588
69350
|
const streamRenderer = new StreamRenderer();
|
|
@@ -68805,13 +69567,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
68805
69567
|
const hits = allCompletions.filter((c3) => c3.toLowerCase().startsWith(lower));
|
|
68806
69568
|
return [hits, line];
|
|
68807
69569
|
}
|
|
68808
|
-
const HISTORY_DIR =
|
|
68809
|
-
const HISTORY_FILE =
|
|
69570
|
+
const HISTORY_DIR = join72(homedir19(), ".open-agents");
|
|
69571
|
+
const HISTORY_FILE = join72(HISTORY_DIR, "repl-history");
|
|
68810
69572
|
const MAX_HISTORY_LINES = 500;
|
|
68811
69573
|
let savedHistory = [];
|
|
68812
69574
|
try {
|
|
68813
|
-
if (
|
|
68814
|
-
const raw =
|
|
69575
|
+
if (existsSync55(HISTORY_FILE)) {
|
|
69576
|
+
const raw = readFileSync44(HISTORY_FILE, "utf8").trim();
|
|
68815
69577
|
if (raw)
|
|
68816
69578
|
savedHistory = raw.split("\n").reverse();
|
|
68817
69579
|
}
|
|
@@ -68914,10 +69676,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
68914
69676
|
if (!line.trim())
|
|
68915
69677
|
return;
|
|
68916
69678
|
try {
|
|
68917
|
-
|
|
68918
|
-
|
|
69679
|
+
mkdirSync29(HISTORY_DIR, { recursive: true });
|
|
69680
|
+
appendFileSync5(HISTORY_FILE, line + "\n", "utf8");
|
|
68919
69681
|
if (Math.random() < 0.02) {
|
|
68920
|
-
const all =
|
|
69682
|
+
const all = readFileSync44(HISTORY_FILE, "utf8").trim().split("\n");
|
|
68921
69683
|
if (all.length > MAX_HISTORY_LINES) {
|
|
68922
69684
|
writeFileSync27(HISTORY_FILE, all.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
|
|
68923
69685
|
}
|
|
@@ -69103,7 +69865,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
69103
69865
|
} catch {
|
|
69104
69866
|
}
|
|
69105
69867
|
try {
|
|
69106
|
-
const oaDir =
|
|
69868
|
+
const oaDir = join72(repoRoot, ".oa");
|
|
69107
69869
|
const reconnected = await ExposeGateway.checkAndReconnect(oaDir, {
|
|
69108
69870
|
onInfo: (msg) => writeContent(() => renderInfo(msg)),
|
|
69109
69871
|
onError: (msg) => writeContent(() => renderWarning(msg))
|
|
@@ -69135,7 +69897,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
69135
69897
|
} catch {
|
|
69136
69898
|
}
|
|
69137
69899
|
try {
|
|
69138
|
-
const oaDir =
|
|
69900
|
+
const oaDir = join72(repoRoot, ".oa");
|
|
69139
69901
|
const reconnectedP2P = await ExposeP2PGateway.checkAndReconnect(oaDir, new NexusTool(repoRoot), {
|
|
69140
69902
|
onInfo: (msg) => writeContent(() => renderInfo(msg)),
|
|
69141
69903
|
onError: (msg) => writeContent(() => renderWarning(msg))
|
|
@@ -69176,11 +69938,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
69176
69938
|
}
|
|
69177
69939
|
try {
|
|
69178
69940
|
const { homedir: _hd, hostname: _hn, userInfo: _ui } = await import("node:os");
|
|
69179
|
-
const globalNamePath =
|
|
69941
|
+
const globalNamePath = join72(_hd(), ".open-agents", "agent-name");
|
|
69180
69942
|
let agName = "";
|
|
69181
69943
|
try {
|
|
69182
|
-
if (
|
|
69183
|
-
agName =
|
|
69944
|
+
if (existsSync55(globalNamePath))
|
|
69945
|
+
agName = readFileSync44(globalNamePath, "utf8").trim();
|
|
69184
69946
|
} catch {
|
|
69185
69947
|
}
|
|
69186
69948
|
if (!agName) {
|
|
@@ -70156,7 +70918,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70156
70918
|
kind,
|
|
70157
70919
|
targetUrl,
|
|
70158
70920
|
authKey,
|
|
70159
|
-
stateDir:
|
|
70921
|
+
stateDir: join72(repoRoot, ".oa"),
|
|
70160
70922
|
passthrough: passthrough ?? false,
|
|
70161
70923
|
loadbalance: loadbalance ?? false,
|
|
70162
70924
|
endpointAuth: passthrough ? currentConfig.apiKey : void 0,
|
|
@@ -70202,7 +70964,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70202
70964
|
await tunnelGateway.stop();
|
|
70203
70965
|
tunnelGateway = null;
|
|
70204
70966
|
}
|
|
70205
|
-
const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir:
|
|
70967
|
+
const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join72(repoRoot, ".oa") });
|
|
70206
70968
|
newTunnel.on("stats", (stats) => {
|
|
70207
70969
|
statusBar.setExposeStatus({
|
|
70208
70970
|
status: stats.status,
|
|
@@ -70291,9 +71053,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70291
71053
|
if (!result.success)
|
|
70292
71054
|
throw new Error(result.error || "Connect failed");
|
|
70293
71055
|
try {
|
|
70294
|
-
const nexusPidFile =
|
|
70295
|
-
if (
|
|
70296
|
-
const pid = parseInt(
|
|
71056
|
+
const nexusPidFile = join72(repoRoot, ".oa", "nexus", "daemon.pid");
|
|
71057
|
+
if (existsSync55(nexusPidFile)) {
|
|
71058
|
+
const pid = parseInt(readFileSync44(nexusPidFile, "utf8").trim(), 10);
|
|
70297
71059
|
if (pid > 0) {
|
|
70298
71060
|
registry.register({
|
|
70299
71061
|
name: "Nexus",
|
|
@@ -70489,10 +71251,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70489
71251
|
writeContent(() => renderInfo(`Killed ${bgKilled} background task(s).`));
|
|
70490
71252
|
}
|
|
70491
71253
|
try {
|
|
70492
|
-
const nexusDir =
|
|
70493
|
-
const pidFile =
|
|
70494
|
-
if (
|
|
70495
|
-
const pid = parseInt(
|
|
71254
|
+
const nexusDir = join72(repoRoot, OA_DIR, "nexus");
|
|
71255
|
+
const pidFile = join72(nexusDir, "daemon.pid");
|
|
71256
|
+
if (existsSync55(pidFile)) {
|
|
71257
|
+
const pid = parseInt(readFileSync44(pidFile, "utf8").trim(), 10);
|
|
70496
71258
|
if (pid > 0) {
|
|
70497
71259
|
try {
|
|
70498
71260
|
if (process.platform === "win32") {
|
|
@@ -70514,13 +71276,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70514
71276
|
} catch {
|
|
70515
71277
|
}
|
|
70516
71278
|
try {
|
|
70517
|
-
const voiceDir2 =
|
|
71279
|
+
const voiceDir2 = join72(homedir19(), ".open-agents", "voice");
|
|
70518
71280
|
const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
|
|
70519
71281
|
for (const pf of voicePidFiles) {
|
|
70520
|
-
const pidPath =
|
|
70521
|
-
if (
|
|
71282
|
+
const pidPath = join72(voiceDir2, pf);
|
|
71283
|
+
if (existsSync55(pidPath)) {
|
|
70522
71284
|
try {
|
|
70523
|
-
const pid = parseInt(
|
|
71285
|
+
const pid = parseInt(readFileSync44(pidPath, "utf8").trim(), 10);
|
|
70524
71286
|
if (pid > 0) {
|
|
70525
71287
|
if (process.platform === "win32") {
|
|
70526
71288
|
try {
|
|
@@ -70544,8 +71306,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70544
71306
|
execSync35(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
|
|
70545
71307
|
} catch {
|
|
70546
71308
|
}
|
|
70547
|
-
const oaPath =
|
|
70548
|
-
if (
|
|
71309
|
+
const oaPath = join72(repoRoot, OA_DIR);
|
|
71310
|
+
if (existsSync55(oaPath)) {
|
|
70549
71311
|
let deleted = false;
|
|
70550
71312
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
70551
71313
|
try {
|
|
@@ -70628,19 +71390,19 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
70628
71390
|
try {
|
|
70629
71391
|
const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
|
|
70630
71392
|
if (isPersonaPlexRunning2()) {
|
|
70631
|
-
const ppPidFile =
|
|
70632
|
-
const ppPortFile =
|
|
70633
|
-
if (
|
|
70634
|
-
const ppPid = parseInt(
|
|
70635
|
-
const ppPort =
|
|
71393
|
+
const ppPidFile = join72(homedir19(), ".open-agents", "voice", "personaplex", "daemon.pid");
|
|
71394
|
+
const ppPortFile = join72(homedir19(), ".open-agents", "voice", "personaplex", "daemon.port");
|
|
71395
|
+
if (existsSync55(ppPidFile)) {
|
|
71396
|
+
const ppPid = parseInt(readFileSync44(ppPidFile, "utf8").trim(), 10);
|
|
71397
|
+
const ppPort = existsSync55(ppPortFile) ? parseInt(readFileSync44(ppPortFile, "utf8").trim(), 10) : void 0;
|
|
70636
71398
|
if (ppPid > 0 && !registry.daemons.has("PersonaPlex")) {
|
|
70637
71399
|
registry.register({ name: "PersonaPlex", pid: ppPid, port: ppPort, startedAt: Date.now(), status: "running" });
|
|
70638
71400
|
}
|
|
70639
71401
|
}
|
|
70640
71402
|
}
|
|
70641
|
-
const nexusPidFile =
|
|
70642
|
-
if (
|
|
70643
|
-
const nPid = parseInt(
|
|
71403
|
+
const nexusPidFile = join72(repoRoot, ".oa", "nexus", "daemon.pid");
|
|
71404
|
+
if (existsSync55(nexusPidFile)) {
|
|
71405
|
+
const nPid = parseInt(readFileSync44(nexusPidFile, "utf8").trim(), 10);
|
|
70644
71406
|
if (nPid > 0 && !registry.daemons.has("Nexus")) {
|
|
70645
71407
|
try {
|
|
70646
71408
|
process.kill(nPid, 0);
|
|
@@ -70982,8 +71744,8 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
70982
71744
|
}
|
|
70983
71745
|
}
|
|
70984
71746
|
const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
|
|
70985
|
-
const isImage = isImagePath(cleanPath) &&
|
|
70986
|
-
const isMedia = !isImage && isTranscribablePath(cleanPath) &&
|
|
71747
|
+
const isImage = isImagePath(cleanPath) && existsSync55(resolve32(repoRoot, cleanPath));
|
|
71748
|
+
const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync55(resolve32(repoRoot, cleanPath));
|
|
70987
71749
|
if (activeTask) {
|
|
70988
71750
|
if (activeTask.runner.isPaused) {
|
|
70989
71751
|
activeTask.runner.resume();
|
|
@@ -70992,7 +71754,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
70992
71754
|
if (isImage) {
|
|
70993
71755
|
try {
|
|
70994
71756
|
const imgPath = resolve32(repoRoot, cleanPath);
|
|
70995
|
-
const imgBuffer =
|
|
71757
|
+
const imgBuffer = readFileSync44(imgPath);
|
|
70996
71758
|
const base64 = imgBuffer.toString("base64");
|
|
70997
71759
|
const ext = extname11(cleanPath).toLowerCase();
|
|
70998
71760
|
const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
@@ -71207,7 +71969,7 @@ Summarize or analyze this transcription as appropriate.`;
|
|
|
71207
71969
|
|
|
71208
71970
|
NEW TASK: ${fullInput}`;
|
|
71209
71971
|
restoredSessionContext = null;
|
|
71210
|
-
} else if (
|
|
71972
|
+
} else if (existsSync55(join72(repoRoot, ".oa", "context", "session-diary.md"))) {
|
|
71211
71973
|
taskInput = `[Previous sessions exist \u2014 file_read(".oa/context/session-diary.md") to recall]
|
|
71212
71974
|
|
|
71213
71975
|
${fullInput}`;
|
|
@@ -71524,13 +72286,13 @@ async function runWithTUI(task, config, repoPath) {
|
|
|
71524
72286
|
const handle = startTask(task, config, repoRoot);
|
|
71525
72287
|
await handle.promise;
|
|
71526
72288
|
try {
|
|
71527
|
-
const ikDir =
|
|
71528
|
-
const ikFile =
|
|
72289
|
+
const ikDir = join72(repoRoot, ".oa", "identity");
|
|
72290
|
+
const ikFile = join72(ikDir, "self-state.json");
|
|
71529
72291
|
let ikState;
|
|
71530
|
-
if (
|
|
71531
|
-
ikState = JSON.parse(
|
|
72292
|
+
if (existsSync55(ikFile)) {
|
|
72293
|
+
ikState = JSON.parse(readFileSync44(ikFile, "utf8"));
|
|
71532
72294
|
} else {
|
|
71533
|
-
|
|
72295
|
+
mkdirSync29(ikDir, { recursive: true });
|
|
71534
72296
|
ikState = {
|
|
71535
72297
|
self_id: `oa-${Date.now().toString(36)}`,
|
|
71536
72298
|
version: 1,
|
|
@@ -71561,12 +72323,12 @@ async function runWithTUI(task, config, repoPath) {
|
|
|
71561
72323
|
ec.archiveVariantSync(`Task: ${task.slice(0, 200)}`, "success \u2014 completed", ["general"]);
|
|
71562
72324
|
} catch {
|
|
71563
72325
|
try {
|
|
71564
|
-
const archeDir =
|
|
71565
|
-
const archeFile =
|
|
72326
|
+
const archeDir = join72(repoRoot, ".oa", "arche");
|
|
72327
|
+
const archeFile = join72(archeDir, "variants.json");
|
|
71566
72328
|
let variants = [];
|
|
71567
72329
|
try {
|
|
71568
|
-
if (
|
|
71569
|
-
variants = JSON.parse(
|
|
72330
|
+
if (existsSync55(archeFile))
|
|
72331
|
+
variants = JSON.parse(readFileSync44(archeFile, "utf8"));
|
|
71570
72332
|
} catch {
|
|
71571
72333
|
}
|
|
71572
72334
|
variants.push({
|
|
@@ -71581,15 +72343,15 @@ async function runWithTUI(task, config, repoPath) {
|
|
|
71581
72343
|
});
|
|
71582
72344
|
if (variants.length > 50)
|
|
71583
72345
|
variants = variants.slice(-50);
|
|
71584
|
-
|
|
72346
|
+
mkdirSync29(archeDir, { recursive: true });
|
|
71585
72347
|
writeFileSync27(archeFile, JSON.stringify(variants, null, 2));
|
|
71586
72348
|
} catch {
|
|
71587
72349
|
}
|
|
71588
72350
|
}
|
|
71589
72351
|
try {
|
|
71590
|
-
const metaFile =
|
|
71591
|
-
if (
|
|
71592
|
-
const store = JSON.parse(
|
|
72352
|
+
const metaFile = join72(repoRoot, ".oa", "memory", "metabolism", "store.json");
|
|
72353
|
+
if (existsSync55(metaFile)) {
|
|
72354
|
+
const store = JSON.parse(readFileSync44(metaFile, "utf8"));
|
|
71593
72355
|
const surfaced = store.filter((m) => m.type !== "quarantine" && m.scores?.confidence > 0.15).sort((a, b) => b.scores.utility * b.scores.confidence - a.scores.utility * a.scores.confidence).slice(0, 5);
|
|
71594
72356
|
let updated = false;
|
|
71595
72357
|
for (const item of surfaced) {
|
|
@@ -71653,9 +72415,9 @@ Rules:
|
|
|
71653
72415
|
try {
|
|
71654
72416
|
const { initDb: initDb2 } = __require("@open-agents/memory");
|
|
71655
72417
|
const { ProceduralMemoryStore: ProceduralMemoryStore2 } = __require("@open-agents/memory");
|
|
71656
|
-
const dbDir =
|
|
71657
|
-
|
|
71658
|
-
const db = initDb2(
|
|
72418
|
+
const dbDir = join72(repoRoot, ".oa", "memory");
|
|
72419
|
+
mkdirSync29(dbDir, { recursive: true });
|
|
72420
|
+
const db = initDb2(join72(dbDir, "structured.db"));
|
|
71659
72421
|
const memStore = new ProceduralMemoryStore2(db);
|
|
71660
72422
|
memStore.createWithEmbedding({
|
|
71661
72423
|
content: content.slice(0, 600),
|
|
@@ -71670,12 +72432,12 @@ Rules:
|
|
|
71670
72432
|
db.close();
|
|
71671
72433
|
} catch {
|
|
71672
72434
|
}
|
|
71673
|
-
const metaDir =
|
|
71674
|
-
const storeFile =
|
|
72435
|
+
const metaDir = join72(repoRoot, ".oa", "memory", "metabolism");
|
|
72436
|
+
const storeFile = join72(metaDir, "store.json");
|
|
71675
72437
|
let store = [];
|
|
71676
72438
|
try {
|
|
71677
|
-
if (
|
|
71678
|
-
store = JSON.parse(
|
|
72439
|
+
if (existsSync55(storeFile))
|
|
72440
|
+
store = JSON.parse(readFileSync44(storeFile, "utf8"));
|
|
71679
72441
|
} catch {
|
|
71680
72442
|
}
|
|
71681
72443
|
store.push({
|
|
@@ -71691,26 +72453,26 @@ Rules:
|
|
|
71691
72453
|
});
|
|
71692
72454
|
if (store.length > 100)
|
|
71693
72455
|
store = store.slice(-100);
|
|
71694
|
-
|
|
72456
|
+
mkdirSync29(metaDir, { recursive: true });
|
|
71695
72457
|
writeFileSync27(storeFile, JSON.stringify(store, null, 2));
|
|
71696
72458
|
}
|
|
71697
72459
|
}
|
|
71698
72460
|
} catch {
|
|
71699
72461
|
}
|
|
71700
72462
|
try {
|
|
71701
|
-
const cohereSettingsFile =
|
|
72463
|
+
const cohereSettingsFile = join72(repoRoot, ".oa", "settings.json");
|
|
71702
72464
|
let cohereActive = false;
|
|
71703
72465
|
try {
|
|
71704
|
-
if (
|
|
71705
|
-
const settings = JSON.parse(
|
|
72466
|
+
if (existsSync55(cohereSettingsFile)) {
|
|
72467
|
+
const settings = JSON.parse(readFileSync44(cohereSettingsFile, "utf8"));
|
|
71706
72468
|
cohereActive = settings.cohere === true;
|
|
71707
72469
|
}
|
|
71708
72470
|
} catch {
|
|
71709
72471
|
}
|
|
71710
72472
|
if (cohereActive) {
|
|
71711
|
-
const metaFile =
|
|
71712
|
-
if (
|
|
71713
|
-
const store = JSON.parse(
|
|
72473
|
+
const metaFile = join72(repoRoot, ".oa", "memory", "metabolism", "store.json");
|
|
72474
|
+
if (existsSync55(metaFile)) {
|
|
72475
|
+
const store = JSON.parse(readFileSync44(metaFile, "utf8"));
|
|
71714
72476
|
const latest = store.filter((m) => m.sourceTrace === "trajectory-extraction" || m.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
|
|
71715
72477
|
if (latest && latest.scores?.confidence >= 0.6) {
|
|
71716
72478
|
try {
|
|
@@ -71735,18 +72497,18 @@ Rules:
|
|
|
71735
72497
|
}
|
|
71736
72498
|
} catch (err) {
|
|
71737
72499
|
try {
|
|
71738
|
-
const ikFile =
|
|
71739
|
-
if (
|
|
71740
|
-
const ikState = JSON.parse(
|
|
72500
|
+
const ikFile = join72(repoRoot, ".oa", "identity", "self-state.json");
|
|
72501
|
+
if (existsSync55(ikFile)) {
|
|
72502
|
+
const ikState = JSON.parse(readFileSync44(ikFile, "utf8"));
|
|
71741
72503
|
ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.1);
|
|
71742
72504
|
ikState.homeostasis.coherence = Math.max(0, ikState.homeostasis.coherence - 0.05);
|
|
71743
72505
|
ikState.session_count = (ikState.session_count || 0) + 1;
|
|
71744
72506
|
ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
71745
72507
|
writeFileSync27(ikFile, JSON.stringify(ikState, null, 2));
|
|
71746
72508
|
}
|
|
71747
|
-
const metaFile =
|
|
71748
|
-
if (
|
|
71749
|
-
const store = JSON.parse(
|
|
72509
|
+
const metaFile = join72(repoRoot, ".oa", "memory", "metabolism", "store.json");
|
|
72510
|
+
if (existsSync55(metaFile)) {
|
|
72511
|
+
const store = JSON.parse(readFileSync44(metaFile, "utf8"));
|
|
71750
72512
|
const surfaced = store.filter((m) => m.type !== "quarantine" && m.scores?.confidence > 0.15).sort((a, b) => b.scores.utility * b.scores.confidence - a.scores.utility * a.scores.confidence).slice(0, 5);
|
|
71751
72513
|
for (const item of surfaced) {
|
|
71752
72514
|
item.accessCount = (item.accessCount || 0) + 1;
|
|
@@ -71757,12 +72519,12 @@ Rules:
|
|
|
71757
72519
|
writeFileSync27(metaFile, JSON.stringify(store, null, 2));
|
|
71758
72520
|
}
|
|
71759
72521
|
try {
|
|
71760
|
-
const archeDir =
|
|
71761
|
-
const archeFile =
|
|
72522
|
+
const archeDir = join72(repoRoot, ".oa", "arche");
|
|
72523
|
+
const archeFile = join72(archeDir, "variants.json");
|
|
71762
72524
|
let variants = [];
|
|
71763
72525
|
try {
|
|
71764
|
-
if (
|
|
71765
|
-
variants = JSON.parse(
|
|
72526
|
+
if (existsSync55(archeFile))
|
|
72527
|
+
variants = JSON.parse(readFileSync44(archeFile, "utf8"));
|
|
71766
72528
|
} catch {
|
|
71767
72529
|
}
|
|
71768
72530
|
variants.push({
|
|
@@ -71777,7 +72539,7 @@ Rules:
|
|
|
71777
72539
|
});
|
|
71778
72540
|
if (variants.length > 50)
|
|
71779
72541
|
variants = variants.slice(-50);
|
|
71780
|
-
|
|
72542
|
+
mkdirSync29(archeDir, { recursive: true });
|
|
71781
72543
|
writeFileSync27(archeFile, JSON.stringify(variants, null, 2));
|
|
71782
72544
|
} catch {
|
|
71783
72545
|
}
|
|
@@ -71848,13 +72610,13 @@ __export(run_exports, {
|
|
|
71848
72610
|
});
|
|
71849
72611
|
import { resolve as resolve33 } from "node:path";
|
|
71850
72612
|
import { spawn as spawn22 } from "node:child_process";
|
|
71851
|
-
import { mkdirSync as
|
|
72613
|
+
import { mkdirSync as mkdirSync30, writeFileSync as writeFileSync28, readFileSync as readFileSync45, readdirSync as readdirSync23, existsSync as existsSync56 } from "node:fs";
|
|
71852
72614
|
import { randomBytes as randomBytes17 } from "node:crypto";
|
|
71853
|
-
import { join as
|
|
72615
|
+
import { join as join73 } from "node:path";
|
|
71854
72616
|
function jobsDir2(repoPath) {
|
|
71855
72617
|
const root = resolve33(repoPath ?? process.cwd());
|
|
71856
|
-
const dir =
|
|
71857
|
-
|
|
72618
|
+
const dir = join73(root, ".oa", "jobs");
|
|
72619
|
+
mkdirSync30(dir, { recursive: true });
|
|
71858
72620
|
return dir;
|
|
71859
72621
|
}
|
|
71860
72622
|
async function runCommand(opts, config) {
|
|
@@ -71939,7 +72701,7 @@ async function runBackground(task, config, opts) {
|
|
|
71939
72701
|
});
|
|
71940
72702
|
child.unref();
|
|
71941
72703
|
job.pid = child.pid ?? 0;
|
|
71942
|
-
writeFileSync28(
|
|
72704
|
+
writeFileSync28(join73(dir, `${id}.json`), JSON.stringify(job, null, 2));
|
|
71943
72705
|
let output = "";
|
|
71944
72706
|
child.stdout?.on("data", (chunk) => {
|
|
71945
72707
|
output += chunk.toString();
|
|
@@ -71955,7 +72717,7 @@ async function runBackground(task, config, opts) {
|
|
|
71955
72717
|
job.summary = result.summary;
|
|
71956
72718
|
job.durationMs = result.durationMs;
|
|
71957
72719
|
job.error = result.error;
|
|
71958
|
-
writeFileSync28(
|
|
72720
|
+
writeFileSync28(join73(dir, `${id}.json`), JSON.stringify(job, null, 2));
|
|
71959
72721
|
} catch {
|
|
71960
72722
|
}
|
|
71961
72723
|
});
|
|
@@ -71971,13 +72733,13 @@ async function runBackground(task, config, opts) {
|
|
|
71971
72733
|
}
|
|
71972
72734
|
function statusCommand(jobId, repoPath) {
|
|
71973
72735
|
const dir = jobsDir2(repoPath);
|
|
71974
|
-
const file =
|
|
71975
|
-
if (!
|
|
72736
|
+
const file = join73(dir, `${jobId}.json`);
|
|
72737
|
+
if (!existsSync56(file)) {
|
|
71976
72738
|
console.error(`Job not found: ${jobId}`);
|
|
71977
72739
|
console.log(`Available jobs: oa jobs`);
|
|
71978
72740
|
process.exit(1);
|
|
71979
72741
|
}
|
|
71980
|
-
const job = JSON.parse(
|
|
72742
|
+
const job = JSON.parse(readFileSync45(file, "utf-8"));
|
|
71981
72743
|
const runtime = job.completedAt ? `${((new Date(job.completedAt).getTime() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s` : `${((Date.now() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s`;
|
|
71982
72744
|
const icon = job.status === "completed" ? "\u2713" : job.status === "failed" ? "\u2717" : "\u25CF";
|
|
71983
72745
|
console.log(`${icon} ${job.id} [${job.status}] ${runtime}`);
|
|
@@ -72000,7 +72762,7 @@ function jobsCommand(repoPath) {
|
|
|
72000
72762
|
console.log("Jobs:");
|
|
72001
72763
|
for (const file of files) {
|
|
72002
72764
|
try {
|
|
72003
|
-
const job = JSON.parse(
|
|
72765
|
+
const job = JSON.parse(readFileSync45(join73(dir, file), "utf-8"));
|
|
72004
72766
|
const icon = job.status === "completed" ? "\u2713" : job.status === "failed" ? "\u2717" : "\u25CF";
|
|
72005
72767
|
const runtime = job.completedAt ? `${((new Date(job.completedAt).getTime() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s` : `${((Date.now() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s`;
|
|
72006
72768
|
console.log(` ${icon} ${job.id} [${job.status}] ${runtime} \u2014 ${job.task.slice(0, 60)}`);
|
|
@@ -72020,7 +72782,7 @@ import { glob } from "glob";
|
|
|
72020
72782
|
import ignore from "ignore";
|
|
72021
72783
|
import { readFile as readFile23, stat as stat4 } from "node:fs/promises";
|
|
72022
72784
|
import { createHash as createHash6 } from "node:crypto";
|
|
72023
|
-
import { join as
|
|
72785
|
+
import { join as join74, relative as relative4, extname as extname12, basename as basename17 } from "node:path";
|
|
72024
72786
|
var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
|
|
72025
72787
|
var init_codebase_indexer = __esm({
|
|
72026
72788
|
"packages/indexer/dist/codebase-indexer.js"() {
|
|
@@ -72064,7 +72826,7 @@ var init_codebase_indexer = __esm({
|
|
|
72064
72826
|
const ig = ignore.default();
|
|
72065
72827
|
if (this.config.respectGitignore) {
|
|
72066
72828
|
try {
|
|
72067
|
-
const gitignoreContent = await readFile23(
|
|
72829
|
+
const gitignoreContent = await readFile23(join74(this.config.rootDir, ".gitignore"), "utf-8");
|
|
72068
72830
|
ig.add(gitignoreContent);
|
|
72069
72831
|
} catch {
|
|
72070
72832
|
}
|
|
@@ -72079,7 +72841,7 @@ var init_codebase_indexer = __esm({
|
|
|
72079
72841
|
for (const relativePath of files) {
|
|
72080
72842
|
if (ig.ignores(relativePath))
|
|
72081
72843
|
continue;
|
|
72082
|
-
const fullPath =
|
|
72844
|
+
const fullPath = join74(this.config.rootDir, relativePath);
|
|
72083
72845
|
try {
|
|
72084
72846
|
const fileStat = await stat4(fullPath);
|
|
72085
72847
|
if (fileStat.size > this.config.maxFileSize)
|
|
@@ -72125,7 +72887,7 @@ var init_codebase_indexer = __esm({
|
|
|
72125
72887
|
if (!child) {
|
|
72126
72888
|
child = {
|
|
72127
72889
|
name: part,
|
|
72128
|
-
path:
|
|
72890
|
+
path: join74(current.path, part),
|
|
72129
72891
|
type: "directory",
|
|
72130
72892
|
children: []
|
|
72131
72893
|
};
|
|
@@ -72208,13 +72970,13 @@ __export(index_repo_exports, {
|
|
|
72208
72970
|
indexRepoCommand: () => indexRepoCommand
|
|
72209
72971
|
});
|
|
72210
72972
|
import { resolve as resolve34 } from "node:path";
|
|
72211
|
-
import { existsSync as
|
|
72973
|
+
import { existsSync as existsSync57, statSync as statSync17 } from "node:fs";
|
|
72212
72974
|
import { cwd as cwd2 } from "node:process";
|
|
72213
72975
|
async function indexRepoCommand(opts, _config) {
|
|
72214
72976
|
const repoRoot = resolve34(opts.repoPath ?? cwd2());
|
|
72215
72977
|
printHeader("Index Repository");
|
|
72216
72978
|
printInfo(`Indexing: ${repoRoot}`);
|
|
72217
|
-
if (!
|
|
72979
|
+
if (!existsSync57(repoRoot)) {
|
|
72218
72980
|
printError(`Path does not exist: ${repoRoot}`);
|
|
72219
72981
|
process.exit(1);
|
|
72220
72982
|
}
|
|
@@ -72466,7 +73228,7 @@ var config_exports2 = {};
|
|
|
72466
73228
|
__export(config_exports2, {
|
|
72467
73229
|
configCommand: () => configCommand
|
|
72468
73230
|
});
|
|
72469
|
-
import { join as
|
|
73231
|
+
import { join as join75, resolve as resolve35 } from "node:path";
|
|
72470
73232
|
import { homedir as homedir20 } from "node:os";
|
|
72471
73233
|
import { cwd as cwd3 } from "node:process";
|
|
72472
73234
|
function redactIfSensitive(key, value) {
|
|
@@ -72549,7 +73311,7 @@ function handleShow(opts, config) {
|
|
|
72549
73311
|
}
|
|
72550
73312
|
}
|
|
72551
73313
|
printSection("Config File");
|
|
72552
|
-
printInfo(`~/.open-agents/config.json (${
|
|
73314
|
+
printInfo(`~/.open-agents/config.json (${join75(homedir20(), ".open-agents", "config.json")})`);
|
|
72553
73315
|
printSection("Priority Chain");
|
|
72554
73316
|
printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
|
|
72555
73317
|
printInfo(" 2. Project .oa/settings.json (--local)");
|
|
@@ -72588,7 +73350,7 @@ function handleSet(opts, _config) {
|
|
|
72588
73350
|
const coerced = coerceForSettings(key, value);
|
|
72589
73351
|
saveProjectSettings(repoRoot, { [key]: coerced });
|
|
72590
73352
|
printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value)}`);
|
|
72591
|
-
printInfo(`Saved to ${
|
|
73353
|
+
printInfo(`Saved to ${join75(repoRoot, ".oa", "settings.json")}`);
|
|
72592
73354
|
printInfo("This override applies only when running in this workspace.");
|
|
72593
73355
|
} catch (err) {
|
|
72594
73356
|
printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -72731,8 +73493,8 @@ __export(eval_exports, {
|
|
|
72731
73493
|
evalCommand: () => evalCommand
|
|
72732
73494
|
});
|
|
72733
73495
|
import { tmpdir as tmpdir10 } from "node:os";
|
|
72734
|
-
import { mkdirSync as
|
|
72735
|
-
import { join as
|
|
73496
|
+
import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync29 } from "node:fs";
|
|
73497
|
+
import { join as join76 } from "node:path";
|
|
72736
73498
|
async function evalCommand(opts, config) {
|
|
72737
73499
|
const suiteName = opts.suite ?? "basic";
|
|
72738
73500
|
const suite = SUITES[suiteName];
|
|
@@ -72857,9 +73619,9 @@ async function evalCommand(opts, config) {
|
|
|
72857
73619
|
process.exit(failed > 0 ? 1 : 0);
|
|
72858
73620
|
}
|
|
72859
73621
|
function createTempEvalRepo() {
|
|
72860
|
-
const dir =
|
|
72861
|
-
|
|
72862
|
-
writeFileSync29(
|
|
73622
|
+
const dir = join76(tmpdir10(), `open-agents-eval-${Date.now()}`);
|
|
73623
|
+
mkdirSync31(dir, { recursive: true });
|
|
73624
|
+
writeFileSync29(join76(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
|
|
72863
73625
|
return dir;
|
|
72864
73626
|
}
|
|
72865
73627
|
var BASIC_SUITE, FULL_SUITE, SUITES;
|
|
@@ -72919,7 +73681,7 @@ init_updater();
|
|
|
72919
73681
|
import { parseArgs as nodeParseArgs2 } from "node:util";
|
|
72920
73682
|
import { createRequire as createRequire5 } from "node:module";
|
|
72921
73683
|
import { fileURLToPath as fileURLToPath15 } from "node:url";
|
|
72922
|
-
import { dirname as dirname23, join as
|
|
73684
|
+
import { dirname as dirname23, join as join77 } from "node:path";
|
|
72923
73685
|
|
|
72924
73686
|
// packages/cli/dist/cli.js
|
|
72925
73687
|
import { createInterface } from "node:readline";
|
|
@@ -73026,7 +73788,7 @@ init_output();
|
|
|
73026
73788
|
function getVersion5() {
|
|
73027
73789
|
try {
|
|
73028
73790
|
const require2 = createRequire5(import.meta.url);
|
|
73029
|
-
const pkgPath =
|
|
73791
|
+
const pkgPath = join77(dirname23(fileURLToPath15(import.meta.url)), "..", "package.json");
|
|
73030
73792
|
const pkg = require2(pkgPath);
|
|
73031
73793
|
return pkg.version;
|
|
73032
73794
|
} catch {
|
|
@@ -73301,12 +74063,12 @@ function crashLog(label, err) {
|
|
|
73301
74063
|
const logLine = `[${timestamp}] ${label}: ${msg}
|
|
73302
74064
|
`;
|
|
73303
74065
|
try {
|
|
73304
|
-
const { appendFileSync:
|
|
73305
|
-
const { join:
|
|
74066
|
+
const { appendFileSync: appendFileSync6, mkdirSync: mkdirSync32 } = __require("node:fs");
|
|
74067
|
+
const { join: join78 } = __require("node:path");
|
|
73306
74068
|
const { homedir: homedir21 } = __require("node:os");
|
|
73307
|
-
const logDir =
|
|
73308
|
-
|
|
73309
|
-
|
|
74069
|
+
const logDir = join78(homedir21(), ".open-agents");
|
|
74070
|
+
mkdirSync32(logDir, { recursive: true });
|
|
74071
|
+
appendFileSync6(join78(logDir, "crash.log"), logLine);
|
|
73310
74072
|
} catch {
|
|
73311
74073
|
}
|
|
73312
74074
|
try {
|