@meridiona/meridian-darwin-arm64 1.27.2 → 1.27.4

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.27.2
1
+ 1.27.4
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.27.2",
3
+ "version": "1.27.4",
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": {
@@ -31,6 +31,14 @@
31
31
  <string>{{HELPER_BIN}}</string>
32
32
  </array>
33
33
 
34
+ <key>EnvironmentVariables</key>
35
+ <dict>
36
+ <key>HOME</key>
37
+ <string>{{HOME}}</string>
38
+ <key>PATH</key>
39
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
40
+ </dict>
41
+
34
42
  <key>StandardOutPath</key>
35
43
  <string>{{HOME}}/.meridian/logs/a11y-helper.log</string>
36
44
  <key>StandardErrorPath</key>
@@ -41,7 +49,7 @@
41
49
  <key>KeepAlive</key>
42
50
  <true/>
43
51
  <key>ThrottleInterval</key>
44
- <integer>10</integer>
52
+ <integer>30</integer>
45
53
  <key>ProcessType</key>
46
54
  <string>Background</string>
47
55
  </dict>
@@ -67,7 +67,7 @@
67
67
  <key>KeepAlive</key>
68
68
  <true/>
69
69
  <key>ThrottleInterval</key>
70
- <integer>10</integer>
70
+ <integer>30</integer>
71
71
  <key>ProcessType</key>
72
72
  <string>Background</string>
73
73
  </dict>
@@ -21,9 +21,10 @@
21
21
 
22
22
  <key>ProgramArguments</key>
23
23
  <array>
24
- <string>/bin/sh</string>
25
- <string>-c</string>
26
- <string>exec {{SCREENPIPE_BIN}} record --disable-audio --use-pii-removal</string>
24
+ <string>{{SCREENPIPE_BIN}}</string>
25
+ <string>record</string>
26
+ <string>--disable-audio</string>
27
+ <string>--use-pii-removal</string>
27
28
  </array>
28
29
 
29
30
  <key>EnvironmentVariables</key>
@@ -46,7 +47,7 @@
46
47
  <key>KeepAlive</key>
47
48
  <true/>
48
49
  <key>ThrottleInterval</key>
49
- <integer>10</integer>
50
+ <integer>30</integer>
50
51
  <key>ProcessType</key>
51
52
  <string>Background</string>
52
53
  </dict>
@@ -33,7 +33,7 @@
33
33
  <key>EnvironmentVariables</key>
34
34
  <dict>
35
35
  <key>PATH</key>
36
- <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
36
+ <string>{{NODE_BIN_DIR}}/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
37
37
  <key>HOME</key>
38
38
  <string>{{HOME}}</string>
39
39
  <key>MERIDIAN_DB</key>
@@ -55,7 +55,7 @@
55
55
  <key>KeepAlive</key>
56
56
  <true/>
57
57
  <key>ThrottleInterval</key>
58
- <integer>10</integer>
58
+ <integer>30</integer>
59
59
  <key>ProcessType</key>
60
60
  <string>Background</string>
61
61
  </dict>
@@ -52,8 +52,8 @@ ENV_FILE="${REPO_ROOT}/.env"
52
52
  MERIDIAN_OO_AUTH=""
53
53
  MERIDIAN_OTLP_ENDPOINT=""
54
54
  if [[ -f "${ENV_FILE}" ]]; then
55
- MERIDIAN_OO_AUTH="$(grep -E '^MERIDIAN_OO_AUTH=' "${ENV_FILE}" | cut -d= -f2- | tr -d '[:space:]')" || true
56
- MERIDIAN_OTLP_ENDPOINT="$(grep -E '^MERIDIAN_OTLP_ENDPOINT=' "${ENV_FILE}" | cut -d= -f2- | tr -d '[:space:]')" || true
55
+ MERIDIAN_OO_AUTH="$(grep -E '^MERIDIAN_OO_AUTH=' "${ENV_FILE}" | cut -d= -f2- | tr -d '[:space:]' | sed "s/^['\"]//;s/['\"]$//")" || true
56
+ MERIDIAN_OTLP_ENDPOINT="$(grep -E '^MERIDIAN_OTLP_ENDPOINT=' "${ENV_FILE}" | cut -d= -f2- | tr -d '[:space:]' | sed "s/^['\"]//;s/['\"]$//")" || true
57
57
  fi
58
58
  if [[ -z "${MERIDIAN_OO_AUTH}" ]]; then
59
59
  echo " ⚠ MERIDIAN_OO_AUTH not set in <repo>/.env — OTLP export will be disabled"
@@ -131,6 +131,13 @@ resolve_node_runtime() {
131
131
  local ver sha
132
132
  ver="$(grep '^NODE_RUNTIME_VERSION=' "${meta}" | cut -d= -f2 | tr -d '[:space:]')"
133
133
  sha="$(grep '^NODE_RUNTIME_SHA=' "${meta}" | cut -d= -f2 | tr -d '[:space:]')"
134
+ if [[ -z "${ver}" || -z "${sha}" ]]; then
135
+ warn "node-runtime.meta is malformed (missing VERSION or SHA) — falling back to system node"
136
+ for _n in /opt/homebrew/bin/node /usr/local/bin/node /usr/bin/node; do
137
+ [[ -x "${_n}" ]] && { echo "${_n}"; return 0; }
138
+ done
139
+ return 1
140
+ fi
134
141
  local cache_dir="${HOME}/.meridian/node-runtime/v${ver}"
135
142
  local cache_bin="${cache_dir}/bin/node"
136
143
  if [[ -x "${cache_bin}" ]]; then echo "${cache_bin}"; return 0; fi
@@ -239,6 +246,13 @@ configure_editor_accessibility() {
239
246
  # Insert the key after the first `{`. VS Code-family parsers are
240
247
  # JSONC-tolerant, so this preserves existing keys/comments/formatting.
241
248
  perl -0777 -i -pe 's/\{/\{\n\t"editor.accessibilitySupport": "on",/ unless $done++' "$settings"
249
+ # Sanity check: the key must appear in the file; if not, the regex
250
+ # found no `{` (unusual) or perl silently failed — restore backup.
251
+ if ! grep -q '"editor.accessibilitySupport"' "$settings" 2>/dev/null; then
252
+ warn "${ed}: settings.json edit failed — restoring backup"
253
+ cp "${settings}.meridian-bak" "$settings"
254
+ continue
255
+ fi
242
256
  fi
243
257
  ok "${ed}: enabled editor.accessibilitySupport = on"
244
258
  # The setting is read ONCE at editor boot. If the editor is running
@@ -280,7 +294,15 @@ fi
280
294
  command -v node >/dev/null 2>&1 || { info "Installing Node.js…"; brew install node; }
281
295
  PYTHON_BIN=""
282
296
  for p in python3.11 python3; do command -v "$p" >/dev/null 2>&1 && { PYTHON_BIN="$(command -v "$p")"; break; }; done
283
- [[ -n "${PYTHON_BIN}" ]] || { info "Installing Python 3.11…"; brew install python@3.11; PYTHON_BIN="$(command -v python3.11)"; }
297
+ if [[ -z "${PYTHON_BIN}" ]]; then
298
+ info "Installing Python 3.11…"
299
+ brew install python@3.11
300
+ # `command -v` may return empty in a non-interactive shell immediately after
301
+ # `brew install` because the formula's bin dir isn't in launchd's PATH yet.
302
+ # Resolve via `brew --prefix` which is always accurate.
303
+ PYTHON_BIN="$(brew --prefix python@3.11)/bin/python3.11"
304
+ [[ -x "${PYTHON_BIN}" ]] || PYTHON_BIN="$(command -v python3.11 2>/dev/null || true)"
305
+ fi
284
306
  # uv is the package/venv manager for Python services. Install via Homebrew (already
285
307
  # required by this installer) rather than the astral curl|sh installer.
286
308
  UV_BIN=""
@@ -289,7 +311,8 @@ if command -v uv >/dev/null 2>&1; then
289
311
  else
290
312
  info "Installing uv (Python package manager)…"
291
313
  brew install uv
292
- UV_BIN="$(command -v uv)"
314
+ UV_BIN="$(brew --prefix uv)/bin/uv"
315
+ [[ -x "${UV_BIN}" ]] || UV_BIN="$(command -v uv 2>/dev/null || true)"
293
316
  fi
294
317
  ok "node + python ($(${PYTHON_BIN} --version 2>&1)) + uv ($(${UV_BIN} --version 2>&1))"
295
318
 
@@ -405,8 +428,11 @@ _LOCK_HASH="$(shasum -a 256 "${APP_ROOT}/services/uv.lock" 2>/dev/null | cut -d'
405
428
  # enforces this); the venv MUST use exactly 3.11 or the cpython-311 .so files fail
406
429
  # to import. Prefer system python3.11, then uv-managed 3.11, then install it via uv.
407
430
  _extract_venv() {
408
- local tgz="$1" stamp_hash="$2" tarball_python="" py_dir=""
409
- rm -rf "${VENV}"
431
+ local tgz="$1" stamp_hash="$2" tarball_python="" py_dir="" venv_tmp=""
432
+ # Stage the new venv to a temp path; swap atomically only on success so a
433
+ # failed extraction (disk full, corrupt archive) never destroys the live venv.
434
+ venv_tmp="${VENV}.tmp.$$"
435
+ rm -rf "${venv_tmp}"
410
436
  if command -v python3.11 >/dev/null 2>&1; then
411
437
  tarball_python="$(command -v python3.11)"
412
438
  elif "${UV_BIN}" python find 3.11 >/dev/null 2>&1; then
@@ -416,13 +442,16 @@ _extract_venv() {
416
442
  "${UV_BIN}" python install 3.11
417
443
  tarball_python="$("${UV_BIN}" python find 3.11)"
418
444
  fi
419
- "${UV_BIN}" venv --python "${tarball_python}" "${VENV}" 2>/dev/null
420
- py_dir="$(ls "${VENV}/lib/" | grep '^python' | head -1)"
421
- mkdir -p "${VENV}/lib/${py_dir}/site-packages"
422
- tar -xzf "${tgz}" -C "${VENV}/lib/${py_dir}/site-packages"
445
+ "${UV_BIN}" venv --python "${tarball_python}" "${venv_tmp}" 2>/dev/null
446
+ py_dir="$(ls "${venv_tmp}/lib/" | grep '^python' | head -1)"
447
+ mkdir -p "${venv_tmp}/lib/${py_dir}/site-packages"
448
+ tar -xzf "${tgz}" -C "${venv_tmp}/lib/${py_dir}/site-packages"
423
449
  # Install the local editable package (meridian-agents) — no deps needed,
424
450
  # everything is already in site-packages from the tarball.
425
- "${UV_BIN}" pip install --quiet --no-deps --python "${VENV}/bin/python" -e "${APP_ROOT}/services"
451
+ "${UV_BIN}" pip install --quiet --no-deps --python "${venv_tmp}/bin/python" -e "${APP_ROOT}/services"
452
+ # All steps succeeded — atomically replace the live venv.
453
+ rm -rf "${VENV}"
454
+ mv "${venv_tmp}" "${VENV}"
426
455
  printf '%s\n' "${stamp_hash}" > "${VENV_STAMP}"
427
456
  ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
428
457
  }
@@ -544,6 +573,9 @@ elif [[ -d "${APP_ROOT}/ui" ]]; then
544
573
  # ui/ was preserved by meridian-npm-setup.sh — hash matched, no re-extraction needed
545
574
  ok "dashboard unchanged — reusing existing build"
546
575
  _ui_changed=0
576
+ else
577
+ err "Dashboard bundle missing from ${APP_ROOT} — re-run the installer: curl -fsSL https://raw.githubusercontent.com/Meridiona/meridian/main/scripts/bootstrap.sh | bash"
578
+ exit 1
547
579
  fi
548
580
 
549
581
  # ── 6. Daemons — restart only what changed ───────────────────────────────────
@@ -564,10 +596,10 @@ _py_src_changed=1
564
596
  if [[ -f "${_PY_SRC_STAMP}" && "$(cat "${_PY_SRC_STAMP}")" == "${_py_src_hash}" ]]; then
565
597
  _py_src_changed=0
566
598
  fi
567
- echo "${_py_src_hash}" > "${_PY_SRC_STAMP}"
568
-
569
599
  if [[ "${_mlx_was_healthy}" -eq 1 && "${_venv_changed}" -eq 0 && "${_py_src_changed}" -eq 0 ]]; then
570
600
  ok "Python services unchanged — MLX server kept running"
601
+ # Stamp only when the server is confirmed healthy (skip case = already healthy).
602
+ echo "${_py_src_hash}" > "${_PY_SRC_STAMP}"
571
603
  else
572
604
  info "Installing MLX inference server launchd agent…"
573
605
  bash "${APP_ROOT}/services/scripts/install-mlx-server-daemon.sh" --port "${MLX_PORT}" || warn "MLX agent install failed"
@@ -576,7 +608,11 @@ else
576
608
  until curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1; do
577
609
  sleep 3; _w=$((_w+3)); [[ $_w -ge 300 ]] && { warn "MLX not ready after 300s — check: meridian logs mlx-server"; break; }
578
610
  done
579
- curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1 && ok "MLX server ready (${_w}s)"
611
+ # Only stamp after confirmed ready prevents stale stamp on a failed restart.
612
+ if curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1; then
613
+ ok "MLX server ready (${_w}s)"
614
+ echo "${_py_src_hash}" > "${_PY_SRC_STAMP}"
615
+ fi
580
616
  fi
581
617
 
582
618
  # Rust daemon: skip restart when binary is identical.
@@ -596,11 +632,13 @@ else
596
632
  fi
597
633
 
598
634
  # Persist component hashes for the next update's differential check.
635
+ # Write to a temp file and rename atomically so a crash mid-write never leaves
636
+ # a half-written or empty hash file (which would force a full reinstall).
599
637
  _final_ui_hash="${_new_ui_hash:-${_OLD_UI_HASH}}"
600
638
  {
601
639
  [[ -n "${_new_daemon_hash}" ]] && printf 'daemon_bin=%s\n' "${_new_daemon_hash}"
602
640
  [[ -n "${_final_ui_hash}" ]] && printf 'ui_tarball=%s\n' "${_final_ui_hash}"
603
- } > "${_HASH_FILE}"
641
+ } > "${_HASH_FILE}.tmp" && mv "${_HASH_FILE}.tmp" "${_HASH_FILE}"
604
642
 
605
643
  ok "all daemons installed"
606
644
 
@@ -608,8 +646,12 @@ ok "all daemons installed"
608
646
  _skill_src="${APP_ROOT}/services/skills/coding-agent/session-summary/SKILL.md"
609
647
  _skill_dst="${HOME}/.claude/commands/session-summary.md"
610
648
  mkdir -p "${HOME}/.claude/commands"
611
- cp "${_skill_src}" "${_skill_dst}"
612
- ok "session-summary command → ~/.claude/commands/session-summary.md"
649
+ if [[ -f "${_skill_src}" ]]; then
650
+ cp "${_skill_src}" "${_skill_dst}"
651
+ ok "session-summary command → ~/.claude/commands/session-summary.md"
652
+ else
653
+ warn "session-summary skill not found in bundle — skipping (${_skill_src})"
654
+ fi
613
655
 
614
656
  # Pipeline smoke test — verify both LLM stages return valid output (no DB writes).
615
657
  echo ""
@@ -34,6 +34,15 @@ if [[ -z "${NPM_BIN}" ]]; then
34
34
  echo "✗ npm not found in PATH — install Node.js 18+ and re-run" >&2
35
35
  exit 1
36
36
  fi
37
+ # Capture the node binary directory so launchd's restricted PATH includes it.
38
+ # `npm run start` invokes `next start` whose shebang resolves `node` via PATH;
39
+ # NVM / fnm / asdf install node outside /opt/homebrew and /usr/local, so the
40
+ # standard launchd PATH misses it. Injecting NODE_BIN_DIR fixes this.
41
+ NODE_BIN="$(command -v node 2>/dev/null || true)"
42
+ NODE_BIN_DIR_PREFIX=""
43
+ if [[ -n "${NODE_BIN}" ]]; then
44
+ NODE_BIN_DIR_PREFIX="$(dirname "${NODE_BIN}"):"
45
+ fi
37
46
 
38
47
  if [[ ! -d "${REPO_ROOT}/ui/.next" ]]; then
39
48
  echo "✗ ui/.next not found — run \`cd ui && npm ci && npm run build\` first" >&2
@@ -49,6 +58,7 @@ sed \
49
58
  -e "s|{{REPO_ROOT}}|${REPO_ROOT}|g" \
50
59
  -e "s|{{HOME}}|${HOME}|g" \
51
60
  -e "s|{{NPM_BIN}}|${NPM_BIN}|g" \
61
+ -e "s|{{NODE_BIN_DIR}}|${NODE_BIN_DIR_PREFIX}|g" \
52
62
  "${TEMPLATE}" > "${PLIST_DEST}"
53
63
 
54
64
  if ! plutil -lint "${PLIST_DEST}" >/dev/null; then
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meridian-agents"
7
- version = "1.27.2"
7
+ version = "1.27.4"
8
8
  description = "Meridian agents — hermes task linking and Jira progress updates for meridian.db"
9
9
  requires-python = ">=3.11"
10
10
  authors = [{ name = "Meridiona" }]
package/ui.tar.gz ADDED
Binary file