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.
Files changed (2) hide show
  1. package/dist/index.js +1199 -437
  2. 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, log) {
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(log, null, 2), "utf-8");
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 log = loadLog(workingDir);
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
- log.entries.push(entry);
2159
- if (log.entries.length > 500) {
2160
- log.entries = log.entries.slice(-500);
2158
+ log2.entries.push(entry);
2159
+ if (log2.entries.length > 500) {
2160
+ log2.entries = log2.entries.slice(-500);
2161
2161
  }
2162
- saveLog(workingDir, log);
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 log = loadLog(workingDir);
2170
+ const log2 = loadLog(workingDir);
2171
2171
  let count = 0;
2172
- for (const entry of log.entries) {
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, log);
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 log = loadLog(workingDir);
2188
- const entry = log.entries.find((e) => e.id === changeId);
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, log);
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 log = loadLog(workingDir);
2201
- return log.entries.filter((e) => e.file === filePath).slice(-limit);
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 log = loadLog(workingDir);
2209
- return log.entries.filter((e) => e.sessionId === _sessionId);
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 log = loadLog(workingDir);
2217
- const recent = log.entries.slice(-limit);
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 log = this.git(repoDir, `log --oneline -${logCount} --no-decorate`);
5265
- if (log) {
5266
- for (const line of log.split("\n").filter((l) => l.trim())) {
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: mkdirSync31, writeFileSync: writeFileSync30 } = await import("node:fs");
13785
+ const { mkdirSync: mkdirSync32, writeFileSync: writeFileSync30 } = await import("node:fs");
13786
13786
  const sessionDir = join22(this.cwd, ".oa", "rlm");
13787
- mkdirSync31(sessionDir, { recursive: true });
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: readFileSync45, existsSync: existsSync57 } = await import("node:fs");
13823
+ const { readFileSync: readFileSync46, existsSync: existsSync58 } = await import("node:fs");
13824
13824
  const sessionPath = join22(this.cwd, ".oa", "rlm", "session.json");
13825
- if (!existsSync57(sessionPath))
13825
+ if (!existsSync58(sessionPath))
13826
13826
  return null;
13827
- return JSON.parse(readFileSync45(sessionPath, "utf8"));
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: readFileSync45 } = await import("node:fs");
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 = readFileSync45(join23(trajDir, file), "utf8").split("\n").filter((l) => l.trim());
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: readFileSync45, existsSync: existsSync57 } = __require("node:fs");
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 (!existsSync57(storeFile))
14394
+ if (!existsSync58(storeFile))
14395
14395
  return "";
14396
14396
  let store = [];
14397
14397
  try {
14398
- store = JSON.parse(readFileSync45(storeFile, "utf8"));
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: readFileSync45, writeFileSync: writeFileSync30, existsSync: existsSync57, mkdirSync: mkdirSync31 } = __require("node:fs");
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 (!existsSync57(storeFile))
14423
+ if (!existsSync58(storeFile))
14424
14424
  return;
14425
14425
  let store = [];
14426
14426
  try {
14427
- store = JSON.parse(readFileSync45(storeFile, "utf8"));
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
- mkdirSync31(metaDir, { recursive: true });
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: readFileSync45, existsSync: existsSync57 } = __require("node:fs");
14874
+ const { readFileSync: readFileSync46, existsSync: existsSync58 } = __require("node:fs");
14875
14875
  const archiveFile = join25(this.cwd, ".oa", "arche", "variants.json");
14876
- if (!existsSync57(archiveFile))
14876
+ if (!existsSync58(archiveFile))
14877
14877
  return "";
14878
14878
  let variants = [];
14879
14879
  try {
14880
- variants = JSON.parse(readFileSync45(archiveFile, "utf8"));
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: readFileSync45, writeFileSync: writeFileSync30, existsSync: existsSync57, mkdirSync: mkdirSync31 } = __require("node:fs");
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 (existsSync57(archiveFile))
14904
- variants = JSON.parse(readFileSync45(archiveFile, "utf8"));
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
- mkdirSync31(dir, { recursive: true });
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: mkdirSync31, writeFileSync: writeFileSync30 } = __require("node:fs");
27426
- const { join: join77 } = __require("node:path");
27427
- const sessionDir = join77(this._workingDirectory, ".oa", "session", this._sessionId);
27428
- mkdirSync31(sessionDir, { recursive: true });
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(join77(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
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: readFileSync45, unlinkSync: unlinkSync13 } = await import("node:fs");
28579
- const { join: join77 } = await import("node:path");
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 = join77(tmpdir11(), `oa_img_in_${Date.now()}.png`);
28582
- const tmpOut = join77(tmpdir11(), `oa_img_out_${Date.now()}.jpg`);
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 = readFileSync45(tmpOut);
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: existsSync57, readFileSync: readFileSync45 } = await import("node:fs");
39561
- const { join: join77 } = await import("node:path");
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 = join77(nexusDir, "status.json");
39568
- if (existsSync57(statusPath)) {
39569
- const status = JSON.parse(readFileSync45(statusPath, "utf8"));
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 = join77(nexusDir, "pricing.json");
39577
- if (existsSync57(pricingPath)) {
39576
+ const pricingPath = join78(nexusDir, "pricing.json");
39577
+ if (existsSync58(pricingPath)) {
39578
39578
  try {
39579
- const pricing = JSON.parse(readFileSync45(pricingPath, "utf8"));
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 = join77(nexusDir, "peer-models-cache.json");
39594
- if (existsSync57(cachePath)) {
39593
+ const cachePath = join78(nexusDir, "peer-models-cache.json");
39594
+ if (existsSync58(cachePath)) {
39595
39595
  try {
39596
- const cache4 = JSON.parse(readFileSync45(cachePath, "utf8"));
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 = join77(nexusDir, "pricing.json");
39712
- if (existsSync57(pricingPath)) {
39711
+ const pricingPath = join78(nexusDir, "pricing.json");
39712
+ if (existsSync58(pricingPath)) {
39713
39713
  try {
39714
- const pricing = JSON.parse(readFileSync45(pricingPath, "utf8"));
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 log = onInfo ?? (() => {
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
- log(`Detected ARM64 platform (${arch2}) \u2014 Jetson/ARM install path`);
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
- log("Creating Python virtual environment...");
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
- log(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
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
- log("Checking system dependencies (libopus)...");
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
- log("ARM64: Checking Rust toolchain for sphn build...");
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
- log("ARM64: Installing Rust toolchain (needed for sphn audio codec)...");
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
- log(`Rust install failed: ${e instanceof Error ? e.message : String(e)}`);
41712
- log("Install Rust manually: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh");
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
- log("Installing PersonaPlex (moshi package)...");
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
- log("ARM64: Building sphn from source (Opus codec bindings)...");
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
- log("ARM64: sphn built successfully");
41732
+ log2("ARM64: sphn built successfully");
41733
41733
  } catch (e) {
41734
- log(`ARM64: sphn build failed \u2014 ${e instanceof Error ? e.message : String(e)}`);
41735
- log("Ensure Rust, libopus-dev, and cmake are installed.");
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
- log("ARM64: Installing moshi (--no-deps to preserve JetPack torch)...");
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
- log("ARM64: Installing remaining moshi dependencies...");
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
- log(`Moshi install failed: ${err instanceof Error ? err.message : String(err)}`);
41748
+ log2(`Moshi install failed: ${err instanceof Error ? err.message : String(err)}`);
41749
41749
  if (isAarch64) {
41750
- log("ARM64: This often means the pip process was OOM-killed.");
41751
- log("Check: dmesg | grep -i 'oom\\|killed' | tail -5");
41752
- log("Ensure JetPack PyTorch is installed: pip3 show torch");
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
- log("Applied seed parameter bug fix to moshi server.");
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
- log("Patched loaders.py with 2-bit TurboQuant native dequant support.");
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
- log("Deploying hybrid_agent.py (OA API integration)...");
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
- log("hybrid_agent.py deployed (OA API + Ollama fallback).");
41889
+ log2("hybrid_agent.py deployed (OA API + Ollama fallback).");
41890
41890
  }
41891
41891
  } catch {
41892
- log("hybrid_agent.py download failed \u2014 hybrid mode will be disabled.");
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
- log("Deploying patched server.py (hybrid mode + API endpoints)...");
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
- log("server.py patched with hybrid intercept + REST APIs.");
41900
+ log2("server.py patched with hybrid intercept + REST APIs.");
41901
41901
  }
41902
41902
  } catch {
41903
- log("server.py download failed \u2014 will use upstream version.");
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
- log("ARM64: Installing bitsandbytes for INT4 inference...");
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
- log(`Weight tier: ${tier} (${repoInfo.sizeGB}GB) \u2014 ${repoInfo.needsToken ? "requires HF_TOKEN" : "public, no token needed"}`);
41926
- log(`Downloading PersonaPlex weights (${repoInfo.sizeGB}GB)...`);
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
- log(`Weights downloaded: ${repoInfo.file}`);
41931
+ log2(`Weights downloaded: ${repoInfo.file}`);
41932
41932
  if (tier !== "original") {
41933
- log("Downloading Mimi codec + tokenizer (no token needed)...");
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
- log("Codec + tokenizer downloaded.");
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
- log(`HF_TOKEN required for ${tier} weights. Set HF_TOKEN or accept license at https://huggingface.co/${repoInfo.repo}`);
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
- log("Auto-downgrading to INT4 weights (no token required)...");
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
- log(`Downloaded INT4 weights instead (${nf4.sizeGB}GB, public).`);
41963
+ log2(`Downloaded INT4 weights instead (${nf4.sizeGB}GB, public).`);
41964
41964
  } catch {
41965
- log("Weight download failed.");
41965
+ log2("Weight download failed.");
41966
41966
  return false;
41967
41967
  }
41968
41968
  }
41969
41969
  } else {
41970
- log(`Weight download failed: ${msg}`);
41971
- log("Weights will download on first server launch.");
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
- log(`PersonaPlex installed (${tier} tier). Use /call to start voice session.`);
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 log = onInfo ?? (() => {
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
- log(`PersonaPlex already running at ${url}`);
41986
+ log2(`PersonaPlex already running at ${url}`);
41987
41987
  return url;
41988
41988
  }
41989
41989
  }
41990
41990
  if (!isPersonaPlexInstalled()) {
41991
- log("PersonaPlex not installed. Run /voice personaplex to set up.");
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
- log(`Weight tier: ${tier} \u2014 distilled NF4 (90% token match, ${repoInfo.sizeGB}GB)...`);
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
- log("Converting .pt checkpoint to safetensors (one-time)...");
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
- log(`Using distilled weights: ${(statSync13(cachedBf16).size / 1024 ** 3).toFixed(1)}GB`);
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
- log(`Failed to load distilled weights \u2014 falling back to standard NF4`);
42026
+ log2(`Failed to load distilled weights \u2014 falling back to standard NF4`);
42027
42027
  }
42028
42028
  } else {
42029
- log(`Weight tier: ${tier} (${repoInfo.sizeGB}GB) \u2014 dequantizing to bf16 cache...`);
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
- log(`Using dequantized cache: ${(statSync13(cachedBf16).size / 1024 ** 3).toFixed(1)}GB`);
42046
+ log2(`Using dequantized cache: ${(statSync13(cachedBf16).size / 1024 ** 3).toFixed(1)}GB`);
42047
42047
  }
42048
42048
  } catch (e) {
42049
- log(`Dequantization failed \u2014 server will try to load original weights`);
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
- log(`Weight file not found \u2014 server will download on first run`);
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
- log(`Hybrid mode: PersonaPlex voice + ${ollamaModel} reasoning`);
42090
+ log2(`Hybrid mode: PersonaPlex voice + ${ollamaModel} reasoning`);
42091
42091
  }
42092
42092
  } catch {
42093
- log("Ollama not detected \u2014 running PersonaPlex standalone (no hybrid)");
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
- log(`GPU has ${caps.vramGB.toFixed(0)}GB VRAM \u2014 enabling CPU offload (model needs ~19GB)`);
42098
+ log2(`GPU has ${caps.vramGB.toFixed(0)}GB VRAM \u2014 enabling CPU offload (model needs ~19GB)`);
42099
42099
  }
42100
- log(`Starting PersonaPlex daemon (${tier} tier${hybridEnabled ? ", hybrid" : ""}${needsOffload ? ", cpu-offload" : ""})...`);
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
- log(`PersonaPlex daemon exited unexpectedly. Check ${fileLink2(LOG_FILE, "daemon.log")}`);
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
- log(`PersonaPlex ready at ${url}`);
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
- log(`Still loading... (${elapsed}s)`);
42173
+ log2(`Still loading... (${elapsed}s)`);
42174
42174
  }
42175
- log(`PersonaPlex daemon failed to start within 120s. Check ${fileLink2(LOG_FILE, "daemon.log")}`);
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 log = onInfo ?? (() => {
42232
+ const log2 = onInfo ?? (() => {
42233
42233
  });
42234
42234
  if (!isPersonaPlexInstalled()) {
42235
- log("PersonaPlex not installed. Run /voice personaplex first.");
42235
+ log2("PersonaPlex not installed. Run /voice personaplex first.");
42236
42236
  return null;
42237
42237
  }
42238
42238
  if (!existsSync37(inputWav)) {
42239
- log(`Input WAV not found: ${inputWav}`);
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
- log(`Voice "${voiceName}" already exists. Delete ${outputPt} to re-clone.`);
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
- log("clone-voice.py not found. Reinstall PersonaPlex.");
42251
+ log2("clone-voice.py not found. Reinstall PersonaPlex.");
42252
42252
  return null;
42253
42253
  }
42254
- log(`Cloning voice "${voiceName}" from ${inputWav}...`);
42255
- log("This requires loading the full 7B model \u2014 may take 30-60s...");
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
- log(l.trim());
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
- log(`Voice "${voiceName}" cloned successfully.`);
42283
+ log2(`Voice "${voiceName}" cloned successfully.`);
42284
42284
  resolve36(outputPt);
42285
42285
  } else {
42286
- log(`Voice cloning failed (exit ${code}).`);
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 log = onInfo ?? (() => {
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
- log(`Deployed voice: ${f.replace(".pt", "")}`);
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 log = onInfo ?? (() => {
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
- log(`Added ${customVoices.length} custom voice(s) to frontend: ${customVoices.map((v) => v.replace(".pt", "")).join(", ")}`);
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 log = onInfo ?? (() => {
42415
+ const log2 = onInfo ?? (() => {
42416
42416
  });
42417
42417
  const caps = detectPersonaPlexCapability();
42418
42418
  if (!caps.supported) {
42419
- log(`PersonaPlex not available: ${caps.reason}`);
42419
+ log2(`PersonaPlex not available: ${caps.reason}`);
42420
42420
  return null;
42421
42421
  }
42422
42422
  const tierInfo = WEIGHT_REPOS[caps.weightTier];
42423
- log(`GPU: ${caps.gpuName} (${caps.vramGB.toFixed(0)}GB) \u2192 ${caps.weightTier} weights (${tierInfo.sizeGB}GB${caps.needsHfToken ? "" : ", no HF token needed"})`);
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
- log("Installing PersonaPlex (first time setup)...");
42426
- const ok = await installPersonaPlex(log, caps.weightTier);
42425
+ log2("Installing PersonaPlex (first time setup)...");
42426
+ const ok = await installPersonaPlex(log2, caps.weightTier);
42427
42427
  if (!ok) {
42428
- log("PersonaPlex installation failed.");
42428
+ log2("PersonaPlex installation failed.");
42429
42429
  return null;
42430
42430
  }
42431
42431
  }
42432
- const deployed = provisionShippedVoices(log);
42432
+ const deployed = provisionShippedVoices(log2);
42433
42433
  if (deployed > 0) {
42434
- log(`Provisioned ${deployed} shipped voice(s)`);
42434
+ log2(`Provisioned ${deployed} shipped voice(s)`);
42435
42435
  }
42436
- patchFrontendVoiceList(log);
42436
+ patchFrontendVoiceList(log2);
42437
42437
  if (isPersonaPlexRunning()) {
42438
42438
  const url = getPersonaPlexWSUrl();
42439
42439
  if (url) {
42440
- log(`PersonaPlex already running at ${url}`);
42440
+ log2(`PersonaPlex already running at ${url}`);
42441
42441
  return url;
42442
42442
  }
42443
42443
  }
42444
- return await startPersonaPlexDaemon(log);
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(log) {
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
- log("Creating Python venv for vision deps...");
43634
+ log2("Creating Python venv for vision deps...");
43635
43635
  if (!hasCmd(pythonCmd) && !hasCmd("python3")) {
43636
- log(`${pythonCmd} not found \u2014 cannot create venv.`);
43636
+ log2(`${pythonCmd} not found \u2014 cannot create venv.`);
43637
43637
  return null;
43638
43638
  }
43639
43639
  if (!isWin2 && !hasVenvModule()) {
43640
- log("python3 venv module not available \u2014 venv creation skipped.");
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
- log("Python venv created at ~/.open-agents/venv");
43651
+ log2("Python venv created at ~/.open-agents/venv");
43652
43652
  return venvDir;
43653
43653
  } catch (err) {
43654
- log(`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`);
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, log, cachedPasswordRef) {
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
- log("Authentication failed \u2014 please re-enter password.");
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, log, cachedPasswordRef, timeoutMs = 12e4) {
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, log, cachedPasswordRef);
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
- log(`Install command failed (not an auth issue) \u2014 package may not be available for this OS.`);
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 log = onInfo ?? (() => {
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
- log(`No supported package manager (choco/winget/apt/brew) \u2014 ${d.label} unavailable. Install manually or install Chocolatey: https://chocolatey.org/install`);
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
- log(`Installing ${labels} via ${pm2} (requires admin \u2014 UAC prompt will appear)...`);
43763
+ log2(`Installing ${labels} via ${pm2} (requires admin \u2014 UAC prompt will appear)...`);
43764
43764
  }
43765
43765
  if (!winNeedsElevation)
43766
- log(`Installing ${labels} via ${pm2}...`);
43766
+ log2(`Installing ${labels} via ${pm2}...`);
43767
43767
  } else {
43768
- log(`Installing ${labels} via ${pm2}...`);
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
- log(`${d.label} not available for ${pm2}.`);
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, log, cachedPasswordRef, 18e4);
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
- log(`${d.label} installed.`);
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
- log(`${d.label} could not be installed \u2014 features will be limited.${reason}`);
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
- log("Installing python3-pip...");
43860
- const ok = await sudoInstall(pipCmd, getPassword, log, cachedPasswordRef);
43859
+ log2("Installing python3-pip...");
43860
+ const ok = await sudoInstall(pipCmd, getPassword, log2, cachedPasswordRef);
43861
43861
  if (!ok) {
43862
- log("python3-pip could not be installed \u2014 moondream-station may be unavailable.");
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
- log("Installing python3-venv...");
43883
- const ok = await sudoInstall(cmd, getPassword, log, cachedPasswordRef);
43882
+ log2("Installing python3-venv...");
43883
+ const ok = await sudoInstall(cmd, getPassword, log2, cachedPasswordRef);
43884
43884
  if (!ok) {
43885
- log("python3-venv could not be installed \u2014 moondream-station may be unavailable.");
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(log);
43894
+ const venv = ensureVenv(log2);
43895
43895
  if (venv && !hasCmd("moondream-station") && !existsSync38(venvMoondream)) {
43896
43896
  const venvPip2 = join55(venvBin, "pip");
43897
- log("Installing moondream-station in ~/.open-agents/venv...");
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
- log("moondream-station installed successfully.");
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
- log("moondream-station package installed.");
43906
+ log2("moondream-station package installed.");
43907
43907
  }
43908
43908
  } catch {
43909
- log("moondream-station install completed.");
43909
+ log2("moondream-station install completed.");
43910
43910
  }
43911
43911
  }
43912
43912
  } catch (err) {
43913
- log(`moondream-station install failed: ${err instanceof Error ? err.message : String(err)}`);
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
- log("Installing OCR Python stack (pytesseract, OpenCV, Pillow, numpy)...");
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
- log("OCR Python stack installed successfully.");
43932
+ log2("OCR Python stack installed successfully.");
43933
43933
  } catch {
43934
- log("OCR Python stack install completed but import verification failed.");
43934
+ log2("OCR Python stack install completed but import verification failed.");
43935
43935
  }
43936
43936
  } catch (err) {
43937
- log(`OCR Python stack install failed: ${err instanceof Error ? err.message : String(err)}`);
43937
+ log2(`OCR Python stack install failed: ${err instanceof Error ? err.message : String(err)}`);
43938
43938
  }
43939
43939
  }
43940
43940
  } else {
43941
- log("Python venv unavailable \u2014 advanced OCR pipeline will fall back to basic tesseract.");
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 log = onInfo ?? (() => {
43951
+ const log2 = onInfo ?? (() => {
43952
43952
  });
43953
- log("Installing cloudflared for live voice sessions...");
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
- log("curl not available \u2014 cannot install cloudflared.");
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
- log("cloudflared installed.");
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
- log("cloudflared installed.");
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
- log("cloudflared installed via Homebrew.");
43987
+ log2("cloudflared installed via Homebrew.");
43988
43988
  return true;
43989
43989
  }
43990
43990
  } catch {
43991
43991
  }
43992
43992
  }
43993
- log("cloudflared not available \u2014 live voice sessions disabled.");
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: mkdirSync31, existsSync: exists } = await import("node:fs");
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
- mkdirSync31(destDir, { recursive: true });
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: mkdirSync31 } = await import("node:fs");
52969
- const { join: join77, dirname: dirname24 } = await import("node:path");
52970
- const cachePath = join77(ctx.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
52971
- mkdirSync31(dirname24(cachePath), { recursive: true });
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: join77 } = await import("node:path");
53172
- const { existsSync: existsSync57 } = await import("node:fs");
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
- join77(thisDir, "..", "package.json"),
53177
- join77(thisDir, "..", "..", "package.json"),
53178
- join77(thisDir, "..", "..", "..", "package.json")
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 (existsSync57(pkgPath)) {
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 log = execSync32("git log --oneline -5 --no-decorate", { cwd: repoRoot, encoding: "utf-8", stdio: "pipe" }).trim();
54100
- if (log)
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
- ${log}`);
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/profiles.js
65242
- import { existsSync as existsSync52, readFileSync as readFileSync41, writeFileSync as writeFileSync25, mkdirSync as mkdirSync26, readdirSync as readdirSync20, unlinkSync as unlinkSync12 } from "node:fs";
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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
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 join69(homedir18(), ".open-agents", "profiles");
65950
+ return join70(homedir18(), ".open-agents", "profiles");
65248
65951
  }
65249
65952
  function projectProfileDir(projectDir) {
65250
- return join69(projectDir || process.cwd(), ".oa", "profiles");
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 (existsSync52(projDir)) {
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(readFileSync41(join69(projDir, f), "utf8"));
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 (existsSync52(globDir)) {
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(readFileSync41(join69(globDir, f), "utf8"));
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 = join69(projectProfileDir(projectDir), `${sanitized}.json`);
65295
- const globPath = join69(globalProfileDir(), `${sanitized}.json`);
65296
- const filePath = existsSync52(projPath) ? projPath : existsSync52(globPath) ? globPath : null;
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(readFileSync41(filePath, "utf8"));
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
- mkdirSync26(dir, { recursive: true });
66012
+ mkdirSync27(dir, { recursive: true });
65310
66013
  const sanitized = profile.name.replace(/[^a-zA-Z0-9_-]/g, "");
65311
- const filePath = join69(dir, `${sanitized}.json`);
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 = join69(dir, `${sanitized}.json`);
65325
- if (existsSync52(filePath)) {
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 join70, resolve as resolve31 } from "node:path";
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 mkdirSync27, writeFileSync as writeFileSync26, readFileSync as readFileSync42, readdirSync as readdirSync21, existsSync as existsSync53 } from "node:fs";
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
- join70(thisDir, "..", "package.json"),
65428
- join70(thisDir, "..", "..", "package.json"),
65429
- join70(thisDir, "..", "..", "..", "package.json")
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 (!existsSync53(pkgPath))
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 corsHeaders(res) {
65634
- res.setHeader("Access-Control-Allow-Origin", "*");
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 = join70(root, ".oa", "jobs");
65729
- mkdirSync27(dir, { recursive: true });
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 = join70(jobsDir(), `${id}.json`);
65734
- if (!existsSync53(file))
66442
+ const file = join71(jobsDir(), `${id}.json`);
66443
+ if (!existsSync54(file))
65735
66444
  return null;
65736
66445
  try {
65737
- return JSON.parse(readFileSync42(file, "utf-8"));
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 (!existsSync53(dir))
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(readFileSync42(join70(dir, file), "utf-8")));
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 = join70(dir, "..", "workspaces", id);
66144
- mkdirSync27(wsDir, { recursive: true });
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(join70(dir, `${id}.json`), JSON.stringify(job, null, 2));
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(join70(dir, `${id}.json`), JSON.stringify(job, null, 2));
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(join70(dir, `${id}.json`), JSON.stringify(job, null, 2));
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(join70(dir, `${id}.json`), JSON.stringify(job, null, 2));
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
- if (verbose) {
66463
- const ts = (/* @__PURE__ */ new Date()).toISOString().split("T")[1]?.slice(0, 8) ?? "";
66464
- process.stderr.write(`[${ts}] ${method} ${pathname}
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 log = options.quiet ? (_msg) => {
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 server = http.createServer((req, res) => {
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
- log(` Port ${port} in use \u2014 reclaiming...
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
- log(` Port ${port} still in use after reclaim attempt \u2014 API server skipped (non-fatal).
67488
+ log2(` Port ${port} still in use after reclaim attempt \u2014 API server skipped (non-fatal).
66733
67489
  `);
66734
67490
  } else {
66735
- log(` API server error (non-fatal): ${err.message}
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
- log(`
67512
+ log2(`
66757
67513
  open-agents API server v${version}
66758
67514
  `);
66759
- log(` Listening on http://${host}:${port}
67515
+ const proto = useTls ? "https" : "http";
67516
+ log2(` Listening on ${proto}://${host}:${port}
66760
67517
  `);
66761
- log(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
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
- log(` Auth: ${keyCount} scoped key(s) (read/run/admin)
67522
+ log2(` Auth: ${keyCount} scoped key(s) (read/run/admin)
66766
67523
  `);
66767
67524
  } else if (process.env["OA_API_KEY"]) {
66768
- log(` Auth: single Bearer token (admin scope)
67525
+ log2(` Auth: single Bearer token (admin scope)
66769
67526
  `);
66770
67527
  } else {
66771
- log(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
67528
+ log2(` Auth: disabled (set OA_API_KEY or OA_API_KEYS to enable)
66772
67529
  `);
66773
67530
  }
66774
- log(` Discovering sponsors in background...
67531
+ log2(` Discovering sponsors in background...
66775
67532
 
66776
67533
  `);
66777
67534
  refreshEndpointRegistry().then(() => {
66778
67535
  if (endpointRegistry.length > 1) {
66779
- log(` Sponsors: ${endpointRegistry.length - 1} endpoint(s) discovered
67536
+ log2(` Sponsors: ${endpointRegistry.length - 1} endpoint(s) discovered
66780
67537
  `);
66781
67538
  for (const ep of endpointRegistry.slice(1)) {
66782
- log(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
67539
+ log2(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
66783
67540
  `);
66784
67541
  }
66785
- log(`
67542
+ log2(`
66786
67543
  `);
66787
67544
  }
66788
67545
  }).catch(() => {
66789
67546
  });
66790
67547
  });
66791
67548
  const shutdown = () => {
66792
- log("\n Shutting down API server...\n");
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
- log(" Server stopped.\n");
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 join71, dirname as dirname22, extname as extname11 } from "node:path";
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 readFileSync43, writeFileSync as writeFileSync27, appendFileSync as appendFileSync4, rmSync as rmSync3, readdirSync as readdirSync22, mkdirSync as mkdirSync28 } from "node:fs";
66847
- import { existsSync as existsSync54 } from "node:fs";
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
- join71(thisDir, "..", "package.json"),
66869
- join71(thisDir, "..", "..", "package.json"),
66870
- join71(thisDir, "..", "..", "..", "package.json")
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 (existsSync54(pkgPath)) {
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
- join71(root, ".oa", "memory"),
67113
- join71(root, ".open-agents", "memory")
67874
+ join72(root, ".oa", "memory"),
67875
+ join72(root, ".open-agents", "memory")
67114
67876
  ];
67115
67877
  for (const dir of dirs) {
67116
- if (!existsSync54(dir))
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(readFileSync43(join71(dir, f), "utf-8"));
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 = join71(repoRoot, ".oa", "arche", "variants.json");
67278
- if (existsSync54(archeFile)) {
67279
- const variants = JSON.parse(readFileSync43(archeFile, "utf8"));
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 = join71(repoRoot, ".oa", "identity", "self-state.json");
67449
- if (existsSync54(ikStateFile)) {
67450
- const selfState = JSON.parse(readFileSync43(ikStateFile, "utf8"));
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 = join71(repoRoot, ".oa", "identity");
68095
- const ikFile = join71(ikDir, "self-state.json");
68856
+ const ikDir = join72(repoRoot, ".oa", "identity");
68857
+ const ikFile = join72(ikDir, "self-state.json");
68096
68858
  let ikState;
68097
- if (existsSync54(ikFile)) {
68098
- ikState = JSON.parse(readFileSync43(ikFile, "utf8"));
68859
+ if (existsSync55(ikFile)) {
68860
+ ikState = JSON.parse(readFileSync44(ikFile, "utf8"));
68099
68861
  } else {
68100
- mkdirSync28(ikDir, { recursive: true });
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 = join71(repoRoot, ".oa", "identity", "self-state.json");
68142
- if (existsSync54(ikFile)) {
68143
- const ikState = JSON.parse(readFileSync43(ikFile, "utf8"));
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 = join71(repoRoot, ".oa");
68246
- const nexusPidFile = join71(oaDir, "nexus", "daemon.pid");
68247
- if (existsSync54(nexusPidFile)) {
68248
- const pid = parseInt(readFileSync43(nexusPidFile, "utf8").trim(), 10);
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(join71(repoRoot, ".oa", "vault.enc"));
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 = join71(homedir19(), ".open-agents");
68809
- const HISTORY_FILE = join71(HISTORY_DIR, "repl-history");
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 (existsSync54(HISTORY_FILE)) {
68814
- const raw = readFileSync43(HISTORY_FILE, "utf8").trim();
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
- mkdirSync28(HISTORY_DIR, { recursive: true });
68918
- appendFileSync4(HISTORY_FILE, line + "\n", "utf8");
69679
+ mkdirSync29(HISTORY_DIR, { recursive: true });
69680
+ appendFileSync5(HISTORY_FILE, line + "\n", "utf8");
68919
69681
  if (Math.random() < 0.02) {
68920
- const all = readFileSync43(HISTORY_FILE, "utf8").trim().split("\n");
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 = join71(repoRoot, ".oa");
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 = join71(repoRoot, ".oa");
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 = join71(_hd(), ".open-agents", "agent-name");
69941
+ const globalNamePath = join72(_hd(), ".open-agents", "agent-name");
69180
69942
  let agName = "";
69181
69943
  try {
69182
- if (existsSync54(globalNamePath))
69183
- agName = readFileSync43(globalNamePath, "utf8").trim();
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: join71(repoRoot, ".oa"),
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: join71(repoRoot, ".oa") });
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 = join71(repoRoot, ".oa", "nexus", "daemon.pid");
70295
- if (existsSync54(nexusPidFile)) {
70296
- const pid = parseInt(readFileSync43(nexusPidFile, "utf8").trim(), 10);
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 = join71(repoRoot, OA_DIR, "nexus");
70493
- const pidFile = join71(nexusDir, "daemon.pid");
70494
- if (existsSync54(pidFile)) {
70495
- const pid = parseInt(readFileSync43(pidFile, "utf8").trim(), 10);
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 = join71(homedir19(), ".open-agents", "voice");
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 = join71(voiceDir2, pf);
70521
- if (existsSync54(pidPath)) {
71282
+ const pidPath = join72(voiceDir2, pf);
71283
+ if (existsSync55(pidPath)) {
70522
71284
  try {
70523
- const pid = parseInt(readFileSync43(pidPath, "utf8").trim(), 10);
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 = join71(repoRoot, OA_DIR);
70548
- if (existsSync54(oaPath)) {
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 = join71(homedir19(), ".open-agents", "voice", "personaplex", "daemon.pid");
70632
- const ppPortFile = join71(homedir19(), ".open-agents", "voice", "personaplex", "daemon.port");
70633
- if (existsSync54(ppPidFile)) {
70634
- const ppPid = parseInt(readFileSync43(ppPidFile, "utf8").trim(), 10);
70635
- const ppPort = existsSync54(ppPortFile) ? parseInt(readFileSync43(ppPortFile, "utf8").trim(), 10) : void 0;
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 = join71(repoRoot, ".oa", "nexus", "daemon.pid");
70642
- if (existsSync54(nexusPidFile)) {
70643
- const nPid = parseInt(readFileSync43(nexusPidFile, "utf8").trim(), 10);
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) && existsSync54(resolve32(repoRoot, cleanPath));
70986
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync54(resolve32(repoRoot, 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 = readFileSync43(imgPath);
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 (existsSync54(join71(repoRoot, ".oa", "context", "session-diary.md"))) {
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 = join71(repoRoot, ".oa", "identity");
71528
- const ikFile = join71(ikDir, "self-state.json");
72289
+ const ikDir = join72(repoRoot, ".oa", "identity");
72290
+ const ikFile = join72(ikDir, "self-state.json");
71529
72291
  let ikState;
71530
- if (existsSync54(ikFile)) {
71531
- ikState = JSON.parse(readFileSync43(ikFile, "utf8"));
72292
+ if (existsSync55(ikFile)) {
72293
+ ikState = JSON.parse(readFileSync44(ikFile, "utf8"));
71532
72294
  } else {
71533
- mkdirSync28(ikDir, { recursive: true });
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 = join71(repoRoot, ".oa", "arche");
71565
- const archeFile = join71(archeDir, "variants.json");
72326
+ const archeDir = join72(repoRoot, ".oa", "arche");
72327
+ const archeFile = join72(archeDir, "variants.json");
71566
72328
  let variants = [];
71567
72329
  try {
71568
- if (existsSync54(archeFile))
71569
- variants = JSON.parse(readFileSync43(archeFile, "utf8"));
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
- mkdirSync28(archeDir, { recursive: true });
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 = join71(repoRoot, ".oa", "memory", "metabolism", "store.json");
71591
- if (existsSync54(metaFile)) {
71592
- const store = JSON.parse(readFileSync43(metaFile, "utf8"));
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 = join71(repoRoot, ".oa", "memory");
71657
- mkdirSync28(dbDir, { recursive: true });
71658
- const db = initDb2(join71(dbDir, "structured.db"));
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 = join71(repoRoot, ".oa", "memory", "metabolism");
71674
- const storeFile = join71(metaDir, "store.json");
72435
+ const metaDir = join72(repoRoot, ".oa", "memory", "metabolism");
72436
+ const storeFile = join72(metaDir, "store.json");
71675
72437
  let store = [];
71676
72438
  try {
71677
- if (existsSync54(storeFile))
71678
- store = JSON.parse(readFileSync43(storeFile, "utf8"));
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
- mkdirSync28(metaDir, { recursive: true });
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 = join71(repoRoot, ".oa", "settings.json");
72463
+ const cohereSettingsFile = join72(repoRoot, ".oa", "settings.json");
71702
72464
  let cohereActive = false;
71703
72465
  try {
71704
- if (existsSync54(cohereSettingsFile)) {
71705
- const settings = JSON.parse(readFileSync43(cohereSettingsFile, "utf8"));
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 = join71(repoRoot, ".oa", "memory", "metabolism", "store.json");
71712
- if (existsSync54(metaFile)) {
71713
- const store = JSON.parse(readFileSync43(metaFile, "utf8"));
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 = join71(repoRoot, ".oa", "identity", "self-state.json");
71739
- if (existsSync54(ikFile)) {
71740
- const ikState = JSON.parse(readFileSync43(ikFile, "utf8"));
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 = join71(repoRoot, ".oa", "memory", "metabolism", "store.json");
71748
- if (existsSync54(metaFile)) {
71749
- const store = JSON.parse(readFileSync43(metaFile, "utf8"));
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 = join71(repoRoot, ".oa", "arche");
71761
- const archeFile = join71(archeDir, "variants.json");
72522
+ const archeDir = join72(repoRoot, ".oa", "arche");
72523
+ const archeFile = join72(archeDir, "variants.json");
71762
72524
  let variants = [];
71763
72525
  try {
71764
- if (existsSync54(archeFile))
71765
- variants = JSON.parse(readFileSync43(archeFile, "utf8"));
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
- mkdirSync28(archeDir, { recursive: true });
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 mkdirSync29, writeFileSync as writeFileSync28, readFileSync as readFileSync44, readdirSync as readdirSync23, existsSync as existsSync55 } from "node:fs";
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 join72 } from "node:path";
72615
+ import { join as join73 } from "node:path";
71854
72616
  function jobsDir2(repoPath) {
71855
72617
  const root = resolve33(repoPath ?? process.cwd());
71856
- const dir = join72(root, ".oa", "jobs");
71857
- mkdirSync29(dir, { recursive: true });
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(join72(dir, `${id}.json`), JSON.stringify(job, null, 2));
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(join72(dir, `${id}.json`), JSON.stringify(job, null, 2));
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 = join72(dir, `${jobId}.json`);
71975
- if (!existsSync55(file)) {
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(readFileSync44(file, "utf-8"));
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(readFileSync44(join72(dir, file), "utf-8"));
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 join73, relative as relative4, extname as extname12, basename as basename17 } from "node:path";
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(join73(this.config.rootDir, ".gitignore"), "utf-8");
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 = join73(this.config.rootDir, relativePath);
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: join73(current.path, part),
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 existsSync56, statSync as statSync17 } from "node:fs";
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 (!existsSync56(repoRoot)) {
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 join74, resolve as resolve35 } from "node:path";
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 (${join74(homedir20(), ".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 ${join74(repoRoot, ".oa", "settings.json")}`);
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 mkdirSync30, writeFileSync as writeFileSync29 } from "node:fs";
72735
- import { join as join75 } from "node:path";
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 = join75(tmpdir10(), `open-agents-eval-${Date.now()}`);
72861
- mkdirSync30(dir, { recursive: true });
72862
- writeFileSync29(join75(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
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 join76 } from "node:path";
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 = join76(dirname23(fileURLToPath15(import.meta.url)), "..", "package.json");
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: appendFileSync5, mkdirSync: mkdirSync31 } = __require("node:fs");
73305
- const { join: join77 } = __require("node:path");
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 = join77(homedir21(), ".open-agents");
73308
- mkdirSync31(logDir, { recursive: true });
73309
- appendFileSync5(join77(logDir, "crash.log"), logLine);
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 {