open-agents-ai 0.185.73 → 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 +369 -177
  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 {
@@ -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
  }
@@ -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");
@@ -65534,7 +65534,24 @@ body {
65534
65534
  <textarea id="system-prompt" placeholder="System prompt (optional)"></textarea>
65535
65535
  </div>
65536
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>
65537
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>
65538
65555
 
65539
65556
  <div id="footer">
65540
65557
  <span id="system-prompt-toggle" onclick="toggleSystemPrompt()">sys</span>
@@ -65759,6 +65776,124 @@ function closeKeyModal() {
65759
65776
  document.getElementById('key-modal').classList.remove('visible');
65760
65777
  }
65761
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
+
65762
65897
  // Init
65763
65898
  checkHealth();
65764
65899
  loadModels();
@@ -65774,6 +65909,38 @@ var init_web_ui = __esm({
65774
65909
  }
65775
65910
  });
65776
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
+
65777
65944
  // packages/cli/dist/api/profiles.js
65778
65945
  import { existsSync as existsSync53, readFileSync as readFileSync42, writeFileSync as writeFileSync25, mkdirSync as mkdirSync27, readdirSync as readdirSync20, unlinkSync as unlinkSync12 } from "node:fs";
65779
65946
  import { join as join70 } from "node:path";
@@ -66995,11 +67162,6 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
66995
67162
  const startMs = performance.now();
66996
67163
  const requestId = req.headers["x-request-id"] || randomUUID4();
66997
67164
  res.setHeader("X-Request-ID", requestId);
66998
- if (verbose) {
66999
- const ts = (/* @__PURE__ */ new Date()).toISOString().split("T")[1]?.slice(0, 8) ?? "";
67000
- process.stderr.write(`[${ts}] ${requestId.slice(0, 8)} ${method} ${pathname}
67001
- `);
67002
- }
67003
67165
  if (method === "OPTIONS") {
67004
67166
  if (!corsHeaders(req, res))
67005
67167
  return;
@@ -67214,6 +67376,16 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
67214
67376
  });
67215
67377
  } finally {
67216
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
+ });
67217
67389
  recordAudit({
67218
67390
  ts: (/* @__PURE__ */ new Date()).toISOString(),
67219
67391
  requestId,
@@ -67228,7 +67400,7 @@ async function handleRequest(req, res, ollamaUrl, verbose) {
67228
67400
  }
67229
67401
  }
67230
67402
  function startApiServer(options = {}) {
67231
- const log = options.quiet ? (_msg) => {
67403
+ const log2 = options.quiet ? (_msg) => {
67232
67404
  } : (msg) => process.stderr.write(msg);
67233
67405
  let host = "127.0.0.1";
67234
67406
  let port = 11435;
@@ -67253,7 +67425,24 @@ function startApiServer(options = {}) {
67253
67425
  const ollamaUrl = options.ollamaUrl ?? config.backendUrl;
67254
67426
  const cwd4 = process.cwd();
67255
67427
  initAuditLog(join71(cwd4, ".oa"));
67256
- const server = http.createServer((req, res) => {
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) => {
67257
67446
  handleRequest(req, res, ollamaUrl, verbose).catch((err) => {
67258
67447
  metrics.totalErrors++;
67259
67448
  try {
@@ -67264,12 +67453,13 @@ function startApiServer(options = {}) {
67264
67453
  } catch {
67265
67454
  }
67266
67455
  });
67267
- });
67456
+ };
67457
+ const server = useTls ? https.createServer(tlsOpts, handler) : http.createServer(handler);
67268
67458
  let retried = false;
67269
67459
  server.on("error", (err) => {
67270
67460
  if (err.code === "EADDRINUSE" && !retried) {
67271
67461
  retried = true;
67272
- log(` Port ${port} in use \u2014 reclaiming...
67462
+ log2(` Port ${port} in use \u2014 reclaiming...
67273
67463
  `);
67274
67464
  try {
67275
67465
  const { execSync: es } = __require("node:child_process");
@@ -67295,10 +67485,10 @@ function startApiServer(options = {}) {
67295
67485
  server.listen(port, host);
67296
67486
  }, 2e3);
67297
67487
  } else if (err.code === "EADDRINUSE" && retried) {
67298
- 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).
67299
67489
  `);
67300
67490
  } else {
67301
- log(` API server error (non-fatal): ${err.message}
67491
+ log2(` API server error (non-fatal): ${err.message}
67302
67492
  `);
67303
67493
  }
67304
67494
  });
@@ -67319,43 +67509,44 @@ function startApiServer(options = {}) {
67319
67509
  });
67320
67510
  server.listen(port, host, () => {
67321
67511
  const version = getVersion3();
67322
- log(`
67512
+ log2(`
67323
67513
  open-agents API server v${version}
67324
67514
  `);
67325
- log(` Listening on http://${host}:${port}
67515
+ const proto = useTls ? "https" : "http";
67516
+ log2(` Listening on ${proto}://${host}:${port}
67326
67517
  `);
67327
- log(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
67518
+ log2(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
67328
67519
  `);
67329
67520
  if (process.env["OA_API_KEYS"]) {
67330
67521
  const keyCount = process.env["OA_API_KEYS"].split(",").length;
67331
- log(` Auth: ${keyCount} scoped key(s) (read/run/admin)
67522
+ log2(` Auth: ${keyCount} scoped key(s) (read/run/admin)
67332
67523
  `);
67333
67524
  } else if (process.env["OA_API_KEY"]) {
67334
- log(` Auth: single Bearer token (admin scope)
67525
+ log2(` Auth: single Bearer token (admin scope)
67335
67526
  `);
67336
67527
  } else {
67337
- 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)
67338
67529
  `);
67339
67530
  }
67340
- log(` Discovering sponsors in background...
67531
+ log2(` Discovering sponsors in background...
67341
67532
 
67342
67533
  `);
67343
67534
  refreshEndpointRegistry().then(() => {
67344
67535
  if (endpointRegistry.length > 1) {
67345
- log(` Sponsors: ${endpointRegistry.length - 1} endpoint(s) discovered
67536
+ log2(` Sponsors: ${endpointRegistry.length - 1} endpoint(s) discovered
67346
67537
  `);
67347
67538
  for (const ep of endpointRegistry.slice(1)) {
67348
- log(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
67539
+ log2(` ${ep.label}: ${ep.url} (${ep.type})${ep.authKey ? " [auth]" : ""}
67349
67540
  `);
67350
67541
  }
67351
- log(`
67542
+ log2(`
67352
67543
  `);
67353
67544
  }
67354
67545
  }).catch(() => {
67355
67546
  });
67356
67547
  });
67357
67548
  const shutdown = () => {
67358
- log("\n Shutting down API server...\n");
67549
+ log2("\n Shutting down API server...\n");
67359
67550
  for (const [id, child] of runningProcesses) {
67360
67551
  if (!child.killed) {
67361
67552
  child.kill("SIGTERM");
@@ -67363,7 +67554,7 @@ function startApiServer(options = {}) {
67363
67554
  runningProcesses.delete(id);
67364
67555
  }
67365
67556
  server.close(() => {
67366
- log(" Server stopped.\n");
67557
+ log2(" Server stopped.\n");
67367
67558
  process.exit(0);
67368
67559
  });
67369
67560
  setTimeout(() => process.exit(1), 5e3).unref();
@@ -67389,6 +67580,7 @@ var init_serve = __esm({
67389
67580
  init_config();
67390
67581
  init_audit_log();
67391
67582
  init_web_ui();
67583
+ init_logger();
67392
67584
  init_oa_directory();
67393
67585
  init_render();
67394
67586
  init_profiles();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.185.73",
3
+ "version": "0.185.74",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",