From 117e6af52800175e5a5f851627a5b3d7c91d49cc Mon Sep 17 00:00:00 2001 From: Brandon Lucas Date: Mon, 16 Feb 2026 06:12:58 -0500 Subject: [PATCH] Implement self-contained grapho architecture with four data types Major rewrite of grapho CLI to support: - Type 1 (Config): grapho init clones NixOS config - Type 2 (Sync): Isolated Syncthing on port 8385 (separate from system) - Type 3 (Backup): Restic integration with systemd timer - Type 4 (Server): Mount point for central server data New features: - Welcome flow on first run (detects ~/.config/grapho/grapho.toml) - grapho setup wizard creates directory structure - grapho sync/backup/server subcommands - grapho status shows all four data types - grapho doctor checks system health Added modules/grapho.nix NixOS module: - Configures isolated Syncthing (ports 8385, 22001, 21028) - Sets up grapho-backup systemd service and timer - Creates directory structure via tmpfiles - Optional NFS server mount Updated flake.nix: - Export grapho NixOS module - Add grapho CLI package (nix build .#grapho) Documented additional Lux language limitations: - String == comparison broken in C backend - let _ = pattern not supported - List literals with recursion cause segfaults Co-Authored-By: Claude Opus 4.5 --- cli/grapho.lux | 805 +++++++++++++++++++++++++--------------- docs/LUX-LIMITATIONS.md | 83 ++++- flake.nix | 48 ++- grapho | Bin 49288 -> 61992 bytes modules/grapho.nix | 341 +++++++++++++++++ 5 files changed, 984 insertions(+), 293 deletions(-) create mode 100644 modules/grapho.nix diff --git a/cli/grapho.lux b/cli/grapho.lux index 16d4428..16b8d2e 100644 --- a/cli/grapho.lux +++ b/cli/grapho.lux @@ -1,14 +1,22 @@ -// grapho - Unified CLI for the Ultimate Notetaking, Sync & Backup System +// grapho - Unified Personal Data Infrastructure +// +// Four data types: +// 1. Config - Declarative machine setup (NixOS + Git) +// 2. Sync - Real-time bidirectional sync (Syncthing) +// 3. Backup - Periodic snapshots (Restic) +// 4. Server - Large files on central server // // Usage: -// grapho One-line health check (default) -// grapho status Full status dashboard -// grapho status -v Verbose status with all details -// grapho doctor Diagnose issues and suggest fixes -// grapho sync Sync all (nb + syncthing) -// grapho backup Run backup now -// grapho help Show help -// grapho --json Machine-readable JSON output +// grapho Health check (or welcome if uninitialized) +// grapho init Initialize from config repo +// grapho setup Interactive setup wizard +// grapho sync Sync all folders +// grapho sync status Show sync status +// grapho backup Run backup now +// grapho backup list List snapshots +// grapho status Full status dashboard +// grapho doctor Diagnose issues +// grapho help Show help // ============================================================================= // Types @@ -58,70 +66,396 @@ fn homeDir(): String with {Process} = None => "/tmp" } +// String comparison helpers (workaround for Lux C backend bug with == on strings) +fn isYes(s: String): Bool = String.contains(s, "yes") +fn isNo(s: String): Bool = String.contains(s, "no") +fn isActive(s: String): Bool = String.contains(s, "active") + +fn graphoDir(): String with {Process} = + homeDir() + "/.config/grapho" + +fn graphoConfig(): String with {Process} = + graphoDir() + "/grapho.toml" + // ============================================================================= -// Syncthing helpers +// Initialization detection // ============================================================================= -fn syncthingRunning(): Bool with {Process} = { - let result = Process.exec("syncthing cli show system 2>/dev/null | jq -r '.myID' 2>/dev/null || true") - String.length(String.trim(result)) > 10 +fn isInitialized(): Bool with {Process} = { + let result = execQuiet("test -f " + graphoConfig() + " && echo yes || echo no") + result |> isYes } -fn getSyncthingUptime(): String with {Process} = - Process.exec("syncthing cli show system 2>/dev/null | jq -r '.uptime // 0' 2>/dev/null || echo 0") - -fn getSyncthingFolderCount(): String with {Process} = - String.trim(Process.exec("syncthing cli show config 2>/dev/null | jq -r '.folders | length' 2>/dev/null || echo 0")) - -fn getSyncthingDeviceCount(): String with {Process} = - String.trim(Process.exec("syncthing cli show config 2>/dev/null | jq -r '.devices | length' 2>/dev/null || echo 0")) +fn ensureDir(path: String): Unit with {Process} = { + let ignore = Process.exec("mkdir -p " + path) + () +} // ============================================================================= -// One-liner health check (new default) +// Directory structure +// ============================================================================= + +fn createDirectories(): Unit with {Process, Console} = { + let base = graphoDir() + Console.print("Creating directory structure...") + ensureDir(base) + ensureDir(base + "/config-repo") + ensureDir(base + "/syncthing/config") + ensureDir(base + "/syncthing/db") + ensureDir(base + "/sync/notes") + ensureDir(base + "/sync/documents") + ensureDir(base + "/sync/dotfiles") + ensureDir(base + "/restic/cache") + ensureDir(base + "/server") + Console.print(statusIcon(StatusOk) + " Directories created") +} + +// ============================================================================= +// Welcome flow +// ============================================================================= + +fn showWelcome(): Unit with {Console, Process} = { + Console.print("") + Console.print(" Welcome to grapho") + Console.print("") + Console.print(" grapho manages your data across all your devices:") + Console.print("") + Console.print(" * Config - Your machine setup, restored anywhere") + Console.print(" * Sync - Notes & docs synced across devices") + Console.print(" * Backup - Snapshots you can restore anytime") + Console.print(" * Server - Large files on your server") + Console.print("") + Console.print(" To get started, run:") + Console.print("") + Console.print(" grapho setup # Interactive setup wizard") + Console.print(" grapho init # Initialize from existing config repo") + Console.print("") + Console.print(" For more info: grapho help") + Console.print("") +} + +// ============================================================================= +// Setup wizard +// ============================================================================= + +fn doSetup(): Unit with {Console, Process} = { + Console.print("") + Console.print("grapho setup wizard") + Console.print("====================") + Console.print("") + + // Create directories + createDirectories() + Console.print("") + + // Create initial config + Console.print("Creating initial configuration...") + let configContent = "[grapho]\nversion = 1\ndevice_name = \"" + execQuiet("hostname") + "\"\n\n[sync]\ngui_port = 8385\nsync_port = 22001\nfolders = [\"notes\", \"documents\", \"dotfiles\"]\n\n[backup]\nschedule = \"hourly\"\n\n[services]\n" + + let writeCmd = "cat > " + graphoConfig() + " << 'GRAPHO_EOF'\n" + configContent + "GRAPHO_EOF" + let ignore = Process.exec(writeCmd) + Console.print(statusIcon(StatusOk) + " Configuration created at " + graphoConfig()) + Console.print("") + + // Generate age key if missing + let hasAgeKey = execQuiet("test -f " + graphoDir() + "/age-key.txt && echo yes || echo no") + if hasAgeKey |> isYes then + Console.print(statusIcon(StatusOk) + " Age encryption key exists") + else { + Console.print("Generating age encryption key...") + let ignore = Process.exec("age-keygen -o " + graphoDir() + "/age-key.txt 2>&1 || true") + Console.print(statusIcon(StatusOk) + " Age key generated") + } + Console.print("") + + // Check for Syncthing + if hasCommand("syncthing") then { + Console.print(statusIcon(StatusOk) + " Syncthing is available") + Console.print(" Configure via: grapho sync setup") + } else { + Console.print(statusIcon(StatusWarn) + " Syncthing not found") + Console.print(" Install via nix or enable in your NixOS config") + } + + // Check for restic + if hasCommand("restic") then { + Console.print(statusIcon(StatusOk) + " Restic is available") + Console.print(" Configure via: grapho backup init") + } else { + Console.print(statusIcon(StatusWarn) + " Restic not found") + Console.print(" Install via nix or enable in your NixOS config") + } + + Console.print("") + Console.print("Setup complete!") + Console.print("") + Console.print("Your grapho data is stored in: " + graphoDir()) + Console.print("") + Console.print("Next steps:") + Console.print(" grapho status # Check system health") + Console.print(" grapho sync setup # Configure device pairing") + Console.print(" grapho backup init # Configure backups") + Console.print("") +} + +// ============================================================================= +// Init from repo +// ============================================================================= + +fn doInit(repoUrl: String): Unit with {Console, Process} = { + if String.length(repoUrl) == 0 then { + Console.print("Usage: grapho init ") + Console.print("") + Console.print("Clone a grapho config repository to set up this machine.") + Console.print("") + Console.print("Example:") + Console.print(" grapho init https://github.com/user/grapho-config") + Console.print(" grapho init git@github.com:user/grapho-config.git") + } else { + Console.print("Initializing from " + repoUrl) + Console.print("") + + // Create base directory + createDirectories() + + // Clone repo + Console.print("Cloning config repository...") + let cloneResult = Process.exec("git clone " + repoUrl + " " + graphoDir() + "/config-repo 2>&1") + if String.contains(cloneResult, "fatal") then { + Console.print(statusIcon(StatusErr) + " Failed to clone repository") + Console.print(" " + cloneResult) + } else { + Console.print(statusIcon(StatusOk) + " Repository cloned") + + // Check for flake.nix + let hasFlake = execQuiet("test -f " + graphoDir() + "/config-repo/flake.nix && echo yes || echo no") + if hasFlake |> isYes then { + Console.print(statusIcon(StatusOk) + " Found flake.nix") + Console.print("") + Console.print("To apply the NixOS configuration:") + Console.print(" cd " + graphoDir() + "/config-repo") + Console.print(" sudo nixos-rebuild switch --flake .#") + } else { + Console.print(statusIcon(StatusWarn) + " No flake.nix found") + Console.print(" This repo may not be a NixOS configuration") + } + } + Console.print("") + } +} + +// ============================================================================= +// Syncthing helpers (isolated grapho instance) +// ============================================================================= + +fn graphoSyncthingRunning(): Bool with {Process} = { + // Check if Syncthing is running on grapho port (8385) + let result = execQuiet("curl -s http://127.0.0.1:8385/rest/system/status 2>/dev/null | head -c 1") + String.length(result) > 0 +} + +fn getSyncthingDeviceId(): String with {Process} = { + execQuiet("syncthing cli --home=" + graphoDir() + "/syncthing/config show system 2>/dev/null | grep -o 'myID.*' | cut -d'\"' -f3") +} + +fn getSyncFolderCount(): String with {Process} = { + execQuiet("syncthing cli --home=" + graphoDir() + "/syncthing/config show config 2>/dev/null | jq -r '.folders | length' 2>/dev/null || echo 0") +} + +fn getSyncDeviceCount(): String with {Process} = { + execQuiet("syncthing cli --home=" + graphoDir() + "/syncthing/config show config 2>/dev/null | jq -r '.devices | length' 2>/dev/null || echo 0") +} + +// ============================================================================= +// Sync commands +// ============================================================================= + +fn doSyncStatus(): Unit with {Console, Process} = { + Console.print("grapho sync status") + Console.print("") + + if hasCommand("syncthing") then { + if graphoSyncthingRunning() then { + let folders = getSyncFolderCount() + let devices = getSyncDeviceCount() + Console.print(statusIcon(StatusOk) + " Syncthing running on port 8385") + Console.print(" Folders: " + folders) + Console.print(" Devices: " + devices) + Console.print("") + Console.print(" Web UI: http://127.0.0.1:8385") + } else { + Console.print(statusIcon(StatusWarn) + " Grapho Syncthing not running") + Console.print("") + Console.print(" Start with: systemctl --user start syncthing-grapho") + Console.print(" Or: syncthing serve --home=" + graphoDir() + "/syncthing/config --gui-address=127.0.0.1:8385") + } + } else { + Console.print(statusIcon(StatusErr) + " Syncthing not installed") + Console.print(" Install via nix or NixOS config") + } +} + +fn doSyncSetup(): Unit with {Console, Process} = { + Console.print("grapho sync setup") + Console.print("") + + if hasCommand("syncthing") == false then { + Console.print(statusIcon(StatusErr) + " Syncthing not installed") + Console.print(" Install syncthing first") + } else { + // Initialize Syncthing config if needed + let hasConfig = execQuiet("test -f " + graphoDir() + "/syncthing/config/config.xml && echo yes || echo no") + if hasConfig |> isNo then { + Console.print("Initializing Syncthing for grapho...") + let ignore = Process.exec("syncthing generate --home=" + graphoDir() + "/syncthing/config --no-default-folder 2>&1") + Console.print(statusIcon(StatusOk) + " Syncthing initialized") + } else { + Console.print(statusIcon(StatusOk) + " Syncthing already configured") + } + + // Get device ID + Console.print("") + let deviceId = getSyncthingDeviceId() + if String.length(deviceId) > 10 then { + Console.print("Your device ID:") + Console.print(" " + deviceId) + Console.print("") + Console.print("Share this ID with other devices to pair them.") + } else { + Console.print("Start Syncthing to get your device ID:") + Console.print(" syncthing serve --home=" + graphoDir() + "/syncthing/config --gui-address=127.0.0.1:8385") + } + } +} + +fn doSync(): Unit with {Console, Process} = { + Console.print("Syncing...") + + if graphoSyncthingRunning() then { + Console.print("-> Triggering Syncthing rescan") + let ignore = Process.exec("syncthing cli --home=" + graphoDir() + "/syncthing/config scan 2>/dev/null || true") + Console.print(statusIcon(StatusOk) + " Sync triggered") + } else { + Console.print(statusIcon(StatusWarn) + " Syncthing not running") + Console.print(" Start with: grapho sync setup") + } + Console.print("") +} + +// ============================================================================= +// Backup commands +// ============================================================================= + +fn doBackupInit(): Unit with {Console, Process} = { + Console.print("grapho backup init") + Console.print("") + + if hasCommand("restic") == false then { + Console.print(statusIcon(StatusErr) + " Restic not installed") + Console.print(" Install restic first") + } else { + Console.print("Backup repository setup") + Console.print("") + Console.print("Configure your backup repository in: " + graphoConfig()) + Console.print("") + Console.print("Example configuration:") + Console.print(" [backup]") + Console.print(" repository = \"sftp:server:/backups/grapho\"") + Console.print(" schedule = \"hourly\"") + Console.print("") + Console.print("After configuring, initialize with:") + Console.print(" restic -r init") + } +} + +fn doBackupList(): Unit with {Console, Process} = { + Console.print("grapho backup list") + Console.print("") + + if hasCommand("restic") == false then { + Console.print(statusIcon(StatusErr) + " Restic not installed") + } else { + Console.print("Run: restic -r snapshots") + Console.print("") + Console.print("Check your repository location in: " + graphoConfig()) + } +} + +fn doBackup(): Unit with {Console, Process} = { + Console.print("Running backup...") + + if hasCommand("restic") then { + // Check for systemd service first + let hasService = execQuiet("systemctl cat grapho-backup.service 2>/dev/null || systemctl cat restic-backup.service 2>/dev/null") + if String.length(hasService) > 0 then { + Console.print("-> Triggering backup service") + let ignore = Process.exec("systemctl start grapho-backup.service 2>/dev/null || systemctl start restic-backup.service 2>/dev/null || true") + Console.print(statusIcon(StatusOk) + " Backup triggered") + } else { + Console.print(statusIcon(StatusWarn) + " No systemd backup service configured") + Console.print(" Configure in your NixOS config or run restic manually") + Console.print(" restic -r backup " + graphoDir() + "/sync") + } + } else { + Console.print(statusIcon(StatusErr) + " Restic not installed") + } + Console.print("") +} + +// ============================================================================= +// Server commands +// ============================================================================= + +fn doServerStatus(): Unit with {Console, Process} = { + Console.print("grapho server status") + Console.print("") + + let serverDir = graphoDir() + "/server" + let isMounted = execQuiet("mountpoint -q " + serverDir + " && echo yes || echo no") + + if isMounted |> isYes then { + let usage = execQuiet("df -h " + serverDir + " | tail -1 | tr -s ' ' | cut -d' ' -f3,2,5 | tr ' ' '/'") + Console.print(statusIcon(StatusOk) + " Server mounted at " + serverDir) + Console.print(" Usage: " + usage) + } else { + Console.print(statusIcon(StatusNone) + " Server not mounted") + Console.print(" Configure in: " + graphoConfig()) + Console.print(" Mount point: " + serverDir) + } +} + +// ============================================================================= +// One-liner health check // ============================================================================= fn showHealthCheck(): Unit with {Console, Process} = { - let hasIssues = false - let hasWarnings = false - let summary = "" - - // Check nb - let nbOk = hasCommand("nb") - let nbCount = if nbOk then execQuiet("nb notebooks --names 2>/dev/null | wc -l") else "0" - - // Check syncthing - let stOk = hasCommand("syncthing") - let stRunning = if stOk then syncthingRunning() else false - let stFolders = if stRunning then getSyncthingFolderCount() else "0" - let stDevices = if stRunning then getSyncthingDeviceCount() else "0" - - // Check backup - let resticOk = hasCommand("restic") - let timerActive = execQuiet("systemctl is-active restic-backup.timer") - let backupOk = if timerActive == "active" then true else false - - // Determine overall status and print appropriate message - if nbOk == false || stOk == false || resticOk == false then { - Console.print(statusEmoji(StatusErr) + " Issues detected") - if nbOk == false then - Console.print(" " + statusIcon(StatusErr) + " nb: not installed -> nix develop") - else () - if stOk == false then - Console.print(" " + statusIcon(StatusErr) + " syncthing: not installed -> nix develop") - else () - if resticOk == false then - Console.print(" " + statusIcon(StatusErr) + " restic: not installed -> nix develop") - else () - } else if stRunning == false || backupOk == false then { - Console.print(statusEmoji(StatusWarn) + " Warnings") - if stRunning == false then - Console.print(" " + statusIcon(StatusWarn) + " syncthing: not running -> systemctl --user start syncthing") - else () - if backupOk == false then - Console.print(" " + statusIcon(StatusWarn) + " backup: timer inactive -> sudo systemctl enable --now restic-backup.timer") - else () + // Check if initialized + if isInitialized() == false then { + showWelcome() } else { - Console.print(statusEmoji(StatusOk) + " All systems healthy (" + nbCount + " notebooks, " + stFolders + " folders, " + stDevices + " devices, backup active)") + let configOk = true + let syncOk = graphoSyncthingRunning() + let backupOk = execQuiet("systemctl is-active grapho-backup.timer 2>/dev/null || systemctl is-active restic-backup.timer 2>/dev/null") |> isActive + + let folders = if syncOk then getSyncFolderCount() else "0" + let devices = if syncOk then getSyncDeviceCount() else "0" + + // Determine issues + if syncOk == false || backupOk == false then { + if syncOk == false && backupOk == false then { + Console.print(statusEmoji(StatusWarn) + " Warnings") + Console.print(" " + statusIcon(StatusWarn) + " sync: not running -> systemctl --user start syncthing-grapho") + Console.print(" " + statusIcon(StatusWarn) + " backup: timer inactive -> check grapho backup init") + } else if syncOk == false then { + Console.print(statusEmoji(StatusWarn) + " Sync not running") + Console.print(" " + statusIcon(StatusWarn) + " sync: not running -> systemctl --user start syncthing-grapho") + } else { + Console.print(statusEmoji(StatusWarn) + " Backup timer inactive") + Console.print(" " + statusIcon(StatusWarn) + " backup: timer inactive -> check grapho backup init") + } + } else { + Console.print(statusEmoji(StatusOk) + " All systems healthy (" + folders + " folders, " + devices + " devices, backup active)") + } } } @@ -129,99 +463,56 @@ fn showHealthCheck(): Unit with {Console, Process} = { // Status dashboard // ============================================================================= -fn showStatus(verbose: Bool): Unit with {Console, Process} = { +fn showStatus(): Unit with {Console, Process} = { Console.print("grapho status") Console.print("") - // nb - if hasCommand("nb") then { - let nbCount = execQuiet("nb notebooks --names 2>/dev/null | wc -l") - Console.print(statusIcon(StatusOk) + " nb: " + nbCount + " notebooks") - if verbose then { - let notebooks = execQuiet("nb notebooks --names") - if String.length(notebooks) > 0 then { - let lines = String.lines(notebooks) - printNotebooks(lines) - } else () - } else () + if isInitialized() == false then { + Console.print(statusIcon(StatusNone) + " grapho not initialized") + Console.print(" Run: grapho setup") + Console.print("") } else { - Console.print(statusIcon(StatusErr) + " nb: not installed") - Console.print(" Fix: nix develop") - } - - // Syncthing - if hasCommand("syncthing") then { - if syncthingRunning() then { - let folders = getSyncthingFolderCount() - let devices = getSyncthingDeviceCount() - Console.print(statusIcon(StatusOk) + " syncthing: " + folders + " folders, " + devices + " devices") - if verbose then { - let folderLabels = execQuiet("syncthing cli show config | jq -r '.folders[].label'") - if String.length(folderLabels) > 0 then { - Console.print(" Folders:") - let flines = String.lines(folderLabels) - printLinesIndented(flines) - } else () - let deviceNames = execQuiet("syncthing cli show config | jq -r '.devices[].name'") - if String.length(deviceNames) > 0 then { - Console.print(" Devices:") - let dlines = String.lines(deviceNames) - printLinesIndented(dlines) - } else () - } else () + // Config + let hasRepo = execQuiet("test -d " + graphoDir() + "/config-repo/.git && echo yes || echo no") + if hasRepo |> isYes then { + let repoRemote = execQuiet("cd " + graphoDir() + "/config-repo && git remote get-url origin 2>/dev/null || echo local") + Console.print(statusIcon(StatusOk) + " config: " + repoRemote) } else { - Console.print(statusIcon(StatusWarn) + " syncthing: not running") - Console.print(" Fix: systemctl --user start syncthing") + Console.print(statusIcon(StatusNone) + " config: no repo linked") + Console.print(" Run: grapho init ") } - } else { - Console.print(statusIcon(StatusErr) + " syncthing: not installed") - Console.print(" Fix: nix develop") - } - // Backup - if hasCommand("restic") then { - let timerActive = execQuiet("systemctl is-active restic-backup.timer") - if timerActive == "active" then { + // Sync + if graphoSyncthingRunning() then { + let folders = getSyncFolderCount() + let devices = getSyncDeviceCount() + Console.print(statusIcon(StatusOk) + " sync: " + folders + " folders, " + devices + " devices") + } else { + Console.print(statusIcon(StatusWarn) + " sync: not running") + Console.print(" Run: grapho sync setup") + } + + // Backup + let timerActive = execQuiet("systemctl is-active grapho-backup.timer 2>/dev/null || systemctl is-active restic-backup.timer 2>/dev/null") + if timerActive |> isActive then { Console.print(statusIcon(StatusOk) + " backup: timer active") } else { Console.print(statusIcon(StatusWarn) + " backup: timer inactive") - Console.print(" Fix: sudo systemctl enable --now restic-backup.timer") + Console.print(" Run: grapho backup init") + } + + // Server + let serverDir = graphoDir() + "/server" + let isMounted = execQuiet("mountpoint -q " + serverDir + " && echo yes || echo no") + if isMounted |> isYes then { + Console.print(statusIcon(StatusOk) + " server: mounted") + } else { + Console.print(statusIcon(StatusNone) + " server: not configured") } - } else { - Console.print(statusIcon(StatusErr) + " backup: restic not installed") - Console.print(" Fix: nix develop") } + Console.print("") } -fn printNotebooks(lines: List): Unit with {Console, Process} = - match List.head(lines) { - Some(name) => { - if String.length(name) > 0 then { - let count = execQuiet("find ~/.nb/" + name + " -name '*.md' 2>/dev/null | wc -l") - Console.print(" " + name + ": " + count + " notes") - } else () - match List.tail(lines) { - Some(rest) => printNotebooks(rest), - None => () - } - }, - None => () - } - -fn printLinesIndented(lines: List): Unit with {Console} = - match List.head(lines) { - Some(line) => { - if String.length(line) > 0 then - Console.print(" - " + line) - else () - match List.tail(lines) { - Some(rest) => printLinesIndented(rest), - None => () - } - }, - None => () - } - // ============================================================================= // Doctor command // ============================================================================= @@ -229,8 +520,6 @@ fn printLinesIndented(lines: List): Unit with {Console} = fn showDoctor(): Unit with {Console, Process} = { Console.print("grapho doctor") Console.print("") - Console.print("Checking system health...") - Console.print("") // Check nix if hasCommand("nix") then { @@ -249,166 +538,97 @@ fn showDoctor(): Unit with {Console, Process} = { Console.print(statusIcon(StatusErr) + " git: not found") } - // Check nb - if hasCommand("nb") then { - let nbCount = execQuiet("nb notebooks --names 2>/dev/null | wc -l") - Console.print(statusIcon(StatusOk) + " nb: " + nbCount + " notebooks") - } else { - Console.print(statusIcon(StatusErr) + " nb: not installed") - Console.print(" Fix: nix develop") - } - // Check syncthing if hasCommand("syncthing") then { - if syncthingRunning() then { - let folders = getSyncthingFolderCount() - Console.print(statusIcon(StatusOk) + " syncthing: running (" + folders + " folders)") + if graphoSyncthingRunning() then { + Console.print(statusIcon(StatusOk) + " syncthing: running on port 8385") } else { - Console.print(statusIcon(StatusWarn) + " syncthing: not running") - Console.print(" Fix: systemctl --user start syncthing") + Console.print(statusIcon(StatusWarn) + " syncthing: installed but not running") + Console.print(" Fix: grapho sync setup") } } else { Console.print(statusIcon(StatusErr) + " syncthing: not installed") - Console.print(" Fix: nix develop") } - // Check backup + // Check restic if hasCommand("restic") then { - let timerActive = execQuiet("systemctl is-active restic-backup.timer") - if timerActive == "active" then { - Console.print(statusIcon(StatusOk) + " backup: timer active") - } else { - Console.print(statusIcon(StatusWarn) + " backup: timer inactive") - Console.print(" Fix: sudo systemctl enable --now restic-backup.timer") - } + Console.print(statusIcon(StatusOk) + " restic: installed") } else { Console.print(statusIcon(StatusErr) + " restic: not installed") - Console.print(" Fix: nix develop") } - // Additional checks - Console.print("") - Console.print("Additional checks:") - - // Check age key - let ageKey = execQuiet("test -f ~/.config/sops/age/keys.txt && echo yes") - if ageKey == "yes" then - Console.print(statusIcon(StatusOk) + " Age encryption key exists") - else { - Console.print(statusIcon(StatusWarn) + " No age key") - Console.print(" Fix: age-keygen -o ~/.config/sops/age/keys.txt") - } - - Console.print("") - Console.print("Run 'grapho status -v' for detailed component info") -} - -// ============================================================================= -// JSON output -// ============================================================================= - -fn showJson(): Unit with {Console, Process} = { - // Note: JSON output with quotes is difficult in Lux due to escape sequence limitations - // Using a simple key=value format instead - let nbOk = hasCommand("nb") - let nbCount = if nbOk then execQuiet("nb notebooks --names 2>/dev/null | wc -l") else "0" - let nbStatus = if nbOk then "healthy" else "error" - let nbMsg = if nbOk then nbCount + " notebooks" else "not installed" - - let stOk = hasCommand("syncthing") - let stRunning = if stOk then syncthingRunning() else false - let stStatus = if stOk == false then "error" else if stRunning then "healthy" else "warning" - let stMsg = if stOk == false then "not installed" else if stRunning then "running" else "not running" - - let resticOk = hasCommand("restic") - let timerResult = execQuiet("systemctl is-active restic-backup.timer") - let timerActive = if timerResult == "active" then true else false - let backupStatus = if resticOk == false then "error" else if timerActive then "healthy" else "warning" - let backupMsg = if resticOk == false then "not installed" else if timerActive then "timer active" else "timer inactive" - - // Output in a simple parseable format - Console.print("nb.status=" + nbStatus) - Console.print("nb.message=" + nbMsg) - Console.print("syncthing.status=" + stStatus) - Console.print("syncthing.message=" + stMsg) - Console.print("backup.status=" + backupStatus) - Console.print("backup.message=" + backupMsg) -} - -// ============================================================================= -// Sync command -// ============================================================================= - -fn doSync(): Unit with {Console, Process} = { - Console.print("Syncing...") - - // Sync nb notebooks - if hasCommand("nb") then { - Console.print("-> nb sync --all") - let result = Process.exec("nb sync --all 2>&1 || true") - if String.contains(result, "error") then - Console.print(statusIcon(StatusWarn) + " nb sync had issues") - else - Console.print(statusIcon(StatusOk) + " nb synced") - } else () - - // Trigger Syncthing scan - if hasCommand("syncthing") then { - if syncthingRunning() then { - Console.print("-> syncthing cli scan") - let scanResult = Process.exec("syncthing cli scan 2>/dev/null || true") - Console.print(statusIcon(StatusOk) + " Syncthing scan triggered") - } else () - } else () - - Console.print("Done!") -} - -// ============================================================================= -// Backup command -// ============================================================================= - -fn doBackup(): Unit with {Console, Process} = { - Console.print("Running backup...") - - if hasCommand("restic") then { - let hasService = execQuiet("systemctl cat restic-backup.service") - if String.length(hasService) > 0 then { - Console.print("-> sudo systemctl start restic-backup.service") - let startResult = Process.exec("sudo systemctl start restic-backup.service 2>&1 || true") - Console.print(statusIcon(StatusOk) + " Backup service triggered") + // Check age + if hasCommand("age") then { + let hasKey = execQuiet("test -f " + graphoDir() + "/age-key.txt && echo yes || echo no") + if hasKey |> isYes then { + Console.print(statusIcon(StatusOk) + " age: key exists") } else { - Console.print(statusIcon(StatusWarn) + " No systemd backup service configured") - Console.print(" Configure in modules/backup.nix") + Console.print(statusIcon(StatusWarn) + " age: no key at " + graphoDir() + "/age-key.txt") + Console.print(" Fix: grapho setup") } } else { - Console.print(statusIcon(StatusErr) + " restic not installed") + Console.print(statusIcon(StatusErr) + " age: not installed") } + + // Directory structure + Console.print("") + Console.print("Directory structure:") + let hasGraphoDir = execQuiet("test -d " + graphoDir() + " && echo yes || echo no") + if hasGraphoDir |> isYes then { + Console.print(statusIcon(StatusOk) + " " + graphoDir()) + checkDir("config-repo") + checkDir("syncthing") + checkDir("sync") + checkDir("restic") + checkDir("server") + } else { + Console.print(statusIcon(StatusErr) + " " + graphoDir() + " does not exist") + Console.print(" Fix: grapho setup") + } + + Console.print("") +} + +fn checkDir(dir: String): Unit with {Console, Process} = { + let exists = execQuiet("test -d " + graphoDir() + "/" + dir + " && echo yes || echo no") + if exists |> isYes then + Console.print(" " + statusIcon(StatusOk) + " " + dir + "/") + else + Console.print(" " + statusIcon(StatusNone) + " " + dir + "/ (missing)") } // ============================================================================= // Help // ============================================================================= -fn showHelp(): Unit with {Console} = { +fn showHelp(): Unit with {Console, Process} = { Console.print("grapho - Personal Data Infrastructure") Console.print("") Console.print("Usage:") - Console.print(" grapho Health check (one-liner)") - Console.print(" grapho status Component status") - Console.print(" grapho status -v Verbose status with details") - Console.print(" grapho doctor Diagnose issues and fixes") - Console.print(" grapho sync Sync all (nb + syncthing)") - Console.print(" grapho backup Run backup now") - Console.print(" grapho --json Machine-readable output (key=value format)") - Console.print(" grapho help Show this help") + Console.print(" grapho Health check (or welcome if new)") + Console.print(" grapho init Initialize from config repo") + Console.print(" grapho setup Interactive setup wizard") Console.print("") - Console.print("Quick start:") - Console.print(" nb add Create a new note") - Console.print(" nb search Search notes") - Console.print(" grapho sync Sync to all devices") + Console.print("Sync (Type 2 - Real-time bidirectional):") + Console.print(" grapho sync Trigger sync") + Console.print(" grapho sync status Show sync status") + Console.print(" grapho sync setup Configure Syncthing") + Console.print("") + Console.print("Backup (Type 3 - Periodic snapshots):") + Console.print(" grapho backup Run backup now") + Console.print(" grapho backup list List snapshots") + Console.print(" grapho backup init Configure backup repository") + Console.print("") + Console.print("Server (Type 4 - Central storage):") + Console.print(" grapho server Show server status") + Console.print("") + Console.print("Status:") + Console.print(" grapho status Full status dashboard") + Console.print(" grapho doctor Diagnose issues") + Console.print(" grapho help Show this help") + Console.print("") + Console.print("Data directory: " + graphoDir()) Console.print("") - Console.print("More info: https://git.qrty.ink/blu/grapho") } // ============================================================================= @@ -421,30 +641,37 @@ fn main(): Unit with {Console, Process} = { Some(c) => c, None => "" } + let subcmd = match List.get(args, 2) { + Some(s) => s, + None => "" + } match cmd { "" => showHealthCheck(), - "status" => { - let subcmd = match List.get(args, 2) { - Some(s) => s, - None => "" - } + "init" => doInit(subcmd), + "setup" => doSetup(), + "sync" => { match subcmd { - "-v" => showStatus(true), - "--verbose" => showStatus(true), - "verbose" => showStatus(true), - _ => showStatus(false) + "status" => doSyncStatus(), + "setup" => doSyncSetup(), + "" => doSync(), + _ => doSyncStatus() } }, + "backup" => { + match subcmd { + "init" => doBackupInit(), + "list" => doBackupList(), + "" => doBackup(), + _ => doBackup() + } + }, + "server" => doServerStatus(), + "status" => showStatus(), "doctor" => showDoctor(), - "sync" => doSync(), - "backup" => doBackup(), "help" => showHelp(), "-h" => showHelp(), "--help" => showHelp(), - "--json" => showJson(), - "-j" => showJson(), - "json" => showJson(), _ => showHelp() } } diff --git a/docs/LUX-LIMITATIONS.md b/docs/LUX-LIMITATIONS.md index e6be597..31cbc2f 100644 --- a/docs/LUX-LIMITATIONS.md +++ b/docs/LUX-LIMITATIONS.md @@ -116,15 +116,93 @@ let count = Int.parse(someString) // ERROR: Unknown effect operation --- +## C Backend Issues + +### 7. String Equality Comparison Generates Incorrect C Code +**Severity:** High +**Status:** Bug + +Using `==` to compare strings generates C code that compares pointers instead of string contents. + +```lux +let result = execQuiet("echo yes") +if result == "yes" then ... // C code: (result == "yes") - pointer comparison! +``` + +**Impact:** String comparisons fail in compiled binaries. + +**Workaround:** Use `String.contains` for comparison: +```lux +fn isYes(s: String): Bool = String.contains(s, "yes") +if result |> isYes then ... +``` + +### 8. String.startsWith Not Available in C Backend +**Severity:** Medium +**Status:** Bug + +`String.startsWith` works in interpreter but generates undefined function calls in C. + +```lux +String.startsWith(s, "prefix") // C error: lux_string__startsWith undefined +``` + +**Workaround:** Use `String.contains` instead. + +### 9. `let _ = expr` Pattern Not Supported +**Severity:** Low +**Status:** Bug + +The underscore wildcard pattern for discarding results doesn't work. + +```lux +let _ = Process.exec("...") // ERROR: Expected identifier +``` + +**Workaround:** Use a named binding: +```lux +let ignore = Process.exec("...") +``` + +### 10. List Literals and Recursion Cause Segfaults +**Severity:** High +**Status:** Bug + +Combining list literals with recursive functions can cause segmentation faults in compiled binaries while working fine in interpreter. + +```lux +// This crashes when compiled: +let dirs = ["a", "b", "c"] +fn processDirs(dirs: List): Unit = + match List.head(dirs) { + Some(d) => { ...; match List.tail(dirs) { Some(rest) => processDirs(rest), ... } } + None => () + } +``` + +**Workaround:** Avoid list literals with recursive processing. Inline the operations: +```lux +fn processA(): Unit = ... +fn processB(): Unit = ... +fn processC(): Unit = ... +// Call each individually +``` + +--- + ## Suggestions for Lux 1. **Add escape sequence support** - At minimum `\"`, `\\`, `\n`, `\t` 2. **Fix String.fromChar** to return String, not Int 3. **Add raw string literals** - Something like `r"..."` or `'''...'''` for shell commands -4. **Fix the double execution bug** in the runtime +4. **Fix the double execution bug** in the runtime (DONE) 5. **Support record type literals** matching their declared type 6. **Add Int.parse and Float.parse** for string-to-number conversion 7. **Consider a heredoc syntax** for multi-line strings with special characters +8. **Fix string equality** - Use strcmp in C backend for string == +9. **Support `let _ = `** - Allow underscore as discard binding +10. **Fix String.startsWith** in C backend +11. **Fix list literals with recursion** causing segfaults --- @@ -135,3 +213,6 @@ let count = Int.parse(someString) // ERROR: Unknown effect operation 3. **Quotes in output:** Avoided entirely or generated via shell 4. **Structured types:** Using individual variables instead of records 5. **Numeric parsing:** Keeping counts as strings throughout +6. **String comparison:** Using `String.contains` with helper functions instead of `==` +7. **Discarding results:** Using `let ignore = ...` instead of `let _ = ...` +8. **Lists with recursion:** Replaced with individual function calls diff --git a/flake.nix b/flake.nix index 820c289..f2f56d6 100644 --- a/flake.nix +++ b/flake.nix @@ -14,6 +14,11 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + lux = { + url = "path:/home/blu/src/lux"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + # Optional: Neovim distribution # nixvim = { # url = "github:nix-community/nixvim"; @@ -21,7 +26,7 @@ # }; }; - outputs = { self, nixpkgs, home-manager, sops-nix, ... }@inputs: + outputs = { self, nixpkgs, home-manager, sops-nix, lux, ... }@inputs: let # Supported systems supportedSystems = [ "x86_64-linux" "aarch64-linux" ]; @@ -37,6 +42,7 @@ # Shared modules for all hosts sharedModules = [ + ./modules/grapho.nix ./modules/nb.nix ./modules/syncthing.nix ./modules/backup.nix @@ -60,6 +66,39 @@ }; in { + # Grapho CLI package + packages = forAllSystems (system: + let + pkgs = nixpkgsFor.${system}; + luxPkg = lux.packages.${system}.default; + in { + grapho = pkgs.stdenv.mkDerivation { + pname = "grapho"; + version = "0.1.0"; + src = ./cli; + + nativeBuildInputs = [ luxPkg pkgs.gcc ]; + + buildPhase = '' + ${luxPkg}/bin/lux compile grapho.lux -o grapho + ''; + + installPhase = '' + mkdir -p $out/bin + cp grapho $out/bin/ + ''; + + meta = { + description = "Personal data infrastructure CLI"; + homepage = "https://github.com/user/grapho"; + license = pkgs.lib.licenses.mit; + }; + }; + + default = self.packages.${system}.grapho; + } + ); + # NixOS configurations # Uncomment and customize for your hosts: # @@ -132,7 +171,9 @@ default = pkgs.mkShell { name = "unsbs-dev"; - packages = with pkgs; [ + packages = [ + lux.packages.${system}.default + ] ++ (with pkgs; [ # Tier 2: Notes & Sync nb # Notebook CLI syncthing # File sync @@ -159,7 +200,7 @@ # Nix tools nil # Nix LSP nixpkgs-fmt # Nix formatter - ]; + ]); shellHook = '' printf '\033[1m%s\033[0m\n' "Ultimate Notetaking, Sync & Backup System" @@ -175,6 +216,7 @@ # Export modules for use in other flakes nixosModules = { + grapho = import ./modules/grapho.nix; nb = import ./modules/nb.nix; syncthing = import ./modules/syncthing.nix; backup = import ./modules/backup.nix; diff --git a/grapho b/grapho index eba82dbdfa1c08c228a34936fc346d4b0d512263..d758a2b714195a4955963c5140e4674d33068b8a 100755 GIT binary patch literal 61992 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%0DCMg|5K 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 z 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 aB5}cE>@dG0i*xOO@cuz4I0