@meridiona/meridian-darwin-arm64 1.32.0 → 1.33.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 CHANGED
@@ -1 +1 @@
1
- 1.32.0
1
+ 1.33.0
package/bin/meridian CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meridiona/meridian-darwin-arm64",
3
- "version": "1.32.0",
3
+ "version": "1.33.0",
4
4
  "description": "Prebuilt Meridian app for macOS arm64 (daemon binary + dashboard + Python services). Installed via @meridiona/meridian.",
5
5
  "homepage": "https://github.com/Meridiona/meridian",
6
6
  "repository": {
@@ -86,8 +86,8 @@ fi
86
86
 
87
87
  # ── 4. Install @meridiona/meridian ───────────────────────────────────────────
88
88
  info "Installing @meridiona/meridian@latest…"
89
- info " The npm package is small; the Python venv + Node runtime (~200 MB) download"
90
- info " once during 'meridian setup' below. Budget 2–4 min on a typical connection."
89
+ info " The npm package is small; Python deps + Node runtime download once during"
90
+ info " 'meridian setup' below. Budget 2–4 min on a typical connection."
91
91
  npm install -g @meridiona/meridian@latest
92
92
  ok "meridian installed ($(meridian --version 2>/dev/null || echo 'version unknown'))"
93
93
 
@@ -3,8 +3,8 @@
3
3
  #
4
4
  # Install a PREBUILT release bundle (no cargo/npm build). Run from inside an
5
5
  # unpacked bundle at ~/.meridian/app — bootstrap.sh downloads + unpacks the
6
- # release tarball and execs this. Installs prerequisites, the Python venv + MLX
7
- # deps, and registers the four launchd daemons pointing at this bundle.
6
+ # release tarball and execs this. Installs prerequisites, builds the Python venv
7
+ # from PyPI via uv sync, and registers the four launchd daemons pointing at this bundle.
8
8
  #
9
9
  # bash ~/.meridian/app/scripts/install-from-bundle.sh [--skip-permissions]
10
10
  set -euo pipefail
@@ -337,6 +337,24 @@ if ! command -v screenpipe >/dev/null 2>&1; then
337
337
  fi
338
338
  fi
339
339
  ok "screenpipe"
340
+ # Stage the real screenpipe Mach-O to ~/.meridian/bin/screenpipe — a stable path
341
+ # that is independent of the npm prefix (nvm users get a version-specific path
342
+ # under ~/.nvm that breaks on `nvm use` and is too deep to navigate in System
343
+ # Settings). The launchd plist and TCC grants are written against this path.
344
+ mkdir -p "${HOME}/.meridian/bin"
345
+ _sp_npm_root="$(npm root -g 2>/dev/null || true)"
346
+ _sp_real=""
347
+ if [[ -n "${_sp_npm_root}" && -d "${_sp_npm_root}/screenpipe" ]]; then
348
+ while IFS= read -r _sp_cand; do
349
+ if file "${_sp_cand}" 2>/dev/null | grep -q "Mach-O"; then _sp_real="${_sp_cand}"; break; fi
350
+ done < <(find "${_sp_npm_root}/screenpipe" -type f -name screenpipe -perm +0111 2>/dev/null)
351
+ fi
352
+ if [[ -n "${_sp_real}" ]]; then
353
+ cmp -s "${_sp_real}" "${HOME}/.meridian/bin/screenpipe" 2>/dev/null \
354
+ || cp "${_sp_real}" "${HOME}/.meridian/bin/screenpipe"
355
+ chmod +x "${HOME}/.meridian/bin/screenpipe"
356
+ ok "screenpipe staged → ${HOME}/.meridian/bin/screenpipe"
357
+ fi
340
358
  if ! command -v ffmpeg >/dev/null 2>&1; then info "Installing ffmpeg…"; brew install ffmpeg; fi
341
359
  ok "ffmpeg"
342
360
 
@@ -418,115 +436,39 @@ _mlx_was_healthy=0
418
436
  curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1 && _mlx_was_healthy=1
419
437
 
420
438
  # ── 4. Python venv + MLX deps ────────────────────────────────────────────────
421
- # The venv (~160 MB) is too large for the npm registry, so release builds attach
422
- # it to the GitHub Release and we download it here. Three paths:
423
- # A) Already current: the installed venv's stamp matches the shipped uv.lock
424
- # hash skip the 160 MB download entirely (differential update). uv.lock is
425
- # the deterministic source of truth for the deps and travels in the npm
426
- # package, so this makes most `meridian update` runs touch zero network.
427
- # B) Deps changed / fresh install: download services-venv-<ver>.tar.gz, verify
428
- # it against the remote .sha256, extract site-packages into a fresh Python
429
- # 3.11 venv. No PyPI. Stamp the uv.lock hash for next time.
430
- # C) Dev/source install (no VERSION, or download/verify failed): `uv sync
431
- # --frozen` from uv.lock — downloads from PyPI the first time (~40s).
432
- # NOTE the differential key is the uv.lock hash, NOT the tarball hash: BSD tar
433
- # embeds mtimes so the tarball hash differs on every CI build even when deps are
434
- # identical, which would defeat the skip. The remote .sha256 is used only to
435
- # verify download integrity, never for the change decision.
439
+ # Installed from PyPI via uv sync at install time no pre-built venv shipped.
440
+ # PyPI MLX wheels are tagged macosx_13_0_arm64 / macosx_14_0_arm64 so pip's
441
+ # platform tag filter guarantees the correct ABI for the user's macOS version.
442
+ # Differential skip: if uv.lock hasn't changed and mlx.core imports cleanly,
443
+ # the venv is already correct no network round-trip needed.
436
444
  VENV="${APP_ROOT}/services/.venv"
437
445
  VENV_STAMP="${VENV}/.meridian-venv-hash"
438
- _VERSION="$(cat "${APP_ROOT}/VERSION" 2>/dev/null | tr -d '[:space:]' || true)"
439
- _VENV_URL="https://github.com/Meridiona/meridian/releases/download/v${_VERSION}/services-venv-${_VERSION}.tar.gz"
440
446
  _LOCK_HASH="$(shasum -a 256 "${APP_ROOT}/services/uv.lock" 2>/dev/null | cut -d' ' -f1 || true)"
441
447
 
442
- # Extract a downloaded venv tarball ($1) into a fresh Python 3.11 venv, then stamp
443
- # the dependency hash ($2). The tarball is compiled for Python 3.11 (package-release.sh
444
- # enforces this); the venv MUST use exactly 3.11 or the cpython-311 .so files fail
445
- # to import. Prefer system python3.11, then uv-managed 3.11, then install it via uv.
446
- _extract_venv() {
447
- local tgz="$1" stamp_hash="$2" tarball_python="" py_dir="" venv_tmp=""
448
- # Stage the new venv to a temp path; swap atomically only on success so a
449
- # failed extraction (disk full, corrupt archive) never destroys the live venv.
450
- venv_tmp="${VENV}.tmp.$$"
451
- rm -rf "${venv_tmp}"
452
- if command -v python3.11 >/dev/null 2>&1; then
453
- tarball_python="$(command -v python3.11)"
454
- elif "${UV_BIN}" python find 3.11 >/dev/null 2>&1; then
455
- tarball_python="$("${UV_BIN}" python find 3.11)"
456
- else
457
- info "Installing Python 3.11 (pre-built venv requires it — one-time download)…"
458
- "${UV_BIN}" python install 3.11
459
- tarball_python="$("${UV_BIN}" python find 3.11)"
460
- fi
461
- "${UV_BIN}" venv --python "${tarball_python}" "${venv_tmp}" 2>/dev/null
462
- py_dir="$(ls "${venv_tmp}/lib/" | grep '^python' | head -1)"
463
- mkdir -p "${venv_tmp}/lib/${py_dir}/site-packages"
464
- tar -xzf "${tgz}" -C "${venv_tmp}/lib/${py_dir}/site-packages"
465
- # Install the local editable package (meridian-agents) — no deps needed,
466
- # everything is already in site-packages from the tarball.
467
- "${UV_BIN}" pip install --quiet --no-deps --python "${venv_tmp}/bin/python" -e "${APP_ROOT}/services"
468
- # All steps succeeded — atomically replace the live venv.
469
- rm -rf "${VENV}"
470
- mv "${venv_tmp}" "${VENV}"
471
- printf '%s\n' "${stamp_hash}" > "${VENV_STAMP}"
472
- ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
473
- }
448
+ _venv_changed=1
449
+ _have_hash=""; [[ -f "${VENV_STAMP}" ]] && _have_hash="$(cat "${VENV_STAMP}" 2>/dev/null)"
474
450
 
475
- # uv sync fallback (Path C) used for dev/source installs and as the offline
476
- # failure path. Both extras: mlx (classifier + server) AND pm_worklog_update
477
- # (agno) the one MLX server serves /classify_sessions AND /synthesise_worklog,
478
- # so without agno worklog synthesis 500s with ModuleNotFoundError. Stamp the
479
- # uv.lock hash on success so subsequent runs can skip.
480
- _venv_uv_sync() {
481
- info "Building Python venv from uv.lock (mlx-lm/outlines/fastapi/agno; first run may download a few hundred MB)…"
451
+ if [[ -n "${_LOCK_HASH}" && "${_LOCK_HASH}" == "${_have_hash}" && -x "${VENV}/bin/python" ]] \
452
+ && "${VENV}/bin/python" -c "import mlx.core" 2>/dev/null; then
453
+ ok "Python deps unchanged skipping uv sync"
454
+ _venv_changed=0
455
+ else
456
+ info "Installing Python services from PyPI (mlx-lm/outlines/fastapi/agno; first run ~40–120s)…"
482
457
  if "${UV_BIN}" sync \
483
458
  --project "${APP_ROOT}/services" \
484
459
  --extra mlx \
485
460
  --extra pm_worklog_update \
486
461
  --frozen \
487
- --python "${PYTHON_BIN}"; then
462
+ --python 3.11; then
488
463
  [[ -n "${_LOCK_HASH}" ]] && printf '%s\n' "${_LOCK_HASH}" > "${VENV_STAMP}"
489
464
  ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
490
465
  else
491
466
  warn "uv sync failed — leaving venv as-is; re-run 'meridian setup' to retry"
492
467
  fi
493
- }
494
-
495
- _venv_changed=1
496
- _have_hash=""; [[ -f "${VENV_STAMP}" ]] && _have_hash="$(cat "${VENV_STAMP}" 2>/dev/null)"
497
-
498
- if [[ -n "${_LOCK_HASH}" && "${_LOCK_HASH}" == "${_have_hash}" && -x "${VENV}/bin/python" ]]; then
499
- # Path A — deps unchanged since this venv was built. No network, no rebuild.
500
- ok "Python deps unchanged — reusing existing venv (skipped 160 MB download)"
501
- _venv_changed=0
502
- elif [[ -n "${_VERSION}" ]]; then
503
- # Path B — release install/update: fetch the integrity hash, then the tarball.
504
- _remote_sha="$(curl -fsSL --retry 3 "${_VENV_URL}.sha256" 2>/dev/null | tr -d '[:space:]' || true)"
505
- if [[ -n "${_remote_sha}" ]]; then
506
- info "Downloading pre-built Python venv (~160 MB, one-time per dependency change)…"
507
- _venv_tmp="$(mktemp -d)"; _venv_tgz="${_venv_tmp}/services-venv.tar.gz"
508
- if curl -fsSL --retry 3 "${_VENV_URL}" -o "${_venv_tgz}" \
509
- && [[ "$(shasum -a 256 "${_venv_tgz}" | cut -d' ' -f1)" == "${_remote_sha}" ]]; then
510
- # Stamp the uv.lock hash (deterministic) so future updates can skip.
511
- _extract_venv "${_venv_tgz}" "${_LOCK_HASH:-${_remote_sha}}"
512
- rm -rf "${_venv_tmp}"
513
- else
514
- warn "venv download or SHA-256 verification failed — falling back to uv sync"
515
- rm -rf "${_venv_tmp}"
516
- _venv_uv_sync
517
- fi
518
- else
519
- warn "venv release asset unreachable for v${_VERSION} — falling back to uv sync"
520
- _venv_uv_sync
521
- fi
522
- else
523
- # Path C — dev/source install (no VERSION stamp).
524
- _venv_uv_sync
525
468
  fi
526
469
 
527
470
  # On macOS 26+, install apple-fm-sdk so Apple Intelligence is used instead of
528
- # downloading a large MLX model. This runs after both venv paths (tarball or uv
529
- # sync) so the package is available regardless of how the venv was built.
471
+ # downloading a large MLX model. Runs after uv sync so the venv exists.
530
472
  # apple-fm-sdk only installs on macOS 26+ (links against system frameworks);
531
473
  # on older macOS pip will fail gracefully and MLX is used as the fallback.
532
474
  _macos_major="$(sw_vers -productVersion 2>/dev/null | cut -d. -f1)"
@@ -555,13 +497,19 @@ fi
555
497
  if [[ "${SKIP_PERMISSIONS}" -eq 0 ]]; then
556
498
  echo "→ screenpipe needs 2 macOS permissions: Screen Recording and Accessibility."
557
499
  echo " (Audio capture is disabled, so no Microphone permission is required.)"
500
+ echo " You will add these 2 binaries — both live at the same stable path:"
501
+ echo " ${HOME}/.meridian/bin/screenpipe ← Screen Recording + Accessibility"
502
+ echo " ${HOME}/.meridian/bin/meridian-a11y-helper ← Accessibility only"
503
+ echo " In each pane: click '+' → press ⌘⇧G → paste the path above → Open → toggle ON."
558
504
  read -r -p " Press Enter to open Screen Recording settings… " _ || true
559
505
  open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" 2>/dev/null || true
506
+ echo " Add: ${HOME}/.meridian/bin/screenpipe"
560
507
  read -r -p " Press Enter to open Accessibility settings… " _ || true
561
508
  open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" 2>/dev/null || true
562
- echo " In the SAME Accessibility pane, also add the a11y helper (+ → ⌘⇧G → paste):"
509
+ echo " Add both:"
510
+ echo " ${HOME}/.meridian/bin/screenpipe"
563
511
  echo " ${HOME}/.meridian/bin/meridian-a11y-helper"
564
- echo " Without it, Electron apps (Claude, Codex, Slack, …) stay invisible to capture."
512
+ echo " Without the a11y helper, Electron apps (Claude, Codex, Slack, …) stay invisible to capture."
565
513
  read -r -p " Press Enter once all are granted… " _ || true
566
514
  fi
567
515
 
@@ -620,10 +568,42 @@ else
620
568
  info "Installing MLX inference server launchd agent…"
621
569
  bash "${APP_ROOT}/services/scripts/install-mlx-server-daemon.sh" --port "${MLX_PORT}" || warn "MLX agent install failed"
622
570
  info "Waiting for the MLX server to load the model…"
571
+ _MLX_LOG="${HOME}/.meridian/logs/mlx-server.log"
572
+ _MLX_ERR="${HOME}/.meridian/logs/mlx-server-error.log"
623
573
  _w=0
574
+
575
+ # Stream both log files while polling so the user can see which model is
576
+ # loading and whether a download is in progress.
577
+ # mlx-server.log — JSON structured logs: model selection, readiness
578
+ # mlx-server-error.log — raw stderr: huggingface_hub download progress
579
+ # tail -F follows by name and retries if the file doesn't exist yet.
580
+ (tail -F "${_MLX_LOG}" 2>/dev/null | python3 -u -c '
581
+ import sys, json
582
+ for line in sys.stdin:
583
+ line = line.rstrip()
584
+ if not line:
585
+ continue
586
+ try:
587
+ d = json.loads(line)
588
+ lvl = d.get("level", "INFO")
589
+ msg = d.get("message", line)
590
+ prefix = " ⚠ " if lvl in ("WARNING", "ERROR") else " · "
591
+ print(prefix + msg, flush=True)
592
+ except Exception:
593
+ print(" " + line, flush=True)
594
+ ') &
595
+ _log_pid=$!
596
+ (tail -F "${_MLX_ERR}" 2>/dev/null | while IFS= read -r _eline; do
597
+ printf ' %s\n' "${_eline}"
598
+ done) &
599
+ _err_pid=$!
600
+
624
601
  until curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1; do
625
- sleep 3; _w=$((_w+3)); [[ $_w -ge 300 ]] && { warn "MLX not ready after 300s — check: meridian logs mlx-server"; break; }
602
+ sleep 3; _w=$((_w+3))
603
+ [[ $_w -ge 300 ]] && { warn "MLX not ready after 300s — check: meridian logs mlx-server"; break; }
626
604
  done
605
+ { kill "${_log_pid}" "${_err_pid}" 2>/dev/null; wait "${_log_pid}" "${_err_pid}" 2>/dev/null; } || true
606
+
627
607
  # Only stamp after confirmed ready — prevents stale stamp on a failed restart.
628
608
  if curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1; then
629
609
  ok "MLX server ready (${_w}s)"
@@ -20,12 +20,11 @@ mkdir -p "$(dirname "${APP}")"
20
20
  keep=""
21
21
  if [[ -f "${APP}/.env" ]]; then keep="$(mktemp)"; cp "${APP}/.env" "${keep}"; fi
22
22
 
23
- # Preserve the Python venv across updates. Rebuilding it (python -m venv + pip
24
- # install mlx-lm/outlines/…) costs minutes; most releases don't change Python
25
- # deps, so move it aside and restore it to the SAME absolute path (its baked-in
26
- # shebangs stay valid). install-from-bundle.sh then only re-pips when the deps
27
- # hash actually changes. Kept in a sibling dir under ~/.meridian so the move is
28
- # an instant rename (same filesystem), never a cross-volume copy.
23
+ # Preserve the Python venv across updates. The venv is built from PyPI via
24
+ # uv sync at install time; preserving it means install-from-bundle.sh only
25
+ # re-syncs when uv.lock actually changed. Kept in a sibling dir under
26
+ # ~/.meridian so the move is an instant rename (same filesystem), never a
27
+ # cross-volume copy.
29
28
  venv_keep="${HOME}/.meridian/.venv-update-keep"
30
29
  rm -rf "${venv_keep}"
31
30
  if [[ -d "${APP}/services/.venv" ]]; then mv "${APP}/services/.venv" "${venv_keep}"; fi
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meridian-agents"
7
- version = "1.32.0"
8
- description = "Meridian agents — hermes task linking and Jira progress updates for meridian.db"
7
+ version = "1.33.0"
8
+ description = "Meridian agents — MLX classifier server and Jira worklog synthesis for meridian.db"
9
9
  requires-python = ">=3.11"
10
10
  authors = [{ name = "Meridiona" }]
11
11
  license = { text = "MIT" }
@@ -53,9 +53,9 @@ meridian-server = "agents.server:main"
53
53
 
54
54
  [tool.uv]
55
55
  # Lock all extras so `uv lock` produces a complete, reproducible uv.lock.
56
- # The shipped venv installs BOTH server-side extras — `uv sync --extra mlx
57
- # --extra pm_worklog_update` — because the one MLX server serves
58
- # /classify_sessions (mlx) AND /synthesise_worklog (agno, pm_worklog_update).
56
+ # At install time both extras are synced — `uv sync --extra mlx --extra
57
+ # pm_worklog_update` — because the one MLX server serves /classify_sessions
58
+ # (mlx) AND /synthesise_worklog (agno, pm_worklog_update).
59
59
  constraint-dependencies = []
60
60
 
61
61
  [tool.setuptools.packages.find]
package/ui.tar.gz CHANGED
Binary file