From 4947a6b0c3438cb09acba8caffe7312a377b4c8e Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Fri, 10 Apr 2026 10:02:28 -0700 Subject: [PATCH] v0.44.0: approval fix, login CSP, update diagnostics, Lucide icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: approval pending check broken by stale has_pending import (#228) api/routes.py imported has_pending/pop_pending from tools.approval, but the agent module renamed has_pending to has_blocking_approval (checks gateway queue, not _pending dict) and removed pop_pending. The import fell through to fallback lambdas that always returned False, making GET /api/approval/pending always return {pending:null} even after a successful inject_test. Fix: check _pending directly under _lock — same dict submit_pending writes to. Stale imports removed. Before: 554 pass, 1 fail | After: 555 pass, 0 fail * fix: move login JS into external file, remove inline handlers (#226) Login page used inline onsubmit/onkeydown handlers and an inline ''' + +''' # ── GET routes ──────────────────────────────────────────────────────────────── @@ -177,8 +161,8 @@ def handle_get(handler, parsed) -> bool: .replace('{{LOGIN_SUBTITLE}}', _html.escape(_login_strings['subtitle'])) .replace('{{LOGIN_PLACEHOLDER}}', _html.escape(_login_strings['placeholder'])) .replace('{{LOGIN_BTN}}', _html.escape(_login_strings['btn'])) - .replace('{{LOGIN_INVALID_PW}}', _login_strings['invalid_pw'].replace('\\','\\\\').replace("'","\\'")) - .replace('{{LOGIN_CONN_FAILED}}', _login_strings['conn_failed'].replace('\\','\\\\').replace("'","\\'")) + .replace('{{LOGIN_INVALID_PW}}', _html.escape(_login_strings['invalid_pw'])) + .replace('{{LOGIN_CONN_FAILED}}', _html.escape(_login_strings['conn_failed'])) ) return t(handler, _page, content_type='text/html; charset=utf-8') @@ -969,10 +953,10 @@ def _handle_file_read(handler, parsed): def _handle_approval_pending(handler, parsed): sid = parse_qs(parsed.query).get('session_id', [''])[0] - if has_pending(sid): - with _lock: - p = dict(_pending.get(sid, {})) - return j(handler, {'pending': p}) + with _lock: + p = _pending.get(sid) + if p: + return j(handler, {'pending': dict(p)}) return j(handler, {'pending': None}) diff --git a/api/updates.py b/api/updates.py index 2bda66c..0f2aa2b 100644 --- a/api/updates.py +++ b/api/updates.py @@ -148,6 +148,17 @@ def _apply_update_inner(target): branch = _detect_default_branch(path) compare_ref = f'origin/{branch}' + # Fetch before attempting pull, so the remote ref is current. + _, fetch_ok = _run_git(['fetch', 'origin', '--quiet'], path, timeout=15) + if not fetch_ok: + return { + 'ok': False, + 'message': ( + 'Could not reach the remote repository. ' + 'Check your internet connection and try again.' + ), + } + # Check for dirty working tree status_out, _ = _run_git(['status', '--porcelain'], path) stashed = False @@ -162,7 +173,31 @@ def _apply_update_inner(target): if not pull_ok: if stashed: _run_git(['stash', 'pop'], path) - return {'ok': False, 'message': f'Pull failed: {pull_out[:200]}'} + + # Diagnose the most common failure modes and surface actionable messages. + pull_lower = pull_out.lower() + if 'not possible to fast-forward' in pull_lower or 'diverged' in pull_lower: + return { + 'ok': False, + 'message': ( + f'The local {target} repo has commits that are not on the remote ' + 'branch, so a fast-forward update is not possible. ' + 'Run: git -C ' + str(path) + ' fetch origin && ' + 'git -C ' + str(path) + ' reset --hard ' + compare_ref + ), + 'diverged': True, + } + if 'does not track' in pull_lower or 'no tracking information' in pull_lower: + return { + 'ok': False, + 'message': ( + f'The local {target} branch has no upstream tracking branch configured. ' + 'Run: git -C ' + str(path) + ' branch --set-upstream-to=' + compare_ref + ), + } + # Generic fallback — include the raw git output for debugging. + detail = pull_out.strip()[:300] if pull_out.strip() else '(no output from git)' + return {'ok': False, 'message': f'Pull failed: {detail}'} # Pop stash if we stashed if stashed: diff --git a/static/icons.js b/static/icons.js new file mode 100644 index 0000000..6463cda --- /dev/null +++ b/static/icons.js @@ -0,0 +1,69 @@ +// ── Lucide icon library (self-hosted SVG paths, no CDN dependency) ────────── +// All icons are 24×24 viewBox, stroke-based, currentColor. +// Usage: li('folder') → returns a ready-to-embed SVG string +// The returned SVG uses display:inline-block + vertical-align so it sits +// neatly beside text in both HTML templates and innerHTML assignments. + +const LI_PATHS = { + // Navigation tabs + 'message-square': '', + 'calendar': '', + 'layers': '', + 'lightbulb': '', + 'folder': '', + 'list-todo': '', + // Editing / actions + 'pencil': '', + 'chevron-down': '', + 'download': '', + 'upload': '', + 'braces': '', + 'trash-2': '', + 'settings': '', + 'alert-triangle': '', + 'refresh-cw': '', + 'check': '', + 'lock': '', + 'star': '', + 'x': '', + 'square': '', + 'plus': '', + 'arrow-up': '', + 'loader': '', + // Tool icons + 'terminal': '', + 'file-text': '', + 'file-pen': '', + 'search': '', + 'globe': '', + 'play': '', + 'wrench': '', + 'brain': '', + 'book-open': '', + 'clock': '', + 'bot': '', + 'eye': '', + 'shuffle': '', + // File-type icons + 'image': '', + 'file-code': '', + 'zap': '', + // Suggestion buttons + 'clipboard-list': '', + 'map': '', +}; + +/** + * Returns a Lucide SVG string for the given icon name. + * @param {string} name – key in LI_PATHS (e.g. 'folder', 'trash-2') + * @param {number} size – width/height in px (default 16) + * @returns {string} SVG element string ready for innerHTML + */ +function li(name, size = 16) { + const p = LI_PATHS[name]; + if (!p) { console.warn('li(): unknown icon', name); return ''; } + return ``; +} diff --git a/static/index.html b/static/index.html index 8bcb985..e410036 100644 --- a/static/index.html +++ b/static/index.html @@ -14,15 +14,15 @@
- ⚠ A response may have been in progress when you last left. Reload messages? + A response may have been in progress when you last left. Reload messages?
- +
@@ -227,20 +243,20 @@
@@ -249,10 +265,10 @@