arkaos 3.73.0 → 3.73.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.73.0
1
+ 3.73.1
package/installer/cli.js CHANGED
@@ -92,10 +92,12 @@ async function main() {
92
92
  break;
93
93
  }
94
94
 
95
- case "doctor":
95
+ case "doctor": {
96
96
  const { doctor } = await import("./doctor.js");
97
- await doctor();
97
+ const fixMode = positionals.slice(1).includes("--fix") || values.fix === true;
98
+ await doctor({ fix: fixMode });
98
99
  break;
100
+ }
99
101
 
100
102
  case "update":
101
103
  const { update } = await import("./update.js");
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { execSync } from "node:child_process";
5
- import { getArkaosPython, getVenvPython, canImportCore, getRepoRoot } from "./python-resolver.js";
5
+ import { getArkaosPython, getVenvPython, canImportCore, getRepoRoot, diagnoseVenv, ensureVenvHealthy } from "./python-resolver.js";
6
6
  import { IS_WINDOWS, HOOK_EXT, CMD_FINDER } from "./platform.js";
7
7
  import { checkNode, checkObsidian, checkOllama } from "./system-tools.js";
8
8
 
@@ -54,10 +54,21 @@ const checks = [
54
54
  },
55
55
  {
56
56
  name: "venv",
57
- description: "ArkaOS virtual environment exists",
58
- severity: "warn",
59
- check: () => existsSync(getVenvPython()),
60
- fix: () => "Run: npx arkaos@latest update (creates venv automatically)",
57
+ // PR2 v3.73.1: promoted from "warn" to "fail" — without the venv, the
58
+ // dashboard cannot start at all (start-dashboard.{sh,ps1} now fail fast
59
+ // instead of falling back to ambient python3 with missing deps).
60
+ description: "ArkaOS virtual environment exists and is runnable",
61
+ severity: "fail",
62
+ check: () => {
63
+ const venvDir = join(INSTALL_DIR, "venv");
64
+ const d = diagnoseVenv(venvDir);
65
+ return d.healthy;
66
+ },
67
+ fix: () => {
68
+ const venvDir = join(INSTALL_DIR, "venv");
69
+ const d = diagnoseVenv(venvDir);
70
+ return `Run: npx arkaos doctor --fix (current state: ${d.reason})`;
71
+ },
61
72
  },
62
73
  {
63
74
  name: "hooks-dir",
@@ -241,8 +252,33 @@ if (IS_WINDOWS) {
241
252
  );
242
253
  }
243
254
 
244
- export async function doctor() {
245
- console.log("\n ArkaOS Doctor Health Checks\n");
255
+ export async function doctor(options = {}) {
256
+ const fixMode = !!options.fix;
257
+ console.log(`\n ArkaOS Doctor — Health Checks${fixMode ? " (--fix)" : ""}\n`);
258
+
259
+ // ─── --fix: repair the venv before reporting checks (PR2 v3.73.1) ────
260
+ // Targeted, idempotent self-heal: detects broken symlinks / version
261
+ // drift / missing bin/python and recreates the venv with --clear so
262
+ // the subsequent venv check has a chance of passing.
263
+ if (fixMode) {
264
+ const venvDir = join(INSTALL_DIR, "venv");
265
+ const before = diagnoseVenv(venvDir);
266
+ if (before.healthy) {
267
+ console.log(" ℹ Venv already healthy — no repair needed");
268
+ } else {
269
+ console.log(` → Repairing venv (current state: ${before.reason})`);
270
+ const result = ensureVenvHealthy({
271
+ venvDir,
272
+ log: (msg) => console.log(" " + msg.trim()),
273
+ });
274
+ if (result.healthy && result.repaired) {
275
+ console.log(" ✓ Venv repaired");
276
+ } else if (!result.healthy) {
277
+ console.log(` ✗ Venv repair failed (${result.reason})`);
278
+ }
279
+ }
280
+ console.log("");
281
+ }
246
282
 
247
283
  let passed = 0;
248
284
  let warned = 0;
@@ -6,7 +6,7 @@
6
6
  * and guarantees the doctor checks the same interpreter the installer uses.
7
7
  */
8
8
 
9
- import { existsSync, readFileSync } from "node:fs";
9
+ import { existsSync, lstatSync, readFileSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
  import { homedir, platform } from "node:os";
12
12
  import { execSync } from "node:child_process";
@@ -94,6 +94,134 @@ export function findSystemPython() {
94
94
  return null;
95
95
  }
96
96
 
97
+ /**
98
+ * Diagnose a venv directory. Pure read-only — does not modify anything.
99
+ * Returns { healthy: bool, reason: string, pythonPath?: string }.
100
+ *
101
+ * Reasons:
102
+ * - "missing" — venv dir absent OR bin/python absent (no symlink)
103
+ * - "broken-symlink" — bin/python is a symlink to a missing target
104
+ * (typical after Homebrew rotates Python patch versions)
105
+ * - "version-failed" — python --version exec failed (corrupt binary)
106
+ * - "ok" — venv healthy, python runs
107
+ */
108
+ export function diagnoseVenv(venvDir) {
109
+ const isWin = platform() === "win32";
110
+ const pythonPath = isWin
111
+ ? join(venvDir, "Scripts", "python.exe")
112
+ : join(venvDir, "bin", "python");
113
+
114
+ // existsSync FOLLOWS symlinks, so a broken symlink returns false here
115
+ // even when the symlink itself is present on disk. Distinguish via lstat.
116
+ if (!existsSync(pythonPath)) {
117
+ let isBroken = false;
118
+ try {
119
+ const stat = lstatSync(pythonPath);
120
+ if (stat.isSymbolicLink()) isBroken = true;
121
+ } catch {
122
+ // pythonPath doesn't exist at all — fall through as "missing"
123
+ }
124
+ return {
125
+ healthy: false,
126
+ reason: isBroken ? "broken-symlink" : "missing",
127
+ };
128
+ }
129
+
130
+ // pythonPath exists. Try to run it — guards against corrupt-but-present
131
+ // binaries (e.g., a non-executable file placed at bin/python by accident).
132
+ try {
133
+ const out = execSync(`"${pythonPath}" --version 2>&1`, {
134
+ stdio: "pipe",
135
+ timeout: 5000,
136
+ }).toString();
137
+ if (!/Python 3/.test(out)) {
138
+ return { healthy: false, reason: "version-failed", pythonPath };
139
+ }
140
+ return { healthy: true, reason: "ok", pythonPath };
141
+ } catch {
142
+ return { healthy: false, reason: "version-failed", pythonPath };
143
+ }
144
+ }
145
+
146
+
147
+ /**
148
+ * Ensure the venv is healthy, repairing if needed.
149
+ * Returns { healthy: bool, repaired: bool, reason: string }.
150
+ *
151
+ * Repair strategy: `python -m venv --clear` removes the stale bin/ Scripts
152
+ * directories (closing the broken-symlink and version-failed cases) and
153
+ * recreates them against the currently resolvable system Python. The
154
+ * post-repair venv is re-diagnosed to confirm health before returning.
155
+ *
156
+ * Options:
157
+ * - venvDir (default: ~/.arkaos/venv)
158
+ * - log (default: console.log)
159
+ * - skipDeps (default: false) — when true, do not attempt pip upgrades
160
+ * after repair. Used by tests to keep them fast/offline.
161
+ */
162
+ export function ensureVenvHealthy(options = {}) {
163
+ const venvDir = options.venvDir || join(INSTALL_DIR, "venv");
164
+ const log = options.log || console.log;
165
+ const skipDeps = !!options.skipDeps;
166
+
167
+ const diagnosis = diagnoseVenv(venvDir);
168
+ if (diagnosis.healthy) {
169
+ log(` ✓ Venv healthy at ${venvDir}`);
170
+ return { healthy: true, repaired: false, reason: "already-healthy" };
171
+ }
172
+
173
+ log(` ⚠ Venv ${diagnosis.reason} at ${venvDir} — repairing`);
174
+
175
+ const systemPython = findSystemPython();
176
+ if (!systemPython) {
177
+ return {
178
+ healthy: false,
179
+ repaired: false,
180
+ reason: `${diagnosis.reason}-and-no-system-python`,
181
+ };
182
+ }
183
+
184
+ try {
185
+ execSync(`"${systemPython}" -m venv --clear "${venvDir}"`, {
186
+ stdio: "pipe",
187
+ timeout: 60000,
188
+ });
189
+ log(` ✓ Venv recreated at ${venvDir}`);
190
+ } catch (err) {
191
+ const msg = (err && err.message ? err.message : String(err)).slice(0, 100);
192
+ return {
193
+ healthy: false,
194
+ repaired: false,
195
+ reason: `recreate-failed: ${msg}`,
196
+ };
197
+ }
198
+
199
+ const post = diagnoseVenv(venvDir);
200
+ if (!post.healthy) {
201
+ return {
202
+ healthy: false,
203
+ repaired: true,
204
+ reason: `repaired-but-still-unhealthy: ${post.reason}`,
205
+ };
206
+ }
207
+
208
+ if (!skipDeps) {
209
+ try {
210
+ execSync(`"${post.pythonPath}" -m pip install --upgrade pip --quiet`, {
211
+ stdio: "pipe",
212
+ timeout: 60000,
213
+ });
214
+ } catch { /* pip upgrade is non-critical */ }
215
+ }
216
+
217
+ return {
218
+ healthy: true,
219
+ repaired: true,
220
+ reason: `repaired-from-${diagnosis.reason}`,
221
+ };
222
+ }
223
+
224
+
97
225
  /**
98
226
  * Create the ArkaOS venv if it doesn't exist.
99
227
  * Returns true on success, false on failure.
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, writeFileSync, copyFileSync, chmodSync, mkdir
2
2
  import { join, dirname, resolve } from "node:path";
3
3
  import { homedir } from "node:os";
4
4
  import { execSync } from "node:child_process";
5
- import { ensureVenv, getArkaosPython, pipInstall } from "./python-resolver.js";
5
+ import { ensureVenv, ensureVenvHealthy, getArkaosPython, pipInstall } from "./python-resolver.js";
6
6
  import { getRuntimeConfig } from "./detect-runtime.js";
7
7
  import { loadAdapter } from "./index.js";
8
8
  import { migrateUserData, printMigrationReport } from "./migrate-user-data.js";
@@ -103,10 +103,15 @@ export async function update() {
103
103
  // ── 1. Update Python deps (using venv) ──
104
104
  console.log(" [1/8] Updating Python dependencies...");
105
105
 
106
- // Ensure venv exists (creates one if missing fixes PEP 668)
107
- const venvOk = ensureVenv((msg) => console.log(msg));
108
- if (!venvOk) {
109
- console.log(" \u26a0 Could not create venv — trying system Python with PEP 668 handling");
106
+ // Ensure venv is healthy (creates, repairs broken symlinks, or no-ops).
107
+ // PR2 v3.73.1 previously a stale broken-symlink venv could pass the
108
+ // existence check, and the dashboard would silently fall back to ambient
109
+ // python3 without sqlite-vec/fastembed.
110
+ const venvHealth = ensureVenvHealthy({ log: (msg) => console.log(msg) });
111
+ if (!venvHealth.healthy) {
112
+ console.log(` \u26a0 Venv unhealthy (${venvHealth.reason}) - falling back to system Python with PEP 668 handling`);
113
+ } else if (venvHealth.repaired) {
114
+ console.log(` \u2713 Venv repaired (${venvHealth.reason})`);
110
115
  }
111
116
 
112
117
  const pythonCmd = getArkaosPython();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "3.73.0",
3
+ "version": "3.73.1",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "3.73.0"
3
+ version = "3.73.1"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -82,10 +82,13 @@ Write-Host ''
82
82
  Write-Host ' ArkaOS Dashboard'
83
83
  Write-Host ' -----------------'
84
84
 
85
- # --- Locate Python ---------------------------------------------------------
86
- # Prefer the ArkaOS venv python recorded in the install manifest so the
87
- # dashboard API runs against the same interpreter the installer uses.
88
- function Find-Python {
85
+ # --- Locate ArkaOS venv Python (PR2 v3.73.1 — no ambient fallback) --------
86
+ # Previously this function fell back to system python/python3/py when the
87
+ # venv wasn't available. That hid broken-venv conditions and produced
88
+ # half-working dashboards without sqlite-vec/fastembed. Now we look only at
89
+ # the manifest pythonCmd and the venv path, and fail fast with actionable
90
+ # remediation otherwise.
91
+ function Find-VenvPython {
89
92
  $manifest = Join-Path $arkaosHome 'install-manifest.json'
90
93
  if (Test-Path -LiteralPath $manifest) {
91
94
  try {
@@ -97,17 +100,22 @@ function Find-Python {
97
100
  }
98
101
  $venvPy = Join-Path $arkaosHome 'venv\Scripts\python.exe'
99
102
  if (Test-Path -LiteralPath $venvPy) { return $venvPy }
100
- foreach ($cmd in 'python','python3','py') {
101
- $found = Get-Command $cmd -ErrorAction SilentlyContinue
102
- if ($found) { return $found.Source }
103
- }
104
103
  return $null
105
104
  }
106
105
 
107
- $python = Find-Python
106
+ $python = Find-VenvPython
108
107
  if (-not $python) {
109
- Write-Host ' Error: no usable Python interpreter found.' -ForegroundColor Red
110
- Write-Host ' Install Python 3.11+ and rerun.' -ForegroundColor DarkGray
108
+ Write-Host ''
109
+ Write-Host " X ArkaOS venv unavailable at $arkaosHome\venv\Scripts\python.exe" -ForegroundColor Red
110
+ Write-Host ''
111
+ Write-Host ' The dashboard must run from the ArkaOS venv so that' -ForegroundColor DarkGray
112
+ Write-Host ' sqlite-vec, fastembed, fastapi, and uvicorn are present.' -ForegroundColor DarkGray
113
+ Write-Host ' The ambient python fallback was removed in v3.73.1.' -ForegroundColor DarkGray
114
+ Write-Host ''
115
+ Write-Host ' Fix:' -ForegroundColor DarkGray
116
+ Write-Host ' npx arkaos doctor --fix (repairs broken venv in place)' -ForegroundColor DarkGray
117
+ Write-Host ' npx arkaos@latest update (full reinstall, slower)' -ForegroundColor DarkGray
118
+ Write-Host ''
111
119
  exit 1
112
120
  }
113
121
 
@@ -6,9 +6,31 @@ ARKAOS_ROOT="${ARKAOS_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
6
6
  DASHBOARD_DIR="${ARKAOS_ROOT}/dashboard"
7
7
  PID_FILE="$HOME/.arkaos/dashboard.pid"
8
8
  PORT_FILE="$HOME/.arkaos/dashboard.ports"
9
+ VENV_PYTHON="$HOME/.arkaos/venv/bin/python"
9
10
 
10
11
  mkdir -p "$HOME/.arkaos"
11
12
 
13
+ # ── Venv guard (PR2 v3.73.1 — Force Specialist Dispatch dogfood) ──
14
+ # Previously the dashboard fell back to ambient `python3` when the venv
15
+ # wasn't available. That hid broken-venv conditions (Homebrew patch
16
+ # rotations leaving dangling symlinks) and produced half-working dashboards
17
+ # without sqlite-vec / fastembed. Now we fail fast with a clear remediation.
18
+ # `[ -x ]` follows symlinks, so a broken symlink correctly fails the test.
19
+ if [ ! -x "$VENV_PYTHON" ]; then
20
+ echo ""
21
+ echo " ✗ ArkaOS venv unavailable at $VENV_PYTHON"
22
+ echo ""
23
+ echo " The dashboard must run from the ArkaOS venv so that"
24
+ echo " sqlite-vec, fastembed, fastapi, and uvicorn are present."
25
+ echo " The ambient python3 fallback was removed in v3.73.1."
26
+ echo ""
27
+ echo " Fix:"
28
+ echo " npx arkaos doctor --fix (repairs broken venv in place)"
29
+ echo " npx arkaos@latest update (full reinstall, slower)"
30
+ echo ""
31
+ exit 1
32
+ fi
33
+
12
34
  # ── Kill existing if running ──
13
35
  if [ -f "$PID_FILE" ]; then
14
36
  while read -r pid; do
@@ -38,7 +60,7 @@ echo " ─────────────────"
38
60
  # ── Start FastAPI backend ──
39
61
  API_LOG="$HOME/.arkaos/api.log"
40
62
  echo " Starting API on :${API_PORT}..."
41
- ARKAOS_ROOT="$ARKAOS_ROOT" python3 "${ARKAOS_ROOT}/scripts/dashboard-api.py" --port "$API_PORT" > "$API_LOG" 2>&1 &
63
+ ARKAOS_ROOT="$ARKAOS_ROOT" "$VENV_PYTHON" "${ARKAOS_ROOT}/scripts/dashboard-api.py" --port "$API_PORT" > "$API_LOG" 2>&1 &
42
64
  API_PID=$!
43
65
 
44
66
  # Wait for API with health check (up to 10 seconds)