This commit is contained in:
2026-09-11 14:55:34 -04:00
parent 660294d0aa
commit 25b2c4be2e
33 changed files with 1293 additions and 34 deletions
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VERSION="${JARVIS_VERSION:-$(node -p "require('${ROOT_DIR}/package.json').version")}"
ARCH="${JARVIS_DEB_ARCH:-$(dpkg --print-architecture)}"
OUT_DIR="${ROOT_DIR}/dist"
PKG_DIR="${OUT_DIR}/jarvis-qvac_${VERSION}_${ARCH}"
rm -rf "${PKG_DIR}"
mkdir -p "${PKG_DIR}/DEBIAN" "${PKG_DIR}/usr/lib/jarvis-qvac" "${PKG_DIR}/usr/share/doc/jarvis-qvac"
cat > "${PKG_DIR}/DEBIAN/control" <<EOF
Package: jarvis-qvac
Version: ${VERSION}
Section: utils
Priority: optional
Architecture: ${ARCH}
Maintainer: JARVIS-QVAC maintainers
Description: Local GPU-backed GNOME voice assistant
JARVIS-QVAC is a local-first voice assistant for Ubuntu GNOME.
EOF
tar --exclude='.git' --exclude='./.agents' --exclude='./.codex' --exclude='./dist' --exclude='./node_modules' --exclude='*/node_modules' --exclude='*/__pycache__' --exclude='*.pyc' -cf - -C "${ROOT_DIR}" . | tar -xf - -C "${PKG_DIR}/usr/lib/jarvis-qvac"
cp "${ROOT_DIR}/README.md" "${PKG_DIR}/usr/share/doc/jarvis-qvac/README.md"
cp "${ROOT_DIR}/docs/RELEASE.md" "${PKG_DIR}/usr/share/doc/jarvis-qvac/RELEASE.md"
dpkg-deb --build --root-owner-group "${PKG_DIR}" "${OUT_DIR}/jarvis-qvac_${VERSION}_${ARCH}.deb" >/dev/null
rm -rf "${PKG_DIR}"
echo "${OUT_DIR}/jarvis-qvac_${VERSION}_${ARCH}.deb"
+9
View File
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUT_DIR="${ROOT_DIR}/dist"
mkdir -p "${OUT_DIR}"
rm -f "${OUT_DIR}/jarvis-qvac-extension.zip"
( cd "${ROOT_DIR}/apps/gnome-extension" && zip -q -r "${OUT_DIR}/jarvis-qvac-extension.zip" "[email protected]" -x '*/__pycache__/*' -x '*.pyc' )
test -s "${OUT_DIR}/jarvis-qvac-extension.zip"
echo "${OUT_DIR}/jarvis-qvac-extension.zip"
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT_DIR}"
mkdir -p dist
rm -f dist/jarvis-qvac_*.deb dist/jarvis-qvac-extension.zip dist/SHA256SUMS
bash packaging/build-extension.sh
bash packaging/build-deb.sh
(cd dist && sha256sum jarvis-qvac_*.deb jarvis-qvac-extension.zip > SHA256SUMS)
echo "Release artifacts written to ${ROOT_DIR}/dist"
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT_DIR}"
echo "JARVIS-QVAC first-run setup"
echo "The daemon is GPU-gated and keeps QVAC behind one master owner."
echo
echo "[1/4] GPU and QVAC preflight"
npm run gpu-doctor || true
npm run qvac:doctor || true
echo
echo "[2/4] Microphone and PipeWire preflight"
npm run voice-doctor || true
echo
echo "[3/5] Wake phrase and model profile"
read -r -p "Wake phrase [hey jarvis]: " WAKE_PHRASE
WAKE_PHRASE="${WAKE_PHRASE:-hey jarvis}"
read -r -p "Model profile [laptop-8gb/laptop-16gb/desktop-gpu] (default: laptop-16gb): " MODEL_PROFILE
MODEL_PROFILE="${MODEL_PROFILE:-laptop-16gb}"
case "${MODEL_PROFILE}" in
laptop-8gb|laptop-16gb|desktop-gpu) ;;
*) echo "Unknown model profile: ${MODEL_PROFILE}" >&2; exit 2 ;;
esac
read -r -p "Enable TTS preview? [Y/n]: " TTS_CHOICE
TTS_ENABLED=true
[[ "${TTS_CHOICE:-Y}" =~ ^[Nn]$ ]] && TTS_ENABLED=false
mkdir -p "${HOME}/.config/jarvis"
node -e 'const fs=require("fs"); const p=process.argv[1], phrase=process.argv[2], profile=process.argv[3], tts=process.argv[4]==="true"; let c={}; try{c=JSON.parse(fs.readFileSync(p,"utf8"))}catch{}; Object.assign(c,{wakePhrase:phrase,modelProfile:profile,ttsEnabled:tts}); fs.writeFileSync(p,JSON.stringify(c,null,2)+"\n")' "${HOME}/.config/jarvis/config.json" "${WAKE_PHRASE}" "${MODEL_PROFILE}" "${TTS_ENABLED}"
echo "Saved wake phrase: ${WAKE_PHRASE}; model profile: ${MODEL_PROFILE}; TTS: ${TTS_ENABLED}"
echo
echo "[4/5] TTS preview"
if [[ "${TTS_ENABLED}" == true && -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
busctl --user call io.qvac.Jarvis /io/qvac/Jarvis io.qvac.Jarvis.Session Say s "JARVIS local voice preview" || true
else
echo "Session D-Bus is unavailable or TTS is disabled; preview skipped."
fi
echo
echo "[5/5] Typed smoke test"
read -r -p "Type a test prompt (default: say hello): " PROMPT
PROMPT="${PROMPT:-say hello}"
if command -v busctl >/dev/null && [[ -n "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
busctl --user call io.qvac.Jarvis /io/qvac/Jarvis io.qvac.Jarvis.Session Ask s "${PROMPT}" || true
else
echo "Session D-Bus is unavailable; saved setup and skipped live prompt."
fi
echo "First-run setup complete. Start with: systemctl --user enable --now jarvisd.service"
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
APP_DIR="${HOME}/.local/share/jarvis-qvac"
EXT_DIR="${HOME}/.local/share/gnome-shell/extensions/[email protected]"
CONFIG_DIR="${HOME}/.config/jarvis"
CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/jarvis/models"
mkdir -p "${APP_DIR}" "${EXT_DIR}" "${CONFIG_DIR}" "${HOME}/.config/systemd/user" "${CACHE_DIR}"
tar --exclude='.git' --exclude='./.agents' --exclude='./.codex' --exclude='./dist' --exclude='./node_modules' --exclude='*/node_modules' --exclude='*/__pycache__' --exclude='*.pyc' -cf - -C "${ROOT_DIR}" . | tar -xf - -C "${APP_DIR}"
if [[ ! -d "${APP_DIR}/node_modules" ]]; then
( cd "${APP_DIR}" && npm ci --omit=dev --ignore-scripts )
fi
rm -rf "${EXT_DIR}"
mkdir -p "${EXT_DIR}"
cp -a "${ROOT_DIR}/apps/gnome-extension/[email protected]/." "${EXT_DIR}/"
cp "${ROOT_DIR}/packaging/jarvisd.service" "${HOME}/.config/systemd/user/jarvisd.service"
if [[ ! -f "${CONFIG_DIR}/qvac.config.json" ]]; then
sed "s#__JARVIS_CACHE__#${CACHE_DIR}#g" "${ROOT_DIR}/packaging/qvac.config.template.json" > "${CONFIG_DIR}/qvac.config.json"
fi
if [[ "${1:-}" == "--enable" ]]; then
systemctl --user daemon-reload
systemctl --user enable --now jarvisd.service
fi
echo "Installed JARVIS-QVAC to ${APP_DIR}"
echo "Run: ${APP_DIR}/packaging/first-run.sh"
+17
View File
@@ -0,0 +1,17 @@
[Unit]
Description=JARVIS-QVAC local voice assistant
After=pipewire.service wireplumber.service graphical-session.target
Wants=pipewire.service
[Service]
Type=simple
WorkingDirectory=%h/.local/share/jarvis-qvac
ExecStart=/usr/bin/node %h/.local/share/jarvis-qvac/daemon/index.js
Environment=QVAC_CONFIG_PATH=%h/.config/jarvis/qvac.config.json
Environment=JARVIS_GPU_REQUIRED=1
Environment=JARVIS_QVAC_OWNER=jarvisd
Restart=on-failure
RestartSec=2
[Install]
WantedBy=graphical-session.target
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
: "${GITEA_SERVER_URL:?GITEA_SERVER_URL is required}"
: "${GITEA_REPOSITORY:?GITEA_REPOSITORY is required}"
: "${GITEA_SHA:?GITEA_SHA is required}"
: "${RELEASE_TOKEN:?RELEASE_TOKEN is required}"
API="${GITEA_SERVER_URL%/}/api/v1/repos/${GITEA_REPOSITORY}"
AUTH=(-H "Authorization: token ${RELEASE_TOKEN}" -H 'Accept: application/json' -H 'Content-Type: application/json')
TAG="rolling"
release_json="$(curl -fsS "${AUTH[@]}" "${API}/releases/tags/${TAG}" 2>/dev/null || true)"
if [[ -n "${release_json}" ]]; then
release_id="$(jq -r '.id // empty' <<<"${release_json}")"
if [[ -n "${release_id}" ]]; then
curl -fsS -X DELETE "${AUTH[@]}" "${API}/releases/${release_id}" >/dev/null
fi
fi
tag_payload="$(jq -nc --arg sha "${GITEA_SHA}" '{new_target:$sha, force:true}')"
if ! curl -fsS -X PATCH "${AUTH[@]}" "${API}/git/refs/tags/${TAG}" --data "${tag_payload}" >/dev/null 2>&1; then
create_tag="$(jq -nc --arg sha "${GITEA_SHA}" --arg ref "refs/tags/${TAG}" '{ref:$ref, sha:$sha}')"
curl -fsS -X POST "${AUTH[@]}" "${API}/git/refs" --data "${create_tag}" >/dev/null
fi
release_payload="$(jq -nc --arg tag "${TAG}" --arg sha "${GITEA_SHA}" \
'{tag_name:$tag, target_commitish:$sha, name:("Rolling " + ($sha[0:12])), body:"Automated Gitea rolling release from main.", draft:false, prerelease:true}')"
created="$(curl -fsS -X POST "${AUTH[@]}" "${API}/releases" --data "${release_payload}")"
release_id="$(jq -r '.id // empty' <<<"${created}")"
test -n "${release_id}"
for asset in dist/jarvis-qvac_*.deb dist/jarvis-qvac-extension.zip dist/SHA256SUMS; do
[[ -f "${asset}" ]] || continue
curl -fsS -X POST "${AUTH[@]}" \
"${API}/releases/${release_id}/assets?name=$(basename "${asset}")" \
-H 'Content-Type: application/octet-stream' --data-binary "@${asset}" >/dev/null
done
echo "Published Gitea rolling release ${TAG} for ${GITEA_SHA:0:12}"
+15
View File
@@ -0,0 +1,15 @@
{
"cacheDirectory": "__JARVIS_CACHE__",
"serve": {
"host": "127.0.0.1",
"port": 11434,
"models": {
"jarvis-master": {
"model": "QWEN3_5_4B_MULTIMODAL_Q4_K_M",
"default": true,
"preload": false,
"config": { "ctx_size": 8192 }
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT_DIR}"
command -v dpkg-deb >/dev/null
command -v zip >/dev/null
bash packaging/build-release.sh >/tmp/jarvis-package-test.log
DEB="$(find dist -maxdepth 1 -name 'jarvis-qvac_*.deb' -print -quit)"
test -n "${DEB}"
dpkg-deb --info "${DEB}" >/dev/null
dpkg-deb --contents "${DEB}" | grep 'usr/lib/jarvis-qvac/daemon/index.js' >/dev/null
unzip -t dist/jarvis-qvac-extension.zip >/dev/null
test -s dist/SHA256SUMS
echo "package smoke test passed"
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ "${1:-}" != "--yes" ]]; then
echo "Refusing to uninstall without --yes. Jarvis data is preserved by default." >&2
exit 2
fi
systemctl --user disable --now jarvisd.service 2>/dev/null || true
rm -f "${HOME}/.config/systemd/user/jarvisd.service"
rm -rf "${HOME}/.local/share/jarvis-qvac" "${HOME}/.local/share/gnome-shell/extensions/[email protected]"
systemctl --user daemon-reload 2>/dev/null || true
if [[ "${2:-}" == "--purge-data" ]]; then
rm -rf "${HOME}/.local/share/jarvis" "${HOME}/.cache/jarvis" "${HOME}/Jarvis"
fi
echo "JARVIS-QVAC removed; user data was preserved unless --purge-data was supplied."