arkaos 4.1.0 → 4.2.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/VERSION +1 -1
- package/arka/skills/flow/SKILL.md +15 -0
- package/config/constitution.yaml +15 -7
- package/config/hooks/post-tool-use.sh +16 -1
- package/config/hooks/pre-tool-use.sh +16 -1
- package/config/hooks/session-start.sh +22 -0
- package/config/hooks/stop.sh +16 -1
- package/config/hooks/user-prompt-submit.sh +16 -1
- package/config/models.yaml +70 -0
- package/core/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/cognition/__pycache__/dreaming.cpython-313.pyc +0 -0
- package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/persistence.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/renderer.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
- package/core/forge/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/closing_marker_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/compliance_telemetry.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/constitution.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/kb_cite_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/meta_tag_check.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/meta_tag_check.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/qg_verdict.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/skill_proposer.cpython-313.pyc +0 -0
- package/core/governance/__pycache__/skill_proposer.cpython-314.pyc +0 -0
- package/core/governance/__pycache__/sycophancy_detector.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/_shared.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/pre_tool_use.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-312.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
- package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
- package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/core/hooks/pre_tool_use.py +55 -3
- package/core/jobs/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/jobs/__pycache__/auto_doc_worker.cpython-314.pyc +0 -0
- package/core/jobs/__pycache__/manager.cpython-314.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/embedder.cpython-313.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-312.pyc +0 -0
- package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
- package/core/memory/__pycache__/session_store.cpython-314.pyc +0 -0
- package/core/obsidian/__pycache__/relator.cpython-313.pyc +0 -0
- package/core/obsidian/relator.py +18 -8
- package/core/runtime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/base.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/capabilities_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/claude_code.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/codex_cli.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/cost_governor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/cursor.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/gemini_cli.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_provider.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_retry.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/model_router.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/model_router_cli.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/native_usage.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/ollama_provider.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/openrouter_provider.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/openrouter_provider.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/pricing.cpython-314.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-312.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/registry.cpython-314.pyc +0 -0
- package/core/runtime/llm_provider.py +10 -1
- package/core/runtime/model_router.py +204 -0
- package/core/runtime/model_router_cli.py +82 -0
- package/core/runtime/openrouter_provider.py +172 -0
- package/core/shared/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/shared/__pycache__/safe_session_id.cpython-314.pyc +0 -0
- package/core/shared/__pycache__/test_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
- package/core/shared/__pycache__/test_evidence.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/cache.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/graph_context_layer.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/kb_cache.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-314.pyc +0 -0
- package/core/sync/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/sync/__pycache__/engine.cpython-312.pyc +0 -0
- package/core/sync/__pycache__/manifest.cpython-312.pyc +0 -0
- package/core/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/flow_enforcer.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/frontend_gate.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/gate_checkpoint.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/loader.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/marker_cache.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/research_gate.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/schema.cpython-314.pyc +0 -0
- package/core/workflow/__pycache__/specialist_enforcer.cpython-313.pyc +0 -0
- package/core/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/core/workflow/frontend_gate.py +162 -0
- package/installer/autostart.js +16 -2
- package/installer/cli.js +23 -0
- package/installer/keys.js +1 -0
- package/package.json +1 -1
- package/pyproject.toml +3 -1
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
- package/scripts/start-dashboard.sh +25 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Frontend excellence gate — PreToolUse enforcement for UI file edits.
|
|
2
|
+
|
|
3
|
+
Constitution: ``excellence-mandate`` (NON-NEGOTIABLE, Excellence Reform
|
|
4
|
+
2026-07-05). UI work must load the frontend design skills and judge
|
|
5
|
+
against a named benchmark BEFORE code is written. This gate makes that
|
|
6
|
+
duty mechanical: a Write/Edit/MultiEdit touching a UI file requires an
|
|
7
|
+
``[arka:design] <skills/benchmark>`` marker in the recent assistant
|
|
8
|
+
messages — the same ceremony contract as ``[arka:routing]``
|
|
9
|
+
(flow_enforcer) and ``[arka:dispatch]`` (specialist_enforcer).
|
|
10
|
+
|
|
11
|
+
Modes via ``hooks.frontendGate`` in ``~/.arkaos/config.json``:
|
|
12
|
+
|
|
13
|
+
absent / "warn" → nudge on stderr, allow (rollout default)
|
|
14
|
+
true / "hard" → deny UI edits without the marker
|
|
15
|
+
false / "off" → gate disabled
|
|
16
|
+
|
|
17
|
+
Bypasses: ``[arka:trivial] <reason>`` marker (same contract as the
|
|
18
|
+
evidence flow — single-file edits under 10 lines) and
|
|
19
|
+
``ARKA_BYPASS_DESIGN=1`` env (logged for accountability).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import re
|
|
27
|
+
from dataclasses import asdict, dataclass
|
|
28
|
+
from datetime import datetime, timezone
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
from core.shared import safe_session_id as _safe_session_id_module
|
|
32
|
+
from core.workflow.flow_enforcer import (
|
|
33
|
+
TRIVIAL_RE,
|
|
34
|
+
_load_last_assistant_messages,
|
|
35
|
+
)
|
|
36
|
+
from core.workflow.specialist_enforcer import _locked_append
|
|
37
|
+
|
|
38
|
+
CONFIG_PATH = Path.home() / ".arkaos" / "config.json"
|
|
39
|
+
TELEMETRY_PATH = Path.home() / ".arkaos" / "telemetry" / "frontend-gate.jsonl"
|
|
40
|
+
|
|
41
|
+
ASSISTANT_WINDOW = 20
|
|
42
|
+
|
|
43
|
+
DESIGN_RE = re.compile(r"\[arka:design\]\s*\S+", re.IGNORECASE)
|
|
44
|
+
|
|
45
|
+
_GATED_TOOLS = frozenset({"Write", "Edit", "MultiEdit"})
|
|
46
|
+
|
|
47
|
+
# Component + stylesheet extensions. Plain .ts/.js are excluded: they are
|
|
48
|
+
# ambiguous (stores, utils, configs) and would flood the gate with false
|
|
49
|
+
# positives — the component file is where the visual decision lands.
|
|
50
|
+
UI_SUFFIXES = frozenset({
|
|
51
|
+
".vue", ".tsx", ".jsx", ".svelte", ".astro",
|
|
52
|
+
".css", ".scss", ".sass", ".less",
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class Decision:
|
|
58
|
+
"""Outcome of frontend-gate evaluation."""
|
|
59
|
+
|
|
60
|
+
allow: bool
|
|
61
|
+
reason: str
|
|
62
|
+
mode: str = "warn"
|
|
63
|
+
target_file: str = ""
|
|
64
|
+
marker_found: str | None = None
|
|
65
|
+
|
|
66
|
+
def to_stderr_message(self) -> str:
|
|
67
|
+
if self.reason not in ("no-design-marker",):
|
|
68
|
+
return ""
|
|
69
|
+
head = "[arka:suggest]" if self.allow else "[ARKA:DESIGN]"
|
|
70
|
+
verb = "should" if self.allow else "MUST"
|
|
71
|
+
return (
|
|
72
|
+
f"{head} UI edit to {self.target_file or 'this file'} without "
|
|
73
|
+
f"design evidence. Frontend work {verb} load the design skills "
|
|
74
|
+
f"(frontend-design, ui-ux-pro-max, project design system) at "
|
|
75
|
+
f"maximum effort and judge against a named benchmark FIRST "
|
|
76
|
+
f"(constitution `excellence-mandate`). Emit "
|
|
77
|
+
f"`[arka:design] <skills> benchmark=<name>` before UI edits, "
|
|
78
|
+
f"or `[arka:trivial] <reason>` for sub-10-line fixes."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _mode() -> str:
|
|
83
|
+
"""Resolve ``hooks.frontendGate`` to 'off' | 'warn' | 'hard'."""
|
|
84
|
+
if not CONFIG_PATH.exists():
|
|
85
|
+
return "warn"
|
|
86
|
+
try:
|
|
87
|
+
data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
|
|
88
|
+
except (json.JSONDecodeError, OSError):
|
|
89
|
+
return "warn"
|
|
90
|
+
raw = data.get("hooks", {}).get("frontendGate", "warn")
|
|
91
|
+
if raw in (False, "off", "false"):
|
|
92
|
+
return "off"
|
|
93
|
+
if raw in (True, "hard", "true"):
|
|
94
|
+
return "hard"
|
|
95
|
+
return "warn"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def is_ui_file(file_path: str) -> bool:
|
|
99
|
+
"""True when the path carries a gated UI extension."""
|
|
100
|
+
return Path(file_path).suffix.lower() in UI_SUFFIXES
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _scan(messages: list[str]) -> str | None:
|
|
104
|
+
"""Return the matched evidence marker, or None."""
|
|
105
|
+
for message in messages:
|
|
106
|
+
match = DESIGN_RE.search(message) or TRIVIAL_RE.search(message)
|
|
107
|
+
if match:
|
|
108
|
+
return match.group(0)
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def evaluate(
|
|
113
|
+
tool_name: str,
|
|
114
|
+
transcript_path: str,
|
|
115
|
+
session_id: str,
|
|
116
|
+
cwd: str,
|
|
117
|
+
tool_input: dict,
|
|
118
|
+
messages: list[str] | None = None,
|
|
119
|
+
) -> Decision:
|
|
120
|
+
"""Evaluate one tool call against the frontend excellence gate."""
|
|
121
|
+
file_path = str(tool_input.get("file_path", ""))
|
|
122
|
+
if tool_name not in _GATED_TOOLS or not is_ui_file(file_path):
|
|
123
|
+
return Decision(allow=True, reason="not-ui-scope", target_file=file_path)
|
|
124
|
+
mode = _mode()
|
|
125
|
+
if mode == "off":
|
|
126
|
+
return Decision(allow=True, reason="flag-off", mode=mode,
|
|
127
|
+
target_file=file_path)
|
|
128
|
+
if os.environ.get("ARKA_BYPASS_DESIGN") == "1":
|
|
129
|
+
return Decision(allow=True, reason="env-bypass", mode=mode,
|
|
130
|
+
target_file=file_path)
|
|
131
|
+
if messages is None:
|
|
132
|
+
messages = _load_last_assistant_messages(
|
|
133
|
+
transcript_path, ASSISTANT_WINDOW
|
|
134
|
+
)
|
|
135
|
+
marker = _scan(messages)
|
|
136
|
+
if marker is not None:
|
|
137
|
+
return Decision(allow=True, reason="design-evidence", mode=mode,
|
|
138
|
+
target_file=file_path, marker_found=marker)
|
|
139
|
+
return Decision(allow=(mode != "hard"), reason="no-design-marker",
|
|
140
|
+
mode=mode, target_file=file_path)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def record_telemetry(session_id: str, tool: str, decision: Decision) -> None:
|
|
144
|
+
"""Append a structured record to the frontend-gate telemetry log.
|
|
145
|
+
|
|
146
|
+
Drops the record silently when session_id fails the safe-id check
|
|
147
|
+
(path-traversal mitigation, CWE-22). Never blocks the hook.
|
|
148
|
+
"""
|
|
149
|
+
safe = _safe_session_id_module.safe_session_id(session_id)
|
|
150
|
+
if safe is None:
|
|
151
|
+
return
|
|
152
|
+
entry = {
|
|
153
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
154
|
+
"session_id": safe,
|
|
155
|
+
"tool": tool,
|
|
156
|
+
**asdict(decision),
|
|
157
|
+
}
|
|
158
|
+
try:
|
|
159
|
+
with _locked_append(TELEMETRY_PATH) as fh:
|
|
160
|
+
fh.write(json.dumps(entry) + "\n")
|
|
161
|
+
except OSError:
|
|
162
|
+
pass
|
package/installer/autostart.js
CHANGED
|
@@ -29,6 +29,9 @@ export function unitFor(os, { repoRoot, home }) {
|
|
|
29
29
|
if (os === "darwin") {
|
|
30
30
|
const startScript = `${repoRoot}/scripts/start-dashboard.sh`;
|
|
31
31
|
const log = `${home}/.arkaos/logs/dashboard-autostart.log`;
|
|
32
|
+
// AbandonProcessGroup: the launcher backgrounds the API/UI and exits;
|
|
33
|
+
// without it launchd kills the whole process group on exit and the
|
|
34
|
+
// dashboard dies seconds after boot. `ensure` keeps re-loads idempotent.
|
|
32
35
|
const content = `<?xml version="1.0" encoding="UTF-8"?>
|
|
33
36
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
34
37
|
<plist version="1.0">
|
|
@@ -39,14 +42,19 @@ export function unitFor(os, { repoRoot, home }) {
|
|
|
39
42
|
<array>
|
|
40
43
|
<string>/bin/bash</string>
|
|
41
44
|
<string>${startScript}</string>
|
|
45
|
+
<string>ensure</string>
|
|
42
46
|
</array>
|
|
43
47
|
<key>EnvironmentVariables</key>
|
|
44
48
|
<dict>
|
|
45
49
|
<key>ARKAOS_ROOT</key>
|
|
46
50
|
<string>${repoRoot}</string>
|
|
51
|
+
<key>ARKAOS_NO_BROWSER</key>
|
|
52
|
+
<string>1</string>
|
|
47
53
|
</dict>
|
|
48
54
|
<key>RunAtLoad</key>
|
|
49
55
|
<true/>
|
|
56
|
+
<key>AbandonProcessGroup</key>
|
|
57
|
+
<true/>
|
|
50
58
|
<key>StandardOutPath</key>
|
|
51
59
|
<string>${log}</string>
|
|
52
60
|
<key>StandardErrorPath</key>
|
|
@@ -63,14 +71,19 @@ export function unitFor(os, { repoRoot, home }) {
|
|
|
63
71
|
|
|
64
72
|
if (os === "linux") {
|
|
65
73
|
const startScript = `${repoRoot}/scripts/start-dashboard.sh`;
|
|
74
|
+
// oneshot + RemainAfterExit: the launcher backgrounds the API/UI and
|
|
75
|
+
// exits; Type=simple would mark the unit dead on exit and systemd would
|
|
76
|
+
// reap the children with the cgroup. `ensure` keeps restarts idempotent.
|
|
66
77
|
const content = `[Unit]
|
|
67
78
|
Description=ArkaOS Dashboard (Python API + UI)
|
|
68
79
|
After=network.target
|
|
69
80
|
|
|
70
81
|
[Service]
|
|
71
|
-
Type=
|
|
82
|
+
Type=oneshot
|
|
83
|
+
RemainAfterExit=yes
|
|
72
84
|
Environment=ARKAOS_ROOT=${repoRoot}
|
|
73
|
-
|
|
85
|
+
Environment=ARKAOS_NO_BROWSER=1
|
|
86
|
+
ExecStart=/bin/bash ${startScript} ensure
|
|
74
87
|
Restart=on-failure
|
|
75
88
|
|
|
76
89
|
[Install]
|
|
@@ -87,6 +100,7 @@ WantedBy=default.target
|
|
|
87
100
|
const ps1 = join(repoRoot, "scripts", "start-dashboard.ps1");
|
|
88
101
|
const content = `@echo off
|
|
89
102
|
rem ArkaOS dashboard autostart (login).
|
|
103
|
+
set ARKAOS_NO_BROWSER=1
|
|
90
104
|
powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "${ps1}"
|
|
91
105
|
`;
|
|
92
106
|
return {
|
package/installer/cli.js
CHANGED
|
@@ -25,6 +25,9 @@ const { values, positionals } = parseArgs({
|
|
|
25
25
|
// `values.fix` rather than as a free positional under strict:false.
|
|
26
26
|
// Eliminates the dead-branch fallback flagged by Marta in PR2's QG.
|
|
27
27
|
fix: { type: "boolean" },
|
|
28
|
+
// Model Fabric PR-A — `npx arkaos models [--json] [set ... --effort max]`
|
|
29
|
+
json: { type: "boolean" },
|
|
30
|
+
effort: { type: "string" },
|
|
28
31
|
},
|
|
29
32
|
allowPositionals: true,
|
|
30
33
|
strict: false,
|
|
@@ -51,6 +54,8 @@ Usage:
|
|
|
51
54
|
npx arkaos dashboard Start monitoring dashboard
|
|
52
55
|
npx arkaos autostart <enable|disable|status> Start dashboard on boot
|
|
53
56
|
npx arkaos keys Manage API keys (OpenAI, fal.ai, etc.)
|
|
57
|
+
npx arkaos models Model Fabric: which model runs each role
|
|
58
|
+
npx arkaos models set <role> <provider>/<model> Re-route a role
|
|
54
59
|
npx arkaos doctor Run health checks
|
|
55
60
|
npx arkaos uninstall Remove ArkaOS
|
|
56
61
|
|
|
@@ -154,6 +159,24 @@ async function main() {
|
|
|
154
159
|
break;
|
|
155
160
|
}
|
|
156
161
|
|
|
162
|
+
case "models": {
|
|
163
|
+
const { execSync } = await import("node:child_process");
|
|
164
|
+
const repoRootModels = join(__dirname, "..");
|
|
165
|
+
const pyModels = getArkaosPython();
|
|
166
|
+
if (!pyModels) { console.error("No Python found. Run: npx arkaos install"); process.exit(1); }
|
|
167
|
+
const modelArgs = positionals.slice(1).map((a) => `"${a}"`).join(" ");
|
|
168
|
+
const effortFlag = values.effort ? ` --effort "${values.effort}"` : "";
|
|
169
|
+
const jsonFlag = values.json ? " --json" : "";
|
|
170
|
+
try {
|
|
171
|
+
execSync(`"${pyModels}" -m core.runtime.model_router_cli ${modelArgs}${effortFlag}${jsonFlag}`, {
|
|
172
|
+
stdio: "inherit",
|
|
173
|
+
cwd: repoRootModels,
|
|
174
|
+
env: { ...process.env, ARKAOS_ROOT: repoRootModels, PYTHONPATH: repoRootModels },
|
|
175
|
+
});
|
|
176
|
+
} catch { process.exit(1); }
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
|
|
157
180
|
case "index": {
|
|
158
181
|
const { execSync } = await import("node:child_process");
|
|
159
182
|
const indexArgs = positionals.slice(1).join(" ");
|
package/installer/keys.js
CHANGED
|
@@ -6,6 +6,7 @@ const KEYS_PATH = join(homedir(), ".arkaos", "keys.json");
|
|
|
6
6
|
|
|
7
7
|
const PROVIDERS = {
|
|
8
8
|
OPENAI_API_KEY: { name: "OpenAI", used_for: "Whisper transcription, embeddings, GPT" },
|
|
9
|
+
OPENROUTER_API_KEY: { name: "OpenRouter", used_for: "Model Fabric: one key, hundreds of models (Kimi, DeepSeek, GPT, Gemini) for roles + fusion" },
|
|
9
10
|
GOOGLE_API_KEY: { name: "Google", used_for: "Gemini API, Nano Banana, Google Cloud AI" },
|
|
10
11
|
FAL_API_KEY: { name: "fal.ai", used_for: "Image generation, video generation" },
|
|
11
12
|
MAGIC_API_KEY: { name: "21st.dev Magic", used_for: "Frontend UI/UX component generation (Magic MCP)" },
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "arkaos-core"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.2.0"
|
|
4
4
|
description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -35,6 +35,8 @@ knowledge = [
|
|
|
35
35
|
dashboard = [
|
|
36
36
|
"fastapi>=0.115.0",
|
|
37
37
|
"uvicorn>=0.32.0",
|
|
38
|
+
"python-multipart>=0.0.9",
|
|
39
|
+
"websockets>=12.0",
|
|
38
40
|
]
|
|
39
41
|
ingest = [
|
|
40
42
|
"yt-dlp>=2024.0",
|
|
Binary file
|
|
@@ -10,6 +10,30 @@ VENV_PYTHON="$HOME/.arkaos/venv/bin/python"
|
|
|
10
10
|
|
|
11
11
|
mkdir -p "$HOME/.arkaos"
|
|
12
12
|
|
|
13
|
+
# ── Args ──
|
|
14
|
+
# `ensure` — idempotent mode: exit 0 if API+UI already healthy,
|
|
15
|
+
# otherwise fall through to a full start. Safe to call from
|
|
16
|
+
# SessionStart hooks and launchd/systemd boot units.
|
|
17
|
+
# `--no-browser` — do not open the browser after start (also via
|
|
18
|
+
# ARKAOS_NO_BROWSER=1 env, for boot/hook contexts).
|
|
19
|
+
MODE="start"
|
|
20
|
+
for arg in "$@"; do
|
|
21
|
+
case "$arg" in
|
|
22
|
+
ensure) MODE="ensure" ;;
|
|
23
|
+
--no-browser) ARKAOS_NO_BROWSER=1 ;;
|
|
24
|
+
esac
|
|
25
|
+
done
|
|
26
|
+
|
|
27
|
+
if [ "$MODE" = "ensure" ] && [ -f "$PORT_FILE" ]; then
|
|
28
|
+
# shellcheck disable=SC1090
|
|
29
|
+
source "$PORT_FILE"
|
|
30
|
+
if curl -sf --max-time 2 "http://localhost:${API_PORT:-0}/api/overview" >/dev/null 2>&1 \
|
|
31
|
+
&& curl -sf --max-time 2 "http://localhost:${UI_PORT:-0}/" >/dev/null 2>&1; then
|
|
32
|
+
echo " ✓ Dashboard already running (API :${API_PORT}, UI :${UI_PORT})"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
36
|
+
|
|
13
37
|
# ── Venv guard (PR2 v3.73.1 — Force Specialist Dispatch dogfood) ──
|
|
14
38
|
# Previously the dashboard fell back to ambient `python3` when the venv
|
|
15
39
|
# wasn't available. That hid broken-venv conditions (Homebrew patch
|
|
@@ -133,7 +157,7 @@ echo " or kill \$(cat $PID_FILE)"
|
|
|
133
157
|
echo ""
|
|
134
158
|
|
|
135
159
|
# Wait for UI to be ready
|
|
136
|
-
if [ -n "$UI_PID" ]; then
|
|
160
|
+
if [ -n "$UI_PID" ] && [ -z "${ARKAOS_NO_BROWSER:-}" ]; then
|
|
137
161
|
sleep 5
|
|
138
162
|
# Open browser
|
|
139
163
|
if command -v open >/dev/null 2>&1; then
|