Skip to content

Commit 3a596fa

Browse files
DavidsonGomesclaude
andcommitted
release: v0.18.4 — restore auto-restart + SQLite datetime fix + setup prompt clarity
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b666924 commit 3a596fa

6 files changed

Lines changed: 40 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.18.4] - 2026-04-12
9+
10+
### Changed
11+
12+
- **`make restore` stops/restarts services** — restore now kills Flask and terminal-server before extracting, then restarts via `start-services.sh` after. Prevents SQLite lock conflicts and ensures auto-migrate runs on the restored database
13+
- **Setup prompt clarified** — "Type 1 or 2" instead of "Choice" for Dashboard Access, rejects invalid input with clear message
14+
15+
### Fixed
16+
17+
- **SQLite auto-migrate fixes corrupted datetime columns** — on startup, Flask now detects and repairs NULL or non-string `created_at` values in `roles` and `users` tables. Prevents crash after restoring a backup from an older version
18+
819
## [0.18.3] - 2026-04-12
920

1021
### Added

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,13 @@ backup-s3: ## ☁️ Backup workspace data to local ZIP + S3 upload
224224
$(PYTHON) backup.py backup --target s3
225225

226226
restore: ## 📥 Restore workspace from backup ZIP: make restore FILE=<path> [MODE=merge|replace]
227+
@echo "▶ Stopping services before restore..."
228+
@pkill -f "dashboard/terminal-server/bin/server.js" 2>/dev/null || true
229+
@pkill -f "app.py" 2>/dev/null || true
230+
@sleep 1
227231
$(PYTHON) backup.py restore $(FILE) --mode $(or $(MODE),merge)
232+
@echo "▶ Restarting services..."
233+
@if [ -f start-services.sh ]; then bash start-services.sh; sleep 3; echo "✅ Services restarted"; else echo "ℹ Run ./start-services.sh or make dashboard-app to start"; fi
228234

229235
backup-list: ## 📋 List available backups (local or S3: make backup-list TARGET=s3)
230236
$(PYTHON) backup.py list --target $(or $(TARGET),local)

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@evoapi/evo-nexus",
3-
"version": "0.18.3",
3+
"version": "0.18.4",
44
"description": "Unofficial open source toolkit for Claude Code — AI-powered business operating system",
55
"keywords": [
66
"claude-code",

dashboard/backend/app.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,17 @@
5858
if "agent_access_json" not in _existing_cols:
5959
_cur.execute("ALTER TABLE roles ADD COLUMN agent_access_json TEXT DEFAULT '{\"mode\": \"all\"}'")
6060
_conn.commit()
61+
62+
# Fix corrupted datetime columns (NULL or non-string values crash SQLAlchemy)
63+
for _tbl, _col in [("roles", "created_at"), ("users", "created_at"), ("users", "last_login")]:
64+
try:
65+
_tbl_cols = {row[1] for row in _cur.execute(f"PRAGMA table_info({_tbl})").fetchall()}
66+
if _col in _tbl_cols:
67+
_cur.execute(f"UPDATE {_tbl} SET {_col} = datetime('now') WHERE {_col} IS NOT NULL AND typeof({_col}) != 'text'")
68+
_cur.execute(f"UPDATE {_tbl} SET {_col} = datetime('now') WHERE {_col} IS NOT NULL AND {_col} != '' AND {_col} NOT LIKE '____-__-__%'")
69+
except Exception:
70+
pass
71+
_conn.commit()
6172
_conn.close()
6273
# --- End auto-migrate ---
6374

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "evo-nexus"
3-
version = "0.18.3"
3+
version = "0.18.4"
44
description = "Unofficial open source toolkit for Claude Code — AI-powered business operating system"
55
requires-python = ">=3.10"
66
dependencies = [

setup.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,21 @@ def configure_access() -> dict:
173173
print(f" {BOLD}1{RESET}) Local only (http://localhost:8080)")
174174
print(f" {BOLD}2{RESET}) Domain with SSL (recommended for remote servers)")
175175

176-
choice = ask("Choice", "1")
177-
if choice != "2":
176+
choice = ask("Type 1 or 2", "1")
177+
178+
if choice not in ("1", "2"):
179+
print(f" {YELLOW}!{RESET} Invalid choice '{choice}'. Using local mode.")
180+
return {"mode": "local", "url": "http://localhost:8080"}
181+
182+
if choice == "1":
178183
return {"mode": "local", "url": "http://localhost:8080"}
179184

180-
domain = ask("Domain", "")
185+
domain = ask("Domain (e.g. nexus.example.com)", "")
181186
if not domain:
182187
print(f" {YELLOW}!{RESET} No domain provided, using local mode")
183188
return {"mode": "local", "url": "http://localhost:8080"}
189+
# Clean up if user pasted a full URL
190+
domain = domain.strip().replace("http://", "").replace("https://", "").rstrip("/")
184191

185192
# Step 1: Install nginx
186193
if not shutil.which("nginx"):

0 commit comments

Comments
 (0)