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 +1 -1
- package/core/governance/__pycache__/specialist_telemetry.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/specialist_enforcer.cpython-313.pyc +0 -0
- package/installer/cli.js +4 -2
- package/installer/doctor.js +43 -7
- package/installer/python-resolver.js +129 -1
- package/installer/update.js +10 -5
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/start-dashboard.ps1 +19 -11
- package/scripts/start-dashboard.sh +23 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.73.
|
|
1
|
+
3.73.1
|
|
Binary file
|
|
Binary file
|
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
|
-
|
|
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");
|
package/installer/doctor.js
CHANGED
|
@@ -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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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.
|
package/installer/update.js
CHANGED
|
@@ -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
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
package/pyproject.toml
CHANGED
|
@@ -82,10 +82,13 @@ Write-Host ''
|
|
|
82
82
|
Write-Host ' ArkaOS Dashboard'
|
|
83
83
|
Write-Host ' -----------------'
|
|
84
84
|
|
|
85
|
-
# --- Locate Python
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
|
|
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-
|
|
106
|
+
$python = Find-VenvPython
|
|
108
107
|
if (-not $python) {
|
|
109
|
-
Write-Host '
|
|
110
|
-
Write-Host
|
|
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"
|
|
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)
|