From 138401ce1baa919650c3b77a95074438de268d0e Mon Sep 17 00:00:00 2001 From: oss-sync Date: Wed, 17 Jun 2026 01:04:08 +0000 Subject: [PATCH] sync: update from private repo (a67c053) --- .env.example | 56 +++++- docs/getting-started.ja.md | 21 ++ docs/getting-started.md | 21 ++ scripts/upgrade.sh | 142 ++++++++++++++ ui/src/components/userfolder/MemoryPanel.tsx | 195 ++++++++++++++----- ui/src/i18n/locales/en/userfolder.json | 5 + ui/src/i18n/locales/ja/userfolder.json | 5 + ui/src/lib/memorySummary.test.ts | 48 +++++ ui/src/lib/memorySummary.ts | 45 +++++ 9 files changed, 489 insertions(+), 49 deletions(-) create mode 100755 scripts/upgrade.sh create mode 100644 ui/src/lib/memorySummary.test.ts create mode 100644 ui/src/lib/memorySummary.ts diff --git a/.env.example b/.env.example index 4ec3cb8..1b5b762 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,58 @@ -# Docker Compose reaches an LLM running on the host through this address. +# --------------------------------------------------------------------------- +# MAESTRO environment overrides +# +# cp .env.example .env +# +# Loaded by: +# - Docker Compose (docker-compose.yml: env_file: .env) +# - scripts/server.sh (bare-metal: each KEY=value is exported before launch) +# +# Precedence: explicit shell env > .env > config.yaml > built-in defaults. +# Every key is OPTIONAL. The uncommented defaults below target the Docker path; +# bare-metal installs usually generate config.yaml with `npm run setup` and only +# need this file for the LLM endpoint and HOST. +# +# NOTE: under Docker Compose, PORT / DB_PATH / WORKTREE_DIR are also pinned in +# docker-compose.yml's `environment:` block, which overrides this file. Editing +# them here only takes effect on a bare-metal (scripts/server.sh) launch. +# --------------------------------------------------------------------------- + +# --- LLM connection -------------------------------------------------------- +# OpenAI-compatible endpoint. From inside a Docker container, host.docker.internal +# reaches an LLM (Ollama / vLLM, ...) running on the host. OLLAMA_BASE_URL=http://host.docker.internal:11434/v1 OLLAMA_MODEL=qwen3:32b -# Required only when enabling MCP or SSH. +# --- Network bind ---------------------------------------------------------- +# Address the HTTP(S) server binds to. Default: 127.0.0.1 (loopback only). +# The agent API includes a Bash tool, so an auth-less instance reachable on the +# LAN is effectively unauthenticated remote code execution. +# +# To reach MAESTRO from another machine on a BARE-METAL install, set HOST +# explicitly (e.g. 0.0.0.0) -- and enable auth in config.yaml first. +# (The Docker image already sets HOST=0.0.0.0 inside the container; external +# access is gated by the 127.0.0.1:9876 port mapping in docker-compose.yml.) +# HOST=0.0.0.0 + +# HTTP(S) listen port (default 9876). +# PORT=9876 + +# --- Storage paths --------------------------------------------------------- +# SQLite database file (default ./data/maestro.db). +# DB_PATH=./data/maestro.db + +# Agent workspace base directory (default ./data/workspaces; mirrors +# storage.worktree_dir in config.yaml). +# WORKTREE_DIR=./data/workspaces + +# --- Runtime --------------------------------------------------------------- +# Max concurrent jobs across all workers (overrides `concurrency` in config.yaml). +# CONCURRENCY=4 + +# Log verbosity: debug | info | warn | error (default info). +# LOG_LEVEL=info + +# --- Secrets --------------------------------------------------------------- +# Required ONLY when using MCP servers or the SSH tool. 64 hex characters. +# Generate one with: openssl rand -hex 32 # MCP_ENCRYPTION_KEY=replace-with-64-hex-characters diff --git a/docs/getting-started.ja.md b/docs/getting-started.ja.md index d89c517..2f163f6 100644 --- a/docs/getting-started.ja.md +++ b/docs/getting-started.ja.md @@ -73,6 +73,27 @@ scripts/server.sh stop > `--skip-python`。システム Python への書き込みに権限が要る環境では > `sudo bash scripts/prebake-python.sh` を別途実行する。 +### 既存環境の更新(`git pull` の後) + +bare-metal の既存環境を更新するときは次を実行する。 + +```bash +scripts/upgrade.sh # git pull → 再ビルド(依存 + サーバー + UI)→ 再起動 +``` + +`scripts/server.sh restart` はサーバーしか再ビルドせず、**UI は再ビルドしない** +(`ui/dist` は gitignore 対象で別途ビルドされる)。npm 依存も更新しない。そのため +依存やフロントエンドが変わった pull の後にそのまま restart すると、UI バンドルが +古いまま残ることがある。`scripts/upgrade.sh` は正しい手順を一括で実行する。 + +**ネットワークバインドの移行**も扱う。2026-06-10 以降、サーバーのデフォルトバインドが +セキュリティのため `0.0.0.0` から `127.0.0.1`(loopback 限定)に変わった(エージェント +API には Bash ツールが含まれるため、認証なしで LAN に晒すと実質的に認証なし RCE になる)。 +別マシンから MAESTRO にアクセスしていて、更新後に突然 `ERR_CONNECTION_REFUSED` が出る +場合はこれが原因。`HOST` を明示的に設定し(例: `.env` に `HOST=0.0.0.0`)、先に +`config.yaml` で認証を有効にすること。upgrade スクリプトはこの状況を検出して設定を +提案する。 + ## 5. Docker で起動 ```bash diff --git a/docs/getting-started.md b/docs/getting-started.md index 7bfd049..0ea4f07 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -73,6 +73,27 @@ scripts/server.sh stop > `--skip-python`. In environments where writing to the system Python requires permissions, run > `sudo bash scripts/prebake-python.sh` separately. +### Updating an existing install (after `git pull`) + +To update a bare-metal install, run: + +```bash +scripts/upgrade.sh # git pull -> rebuild (deps + server + UI) -> restart +``` + +A plain `scripts/server.sh restart` rebuilds only the server, **not** the UI +(`ui/dist` is gitignored and built separately) and never refreshes npm deps, so +after a pull that changed dependencies or the frontend it can leave a stale UI +bundle. `scripts/upgrade.sh` runs the full, correct sequence instead. + +It also handles the **network bind migration**: since 2026-06-10 the server +binds `127.0.0.1` (loopback only) by default instead of `0.0.0.0`, for security +(the agent API includes a Bash tool, so an auth-less instance on the LAN is +effectively unauthenticated RCE). If you reach MAESTRO from another machine and +suddenly get `ERR_CONNECTION_REFUSED` after updating, that is why — set `HOST` +explicitly (e.g. `HOST=0.0.0.0` in `.env`) and enable auth in `config.yaml` +first. The upgrade script detects this and offers to set it for you. + ## 5. Launch with Docker ```bash diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh new file mode 100755 index 0000000..26cb117 --- /dev/null +++ b/scripts/upgrade.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# +# upgrade.sh — bring an existing (bare-metal) MAESTRO checkout safely up to the +# latest version after a `git pull`. +# +# Why this exists +# --------------- +# `scripts/server.sh restart` rebuilds the server (dist/) but NOT the UI: +# `ui/dist` is gitignored and built separately, and npm deps are never +# refreshed. So after a `git pull` that touched dependencies or the frontend, a +# plain restart can leave a stale/missing UI bundle or ABI-mismatched native +# modules. This script runs the full, correct update sequence instead: +# +# git pull (optional) -> build-all.sh (deps + server + UI) -> restart +# +# Network bind migration (2026-06-10) +# ----------------------------------- +# A security fix changed the default bind from 0.0.0.0 to 127.0.0.1 (loopback +# only). If you used to reach MAESTRO from another machine on a bare-metal +# install, after upgrading you'll get ERR_CONNECTION_REFUSED until you set HOST +# explicitly. This script detects that and offers to write HOST to .env (with a +# warning that you must enable auth before exposing a non-loopback interface). +# +# Usage: +# scripts/upgrade.sh # interactive: pull, build, restart +# scripts/upgrade.sh --no-pull # skip `git pull` (build + restart only) +# scripts/upgrade.sh --no-restart # build only, leave the server as-is +# scripts/upgrade.sh --yes # non-interactive (CI); never edits .env +# scripts/upgrade.sh --help +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +ENV_FILE="$PROJECT_DIR/.env" + +DO_PULL=1 +DO_RESTART=1 +ASSUME_YES=0 + +for arg in "$@"; do + case "$arg" in + --no-pull) DO_PULL=0 ;; + --no-restart) DO_RESTART=0 ;; + --yes|-y) ASSUME_YES=1 ;; + -h|--help) + sed -n '2,29p' "$0" | sed 's/^# \{0,1\}//' + exit 0 + ;; + *) + echo "upgrade.sh: unknown option '$arg' (try --help)" >&2 + exit 2 + ;; + esac +done + +cd "$PROJECT_DIR" + +say() { printf '\n\033[1;36m==>\033[0m %s\n' "$1"; } +warn() { printf '\033[1;33mWARN:\033[0m %s\n' "$1" >&2; } + +# --------------------------------------------------------------------------- +# 1. git pull (fast-forward only; never silently merge or rewrite local work) +# --------------------------------------------------------------------------- +if [[ "$DO_PULL" -eq 1 ]]; then + if [[ -n "$(git -C "$PROJECT_DIR" status --porcelain 2>/dev/null)" ]]; then + warn "working tree has local changes — skipping 'git pull'. Commit/stash and re-run, or use --no-pull." + else + say "Pulling latest changes (git pull --ff-only)" + if ! git -C "$PROJECT_DIR" pull --ff-only; then + warn "git pull --ff-only failed (diverged or offline). Continuing with the current checkout." + fi + fi +else + say "Skipping git pull (--no-pull)" +fi + +# --------------------------------------------------------------------------- +# 2. Full rebuild: refresh deps + build server dist/ AND ui/dist/ + prebake. +# This is the step a plain `server.sh restart` skips for the UI. +# --------------------------------------------------------------------------- +say "Rebuilding (deps + server + UI) via scripts/build-all.sh" +"$SCRIPT_DIR/build-all.sh" + +# --------------------------------------------------------------------------- +# 3. Network bind migration check (0.0.0.0 -> 127.0.0.1 default since 2026-06-10) +# --------------------------------------------------------------------------- +host_in_env="" +if [[ -f "$ENV_FILE" ]]; then + # First non-comment HOST=... line, value with surrounding quotes stripped. + host_in_env="$(grep -E '^[[:space:]]*(export[[:space:]]+)?HOST=' "$ENV_FILE" \ + | grep -vE '^[[:space:]]*#' \ + | head -1 | sed -E 's/^[[:space:]]*(export[[:space:]]+)?HOST=//; s/^["'\'']//; s/["'\'']$//')" +fi + +say "Checking network bind" +if [[ -n "${HOST:-}" ]]; then + echo " HOST is set in the environment ($HOST) — bind is explicit, nothing to migrate." +elif [[ -n "$host_in_env" ]]; then + echo " HOST=$host_in_env found in .env — bind is explicit, nothing to migrate." +else + cat <<'EOF' + HOST is not set, so the server now binds 127.0.0.1 (loopback only). + Since 2026-06-10 the default changed from 0.0.0.0 for security: the agent + API includes a Bash tool, so an auth-less instance on the LAN is effectively + unauthenticated remote code execution. + + If you ONLY open MAESTRO from this same machine (http://localhost:9876), + no action is needed. + + If you reach it from ANOTHER machine, that is why you now get + ERR_CONNECTION_REFUSED. To restore remote access you must set HOST + explicitly -- and you should enable auth (auth.local or OAuth) in + config.yaml before exposing a non-loopback interface. +EOF + if [[ "$ASSUME_YES" -eq 0 ]]; then + printf '\n Append HOST=0.0.0.0 to .env now? Only do this if auth is (or will be) enabled. [y/N] ' + read -r reply || reply="" + if [[ "$reply" =~ ^[Yy]$ ]]; then + { [[ -f "$ENV_FILE" && -n "$(tail -c1 "$ENV_FILE" 2>/dev/null)" ]] && echo "" >> "$ENV_FILE"; } || true + printf '\n# Added by scripts/upgrade.sh: restore non-loopback bind (enable auth first!)\nHOST=0.0.0.0\n' >> "$ENV_FILE" + echo " Wrote HOST=0.0.0.0 to $ENV_FILE" + else + echo " Left .env unchanged. Set HOST=0.0.0.0 in .env manually when ready." + fi + else + warn "non-interactive (--yes): not editing .env. Set HOST in .env manually if you need remote access." + fi +fi + +# --------------------------------------------------------------------------- +# 4. Restart +# --------------------------------------------------------------------------- +if [[ "$DO_RESTART" -eq 1 ]]; then + say "Restarting server (scripts/server.sh restart)" + "$SCRIPT_DIR/server.sh" restart + echo + echo "Done. If the server is healthy, open it at the bind address reported above" + echo "(loopback default: http://localhost:9876)." +else + say "Build complete (--no-restart). Start the server with: scripts/server.sh restart" +fi diff --git a/ui/src/components/userfolder/MemoryPanel.tsx b/ui/src/components/userfolder/MemoryPanel.tsx index 423ff7c..2d65b63 100644 --- a/ui/src/components/userfolder/MemoryPanel.tsx +++ b/ui/src/components/userfolder/MemoryPanel.tsx @@ -6,15 +6,29 @@ * なので User Folder へ移設(Settings 側は Reflection タイムラインのみ)。 */ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { HelpText } from '../settings/HelpText'; +import { summarizeMemory, filterMemory, MEMORY_TYPES, type MemoryType } from '../../lib/memorySummary'; + +// ── Per-type accent classes (literal strings so Tailwind keeps them) ─────────── + +const TYPE_BADGE: Record = { + user: 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-500/15 dark:text-blue-300 dark:border-blue-500/30', + feedback: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-500/15 dark:text-amber-300 dark:border-amber-500/30', + project: 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-500/15 dark:text-emerald-300 dark:border-emerald-500/30', + reference: 'bg-violet-50 text-violet-700 border-violet-200 dark:bg-violet-500/15 dark:text-violet-300 dark:border-violet-500/30', +}; +const TYPE_DOT: Record = { + user: 'bg-blue-500', + feedback: 'bg-amber-500', + project: 'bg-emerald-500', + reference: 'bg-violet-500', +}; // ── API types ───────────────────────────────────────────────────────────────── -type MemoryType = 'user' | 'feedback' | 'project' | 'reference'; - // Mirrors the server's flat shape from `listMemoryEntries` in // src/user-folder/memory.ts and `GET /api/local/memory/entries` in // src/bridge/memory-api.ts. If you change this shape, update both. @@ -88,8 +102,6 @@ interface EntryFormState { body: string; } -const MEMORY_TYPES: MemoryType[] = ['user', 'feedback', 'project', 'reference']; - function MemoryEntryModal({ initial, isNew, @@ -129,7 +141,7 @@ function MemoryEntryModal({ return (
-
+

{isNew ? t('memory.modal.newTitle') : t('memory.modal.editTitle', { name: initial.name })} @@ -143,7 +155,7 @@ function MemoryEntryModal({

-
+
{/* Name — only editable when creating */}
-
+