@meridiona/meridian-darwin-arm64 1.31.3 → 1.32.1
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/.env.example +11 -9
- package/VERSION +1 -1
- package/bin/meridian +0 -0
- package/package.json +1 -1
- package/scripts/install-from-bundle.sh +56 -98
- package/scripts/lib-github-setup.sh +125 -0
- package/scripts/meridian-npm-setup.sh +5 -6
- package/services/pyproject.toml +1 -1
- package/ui.tar.gz +0 -0
package/.env.example
CHANGED
|
@@ -68,18 +68,20 @@
|
|
|
68
68
|
# PM_WORKLOG_SYNTH_TIMEOUT_S=300
|
|
69
69
|
|
|
70
70
|
# ---------------------------------------------------------------------------
|
|
71
|
-
# GitHub (GITHUB_TOKEN
|
|
72
|
-
# Syncs the OPEN issues assigned to you
|
|
73
|
-
# structured issue comments (GitHub has no
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
71
|
+
# GitHub (GITHUB_TOKEN required; GITHUB_PROJECT_IDS selects which Projects sync)
|
|
72
|
+
# Syncs the OPEN issues assigned to you from the listed GitHub Projects v2 (read
|
|
73
|
+
# via the GraphQL API); logs worklogs as structured issue comments (GitHub has no
|
|
74
|
+
# native time tracking).
|
|
75
|
+
# Token: easiest is the gh CLI — `meridian setup` runs `gh auth token` and adds
|
|
76
|
+
# the read:project scope for you (no PAT). Otherwise create a classic PAT with
|
|
77
|
+
# the `repo`, `read:org`, `read:project` scopes (read:project reads Projects;
|
|
78
|
+
# repo posts worklog comments).
|
|
79
|
+
# GITHUB_PROJECT_IDS: comma-separated Projects v2 node IDs (PVT_xxx). Find them:
|
|
80
|
+
# gh api graphql -f query='{ viewer { projectsV2(first:10){nodes{id title}} } }'
|
|
78
81
|
# ---------------------------------------------------------------------------
|
|
79
82
|
|
|
80
83
|
# GITHUB_TOKEN=ghp_your_personal_access_token
|
|
81
|
-
#
|
|
82
|
-
# GITHUB_REPOS=your-org/api,your-org/web # optional — comma-separated owner/repo; empty = all repos under the owner
|
|
84
|
+
# GITHUB_PROJECT_IDS=PVT_xxx,PVT_yyy
|
|
83
85
|
|
|
84
86
|
# ---------------------------------------------------------------------------
|
|
85
87
|
# Linear (LINEAR_API_KEY required to enable the Linear connector)
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.32.1
|
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.
|
|
3
|
+
"version": "1.32.1",
|
|
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": {
|
|
@@ -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
|
|
7
|
-
#
|
|
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
|
|
@@ -94,9 +94,16 @@ collect_credentials() {
|
|
|
94
94
|
fi
|
|
95
95
|
echo >&2
|
|
96
96
|
if prompt_category "GitHub"; then
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
if ! _try_gh_token "$env_file"; then
|
|
98
|
+
echo >&2
|
|
99
|
+
echo " Alternatively, create a personal access token (classic) at:" >&2
|
|
100
|
+
echo " https://github.com/settings/tokens/new" >&2
|
|
101
|
+
echo " Required scopes: repo, read:org, read:project" >&2
|
|
102
|
+
echo " (read:project lets meridian read your GitHub Projects; repo posts worklog comments)" >&2
|
|
103
|
+
echo >&2
|
|
104
|
+
prompt_env_var "GITHUB_TOKEN" "GitHub personal access token" 1 "$env_file"
|
|
105
|
+
fi
|
|
106
|
+
_pick_github_projects "$env_file"
|
|
100
107
|
fi
|
|
101
108
|
echo >&2
|
|
102
109
|
if prompt_category "Linear"; then
|
|
@@ -106,6 +113,9 @@ collect_credentials() {
|
|
|
106
113
|
ok "Credential collection complete"
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
# GitHub setup helpers — shared with install.sh.
|
|
117
|
+
source "${APP_ROOT}/scripts/lib-github-setup.sh"
|
|
118
|
+
|
|
109
119
|
GUI_TARGET="gui/$(id -u)"
|
|
110
120
|
LAUNCH_AGENTS="${HOME}/Library/LaunchAgents"
|
|
111
121
|
|
|
@@ -327,6 +337,24 @@ if ! command -v screenpipe >/dev/null 2>&1; then
|
|
|
327
337
|
fi
|
|
328
338
|
fi
|
|
329
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
|
|
330
358
|
if ! command -v ffmpeg >/dev/null 2>&1; then info "Installing ffmpeg…"; brew install ffmpeg; fi
|
|
331
359
|
ok "ffmpeg"
|
|
332
360
|
|
|
@@ -408,115 +436,39 @@ _mlx_was_healthy=0
|
|
|
408
436
|
curl -sf "http://127.0.0.1:${MLX_PORT}/health" >/dev/null 2>&1 && _mlx_was_healthy=1
|
|
409
437
|
|
|
410
438
|
# ── 4. Python venv + MLX deps ────────────────────────────────────────────────
|
|
411
|
-
#
|
|
412
|
-
#
|
|
413
|
-
#
|
|
414
|
-
#
|
|
415
|
-
#
|
|
416
|
-
# package, so this makes most `meridian update` runs touch zero network.
|
|
417
|
-
# B) Deps changed / fresh install: download services-venv-<ver>.tar.gz, verify
|
|
418
|
-
# it against the remote .sha256, extract site-packages into a fresh Python
|
|
419
|
-
# 3.11 venv. No PyPI. Stamp the uv.lock hash for next time.
|
|
420
|
-
# C) Dev/source install (no VERSION, or download/verify failed): `uv sync
|
|
421
|
-
# --frozen` from uv.lock — downloads from PyPI the first time (~40s).
|
|
422
|
-
# NOTE the differential key is the uv.lock hash, NOT the tarball hash: BSD tar
|
|
423
|
-
# embeds mtimes so the tarball hash differs on every CI build even when deps are
|
|
424
|
-
# identical, which would defeat the skip. The remote .sha256 is used only to
|
|
425
|
-
# 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.
|
|
426
444
|
VENV="${APP_ROOT}/services/.venv"
|
|
427
445
|
VENV_STAMP="${VENV}/.meridian-venv-hash"
|
|
428
|
-
_VERSION="$(cat "${APP_ROOT}/VERSION" 2>/dev/null | tr -d '[:space:]' || true)"
|
|
429
|
-
_VENV_URL="https://github.com/Meridiona/meridian/releases/download/v${_VERSION}/services-venv-${_VERSION}.tar.gz"
|
|
430
446
|
_LOCK_HASH="$(shasum -a 256 "${APP_ROOT}/services/uv.lock" 2>/dev/null | cut -d' ' -f1 || true)"
|
|
431
447
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
# enforces this); the venv MUST use exactly 3.11 or the cpython-311 .so files fail
|
|
435
|
-
# to import. Prefer system python3.11, then uv-managed 3.11, then install it via uv.
|
|
436
|
-
_extract_venv() {
|
|
437
|
-
local tgz="$1" stamp_hash="$2" tarball_python="" py_dir="" venv_tmp=""
|
|
438
|
-
# Stage the new venv to a temp path; swap atomically only on success so a
|
|
439
|
-
# failed extraction (disk full, corrupt archive) never destroys the live venv.
|
|
440
|
-
venv_tmp="${VENV}.tmp.$$"
|
|
441
|
-
rm -rf "${venv_tmp}"
|
|
442
|
-
if command -v python3.11 >/dev/null 2>&1; then
|
|
443
|
-
tarball_python="$(command -v python3.11)"
|
|
444
|
-
elif "${UV_BIN}" python find 3.11 >/dev/null 2>&1; then
|
|
445
|
-
tarball_python="$("${UV_BIN}" python find 3.11)"
|
|
446
|
-
else
|
|
447
|
-
info "Installing Python 3.11 (pre-built venv requires it — one-time download)…"
|
|
448
|
-
"${UV_BIN}" python install 3.11
|
|
449
|
-
tarball_python="$("${UV_BIN}" python find 3.11)"
|
|
450
|
-
fi
|
|
451
|
-
"${UV_BIN}" venv --python "${tarball_python}" "${venv_tmp}" 2>/dev/null
|
|
452
|
-
py_dir="$(ls "${venv_tmp}/lib/" | grep '^python' | head -1)"
|
|
453
|
-
mkdir -p "${venv_tmp}/lib/${py_dir}/site-packages"
|
|
454
|
-
tar -xzf "${tgz}" -C "${venv_tmp}/lib/${py_dir}/site-packages"
|
|
455
|
-
# Install the local editable package (meridian-agents) — no deps needed,
|
|
456
|
-
# everything is already in site-packages from the tarball.
|
|
457
|
-
"${UV_BIN}" pip install --quiet --no-deps --python "${venv_tmp}/bin/python" -e "${APP_ROOT}/services"
|
|
458
|
-
# All steps succeeded — atomically replace the live venv.
|
|
459
|
-
rm -rf "${VENV}"
|
|
460
|
-
mv "${venv_tmp}" "${VENV}"
|
|
461
|
-
printf '%s\n' "${stamp_hash}" > "${VENV_STAMP}"
|
|
462
|
-
ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
|
|
463
|
-
}
|
|
448
|
+
_venv_changed=1
|
|
449
|
+
_have_hash=""; [[ -f "${VENV_STAMP}" ]] && _have_hash="$(cat "${VENV_STAMP}" 2>/dev/null)"
|
|
464
450
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
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)…"
|
|
472
457
|
if "${UV_BIN}" sync \
|
|
473
458
|
--project "${APP_ROOT}/services" \
|
|
474
459
|
--extra mlx \
|
|
475
460
|
--extra pm_worklog_update \
|
|
476
461
|
--frozen \
|
|
477
|
-
--python
|
|
462
|
+
--python 3.11; then
|
|
478
463
|
[[ -n "${_LOCK_HASH}" ]] && printf '%s\n' "${_LOCK_HASH}" > "${VENV_STAMP}"
|
|
479
464
|
ok "Python services ready ($(${VENV}/bin/python --version 2>&1))"
|
|
480
465
|
else
|
|
481
466
|
warn "uv sync failed — leaving venv as-is; re-run 'meridian setup' to retry"
|
|
482
467
|
fi
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
_venv_changed=1
|
|
486
|
-
_have_hash=""; [[ -f "${VENV_STAMP}" ]] && _have_hash="$(cat "${VENV_STAMP}" 2>/dev/null)"
|
|
487
|
-
|
|
488
|
-
if [[ -n "${_LOCK_HASH}" && "${_LOCK_HASH}" == "${_have_hash}" && -x "${VENV}/bin/python" ]]; then
|
|
489
|
-
# Path A — deps unchanged since this venv was built. No network, no rebuild.
|
|
490
|
-
ok "Python deps unchanged — reusing existing venv (skipped 160 MB download)"
|
|
491
|
-
_venv_changed=0
|
|
492
|
-
elif [[ -n "${_VERSION}" ]]; then
|
|
493
|
-
# Path B — release install/update: fetch the integrity hash, then the tarball.
|
|
494
|
-
_remote_sha="$(curl -fsSL --retry 3 "${_VENV_URL}.sha256" 2>/dev/null | tr -d '[:space:]' || true)"
|
|
495
|
-
if [[ -n "${_remote_sha}" ]]; then
|
|
496
|
-
info "Downloading pre-built Python venv (~160 MB, one-time per dependency change)…"
|
|
497
|
-
_venv_tmp="$(mktemp -d)"; _venv_tgz="${_venv_tmp}/services-venv.tar.gz"
|
|
498
|
-
if curl -fsSL --retry 3 "${_VENV_URL}" -o "${_venv_tgz}" \
|
|
499
|
-
&& [[ "$(shasum -a 256 "${_venv_tgz}" | cut -d' ' -f1)" == "${_remote_sha}" ]]; then
|
|
500
|
-
# Stamp the uv.lock hash (deterministic) so future updates can skip.
|
|
501
|
-
_extract_venv "${_venv_tgz}" "${_LOCK_HASH:-${_remote_sha}}"
|
|
502
|
-
rm -rf "${_venv_tmp}"
|
|
503
|
-
else
|
|
504
|
-
warn "venv download or SHA-256 verification failed — falling back to uv sync"
|
|
505
|
-
rm -rf "${_venv_tmp}"
|
|
506
|
-
_venv_uv_sync
|
|
507
|
-
fi
|
|
508
|
-
else
|
|
509
|
-
warn "venv release asset unreachable for v${_VERSION} — falling back to uv sync"
|
|
510
|
-
_venv_uv_sync
|
|
511
|
-
fi
|
|
512
|
-
else
|
|
513
|
-
# Path C — dev/source install (no VERSION stamp).
|
|
514
|
-
_venv_uv_sync
|
|
515
468
|
fi
|
|
516
469
|
|
|
517
470
|
# On macOS 26+, install apple-fm-sdk so Apple Intelligence is used instead of
|
|
518
|
-
# downloading a large MLX model.
|
|
519
|
-
# 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.
|
|
520
472
|
# apple-fm-sdk only installs on macOS 26+ (links against system frameworks);
|
|
521
473
|
# on older macOS pip will fail gracefully and MLX is used as the fallback.
|
|
522
474
|
_macos_major="$(sw_vers -productVersion 2>/dev/null | cut -d. -f1)"
|
|
@@ -545,13 +497,19 @@ fi
|
|
|
545
497
|
if [[ "${SKIP_PERMISSIONS}" -eq 0 ]]; then
|
|
546
498
|
echo "→ screenpipe needs 2 macOS permissions: Screen Recording and Accessibility."
|
|
547
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."
|
|
548
504
|
read -r -p " Press Enter to open Screen Recording settings… " _ || true
|
|
549
505
|
open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" 2>/dev/null || true
|
|
506
|
+
echo " Add: ${HOME}/.meridian/bin/screenpipe"
|
|
550
507
|
read -r -p " Press Enter to open Accessibility settings… " _ || true
|
|
551
508
|
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" 2>/dev/null || true
|
|
552
|
-
echo "
|
|
509
|
+
echo " Add both:"
|
|
510
|
+
echo " ${HOME}/.meridian/bin/screenpipe"
|
|
553
511
|
echo " ${HOME}/.meridian/bin/meridian-a11y-helper"
|
|
554
|
-
echo " Without
|
|
512
|
+
echo " Without the a11y helper, Electron apps (Claude, Codex, Slack, …) stay invisible to capture."
|
|
555
513
|
read -r -p " Press Enter once all are granted… " _ || true
|
|
556
514
|
fi
|
|
557
515
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# meridian — normalises screenpipe activity into structured app sessions
|
|
3
|
+
#
|
|
4
|
+
# Shared GitHub setup helpers, sourced by both install.sh (source installs) and
|
|
5
|
+
# scripts/install-from-bundle.sh (bundle installs). The sourcing script must
|
|
6
|
+
# already define: info, ok, warn, get_env_value, set_env_value (resolved at
|
|
7
|
+
# call time, so definition order across files does not matter).
|
|
8
|
+
|
|
9
|
+
# Obtain a GitHub token from the gh CLI — no PAT needed. meridian needs two
|
|
10
|
+
# scopes: `repo` (post worklog / task-update issue comments) and `read:project`
|
|
11
|
+
# (read Projects v2 via GraphQL). gh's default web-login grants repo + read:org
|
|
12
|
+
# but not read:project, so add whatever is missing through the same browser flow,
|
|
13
|
+
# then write the OAuth token to GITHUB_TOKEN. Returns non-zero if gh is missing,
|
|
14
|
+
# unauthenticated, or the scope refresh fails, so the caller can fall back to a
|
|
15
|
+
# manual PAT prompt. An existing GITHUB_TOKEN is kept untouched.
|
|
16
|
+
_try_gh_token() {
|
|
17
|
+
local env_file="$1"
|
|
18
|
+
[[ -n "$(get_env_value GITHUB_TOKEN "$env_file")" ]] && {
|
|
19
|
+
ok "GITHUB_TOKEN already set — keeping"; return 0
|
|
20
|
+
}
|
|
21
|
+
command -v gh >/dev/null 2>&1 || return 1
|
|
22
|
+
gh auth status >/dev/null 2>&1 || return 1
|
|
23
|
+
|
|
24
|
+
# Add any missing scope through gh's browser flow. `project` (write) satisfies
|
|
25
|
+
# the read:project requirement too, so accept either.
|
|
26
|
+
local status; status="$(gh auth status 2>&1)"
|
|
27
|
+
local want=()
|
|
28
|
+
grep -q "'repo'" <<< "$status" || want+=("repo")
|
|
29
|
+
grep -qE "'read:project'|'project'" <<< "$status" || want+=("read:project")
|
|
30
|
+
if (( ${#want[@]} > 0 )); then
|
|
31
|
+
local joined; printf -v joined '%s,' "${want[@]}"; joined="${joined%,}"
|
|
32
|
+
info " Granting the ${joined} scope(s) to your gh login (opens a browser)…"
|
|
33
|
+
gh auth refresh -h github.com -s "$joined" >&2 || {
|
|
34
|
+
warn " Could not extend gh scopes — use a personal access token instead"
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
local token
|
|
40
|
+
token="$(gh auth token 2>/dev/null)" || return 1
|
|
41
|
+
[[ -z "$token" ]] && return 1
|
|
42
|
+
set_env_value GITHUB_TOKEN "$token" "$env_file"
|
|
43
|
+
ok "GITHUB_TOKEN set from gh CLI (no PAT needed)"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Interactively pick GitHub Projects and write their node IDs to GITHUB_PROJECT_IDS.
|
|
47
|
+
# Lists both personal and org projects via GraphQL. No-op if already set or if
|
|
48
|
+
# the gh CLI is unavailable or unauthenticated.
|
|
49
|
+
_pick_github_projects() {
|
|
50
|
+
local env_file="$1"
|
|
51
|
+
[[ -n "$(get_env_value GITHUB_PROJECT_IDS "$env_file")" ]] && {
|
|
52
|
+
ok "GITHUB_PROJECT_IDS already set — keeping"; return 0
|
|
53
|
+
}
|
|
54
|
+
command -v gh >/dev/null 2>&1 || return 0
|
|
55
|
+
gh auth status >/dev/null 2>&1 || return 0
|
|
56
|
+
|
|
57
|
+
local raw
|
|
58
|
+
raw="$(gh api graphql -f query='
|
|
59
|
+
{ viewer {
|
|
60
|
+
projectsV2(first: 20) { nodes { id title } }
|
|
61
|
+
organizations(first: 20) {
|
|
62
|
+
nodes { login projectsV2(first: 20) { nodes { id title } } }
|
|
63
|
+
}
|
|
64
|
+
} }' 2>/dev/null)" || {
|
|
65
|
+
warn "Could not list GitHub Projects — add GITHUB_PROJECT_IDS to the config manually if needed"
|
|
66
|
+
return 0
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# One python3 pass emits "id<TAB>label" per project (personal + org). python3
|
|
70
|
+
# is always present on macOS.
|
|
71
|
+
local pairs_raw
|
|
72
|
+
pairs_raw="$(printf '%s' "$raw" | python3 -c "
|
|
73
|
+
import json, sys
|
|
74
|
+
d = json.load(sys.stdin).get('data', {}).get('viewer', {})
|
|
75
|
+
for n in d.get('projectsV2', {}).get('nodes', []):
|
|
76
|
+
print('%s\t%s' % (n['id'], n['title']))
|
|
77
|
+
for org in d.get('organizations', {}).get('nodes', []):
|
|
78
|
+
for n in org.get('projectsV2', {}).get('nodes', []):
|
|
79
|
+
print('%s\t%s / %s' % (n['id'], org['login'], n['title']))
|
|
80
|
+
" 2>/dev/null)" || true
|
|
81
|
+
|
|
82
|
+
# Split each "id<TAB>label" line into parallel arrays (bash 3.2 — no mapfile).
|
|
83
|
+
local _ids=() _labels=()
|
|
84
|
+
local _id _label
|
|
85
|
+
while IFS=$'\t' read -r _id _label; do
|
|
86
|
+
[[ -z "$_id" ]] && continue
|
|
87
|
+
_ids+=("$_id"); _labels+=("$_label")
|
|
88
|
+
done <<< "$pairs_raw"
|
|
89
|
+
local count=${#_ids[@]}
|
|
90
|
+
(( count == 0 )) && { warn "No GitHub Projects found for your account"; return 0; }
|
|
91
|
+
|
|
92
|
+
echo >&2
|
|
93
|
+
echo " Your GitHub Projects:" >&2
|
|
94
|
+
local i=0
|
|
95
|
+
while (( i < count )); do
|
|
96
|
+
printf " %d. %s\n" "$((i+1))" "${_labels[$i]}" >&2
|
|
97
|
+
i=$((i+1))
|
|
98
|
+
done
|
|
99
|
+
echo >&2
|
|
100
|
+
|
|
101
|
+
local selection
|
|
102
|
+
read -r -p " Enter project numbers (comma-sep, e.g. 1,2) or Enter to skip: " selection
|
|
103
|
+
[[ -z "$selection" ]] && { info " (skipped GITHUB_PROJECT_IDS)"; return 0; }
|
|
104
|
+
|
|
105
|
+
local selected_ids=()
|
|
106
|
+
local IFS_save="$IFS"
|
|
107
|
+
IFS=',' read -ra nums <<< "$selection"
|
|
108
|
+
IFS="$IFS_save"
|
|
109
|
+
local n
|
|
110
|
+
for n in "${nums[@]}"; do
|
|
111
|
+
n="${n//[[:space:]]/}"
|
|
112
|
+
if [[ "$n" =~ ^[0-9]+$ ]] && (( n >= 1 && n <= count )); then
|
|
113
|
+
selected_ids+=("${_ids[$((n-1))]}")
|
|
114
|
+
fi
|
|
115
|
+
done
|
|
116
|
+
|
|
117
|
+
if [[ ${#selected_ids[@]} -eq 0 ]]; then
|
|
118
|
+
info " (no valid selection — skipped GITHUB_PROJECT_IDS)"; return 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
local joined
|
|
122
|
+
printf -v joined '%s,' "${selected_ids[@]}"
|
|
123
|
+
set_env_value GITHUB_PROJECT_IDS "${joined%,}" "$env_file"
|
|
124
|
+
ok "GITHUB_PROJECT_IDS set (${#selected_ids[@]} project(s))"
|
|
125
|
+
}
|
|
@@ -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.
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
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
|
package/services/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "meridian-agents"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.32.1"
|
|
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
CHANGED
|
Binary file
|