arkaos 2.10.0 → 2.11.0
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/README.md +318 -107
- package/VERSION +1 -1
- package/config/hooks/cwd-changed.ps1 +144 -0
- package/config/hooks/post-tool-use.ps1 +347 -0
- package/config/hooks/post-tool-use.sh +6 -6
- package/config/hooks/pre-compact.ps1 +238 -0
- package/config/hooks/pre-compact.sh +10 -6
- package/config/hooks/session-start.ps1 +109 -0
- package/config/hooks/session-start.sh +1 -1
- package/config/hooks/user-prompt-submit.ps1 +287 -0
- package/config/hooks/user-prompt-submit.sh +5 -2
- package/config/statusline.ps1 +160 -0
- package/core/cognition/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/collector.cpython-313.pyc +0 -0
- package/core/cognition/capture/__pycache__/store.cpython-313.pyc +0 -0
- package/core/cognition/insights/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/insights/__pycache__/store.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/obsidian.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/schemas.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/vector.cpython-313.pyc +0 -0
- package/core/cognition/memory/__pycache__/writer.cpython-313.pyc +0 -0
- package/core/cognition/research/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/research/__pycache__/profiler.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/cli.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/platform.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/daemon.py +77 -21
- package/core/cognition/scheduler/platform.py +43 -12
- package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/core/knowledge/vector_store.py +50 -25
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/layers.py +2 -2
- package/installer/adapters/claude-code.js +72 -45
- package/installer/cli.js +19 -6
- package/installer/doctor.js +130 -18
- package/installer/index.js +592 -149
- package/installer/platform.js +20 -0
- package/installer/prompts.js +109 -5
- package/installer/python-resolver.js +251 -0
- package/installer/update.js +497 -62
- package/package.json +1 -1
- package/pyproject.toml +2 -2
- package/scripts/start-dashboard.ps1 +271 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArkaOS Platform Helpers — single source of truth for platform branching.
|
|
3
|
+
*
|
|
4
|
+
* Centralises the ~19 scattered `process.platform === "win32"` checks into
|
|
5
|
+
* one importable module. Other installer files should import from here
|
|
6
|
+
* instead of inlining platform ternaries.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { platform } from "node:os";
|
|
10
|
+
|
|
11
|
+
export const IS_WINDOWS = platform() === "win32";
|
|
12
|
+
|
|
13
|
+
/** Hook script extension: `.ps1` on Windows, `.sh` elsewhere. */
|
|
14
|
+
export const HOOK_EXT = IS_WINDOWS ? ".ps1" : ".sh";
|
|
15
|
+
|
|
16
|
+
/** Command-line tool to locate executables. */
|
|
17
|
+
export const CMD_FINDER = IS_WINDOWS ? "where" : "which";
|
|
18
|
+
|
|
19
|
+
/** Python binary name (venv resolution is in python-resolver.js). */
|
|
20
|
+
export const PYTHON_CMD = IS_WINDOWS ? "python" : "python3";
|
package/installer/prompts.js
CHANGED
|
@@ -5,16 +5,26 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { createInterface } from "node:readline";
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// Readline interface is lazily created so headless upgrade runs can
|
|
13
|
+
// return without ever opening stdin. Eagerly constructing the interface
|
|
14
|
+
// at module import caused `npx arkaos install --force` to block on a
|
|
15
|
+
// closed-stdin pipe even when the wizard short-circuited below.
|
|
16
|
+
let rl = null;
|
|
17
|
+
function getRl() {
|
|
18
|
+
if (!rl) {
|
|
19
|
+
rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
20
|
+
}
|
|
21
|
+
return rl;
|
|
22
|
+
}
|
|
13
23
|
|
|
14
24
|
function ask(question, defaultValue = "") {
|
|
15
25
|
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
16
26
|
return new Promise((resolve) => {
|
|
17
|
-
|
|
27
|
+
getRl().question(` ${question}${suffix}: `, (answer) => {
|
|
18
28
|
resolve(answer.trim() || defaultValue);
|
|
19
29
|
});
|
|
20
30
|
});
|
|
@@ -23,7 +33,7 @@ function ask(question, defaultValue = "") {
|
|
|
23
33
|
function askYN(question, defaultYes = true) {
|
|
24
34
|
const suffix = defaultYes ? " [Y/n]" : " [y/N]";
|
|
25
35
|
return new Promise((resolve) => {
|
|
26
|
-
|
|
36
|
+
getRl().question(` ${question}${suffix}: `, (answer) => {
|
|
27
37
|
const a = answer.trim().toLowerCase();
|
|
28
38
|
if (!a) resolve(defaultYes);
|
|
29
39
|
else resolve(a === "y" || a === "yes");
|
|
@@ -37,7 +47,7 @@ function askChoice(question, options) {
|
|
|
37
47
|
options.forEach((opt, i) => {
|
|
38
48
|
console.log(` ${i + 1}) ${opt.label}`);
|
|
39
49
|
});
|
|
40
|
-
|
|
50
|
+
getRl().question(` Choose [1-${options.length}]: `, (answer) => {
|
|
41
51
|
const idx = parseInt(answer.trim()) - 1;
|
|
42
52
|
if (idx >= 0 && idx < options.length) {
|
|
43
53
|
resolve(options[idx].value);
|
|
@@ -48,7 +58,101 @@ function askChoice(question, options) {
|
|
|
48
58
|
});
|
|
49
59
|
}
|
|
50
60
|
|
|
61
|
+
// Canonical ArkaOS data directory. Mirrors the fallback used at the
|
|
62
|
+
// top of installer/index.js::install so headless upgrade detection
|
|
63
|
+
// can reach the existing profile.json before the wizard tries to ask
|
|
64
|
+
// the user where their install lives.
|
|
65
|
+
const DEFAULT_INSTALL_DIR = join(homedir(), ".arkaos");
|
|
66
|
+
|
|
67
|
+
// When called on an upgrade and a valid profile.json already exists,
|
|
68
|
+
// honor the rule documented in .claude/rules/node-installer.md:
|
|
69
|
+
//
|
|
70
|
+
// No interactive prompts during headless/CI runs
|
|
71
|
+
//
|
|
72
|
+
// An upgrade by definition means the user already answered these
|
|
73
|
+
// questions on a prior install, so re-asking every field (language,
|
|
74
|
+
// market, role, company, project dir, vault, feature flags, keys)
|
|
75
|
+
// blocks `npx arkaos install --force` even from a redirected-stdin
|
|
76
|
+
// context like `/dev/null` or CI. readline reads directly from
|
|
77
|
+
// process.stdin and does not honor those redirects.
|
|
78
|
+
//
|
|
79
|
+
// The short-circuit reads the existing profile and returns a config
|
|
80
|
+
// compatible with installer/index.js::install's downstream expectations:
|
|
81
|
+
//
|
|
82
|
+
// - User metadata fields (language, market, role, company,
|
|
83
|
+
// projectsDir, vaultPath) come from profile.json.
|
|
84
|
+
// - installDir is the directory we found the profile in.
|
|
85
|
+
// - Feature flags (installDashboard, installKnowledge,
|
|
86
|
+
// installTranscription) default to false on upgrade — an upgrade
|
|
87
|
+
// should NOT reinstall optional features; if the user wants to
|
|
88
|
+
// add one, they re-run a fresh install with the wizard. This
|
|
89
|
+
// preserves the existing install exactly.
|
|
90
|
+
// - API key fields are left empty; keys live in keys.json and the
|
|
91
|
+
// installer already merges them non-destructively.
|
|
92
|
+
//
|
|
93
|
+
// Returns null when the short-circuit conditions are not met, which
|
|
94
|
+
// falls through to the interactive wizard below (fresh install or
|
|
95
|
+
// upgrade with a corrupt/missing profile).
|
|
96
|
+
function loadExistingProfileConfig(installDir) {
|
|
97
|
+
const profilePath = join(installDir, "profile.json");
|
|
98
|
+
if (!existsSync(profilePath)) return null;
|
|
99
|
+
try {
|
|
100
|
+
const profile = JSON.parse(readFileSync(profilePath, "utf-8"));
|
|
101
|
+
// Require the fields the downstream installer flow actually uses.
|
|
102
|
+
// Any missing field forces the wizard so we never write a
|
|
103
|
+
// half-populated profile back to disk on upgrade.
|
|
104
|
+
if (!profile.language || !profile.role) return null;
|
|
105
|
+
return {
|
|
106
|
+
language: profile.language,
|
|
107
|
+
market: profile.market || "",
|
|
108
|
+
role: profile.role,
|
|
109
|
+
company: profile.company || "",
|
|
110
|
+
projectsDir: profile.projectsDir || join(homedir(), "Projects"),
|
|
111
|
+
vaultPath: profile.vaultPath || "",
|
|
112
|
+
installDir,
|
|
113
|
+
// Upgrades preserve the existing install footprint; they do not
|
|
114
|
+
// re-opt-in to optional features. A user who wants to flip a
|
|
115
|
+
// feature flag on upgrade can re-run with the wizard.
|
|
116
|
+
// Exception: knowledge deps are always installed on upgrade because
|
|
117
|
+
// they are required for `npx arkaos index/search` to use vector
|
|
118
|
+
// search instead of degraded keyword fallback.
|
|
119
|
+
installDashboard: false,
|
|
120
|
+
installKnowledge: true,
|
|
121
|
+
installTranscription: false,
|
|
122
|
+
openaiKey: "",
|
|
123
|
+
googleKey: "",
|
|
124
|
+
falKey: "",
|
|
125
|
+
};
|
|
126
|
+
} catch {
|
|
127
|
+
// Corrupt profile.json — fall through to the wizard so the user
|
|
128
|
+
// has a path to fix it.
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
51
133
|
export async function runSetupPrompts(isUpgrade = false) {
|
|
134
|
+
// Headless upgrade short-circuit. See loadExistingProfileConfig.
|
|
135
|
+
if (isUpgrade) {
|
|
136
|
+
const existing = loadExistingProfileConfig(DEFAULT_INSTALL_DIR);
|
|
137
|
+
if (existing) {
|
|
138
|
+
console.log(`
|
|
139
|
+
╔══════════════════════════════════════════════════════╗
|
|
140
|
+
║ ArkaOS Upgrade — using existing profile ║
|
|
141
|
+
╚══════════════════════════════════════════════════════╝
|
|
142
|
+
|
|
143
|
+
Language: ${existing.language}
|
|
144
|
+
Market: ${existing.market || "(not set)"}
|
|
145
|
+
Role: ${existing.role}
|
|
146
|
+
Company: ${existing.company || "(not set)"}
|
|
147
|
+
Install dir: ${existing.installDir}
|
|
148
|
+
|
|
149
|
+
Optional features are NOT re-installed on upgrade.
|
|
150
|
+
Re-run without --force to flip feature flags interactively.
|
|
151
|
+
`);
|
|
152
|
+
return existing;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
52
156
|
console.log(`
|
|
53
157
|
╔══════════════════════════════════════════════════════╗
|
|
54
158
|
║ ArkaOS Setup — Let's configure your environment ║
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArkaOS Python Resolver — single source of truth for Python interpreter.
|
|
3
|
+
*
|
|
4
|
+
* Strategy: always use ~/.arkaos/venv. This isolates ArkaOS from system
|
|
5
|
+
* Python, avoids PEP 668 issues on macOS/Homebrew and Ubuntu 23.04+,
|
|
6
|
+
* and guarantees the doctor checks the same interpreter the installer uses.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { homedir, platform } from "node:os";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
|
|
14
|
+
const INSTALL_DIR = join(homedir(), ".arkaos");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Return the path to the venv's Python interpreter.
|
|
18
|
+
* On Windows: ~/.arkaos/venv/Scripts/python.exe
|
|
19
|
+
* On Unix: ~/.arkaos/venv/bin/python
|
|
20
|
+
*/
|
|
21
|
+
export function getVenvPython() {
|
|
22
|
+
if (platform() === "win32") {
|
|
23
|
+
return join(INSTALL_DIR, "venv", "Scripts", "python.exe");
|
|
24
|
+
}
|
|
25
|
+
return join(INSTALL_DIR, "venv", "bin", "python");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Return the path to the venv's pip.
|
|
30
|
+
*/
|
|
31
|
+
export function getVenvPip() {
|
|
32
|
+
if (platform() === "win32") {
|
|
33
|
+
return join(INSTALL_DIR, "venv", "Scripts", "pip.exe");
|
|
34
|
+
}
|
|
35
|
+
return join(INSTALL_DIR, "venv", "bin", "pip");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Return the ArkaOS Python interpreter — venv if it exists, fallback to system.
|
|
40
|
+
* This is the ONLY function other modules should use to get a Python path.
|
|
41
|
+
*/
|
|
42
|
+
export function getArkaosPython() {
|
|
43
|
+
const venvPy = getVenvPython();
|
|
44
|
+
if (existsSync(venvPy)) {
|
|
45
|
+
return venvPy;
|
|
46
|
+
}
|
|
47
|
+
// Fallback to system Python (for pre-venv installations)
|
|
48
|
+
return findSystemPython();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Return the ArkaOS pip — venv if it exists, fallback to system.
|
|
53
|
+
*/
|
|
54
|
+
export function getArkaosPip() {
|
|
55
|
+
const venvPip = getVenvPip();
|
|
56
|
+
if (existsSync(venvPip)) {
|
|
57
|
+
return venvPip;
|
|
58
|
+
}
|
|
59
|
+
// Fallback: use python -m pip with PEP 668 handling
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Find system Python 3.11+ (used only during initial venv creation).
|
|
65
|
+
*/
|
|
66
|
+
export function findSystemPython() {
|
|
67
|
+
const isWindows = platform() === "win32";
|
|
68
|
+
const candidates = ["python3", "python"];
|
|
69
|
+
const finder = isWindows ? "where" : "which";
|
|
70
|
+
|
|
71
|
+
for (const cmd of candidates) {
|
|
72
|
+
try {
|
|
73
|
+
// stderr redirected via stdio, not shell operators, so it works the
|
|
74
|
+
// same on bash, zsh, and cmd.exe.
|
|
75
|
+
const version = execSync(`${cmd} --version`, {
|
|
76
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
77
|
+
}).toString().trim();
|
|
78
|
+
const match = version.match(/(\d+)\.(\d+)/);
|
|
79
|
+
if (match && parseInt(match[1]) >= 3 && parseInt(match[2]) >= 11) {
|
|
80
|
+
// Resolve to an absolute path using the platform-native locator.
|
|
81
|
+
try {
|
|
82
|
+
const resolved = execSync(`${finder} ${cmd}`, {
|
|
83
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
84
|
+
}).toString().trim().split(/\r?\n/)[0];
|
|
85
|
+
return resolved || cmd;
|
|
86
|
+
} catch {
|
|
87
|
+
return cmd;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create the ArkaOS venv if it doesn't exist.
|
|
99
|
+
* Returns true on success, false on failure.
|
|
100
|
+
*/
|
|
101
|
+
export function ensureVenv(log = console.log) {
|
|
102
|
+
const venvDir = join(INSTALL_DIR, "venv");
|
|
103
|
+
const venvPy = getVenvPython();
|
|
104
|
+
|
|
105
|
+
if (existsSync(venvPy)) {
|
|
106
|
+
return true; // Already exists
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const systemPython = findSystemPython();
|
|
110
|
+
if (!systemPython) {
|
|
111
|
+
log(" \u26a0 No Python 3.11+ found — cannot create venv");
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
execSync(`"${systemPython}" -m venv "${venvDir}"`, { stdio: "pipe", timeout: 60000 });
|
|
117
|
+
log(` \u2713 Virtual environment created at ${venvDir}`);
|
|
118
|
+
|
|
119
|
+
// Upgrade pip inside the venv
|
|
120
|
+
try {
|
|
121
|
+
execSync(`"${venvPy}" -m pip install --upgrade pip --quiet`, { stdio: "pipe", timeout: 60000 });
|
|
122
|
+
} catch { /* pip upgrade is non-critical */ }
|
|
123
|
+
|
|
124
|
+
return true;
|
|
125
|
+
} catch (err) {
|
|
126
|
+
log(` \u26a0 Failed to create venv: ${err.message.slice(0, 100)}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Extract a meaningful error message from a failed execSync error.
|
|
133
|
+
* Prefers stderr (the actual pip output), falls back to err.message.
|
|
134
|
+
* Collapses multi-line output into a single line and caps the length
|
|
135
|
+
* so a traceback does not dominate the installer log, while keeping
|
|
136
|
+
* the failing package name and the key "No matching distribution" /
|
|
137
|
+
* "error: subprocess-exited-with-error" markers visible.
|
|
138
|
+
*/
|
|
139
|
+
function formatPipError(err) {
|
|
140
|
+
let text = "";
|
|
141
|
+
if (err.stderr) {
|
|
142
|
+
text = err.stderr.toString();
|
|
143
|
+
} else if (err.stdout) {
|
|
144
|
+
text = err.stdout.toString();
|
|
145
|
+
} else if (err.message) {
|
|
146
|
+
text = err.message;
|
|
147
|
+
}
|
|
148
|
+
text = text
|
|
149
|
+
.replace(/\r/g, "")
|
|
150
|
+
.split("\n")
|
|
151
|
+
.map((l) => l.trimEnd())
|
|
152
|
+
.filter((l) => l.trim().length > 0)
|
|
153
|
+
.join(" | ");
|
|
154
|
+
if (text.length > 400) text = text.slice(0, 400) + "...";
|
|
155
|
+
return text;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Install Python packages using the ArkaOS interpreter.
|
|
160
|
+
* Uses venv pip (no PEP 668 issues) or falls back with --break-system-packages.
|
|
161
|
+
*/
|
|
162
|
+
export function pipInstall(packages, opts = {}) {
|
|
163
|
+
const { quiet = true, upgrade = false, editable = null, timeout = 120000, log = console.log } = opts;
|
|
164
|
+
|
|
165
|
+
const venvPip = getArkaosPip();
|
|
166
|
+
const arkaosPy = getArkaosPython();
|
|
167
|
+
|
|
168
|
+
const flags = [];
|
|
169
|
+
if (quiet) flags.push("--quiet");
|
|
170
|
+
if (upgrade) flags.push("--upgrade");
|
|
171
|
+
|
|
172
|
+
const pkgArg = editable ? `-e "${editable}"` : packages;
|
|
173
|
+
|
|
174
|
+
// Strategy 1: Use venv pip directly (preferred — no PEP 668)
|
|
175
|
+
if (venvPip && existsSync(venvPip)) {
|
|
176
|
+
try {
|
|
177
|
+
execSync(`"${venvPip}" install ${flags.join(" ")} ${pkgArg}`, { stdio: "pipe", timeout });
|
|
178
|
+
return true;
|
|
179
|
+
} catch (err) {
|
|
180
|
+
log(` \u26a0 Venv pip install failed for ${packages}: ${formatPipError(err)}`);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Strategy 2: Use python -m pip (system Python — handle PEP 668)
|
|
186
|
+
if (arkaosPy) {
|
|
187
|
+
// Try without --break-system-packages first
|
|
188
|
+
try {
|
|
189
|
+
execSync(`"${arkaosPy}" -m pip install ${flags.join(" ")} ${pkgArg}`, { stdio: "pipe", timeout });
|
|
190
|
+
return true;
|
|
191
|
+
} catch (err) {
|
|
192
|
+
const stderr = err.stderr ? err.stderr.toString() : err.message;
|
|
193
|
+
if (stderr.includes("externally-managed-environment")) {
|
|
194
|
+
// PEP 668 — retry with --break-system-packages
|
|
195
|
+
log(" \u26a0 PEP 668 detected — retrying with --break-system-packages");
|
|
196
|
+
try {
|
|
197
|
+
execSync(`"${arkaosPy}" -m pip install --break-system-packages ${flags.join(" ")} ${pkgArg}`, {
|
|
198
|
+
stdio: "pipe", timeout,
|
|
199
|
+
});
|
|
200
|
+
return true;
|
|
201
|
+
} catch (err2) {
|
|
202
|
+
log(` \u26a0 pip install with --break-system-packages also failed for ${packages}: ${formatPipError(err2)}`);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
log(` \u26a0 pip install failed for ${packages}: ${formatPipError(err)}`);
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
log(" \u26a0 No Python interpreter available for pip install");
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Check if the ArkaOS Python can import the core engine.
|
|
217
|
+
*/
|
|
218
|
+
export function canImportCore() {
|
|
219
|
+
const py = getArkaosPython();
|
|
220
|
+
if (!py || !existsSync(py)) return false;
|
|
221
|
+
try {
|
|
222
|
+
execSync(`"${py}" -c "from core.synapse.engine import SynapseEngine"`, {
|
|
223
|
+
stdio: "pipe",
|
|
224
|
+
timeout: 10000,
|
|
225
|
+
cwd: getRepoRoot(),
|
|
226
|
+
});
|
|
227
|
+
return true;
|
|
228
|
+
} catch {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get the ArkaOS repo root from .repo-path or manifest.
|
|
235
|
+
*/
|
|
236
|
+
export function getRepoRoot() {
|
|
237
|
+
const repoPathFile = join(INSTALL_DIR, ".repo-path");
|
|
238
|
+
if (existsSync(repoPathFile)) {
|
|
239
|
+
const p = readFileSync(repoPathFile, "utf-8").trim();
|
|
240
|
+
if (existsSync(p)) return p;
|
|
241
|
+
}
|
|
242
|
+
// Fallback: try manifest
|
|
243
|
+
const manifestPath = join(INSTALL_DIR, "install-manifest.json");
|
|
244
|
+
if (existsSync(manifestPath)) {
|
|
245
|
+
try {
|
|
246
|
+
const m = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
247
|
+
if (m.repoRoot && existsSync(m.repoRoot)) return m.repoRoot;
|
|
248
|
+
} catch {}
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|