diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml
index 2a8cddbc..2f546c45 100644
--- a/.github/workflows/auto-release.yml
+++ b/.github/workflows/auto-release.yml
@@ -99,3 +99,16 @@ jobs:
with:
ref: ${{ needs.release.outputs.new_tag }}
version: ${{ needs.release.outputs.new_tag }}
+
+ # Build + attach the native desktop installers (macOS/Linux signed if secrets
+ # are set; Windows best-effort). Same in-run pattern as publish-images, because
+ # the GITHUB_TOKEN-created release does not fire a `release` event.
+ desktop-apps:
+ needs: release
+ if: ${{ needs.release.outputs.new_tag != '' }}
+ permissions:
+ contents: write
+ uses: ./.github/workflows/desktop-apps.yml
+ with:
+ tag: ${{ needs.release.outputs.new_tag }}
+ secrets: inherit
diff --git a/.github/workflows/desktop-apps.yml b/.github/workflows/desktop-apps.yml
index f17b85b0..8afaa83e 100644
--- a/.github/workflows/desktop-apps.yml
+++ b/.github/workflows/desktop-apps.yml
@@ -30,6 +30,15 @@ on:
description: 'Release tag to build + attach to (e.g. v0.3.0)'
required: true
type: string
+ # Called in-run from auto-release.yml: a GITHUB_TOKEN-created release does NOT
+ # fire the `release` event, so the apps must be built from within the same run
+ # that cut the release (same pattern as publish-images.yml).
+ workflow_call:
+ inputs:
+ tag:
+ description: 'Release tag to build + attach to'
+ required: true
+ type: string
concurrency:
group: desktop-apps-${{ github.event.release.tag_name || inputs.tag }}
@@ -43,8 +52,11 @@ jobs:
fail-fast: false
matrix:
include:
+ # macOS is Apple Silicon (arm64) only for v1 — see the rationale in
+ # desktop/electron-builder.yml (extraResources can't be arch-split, and
+ # Intel runners queue for hours). Intel x64 is a tracked follow-up.
- os: macos-latest
- args: --mac
+ args: --mac --arm64
- os: windows-latest
args: --win
# Linux is best-effort for now: AppImage/deb are built + uploaded but
@@ -53,12 +65,16 @@ jobs:
- os: ubuntu-latest
args: --linux
runs-on: ${{ matrix.os }}
+ # Hard cap so a stuck native build can never run for hours.
+ timeout-minutes: 30
env:
TAG: ${{ github.event.release.tag_name || inputs.tag }}
# Must match Supervisor.KERNEL_PORT in desktop/src/supervisor.ts — the
# web-ui bakes this kernel URL into its routes-manifest at BUILD time, so
# it cannot be a per-launch random port.
KERNEL_URL: http://127.0.0.1:8769
+ # node-gyp's VS auto-detection is unreliable on windows-latest; pin VS2022.
+ GYP_MSVS_VERSION: '2022'
# macOS signing (hoisted so they're addressable in step-level `if:`).
APPLE_P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64_HIGH5 }}
APPLE_P12_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD_HIGH5 }}
@@ -82,6 +98,21 @@ jobs:
with:
node-version: 22
+ # node-gyp's bundled gyp imports Python's `distutils`, removed in Python
+ # 3.12. The macOS/Windows runners default to 3.12+ (Ubuntu still ships
+ # 3.11), so pin 3.11 everywhere to keep native rebuilds working.
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ # @electron/rebuild pulls @electron/node-gyp as a git+ssh GitHub dependency.
+ # Runners have no SSH key, so rewrite git+ssh GitHub URLs to https (public
+ # repo, no auth) before any npm install/ci, or those installs fail.
+ - name: Allow git-https for git dependencies
+ run: |
+ git config --global url."https://github.com/".insteadOf "ssh://git@github.com/"
+ git config --global url."https://github.com/".insteadOf "git@github.com:"
+
# --- build the omadia runtime the installer bundles -------------------
- name: Build middleware kernel
working-directory: middleware
@@ -101,25 +132,29 @@ jobs:
working-directory: desktop
run: npm ci
- # better-sqlite3/argon2/sharp ship in middleware/node_modules built for the
- # system Node ABI; the kernel runs under Electron's Node (ELECTRON_RUN_AS_NODE)
- # so they must be rebuilt against Electron's ABI before staging. electron-builder
- # rebuilds the APP's own deps but NOT extraResources, so we do it here.
- - name: Rebuild middleware native modules for Electron
- working-directory: desktop
- # Pin the Electron version explicitly: electron-rebuild's auto-detection
- # is unreliable across the --module-dir boundary (electron lives in
- # desktop/, the modules in middleware/), and a wrong-ABI rebuild fails
- # silently until the kernel won't boot.
+ # Make the middleware's native modules load under Electron's Node (the kernel
+ # runs via ELECTRON_RUN_AS_NODE). argon2 + sharp are N-API → ABI-stable across
+ # node/electron, so their shipped binaries already work, no rebuild needed.
+ # Only better-sqlite3 (non-N-API) needs an Electron-ABI build — and it
+ # publishes Electron PREBUILDS, so fetch one with prebuild-install (no
+ # node-gyp / Visual Studio). Fall back to a source rebuild if no prebuild
+ # exists for this Electron version on this platform.
+ - name: Provision better-sqlite3 for Electron
+ shell: bash
run: |
- EV="$(node -p "require('electron/package.json').version")"
- echo "rebuilding middleware native modules for Electron $EV"
- npx electron-rebuild --version "$EV" --module-dir ../middleware \
- --only better-sqlite3,argon2,sharp --force
- # Real ABI check: load a rebuilt native module under Electron's own Node
- # runtime. If the rebuild targeted the wrong ABI this require() throws.
- ELECTRON_RUN_AS_NODE=1 ./node_modules/.bin/electron -e \
- "require('../middleware/node_modules/better-sqlite3'); console.log('native modules load under Electron ABI OK')"
+ EV="$(node -p "require('./desktop/node_modules/electron/package.json').version")"
+ echo "provisioning better-sqlite3 for Electron $EV"
+ ( cd middleware/node_modules/better-sqlite3 \
+ && npx --yes prebuild-install -r electron -t "$EV" --verbose ) \
+ || ( cd desktop \
+ && npx electron-rebuild --version "$EV" --module-dir ../middleware --only better-sqlite3 --force )
+ # Verify ALL three native modules actually load under Electron's runtime
+ # — not just the rebuilt better-sqlite3, but also argon2 + sharp, whose
+ # (un-rebuilt) prebuilds we ASSUME are N-API/ABI-stable. If that
+ # assumption is wrong, this fails here instead of crashing the kernel at
+ # boot in the shipped app.
+ ELECTRON_RUN_AS_NODE=1 ./desktop/node_modules/.bin/electron -e \
+ "require('./middleware/node_modules/better-sqlite3'); require('./middleware/node_modules/argon2'); require('./middleware/node_modules/sharp'); console.log('better-sqlite3 + argon2 + sharp load under Electron ABI OK')"
- name: Build desktop (main + preload + renderer)
working-directory: desktop
@@ -131,7 +166,7 @@ jobs:
# --- macOS signing + notarization (proven for omadia-ui) --------------
- name: Prepare Apple signing (optional — needs _HIGH5 secrets)
- if: ${{ matrix.os == 'macos-latest' && env.APPLE_P12_BASE64 != '' }}
+ if: ${{ startsWith(matrix.os, 'macos') && env.APPLE_P12_BASE64 != '' }}
run: |
printf '%s' "$APPLE_P12_BASE64" | base64 -d > "$RUNNER_TEMP/developer-id.p12"
printf '%s' "$APPLE_ASC_KEY_P8_BASE64" | base64 -d > "$RUNNER_TEMP/asc-key.p8"
@@ -150,8 +185,17 @@ jobs:
security import "$RUNNER_TEMP/developer-id.p12" -k "$KEYCHAIN" \
-P "$APPLE_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KCPASS" "$KEYCHAIN" >/dev/null
- # Prepend our keychain to the user search list (keep the existing ones).
- security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | sed 's/[\"[:space:]]//g')
+ # Prepend our keychain to the user search list, KEEPING the existing ones.
+ # Build the list as an array (bash 3.2 on macOS runners — no mapfile) so
+ # multiple existing keychains aren't mashed into one path by whitespace
+ # stripping. Only quotes + leading/trailing space are trimmed per entry.
+ EXISTING=()
+ while IFS= read -r kc; do
+ kc="${kc//\"/}"
+ kc="$(printf '%s' "$kc" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
+ [ -n "$kc" ] && EXISTING+=("$kc")
+ done < <(security list-keychains -d user)
+ security list-keychains -d user -s "$KEYCHAIN" "${EXISTING[@]}"
# The exact Developer ID Application identity now present in the keychain.
IDENT=$(security find-identity -v -p codesigning "$KEYCHAIN" \
@@ -186,7 +230,7 @@ jobs:
# The .app is notarized + stapled by electron-builder above; the DMG needs
# its own ticket so `stapler validate` passes on the disk image itself.
- name: Notarize + staple the DMGs
- if: ${{ matrix.os == 'macos-latest' && env.APPLE_P12_BASE64 != '' }}
+ if: ${{ startsWith(matrix.os, 'macos') && env.APPLE_P12_BASE64 != '' }}
working-directory: desktop
run: |
for dmg in release/*.dmg; do
@@ -199,11 +243,17 @@ jobs:
done
- name: Verify macOS signatures (acceptance gate)
- if: ${{ matrix.os == 'macos-latest' && env.APPLE_P12_BASE64 != '' }}
+ if: ${{ startsWith(matrix.os, 'macos') && env.APPLE_P12_BASE64 != '' }}
working-directory: desktop
run: |
set -e
- for app in release/mac*/*.app; do
+ shopt -s nullglob
+ apps=(release/mac*/*.app)
+ if [ ${#apps[@]} -eq 0 ]; then
+ echo "FAIL: no .app produced under release/mac*/ — nothing to verify" >&2
+ exit 1
+ fi
+ for app in "${apps[@]}"; do
# `--deep` is deprecated for verification and does NOT descend into
# extraResources — verify the app, then EACH nested native binary
# explicitly, then the real Gatekeeper verdict.
@@ -229,7 +279,31 @@ jobs:
- name: Tear down signing material
if: ${{ always() }}
- run: rm -f "$RUNNER_TEMP/developer-id.p12" "$RUNNER_TEMP/asc-key.p8" "$RUNNER_TEMP/win-cert.p12" || true
+ run: |
+ rm -f "$RUNNER_TEMP/developer-id.p12" "$RUNNER_TEMP/asc-key.p8" "$RUNNER_TEMP/win-cert.p12" || true
+ # Also drop our temp signing keychain (and let macOS fall back to the
+ # default search list) so nothing signing-related lingers.
+ if [ -n "${MAC_SIGN_KEYCHAIN:-}" ]; then
+ security delete-keychain "$MAC_SIGN_KEYCHAIN" 2>/dev/null || true
+ fi
+
+ # Downloadable from the run itself (independent of the Release upload), and
+ # placed AFTER notarize/staple/verify so the artifacts are the final,
+ # stapled installers — not a pre-notarization copy.
+ - name: Upload installers as run artifacts
+ if: ${{ always() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: omadia-installers-${{ matrix.os }}
+ if-no-files-found: ignore
+ path: |
+ desktop/release/*.dmg
+ desktop/release/*.zip
+ desktop/release/*.exe
+ desktop/release/*.AppImage
+ desktop/release/*.deb
+ desktop/release/*.blockmap
+ desktop/release/latest*.yml
- name: Upload installers to the release
env:
@@ -237,15 +311,15 @@ jobs:
working-directory: desktop
run: |
shopt -s nullglob
- # On workflow_dispatch the tag may not correspond to an existing
- # release — fail with a clear message rather than a cryptic gh error.
- if ! gh release view "$TAG" >/dev/null 2>&1; then
- echo "no GitHub Release exists for tag '$TAG' — create the release first" >&2
- exit 1
- fi
files=(release/*.dmg release/*.zip release/*.exe release/*.AppImage release/*.deb release/*.blockmap release/latest*.yml)
if [ ${#files[@]} -eq 0 ]; then
echo "no installer artifacts found in desktop/release/" >&2
exit 1
fi
+ # If TAG has no matching release (e.g. a build-validation dispatch on a
+ # branch name), this is a build-only run — skip upload, don't fail.
+ if ! gh release view "$TAG" >/dev/null 2>&1; then
+ echo "no GitHub Release for '$TAG' — build validated, skipping upload."
+ exit 0
+ fi
gh release upload "$TAG" "${files[@]}" --clobber
diff --git a/desktop/buildResources/afterPack.js b/desktop/buildResources/afterPack.js
new file mode 100644
index 00000000..26464e4c
--- /dev/null
+++ b/desktop/buildResources/afterPack.js
@@ -0,0 +1,110 @@
+// electron-builder afterPack hook — sign the native Mach-O binaries that ship
+// as extraResources (the staged middleware's node_modules: better-sqlite3,
+// argon2, sharp, and any nested .dylib).
+//
+// WHY: electron-builder signs the app bundle + its OWN dependencies, but it does
+// NOT sign extraResources payloads. Under `hardenedRuntime: true` + notarization,
+// Apple's notary service rejects bundles that contain unsigned Mach-O binaries
+// (and hardened-runtime library validation refuses to load unsigned .node at
+// runtime). We must sign these BEFORE electron-builder seals the outer app, so
+// afterPack (post-pack, pre-sign) is the correct hook — the outer signature then
+// records the now-signed nested binaries.
+//
+// Fail-soft: if no Developer ID identity is available (unsigned/local build), it
+// logs and skips, so dev/ad-hoc builds still work.
+
+const { execFileSync } = require('node:child_process');
+const fs = require('node:fs');
+const path = require('node:path');
+
+/**
+ * Resolves the Developer ID Application identity to sign with.
+ *
+ * Prefers an explicit `MAC_SIGN_IDENTITY` env (set by CI) so we don't depend on
+ * electron-builder's temporary keychain already existing when afterPack runs;
+ * falls back to scanning the keychain via `security find-identity`.
+ */
+function findIdentity() {
+ const fromEnv = (process.env.MAC_SIGN_IDENTITY || '').trim();
+ if (fromEnv) return fromEnv;
+ try {
+ const out = execFileSync('security', ['find-identity', '-v', '-p', 'codesigning'], {
+ encoding: 'utf8',
+ });
+ const m = out.match(/"(Developer ID Application:[^"]+)"/);
+ return m ? m[1] : null;
+ } catch {
+ return null;
+ }
+}
+
+function collectMachO(dir, found) {
+ let entries;
+ try {
+ entries = fs.readdirSync(dir);
+ } catch {
+ return found;
+ }
+ for (const name of entries) {
+ const p = path.join(dir, name);
+ let st;
+ try {
+ // statSync FOLLOWS symlinks, so symlinked vendor dirs (e.g. sharp's libvips)
+ // get walked and symlinked .dylibs resolve to their real target — which we
+ // dedupe so we sign each Mach-O once. lstat-based walks miss these and ship
+ // them unsigned, which the verify gate (find follows them) would then fail.
+ st = fs.statSync(p);
+ } catch {
+ continue;
+ }
+ if (st.isDirectory()) {
+ collectMachO(p, found);
+ } else if (st.isFile() && (name.endsWith('.node') || name.endsWith('.dylib'))) {
+ try {
+ found.add(fs.realpathSync(p));
+ } catch {
+ found.add(p);
+ }
+ }
+ }
+ return found;
+}
+
+exports.default = async function afterPack(context) {
+ if (context.electronPlatformName !== 'darwin') return;
+
+ const identity = findIdentity();
+ if (!identity) {
+ // With signing secrets present (MAC_SIGN_EXPECTED=1) a missing identity is a
+ // HARD error — silently shipping unsigned nested modules would only fail
+ // later at notarization. Without secrets, this is the legitimate unsigned
+ // (dev / ad-hoc) path, so we skip.
+ if (process.env.MAC_SIGN_EXPECTED === '1') {
+ throw new Error(
+ '[afterPack] signing was expected (MAC_SIGN_EXPECTED=1) but no Developer ID ' +
+ 'Application identity is available — the signing keychain was not set up ' +
+ 'before packaging. Refusing to ship unsigned native modules.',
+ );
+ }
+ console.log('[afterPack] no Developer ID identity — skipping nested signing (unsigned build).');
+ return;
+ }
+
+ const appName = `${context.packager.appInfo.productFilename}.app`;
+ const omadiaResources = path.join(context.appOutDir, appName, 'Contents', 'Resources', 'omadia');
+ const targets = [...collectMachO(omadiaResources, new Set())];
+ if (targets.length === 0) {
+ console.log('[afterPack] no nested Mach-O binaries found under extraResources.');
+ return;
+ }
+
+ const keychain = (process.env.MAC_SIGN_KEYCHAIN || '').trim();
+ const args = ['--force', '--options', 'runtime', '--timestamp'];
+ if (keychain) args.push('--keychain', keychain);
+ args.push('--sign', identity);
+
+ console.log(`[afterPack] signing ${targets.length} nested Mach-O binaries with "${identity}"`);
+ for (const target of targets) {
+ execFileSync('codesign', [...args, target], { stdio: 'inherit' });
+ }
+};
diff --git a/desktop/buildResources/entitlements.mac.plist b/desktop/buildResources/entitlements.mac.plist
new file mode 100644
index 00000000..7d2cf1bf
--- /dev/null
+++ b/desktop/buildResources/entitlements.mac.plist
@@ -0,0 +1,19 @@
+
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-library-validation
+
+
+ com.apple.security.network.client
+
+
+
diff --git a/desktop/electron-builder.yml b/desktop/electron-builder.yml
index cc1e49d4..1c1959b7 100644
--- a/desktop/electron-builder.yml
+++ b/desktop/electron-builder.yml
@@ -17,7 +17,7 @@ copyright: Copyright © 2026 byte5 GmbH
directories:
output: release
- buildResources: build
+ buildResources: buildResources
# Only the compiled desktop app + its own node_modules go into the app bundle.
files:
@@ -42,20 +42,28 @@ asarUnpack:
# Sign the native Mach-O binaries inside the staged middleware (extraResources)
# BEFORE the outer app is signed — electron-builder does not sign extraResources,
# and notarization rejects unsigned nested Mach-O. Fail-soft when unsigned.
-afterPack: build/afterPack.js
+afterPack: buildResources/afterPack.js
mac:
category: public.app-category.developer-tools
hardenedRuntime: true
gatekeeperAssess: false
- entitlements: build/entitlements.mac.plist
- entitlementsInherit: build/entitlements.mac.plist
+ entitlements: buildResources/entitlements.mac.plist
+ entitlementsInherit: buildResources/entitlements.mac.plist
+ # Apple Silicon only for v1. The native modules ship as extraResources (copied
+ # verbatim, not arch-split), and GitHub's Intel (macos-13) runners queue for
+ # hours — impractical for a release. An x64 DMG built on an arm64 runner would
+ # contain arm64 native modules and crash on Intel. Intel support is a follow-up
+ # (cross-compile x64 prebuilds on the arm64 runner into a second staged tree).
target:
- target: dmg
- arch: [arm64, x64]
+ arch: [arm64]
- target: zip # required by electron-updater on macOS
- arch: [arm64, x64]
- notarize: false # flip to true in CI once APPLE_* secrets are set
+ arch: [arm64]
+ # Notarization is left unset here so it's driven entirely by CI: the workflow
+ # adds `--config.mac.notarize=true` only when the Apple secrets are present
+ # (and electron-builder skips it without notary credentials). A hard-coded
+ # `notarize: false` here would fight that CLI override.
dmg:
title: ${productName} ${version}
diff --git a/desktop/package-lock.json b/desktop/package-lock.json
index 4a8c87b4..54307fce 100644
--- a/desktop/package-lock.json
+++ b/desktop/package-lock.json
@@ -14,6 +14,7 @@
"electron-updater": "^6.3.9"
},
"devDependencies": {
+ "@electron/rebuild": "3.6.1",
"@types/node": "^22.10.0",
"electron": "^37.2.0",
"electron-builder": "^25.1.8",
@@ -127,6 +128,51 @@
"global-agent": "^3.0.0"
}
},
+ "node_modules/@electron/get/node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/@electron/get/node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@electron/get/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@electron/get/node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/@electron/notarize": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz",
@@ -158,29 +204,6 @@
"node": ">=10"
}
},
- "node_modules/@electron/notarize/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@electron/notarize/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/@electron/osx-sign": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz",
@@ -203,21 +226,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/@electron/osx-sign/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@electron/osx-sign/node_modules/isbinaryfile": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
@@ -231,29 +239,6 @@
"url": "https://github.com/sponsors/gjtorikian/"
}
},
- "node_modules/@electron/osx-sign/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@electron/osx-sign/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/@electron/rebuild": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz",
@@ -283,57 +268,6 @@
"node": ">=12.13.0"
}
},
- "node_modules/@electron/rebuild/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@electron/rebuild/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@electron/rebuild/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@electron/rebuild/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/@electron/universal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz",
@@ -385,19 +319,6 @@
"node": ">=14.14"
}
},
- "node_modules/@electron/universal/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
"node_modules/@electron/universal/node_modules/minimatch": {
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
@@ -414,16 +335,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/@electron/universal/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -589,29 +500,6 @@
"node": ">=10"
}
},
- "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@malept/flatpak-bundler/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/@npmcli/fs": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
@@ -626,19 +514,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/@npmcli/fs/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@npmcli/move-file": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
@@ -978,57 +853,6 @@
"electron-builder-squirrel-windows": "25.1.8"
}
},
- "node_modules/app-builder-lib/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/app-builder-lib/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/app-builder-lib/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/app-builder-lib/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/aproba": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz",
@@ -1355,44 +1179,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/builder-util/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/builder-util/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/builder-util/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/cacache": {
"version": "16.1.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
@@ -2103,44 +1889,6 @@
"dmg-license": "^1.0.11"
}
},
- "node_modules/dmg-builder/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/dmg-builder/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/dmg-builder/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/dmg-license": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
@@ -2243,134 +1991,55 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
- "@electron/get": "^2.0.0",
- "@types/node": "^22.7.7",
- "extract-zip": "^2.0.1"
- },
- "bin": {
- "electron": "cli.js"
- },
- "engines": {
- "node": ">= 12.20.55"
- }
- },
- "node_modules/electron-builder": {
- "version": "25.1.8",
- "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.8.tgz",
- "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "app-builder-lib": "25.1.8",
- "builder-util": "25.1.7",
- "builder-util-runtime": "9.2.10",
- "chalk": "^4.1.2",
- "dmg-builder": "25.1.8",
- "fs-extra": "^10.1.0",
- "is-ci": "^3.0.0",
- "lazy-val": "^1.0.5",
- "simple-update-notifier": "2.0.0",
- "yargs": "^17.6.2"
- },
- "bin": {
- "electron-builder": "cli.js",
- "install-app-deps": "install-app-deps.js"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/electron-builder-squirrel-windows": {
- "version": "25.1.8",
- "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz",
- "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "app-builder-lib": "25.1.8",
- "archiver": "^5.3.1",
- "builder-util": "25.1.7",
- "fs-extra": "^10.1.0"
- }
- },
- "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/electron-builder-squirrel-windows/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/electron-builder/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
+ "@electron/get": "^2.0.0",
+ "@types/node": "^22.7.7",
+ "extract-zip": "^2.0.1"
+ },
+ "bin": {
+ "electron": "cli.js"
},
"engines": {
- "node": ">=12"
+ "node": ">= 12.20.55"
}
},
- "node_modules/electron-builder/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "node_modules/electron-builder": {
+ "version": "25.1.8",
+ "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.8.tgz",
+ "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==",
"dev": true,
"license": "MIT",
"dependencies": {
- "universalify": "^2.0.0"
+ "app-builder-lib": "25.1.8",
+ "builder-util": "25.1.7",
+ "builder-util-runtime": "9.2.10",
+ "chalk": "^4.1.2",
+ "dmg-builder": "25.1.8",
+ "fs-extra": "^10.1.0",
+ "is-ci": "^3.0.0",
+ "lazy-val": "^1.0.5",
+ "simple-update-notifier": "2.0.0",
+ "yargs": "^17.6.2"
},
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
+ "bin": {
+ "electron-builder": "cli.js",
+ "install-app-deps": "install-app-deps.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
}
},
- "node_modules/electron-builder/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "node_modules/electron-builder-squirrel-windows": {
+ "version": "25.1.8",
+ "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz",
+ "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
+ "peer": true,
+ "dependencies": {
+ "app-builder-lib": "25.1.8",
+ "archiver": "^5.3.1",
+ "builder-util": "25.1.7",
+ "fs-extra": "^10.1.0"
}
},
"node_modules/electron-publish": {
@@ -2389,44 +2058,6 @@
"mime": "^2.5.2"
}
},
- "node_modules/electron-publish/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/electron-publish/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/electron-publish/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/electron-updater": {
"version": "6.8.9",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.9.tgz",
@@ -2456,32 +2087,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/electron-updater/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/electron-updater/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
"node_modules/electron-updater/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
@@ -2494,15 +2099,6 @@
"node": ">=10"
}
},
- "node_modules/electron-updater/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -2788,18 +2384,17 @@
"peer": true
},
"node_modules/fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "dev": true,
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
},
"engines": {
- "node": ">=6 <7 || >=8"
+ "node": ">=12"
}
},
"node_modules/fs-minipass": {
@@ -2990,20 +2585,6 @@
"node": ">=10.0"
}
},
- "node_modules/global-agent/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
@@ -3493,11 +3074,13 @@
}
},
"node_modules/jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
- "dev": true,
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
@@ -3993,19 +3576,6 @@
"node": ">=10"
}
},
- "node_modules/node-abi/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/node-addon-api": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
@@ -4024,19 +3594,6 @@
"semver": "^7.3.5"
}
},
- "node_modules/node-api-version/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/node-gyp": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz",
@@ -4063,19 +3620,6 @@
"node": "^12.13 || ^14.13 || >=16"
}
},
- "node_modules/node-gyp/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/nopt": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
@@ -4642,13 +4186,16 @@
}
},
"node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
+ "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/semver-compare": {
@@ -4726,19 +4273,6 @@
"node": ">=10"
}
},
- "node_modules/simple-update-notifier/node_modules/semver": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
- "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
@@ -5013,44 +4547,6 @@
"fs-extra": "^10.0.0"
}
},
- "node_modules/temp-file/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/temp-file/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/temp-file/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
"node_modules/tiny-typed-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
@@ -5149,13 +4645,12 @@
}
},
"node_modules/universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "dev": true,
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"license": "MIT",
"engines": {
- "node": ">= 4.0.0"
+ "node": ">= 10.0.0"
}
},
"node_modules/uri-js": {
diff --git a/desktop/package.json b/desktop/package.json
index 8a327d17..e8b6835e 100644
--- a/desktop/package.json
+++ b/desktop/package.json
@@ -3,8 +3,16 @@
"version": "0.1.0",
"private": true,
"description": "omadia native desktop installer — runs the full omadia stack locally with an embedded Postgres+pgvector engine, no Docker.",
- "author": "byte5 GmbH",
+ "author": {
+ "name": "byte5 GmbH",
+ "email": "mwege@byte5.de"
+ },
"license": "Apache-2.0",
+ "homepage": "https://github.com/byte5ai/omadia",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/byte5ai/omadia.git"
+ },
"main": "dist/main.js",
"scripts": {
"build": "tsc -p tsconfig.json && node scripts/copy-renderer.mjs",
@@ -22,7 +30,7 @@
"electron-updater": "^6.3.9"
},
"devDependencies": {
- "@electron/rebuild": "^3.7.1",
+ "@electron/rebuild": "3.6.1",
"@types/node": "^22.10.0",
"electron": "^37.2.0",
"electron-builder": "^25.1.8",
diff --git a/desktop/scripts/stage-runtime.mjs b/desktop/scripts/stage-runtime.mjs
index ec9357d2..48c9b41b 100644
--- a/desktop/scripts/stage-runtime.mjs
+++ b/desktop/scripts/stage-runtime.mjs
@@ -43,6 +43,34 @@ for (const entry of ['dist', 'node_modules', 'packages', 'migrations', 'assets',
const to = path.join(mwDest, entry);
fs.cpSync(from, to, { recursive: true, dereference: true });
}
+
+// fs.cpSync({dereference:true}) does NOT reliably materialise the npm-workspace
+// symlinks `node_modules/@omadia/* → packages/*` — on CI runners they're ABSOLUTE
+// (`/Users/runner/.../packages/x`), so the shipped bundle ends up with DANGLING
+// symlinks to the build machine and the kernel can't resolve any @omadia/* package
+// at runtime (ERR_MODULE_NOT_FOUND → kernel crash). Replace each @omadia symlink
+// with a real copy of the package it resolves to. (Each package's own nested
+// @omadia symlinks may stay dangling but are unused: Node resolves up to this
+// top-level real dir.)
+const scope = path.join(mwDest, 'node_modules', '@omadia');
+if (fs.existsSync(scope)) {
+ let materialised = 0;
+ for (const name of fs.readdirSync(scope)) {
+ const link = path.join(scope, name);
+ if (!fs.lstatSync(link).isSymbolicLink()) continue;
+ let target;
+ try {
+ target = fs.realpathSync(link);
+ } catch {
+ console.error(`[stage-runtime] FATAL: dangling @omadia/${name} symlink — build the middleware first`);
+ process.exit(1);
+ }
+ fs.rmSync(link, { force: true });
+ fs.cpSync(target, link, { recursive: true });
+ materialised++;
+ }
+ console.log(`[stage-runtime] materialised ${materialised} @omadia workspace package(s)`);
+}
console.log('[stage-runtime] staged middleware');
// --- web-ui: flatten the Next standalone output into runtime/web-ui ---
diff --git a/desktop/src/embeddedDb.ts b/desktop/src/embeddedDb.ts
index 031e298c..609134dd 100644
--- a/desktop/src/embeddedDb.ts
+++ b/desktop/src/embeddedDb.ts
@@ -24,6 +24,9 @@ import { log } from './log';
* bottlenecks is a bundled native Postgres.
*/
+// NOT a credential: pglite-socket on loopback does not authenticate, so these
+// only populate the DATABASE_URL the kernel's pg client expects — any value is
+// accepted. Security rests entirely on the 127.0.0.1-only bind, not on this.
const DB_NAME = 'omadia';
const DB_USER = 'omadia';
const DB_PASSWORD = 'omadia';
@@ -85,13 +88,18 @@ export async function startEmbeddedDb(): Promise {
// (pgcrypto is intentionally NOT required: the kernel migrations only used it
// for gen_random_uuid(), which is core since Postgres 13.)
- const port = await stableDbPort();
- const server = new PGLiteSocketServer({
- db,
- port,
- host: '127.0.0.1',
- });
- await server.start();
+ // From here on, any failure must close `db` — otherwise the PGlite instance
+ // leaks and keeps the dataDir lock, so a retry can't reopen the same dir.
+ let server: PGLiteSocketServer;
+ let port: number;
+ try {
+ port = await stableDbPort();
+ server = new PGLiteSocketServer({ db, port, host: '127.0.0.1' });
+ await server.start();
+ } catch (err) {
+ await db.close().catch(() => {});
+ throw err;
+ }
log.info(`[db] embedded Postgres listening on 127.0.0.1:${port}`);
current = { db, server, port };
diff --git a/desktop/src/main.ts b/desktop/src/main.ts
index e386bee3..28d4e479 100644
--- a/desktop/src/main.ts
+++ b/desktop/src/main.ts
@@ -4,7 +4,7 @@ import { Supervisor, setActiveSupervisor, BootProgress } from './supervisor';
import { registerIpc } from './ipc';
import { CH } from './ipcTypes';
import { createTray, setTrayStatus, destroyTray, TrayActions } from './tray';
-import { initUpdater } from './updater';
+import { initUpdater, isUpdateInstalling } from './updater';
import { isSetupComplete } from './setupState';
import { log } from './log';
@@ -184,6 +184,10 @@ if (!gotLock) {
app.on('before-quit', (e) => {
quitting = true;
destroyTray();
+ // During an update install, electron-updater drives the quit and runs the
+ // installer on `will-quit`. It already stopped the supervisor, so we must
+ // NOT preventDefault + app.exit() here — that would bypass the install.
+ if (isUpdateInstalling()) return;
if (quitHandled || !supervisor) return;
// Block the quit just long enough to flush + close the embedded DB and
// terminate the children cleanly, then exit for real.
diff --git a/desktop/src/secrets.ts b/desktop/src/secrets.ts
index f64e7297..e1777d3b 100644
--- a/desktop/src/secrets.ts
+++ b/desktop/src/secrets.ts
@@ -52,7 +52,16 @@ function load(): SecretsBlob {
}
}
cache = { vaultKey: generateVaultKey(), providerKeys: {} };
- persist();
+ try {
+ persist();
+ } catch (err) {
+ // Fail-closed: if we couldn't persist (e.g. OS encryption unavailable in a
+ // packaged build), do NOT leave the unpersisted key cached — a later call
+ // would otherwise return it past the `if (cache)` short-circuit and bypass
+ // the fail-closed guard, diverging from whatever gets persisted next launch.
+ cache = null;
+ throw err;
+ }
return cache;
}
diff --git a/desktop/src/supervisor.ts b/desktop/src/supervisor.ts
index 6b67358a..4b1a1c3b 100644
--- a/desktop/src/supervisor.ts
+++ b/desktop/src/supervisor.ts
@@ -11,7 +11,6 @@ import {
import { findFreePorts, isPortFree } from './ports';
import { startEmbeddedDb, EmbeddedDb } from './embeddedDb';
import { vaultKey, allProviderKeys } from './secrets';
-import { readSetup } from './setupState';
import { log } from './log';
export type BootPhase =
@@ -126,16 +125,18 @@ export class Supervisor extends EventEmitter {
this.progress('ready', 'omadia is ready.');
return this.uiUrl;
} catch (err) {
- // Roll back any partially-started children so a retry starts from a clean
- // slate (and so we don't leak processes or hold the PGlite lock).
- await this.teardownChildren();
- this.state = 'idle';
+ // If a concurrent stop()/restart() superseded this boot (it bumped the
+ // generation), it now owns teardown + the state — do NOT double-tear-down
+ // or stomp its state. Only clean up when we're still the live generation.
+ if (gen === this.generation) {
+ await this.teardownChildren();
+ this.state = 'idle';
+ }
throw err;
}
}
private kernelEnv(port: number): NodeJS.ProcessEnv {
- const setup = readSetup();
const env: NodeJS.ProcessEnv = {
...process.env,
ELECTRON_RUN_AS_NODE: '1',
@@ -159,7 +160,6 @@ export class Supervisor extends EventEmitter {
// v1 wires only persistence + LLM. Embeddings (in-process), diagrams (hosted),
// and the filesystem attachment store are later milestones; leaving their env
// unset means the kernel degrades gracefully rather than failing.
- void setup;
return env;
}
diff --git a/desktop/src/updater.ts b/desktop/src/updater.ts
index ec1ea0b1..e9ea6303 100644
--- a/desktop/src/updater.ts
+++ b/desktop/src/updater.ts
@@ -3,8 +3,15 @@ import { autoUpdater } from 'electron-updater';
import fs from 'node:fs';
import path from 'node:path';
import { embeddedDbDir, snapshotDir } from './paths';
+import { getActiveSupervisor } from './supervisor';
import { log } from './log';
+let installing = false;
+/** True once the user accepted an update and we're handing off to the installer. */
+export function isUpdateInstalling(): boolean {
+ return installing;
+}
+
/**
* Auto-update via electron-updater against GitHub Releases.
*
@@ -28,12 +35,6 @@ export function initUpdater(): void {
);
autoUpdater.on('update-downloaded', async (info) => {
log.info(`[updater] downloaded ${info.version}`);
- try {
- snapshotDbDir(info.version);
- } catch (err) {
- log.error(`[updater] snapshot failed, not auto-installing: ${String(err)}`);
- return;
- }
const { response } = await dialog.showMessageBox({
type: 'info',
buttons: ['Restart now', 'Later'],
@@ -41,11 +42,24 @@ export function initUpdater(): void {
cancelId: 1,
title: 'Update ready',
message: `omadia ${info.version} is ready to install.`,
- detail: 'Your data has been snapshotted. omadia will restart to apply the update.',
+ detail: 'omadia will close, back up your local data, and restart to apply the update.',
});
- if (response === 0) {
- autoUpdater.quitAndInstall();
+ if (response !== 0) return;
+
+ // Quiesce the stack FIRST so the embedded DB is flushed + closed before we
+ // copy its directory — a live cpSync could capture a torn, unrestorable
+ // snapshot. Then snapshot, then hand off to the installer.
+ installing = true;
+ try {
+ const sup = getActiveSupervisor();
+ if (sup) await sup.stop();
+ snapshotDbDir(info.version);
+ } catch (err) {
+ log.error(`[updater] pre-install stop/snapshot failed (installing anyway): ${String(err)}`);
}
+ // quitAndInstall drives the app quit itself; main's before-quit checks
+ // isUpdateInstalling() and steps aside so the installer isn't bypassed.
+ autoUpdater.quitAndInstall();
});
autoUpdater.checkForUpdates().catch((err) => log.error(`[updater] check failed: ${String(err)}`));