refactor: deprecate Vulkan external binaries (#6638)

* refactor: deprecate vulkan binary

refactor: clean up vulkan lib

chore: cleanup

chore: clean up

chore: clean up

fix: build

* fix: skip binaries download env

* Update src-tauri/utils/src/system.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src-tauri/utils/src/system.rs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Louis 2025-09-29 17:47:59 +07:00 committed by GitHub
parent 3c4c6d1c50
commit 5fd249c72d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 249 additions and 318 deletions

View File

@ -70,10 +70,9 @@ jobs:
run: | run: |
echo "Version: ${{ inputs.new_version }}" echo "Version: ${{ inputs.new_version }}"
jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = false' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = false' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
if [ "${{ inputs.channel }}" != "stable" ]; then if [ "${{ inputs.channel }}" != "stable" ]; then
jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun", jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
"usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json
fi fi
jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json
@ -83,7 +82,7 @@ jobs:
jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json
mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json
echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------" echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------"
cat ./src-tauri/plugins/tauri-plugin-hardware/package.json cat ./src-tauri/plugins/tauri-plugin-hardware/package.json
@ -96,7 +95,7 @@ jobs:
ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}" ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}"
echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------" echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------"
cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml
ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}" ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}"
echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------" echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------"
cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml
@ -125,7 +124,7 @@ jobs:
env: env:
RELEASE_CHANNEL: '${{ inputs.channel }}' RELEASE_CHANNEL: '${{ inputs.channel }}'
AUTO_UPDATER_DISABLED: ${{ inputs.disable_updater && 'true' || 'false' }} AUTO_UPDATER_DISABLED: ${{ inputs.disable_updater && 'true' || 'false' }}
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
@ -136,4 +135,4 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: jan-linux-amd64-${{ inputs.new_version }}-AppImage name: jan-linux-amd64-${{ inputs.new_version }}-AppImage
path: ./src-tauri/target/release/bundle/appimage/*.AppImage path: ./src-tauri/target/release/bundle/appimage/*.AppImage

View File

@ -91,10 +91,9 @@ jobs:
echo "Version: ${{ inputs.new_version }}" echo "Version: ${{ inputs.new_version }}"
# Update tauri.conf.json # Update tauri.conf.json
jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
if [ "${{ inputs.channel }}" != "stable" ]; then if [ "${{ inputs.channel }}" != "stable" ]; then
jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun", jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
"usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json
fi fi
jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json
@ -104,7 +103,7 @@ jobs:
jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json
mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json
echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------" echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------"
cat ./src-tauri/plugins/tauri-plugin-hardware/package.json cat ./src-tauri/plugins/tauri-plugin-hardware/package.json
@ -117,7 +116,7 @@ jobs:
ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}" ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}"
echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------" echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------"
cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml
ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}" ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}"
echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------" echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------"
cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml
@ -128,7 +127,7 @@ jobs:
# Temporarily enable devtool on prod build # Temporarily enable devtool on prod build
ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools" ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools"
cat ./src-tauri/Cargo.toml cat ./src-tauri/Cargo.toml
# Change app name for beta and nightly builds # Change app name for beta and nightly builds
if [ "${{ inputs.channel }}" != "stable" ]; then if [ "${{ inputs.channel }}" != "stable" ]; then
@ -139,7 +138,7 @@ jobs:
.github/scripts/rename-tauri-app.sh ./src-tauri/tauri.conf.json ${{ inputs.channel }} .github/scripts/rename-tauri-app.sh ./src-tauri/tauri.conf.json ${{ inputs.channel }}
cat ./src-tauri/tauri.conf.json cat ./src-tauri/tauri.conf.json
# Update Cargo.toml # Update Cargo.toml
ctoml ./src-tauri/Cargo.toml package.name "Jan-${{ inputs.channel }}" ctoml ./src-tauri/Cargo.toml package.name "Jan-${{ inputs.channel }}"
ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools" ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools"
@ -184,4 +183,3 @@ jobs:
with: with:
name: jan-linux-amd64-flatpak-${{ inputs.new_version }}-AppImage name: jan-linux-amd64-flatpak-${{ inputs.new_version }}-AppImage
path: ./src-tauri/target/release/bundle/appimage/*.AppImage path: ./src-tauri/target/release/bundle/appimage/*.AppImage

View File

@ -108,10 +108,9 @@ jobs:
echo "Version: ${{ inputs.new_version }}" echo "Version: ${{ inputs.new_version }}"
# Update tauri.conf.json # Update tauri.conf.json
jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json jq --arg version "${{ inputs.new_version }}" '.version = $version | .bundle.createUpdaterArtifacts = true' ./src-tauri/tauri.conf.json > /tmp/tauri.conf.json
mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json mv /tmp/tauri.conf.json ./src-tauri/tauri.conf.json
if [ "${{ inputs.channel }}" != "stable" ]; then if [ "${{ inputs.channel }}" != "stable" ]; then
jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun", jq '.bundle.linux.deb.files = {"usr/bin/bun": "resources/bin/bun"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
"usr/lib/Jan-${{ inputs.channel }}/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"}' ./src-tauri/tauri.linux.conf.json > /tmp/tauri.linux.conf.json
mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json mv /tmp/tauri.linux.conf.json ./src-tauri/tauri.linux.conf.json
fi fi
jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json jq --arg version "${{ inputs.new_version }}" '.version = $version' web-app/package.json > /tmp/package.json
@ -121,7 +120,7 @@ jobs:
jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json jq --arg version "${{ inputs.new_version }}" '.version = $version' ./src-tauri/plugins/tauri-plugin-hardware/package.json > /tmp/package.json
mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json mv /tmp/package.json ./src-tauri/plugins/tauri-plugin-hardware/package.json
echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------" echo "---------./src-tauri/plugins/tauri-plugin-hardware/package.json---------"
cat ./src-tauri/plugins/tauri-plugin-hardware/package.json cat ./src-tauri/plugins/tauri-plugin-hardware/package.json
@ -134,7 +133,7 @@ jobs:
ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}" ctoml ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml package.version "${{ inputs.new_version }}"
echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------" echo "---------./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml---------"
cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml cat ./src-tauri/plugins/tauri-plugin-hardware/Cargo.toml
ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}" ctoml ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml package.version "${{ inputs.new_version }}"
echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------" echo "---------./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml---------"
cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml cat ./src-tauri/plugins/tauri-plugin-llamacpp/Cargo.toml
@ -156,7 +155,7 @@ jobs:
.github/scripts/rename-tauri-app.sh ./src-tauri/tauri.conf.json ${{ inputs.channel }} .github/scripts/rename-tauri-app.sh ./src-tauri/tauri.conf.json ${{ inputs.channel }}
cat ./src-tauri/tauri.conf.json cat ./src-tauri/tauri.conf.json
# Update Cargo.toml # Update Cargo.toml
ctoml ./src-tauri/Cargo.toml package.name "Jan-${{ inputs.channel }}" ctoml ./src-tauri/Cargo.toml package.name "Jan-${{ inputs.channel }}"
ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools" ctoml ./src-tauri/Cargo.toml dependencies.tauri.features[] "devtools"

View File

@ -43,7 +43,6 @@ endif
dev: install-and-build dev: install-and-build
yarn download:bin yarn download:bin
yarn download:lib
yarn dev yarn dev
# Web application targets # Web application targets
@ -58,7 +57,7 @@ build-web-app: install-web-app
yarn build:core yarn build:core
yarn build:web-app yarn build:web-app
serve-web-app: serve-web-app:
yarn serve:web-app yarn serve:web-app
build-serve-web-app: build-web-app build-serve-web-app: build-web-app
@ -71,7 +70,6 @@ lint: install-and-build
# Testing # Testing
test: lint test: lint
yarn download:bin yarn download:bin
yarn download:lib
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
yarn download:windows-installer yarn download:windows-installer
endif endif

View File

@ -25,11 +25,10 @@
"build:serve:web-app": "yarn build:web-app && yarn serve:web-app", "build:serve:web-app": "yarn build:web-app && yarn serve:web-app",
"dev:tauri": "yarn build:icon && yarn copy:assets:tauri && cross-env IS_CLEAN=true tauri dev", "dev:tauri": "yarn build:icon && yarn copy:assets:tauri && cross-env IS_CLEAN=true tauri dev",
"copy:assets:tauri": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"", "copy:assets:tauri": "cpx \"pre-install/*.tgz\" \"src-tauri/resources/pre-install/\" && cpx \"LICENSE\" \"src-tauri/resources/\"",
"download:lib": "node ./scripts/download-lib.mjs",
"download:bin": "node ./scripts/download-bin.mjs", "download:bin": "node ./scripts/download-bin.mjs",
"download:windows-installer": "node ./scripts/download-win-installer-deps.mjs", "download:windows-installer": "node ./scripts/download-win-installer-deps.mjs",
"build:tauri:win32": "yarn download:bin && yarn download:lib && yarn download:windows-installer && yarn tauri build", "build:tauri:win32": "yarn download:bin && yarn download:windows-installer && yarn tauri build",
"build:tauri:linux": "yarn download:bin && yarn download:lib && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh", "build:tauri:linux": "yarn download:bin && NO_STRIP=1 ./src-tauri/build-utils/shim-linuxdeploy.sh yarn tauri build && ./src-tauri/build-utils/buildAppImage.sh",
"build:tauri:darwin": "yarn download:bin && yarn tauri build --target universal-apple-darwin", "build:tauri:darwin": "yarn download:bin && yarn tauri build --target universal-apple-darwin",
"build:tauri": "yarn build:icon && yarn copy:assets:tauri && run-script-os", "build:tauri": "yarn build:icon && yarn copy:assets:tauri && run-script-os",
"build:tauri:plugin:api": "cd src-tauri/plugins && yarn install && yarn workspaces foreach -Apt run build", "build:tauri:plugin:api": "cd src-tauri/plugins && yarn install && yarn workspaces foreach -Apt run build",

View File

@ -1,4 +1,3 @@
console.log('Script is running')
// scripts/download.js // scripts/download.js
import https from 'https' import https from 'https'
import fs, { copyFile, mkdirSync } from 'fs' import fs, { copyFile, mkdirSync } from 'fs'
@ -69,7 +68,10 @@ function getPlatformArch() {
arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin' arch === 'arm64' ? 'aarch64-apple-darwin' : 'x86_64-apple-darwin'
} else if (platform === 'linux') { } else if (platform === 'linux') {
bunPlatform = arch === 'arm64' ? 'linux-aarch64' : 'linux-x64' bunPlatform = arch === 'arm64' ? 'linux-aarch64' : 'linux-x64'
uvPlatform = arch === 'arm64' ? 'aarch64-unknown-linux-gnu' : 'x86_64-unknown-linux-gnu' uvPlatform =
arch === 'arm64'
? 'aarch64-unknown-linux-gnu'
: 'x86_64-unknown-linux-gnu'
} else if (platform === 'win32') { } else if (platform === 'win32') {
bunPlatform = 'windows-x64' // Bun has limited Windows support bunPlatform = 'windows-x64' // Bun has limited Windows support
uvPlatform = 'x86_64-pc-windows-msvc' uvPlatform = 'x86_64-pc-windows-msvc'
@ -81,6 +83,10 @@ function getPlatformArch() {
} }
async function main() { async function main() {
if (process.env.SKIP_BINARIES) {
console.log('Skipping binaries download.')
process.exit(0)
}
console.log('Starting main function') console.log('Starting main function')
const platform = os.platform() const platform = os.platform()
const { bunPlatform, uvPlatform } = getPlatformArch() const { bunPlatform, uvPlatform } = getPlatformArch()
@ -124,29 +130,45 @@ async function main() {
if (err) { if (err) {
console.log('Add execution permission failed!', err) console.log('Add execution permission failed!', err)
} }
}); })
if (platform === 'darwin') { if (platform === 'darwin') {
copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-x86_64-apple-darwin'), (err) => { copyFile(
if (err) { path.join(binDir, 'bun'),
console.log("Error Found:", err); path.join(binDir, 'bun-x86_64-apple-darwin'),
} (err) => {
})
copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-aarch64-apple-darwin'), (err) => {
if (err) {
console.log("Error Found:", err);
}
})
copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-universal-apple-darwin'), (err) => {
if (err) { if (err) {
console.log("Error Found:", err); console.log('Error Found:', err)
} }
})
} else if (platform === 'linux') {
copyFile(path.join(binDir, 'bun'), path.join(binDir, 'bun-x86_64-unknown-linux-gnu'), (err) => {
if (err) {
console.log("Error Found:", err);
} }
}) )
copyFile(
path.join(binDir, 'bun'),
path.join(binDir, 'bun-aarch64-apple-darwin'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
}
)
copyFile(
path.join(binDir, 'bun'),
path.join(binDir, 'bun-universal-apple-darwin'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
}
)
} else if (platform === 'linux') {
copyFile(
path.join(binDir, 'bun'),
path.join(binDir, 'bun-x86_64-unknown-linux-gnu'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
}
)
} }
} catch (err) { } catch (err) {
// Expect EEXIST error // Expect EEXIST error
@ -157,11 +179,15 @@ async function main() {
path.join(binDir) path.join(binDir)
) )
if (platform === 'win32') { if (platform === 'win32') {
copyFile(path.join(binDir, 'bun.exe'), path.join(binDir, 'bun-x86_64-pc-windows-msvc.exe'), (err) => { copyFile(
if (err) { path.join(binDir, 'bun.exe'),
console.log("Error Found:", err); path.join(binDir, 'bun-x86_64-pc-windows-msvc.exe'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
} }
}) )
} }
} catch (err) { } catch (err) {
// Expect EEXIST error // Expect EEXIST error
@ -176,52 +202,66 @@ async function main() {
await decompress(uvPath, tempBinDir) await decompress(uvPath, tempBinDir)
} }
try { try {
copySync( copySync(path.join(tempBinDir, `uv-${uvPlatform}`, 'uv'), path.join(binDir))
path.join(tempBinDir, `uv-${uvPlatform}`, 'uv'),
path.join(binDir)
)
fs.chmod(path.join(binDir, 'uv'), 0o755, (err) => { fs.chmod(path.join(binDir, 'uv'), 0o755, (err) => {
if (err) { if (err) {
console.log('Add execution permission failed!', err) console.log('Add execution permission failed!', err)
} }
}); })
if (platform === 'darwin') { if (platform === 'darwin') {
copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-x86_64-apple-darwin'), (err) => { copyFile(
if (err) { path.join(binDir, 'uv'),
console.log("Error Found:", err); path.join(binDir, 'uv-x86_64-apple-darwin'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
} }
}) )
copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-aarch64-apple-darwin'), (err) => { copyFile(
if (err) { path.join(binDir, 'uv'),
console.log("Error Found:", err); path.join(binDir, 'uv-aarch64-apple-darwin'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
} }
}) )
copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-universal-apple-darwin'), (err) => { copyFile(
if (err) { path.join(binDir, 'uv'),
console.log("Error Found:", err); path.join(binDir, 'uv-universal-apple-darwin'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
} }
}) )
} else if (platform === 'linux') { } else if (platform === 'linux') {
copyFile(path.join(binDir, 'uv'), path.join(binDir, 'uv-x86_64-unknown-linux-gnu'), (err) => { copyFile(
if (err) { path.join(binDir, 'uv'),
console.log("Error Found:", err); path.join(binDir, 'uv-x86_64-unknown-linux-gnu'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
} }
}) )
} }
} catch (err) { } catch (err) {
// Expect EEXIST error // Expect EEXIST error
} }
try { try {
copySync( copySync(path.join(tempBinDir, 'uv.exe'), path.join(binDir))
path.join(tempBinDir, 'uv.exe'),
path.join(binDir)
)
if (platform === 'win32') { if (platform === 'win32') {
copyFile(path.join(binDir, 'uv.exe'), path.join(binDir, 'uv-x86_64-pc-windows-msvc.exe'), (err) => { copyFile(
if (err) { path.join(binDir, 'uv.exe'),
console.log("Error Found:", err); path.join(binDir, 'uv-x86_64-pc-windows-msvc.exe'),
(err) => {
if (err) {
console.log('Error Found:', err)
}
} }
}) )
} }
} catch (err) { } catch (err) {
// Expect EEXIST error // Expect EEXIST error

View File

@ -1,86 +0,0 @@
console.log('Script is running')
// scripts/download-lib.mjs
import https from 'https'
import fs, { mkdirSync } from 'fs'
import os from 'os'
import path from 'path'
import { copySync } from 'cpx'
function download(url, dest) {
return new Promise((resolve, reject) => {
console.log(`Downloading ${url} to ${dest}`)
const file = fs.createWriteStream(dest)
https
.get(url, (response) => {
console.log(`Response status code: ${response.statusCode}`)
if (
response.statusCode >= 300 &&
response.statusCode < 400 &&
response.headers.location
) {
// Handle redirect
const redirectURL = response.headers.location
console.log(`Redirecting to ${redirectURL}`)
download(redirectURL, dest).then(resolve, reject) // Recursive call
return
} else if (response.statusCode !== 200) {
reject(`Failed to get '${url}' (${response.statusCode})`)
return
}
response.pipe(file)
file.on('finish', () => {
file.close(resolve)
})
})
.on('error', (err) => {
fs.unlink(dest, () => reject(err.message))
})
})
}
async function main() {
console.log('Starting main function')
const platform = os.platform() // 'darwin', 'linux', 'win32'
const arch = os.arch() // 'x64', 'arm64', etc.
if (arch != 'x64') return
let filename
if (platform == 'linux')
filename = 'libvulkan.so'
else if (platform == 'win32')
filename = 'vulkan-1.dll'
else
return
const url = `https://catalog.jan.ai/${filename}`
const libDir = 'src-tauri/resources/lib'
const tempDir = 'scripts/dist'
try {
mkdirSync('scripts/dist')
} catch (err) {
// Expect EEXIST error if the directory already exists
}
console.log(`Downloading libvulkan...`)
const savePath = path.join(tempDir, filename)
if (!fs.existsSync(savePath)) {
await download(url, savePath)
}
// copy to tauri resources
try {
copySync(savePath, libDir)
} catch (err) {
// Expect EEXIST error
}
console.log('Downloads completed.')
}
main().catch((err) => {
console.error('Error:', err)
process.exit(1)
})

View File

@ -11,7 +11,7 @@ exclude = ["/examples", "/dist-js", "/guest-js", "/node_modules"]
links = "tauri-plugin-hardware" links = "tauri-plugin-hardware"
[dependencies] [dependencies]
ash = "0.38.0" vulkano = "0.34"
libc = "0.2" libc = "0.2"
log = "0.4" log = "0.4"
nvml-wrapper = "0.10.0" nvml-wrapper = "0.10.0"

View File

@ -1,14 +1,12 @@
use crate::{ use crate::{
helpers::get_jan_libvulkan_path,
types::{CpuStaticInfo, SystemInfo, SystemUsage}, types::{CpuStaticInfo, SystemInfo, SystemUsage},
vendor::{nvidia, vulkan}, vendor::{nvidia, vulkan},
SYSTEM_INFO, SYSTEM_INFO,
}; };
use sysinfo::System; use sysinfo::System;
use tauri::Runtime;
#[tauri::command] #[tauri::command]
pub fn get_system_info<R: Runtime>(app: tauri::AppHandle<R>) -> SystemInfo { pub fn get_system_info() -> SystemInfo {
SYSTEM_INFO SYSTEM_INFO
.get_or_init(|| { .get_or_init(|| {
let mut system = System::new(); let mut system = System::new();
@ -19,15 +17,7 @@ pub fn get_system_info<R: Runtime>(app: tauri::AppHandle<R>) -> SystemInfo {
gpu_map.insert(gpu.uuid.clone(), gpu); gpu_map.insert(gpu.uuid.clone(), gpu);
} }
// try system vulkan first let vulkan_gpus = vulkan::get_vulkan_gpus();
let paths = vec!["".to_string(), get_jan_libvulkan_path(app.clone())];
let mut vulkan_gpus = vec![];
for path in paths {
vulkan_gpus = vulkan::get_vulkan_gpus(&path);
if !vulkan_gpus.is_empty() {
break;
}
}
for gpu in vulkan_gpus { for gpu in vulkan_gpus {
match gpu_map.get_mut(&gpu.uuid) { match gpu_map.get_mut(&gpu.uuid) {
@ -64,7 +54,7 @@ pub fn get_system_info<R: Runtime>(app: tauri::AppHandle<R>) -> SystemInfo {
} }
#[tauri::command] #[tauri::command]
pub fn get_system_usage<R: Runtime>(app: tauri::AppHandle<R>) -> SystemUsage { pub fn get_system_usage() -> SystemUsage {
let mut system = System::new(); let mut system = System::new();
system.refresh_memory(); system.refresh_memory();
@ -81,7 +71,7 @@ pub fn get_system_usage<R: Runtime>(app: tauri::AppHandle<R>) -> SystemUsage {
cpu: cpu_usage, cpu: cpu_usage,
used_memory: system.used_memory() / 1024 / 1024, // bytes to MiB, used_memory: system.used_memory() / 1024 / 1024, // bytes to MiB,
total_memory: system.total_memory() / 1024 / 1024, // bytes to MiB, total_memory: system.total_memory() / 1024 / 1024, // bytes to MiB,
gpus: get_system_info(app.clone()) gpus: get_system_info()
.gpus .gpus
.iter() .iter()
.map(|gpu| gpu.get_usage()) .map(|gpu| gpu.get_usage())

View File

@ -1,20 +0,0 @@
use tauri::{path::BaseDirectory, Manager, Runtime};
pub fn get_jan_libvulkan_path<R: Runtime>(app: tauri::AppHandle<R>) -> String {
let lib_name = if cfg!(target_os = "windows") {
"vulkan-1.dll"
} else if cfg!(target_os = "linux") {
"libvulkan.so"
} else {
return "".to_string();
};
// NOTE: this does not work in test mode (mock app)
match app.path().resolve(
format!("resources/lib/{}", lib_name),
BaseDirectory::Resource,
) {
Ok(lib_path) => lib_path.to_string_lossy().to_string(),
Err(_) => "".to_string(),
}
}

View File

@ -2,12 +2,10 @@ mod commands;
mod constants; mod constants;
pub mod cpu; pub mod cpu;
pub mod gpu; pub mod gpu;
mod helpers;
mod types; mod types;
pub mod vendor; pub mod vendor;
pub use constants::*; pub use constants::*;
pub use helpers::*;
pub use types::*; pub use types::*;
use std::sync::OnceLock; use std::sync::OnceLock;

View File

@ -4,15 +4,13 @@ use tauri::test::mock_app;
#[test] #[test]
fn test_system_info() { fn test_system_info() {
let app = mock_app(); let info = get_system_info();
let info = get_system_info(app.handle().clone());
println!("System Static Info: {:?}", info); println!("System Static Info: {:?}", info);
} }
#[test] #[test]
fn test_system_usage() { fn test_system_usage() {
let app = mock_app(); let usage = get_system_usage();
let usage = get_system_usage(app.handle().clone());
println!("System Usage Info: {:?}", usage); println!("System Usage Info: {:?}", usage);
} }
@ -23,23 +21,23 @@ mod cpu_tests {
#[test] #[test]
fn test_cpu_static_info_new() { fn test_cpu_static_info_new() {
let cpu_info = CpuStaticInfo::new(); let cpu_info = CpuStaticInfo::new();
// Test that all fields are populated // Test that all fields are populated
assert!(!cpu_info.name.is_empty()); assert!(!cpu_info.name.is_empty());
assert_ne!(cpu_info.name, "unknown"); // Should have detected a CPU name assert_ne!(cpu_info.name, "unknown"); // Should have detected a CPU name
assert!(cpu_info.core_count > 0); assert!(cpu_info.core_count > 0);
assert!(!cpu_info.arch.is_empty()); assert!(!cpu_info.arch.is_empty());
// Architecture should be one of the expected values // Architecture should be one of the expected values
assert!( assert!(
cpu_info.arch == "aarch64" || cpu_info.arch == "aarch64"
cpu_info.arch == "arm64" || || cpu_info.arch == "arm64"
cpu_info.arch == "x86_64" || || cpu_info.arch == "x86_64"
cpu_info.arch == std::env::consts::ARCH || cpu_info.arch == std::env::consts::ARCH
); );
// Extensions should be a valid list (can be empty on non-x86) // Extensions should be a valid list (can be empty on non-x86)
println!("CPU Info: {:?}", cpu_info); println!("CPU Info: {:?}", cpu_info);
} }
@ -48,7 +46,7 @@ mod cpu_tests {
// Test that multiple calls return consistent information // Test that multiple calls return consistent information
let info1 = CpuStaticInfo::new(); let info1 = CpuStaticInfo::new();
let info2 = CpuStaticInfo::new(); let info2 = CpuStaticInfo::new();
assert_eq!(info1.name, info2.name); assert_eq!(info1.name, info2.name);
assert_eq!(info1.core_count, info2.core_count); assert_eq!(info1.core_count, info2.core_count);
assert_eq!(info1.arch, info2.arch); assert_eq!(info1.arch, info2.arch);
@ -72,19 +70,41 @@ mod cpu_tests {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_x86_extensions() { fn test_x86_extensions() {
let cpu_info = CpuStaticInfo::new(); let cpu_info = CpuStaticInfo::new();
// On x86/x86_64, we should always have at least FPU // On x86/x86_64, we should always have at least FPU
assert!(cpu_info.extensions.contains(&"fpu".to_string())); assert!(cpu_info.extensions.contains(&"fpu".to_string()));
// Check that all extensions are valid x86 feature names // Check that all extensions are valid x86 feature names
let valid_extensions = [ let valid_extensions = [
"fpu", "mmx", "sse", "sse2", "sse3", "ssse3", "sse4_1", "sse4_2", "fpu",
"pclmulqdq", "avx", "avx2", "avx512_f", "avx512_dq", "avx512_ifma", "mmx",
"avx512_pf", "avx512_er", "avx512_cd", "avx512_bw", "avx512_vl", "sse",
"avx512_vbmi", "avx512_vbmi2", "avx512_vnni", "avx512_bitalg", "sse2",
"avx512_vpopcntdq", "avx512_vp2intersect", "aes", "f16c" "sse3",
"ssse3",
"sse4_1",
"sse4_2",
"pclmulqdq",
"avx",
"avx2",
"avx512_f",
"avx512_dq",
"avx512_ifma",
"avx512_pf",
"avx512_er",
"avx512_cd",
"avx512_bw",
"avx512_vl",
"avx512_vbmi",
"avx512_vbmi2",
"avx512_vnni",
"avx512_bitalg",
"avx512_vpopcntdq",
"avx512_vp2intersect",
"aes",
"f16c",
]; ];
for ext in &cpu_info.extensions { for ext in &cpu_info.extensions {
assert!( assert!(
valid_extensions.contains(&ext.as_str()), valid_extensions.contains(&ext.as_str()),
@ -98,7 +118,7 @@ mod cpu_tests {
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
fn test_non_x86_extensions() { fn test_non_x86_extensions() {
let cpu_info = CpuStaticInfo::new(); let cpu_info = CpuStaticInfo::new();
// On non-x86 architectures, extensions should be empty // On non-x86 architectures, extensions should be empty
assert!(cpu_info.extensions.is_empty()); assert!(cpu_info.extensions.is_empty());
} }
@ -106,15 +126,15 @@ mod cpu_tests {
#[test] #[test]
fn test_arch_detection() { fn test_arch_detection() {
let cpu_info = CpuStaticInfo::new(); let cpu_info = CpuStaticInfo::new();
// Architecture should be a valid string // Architecture should be a valid string
assert!(!cpu_info.arch.is_empty()); assert!(!cpu_info.arch.is_empty());
// Should be one of the common architectures // Should be one of the common architectures
let common_archs = ["x86_64", "aarch64", "arm", "arm64", "x86"]; let common_archs = ["x86_64", "aarch64", "arm", "arm64", "x86"];
let is_common_arch = common_archs.iter().any(|&arch| cpu_info.arch == arch); let is_common_arch = common_archs.iter().any(|&arch| cpu_info.arch == arch);
let is_compile_time_arch = cpu_info.arch == std::env::consts::ARCH; let is_compile_time_arch = cpu_info.arch == std::env::consts::ARCH;
assert!( assert!(
is_common_arch || is_compile_time_arch, is_common_arch || is_compile_time_arch,
"Unexpected architecture: {}", "Unexpected architecture: {}",
@ -125,11 +145,11 @@ mod cpu_tests {
#[test] #[test]
fn test_cpu_info_serialization() { fn test_cpu_info_serialization() {
let cpu_info = CpuStaticInfo::new(); let cpu_info = CpuStaticInfo::new();
// Test that the struct can be serialized (since it derives Serialize) // Test that the struct can be serialized (since it derives Serialize)
let serialized = serde_json::to_string(&cpu_info); let serialized = serde_json::to_string(&cpu_info);
assert!(serialized.is_ok()); assert!(serialized.is_ok());
let json_str = serialized.unwrap(); let json_str = serialized.unwrap();
assert!(json_str.contains("name")); assert!(json_str.contains("name"));
assert!(json_str.contains("core_count")); assert!(json_str.contains("core_count"));

View File

@ -12,7 +12,7 @@ fn test_get_nvidia_gpus() {
#[test] #[test]
fn test_get_vulkan_gpus() { fn test_get_vulkan_gpus() {
let gpus = vulkan::get_vulkan_gpus(""); let gpus = vulkan::get_vulkan_gpus();
for (i, gpu) in gpus.iter().enumerate() { for (i, gpu) in gpus.iter().enumerate() {
println!("GPU {}:", i); println!("GPU {}:", i);
println!(" {:?}", gpu); println!(" {:?}", gpu);

View File

@ -1,5 +1,8 @@
use crate::types::{GpuInfo, Vendor}; use crate::types::{GpuInfo, Vendor};
use ash::{vk, Entry}; use vulkano::device::physical::PhysicalDeviceType;
use vulkano::instance::{Instance, InstanceCreateInfo};
use vulkano::memory::MemoryHeapFlags;
use vulkano::VulkanLibrary;
#[derive(Debug, Clone, serde::Serialize)] #[derive(Debug, Clone, serde::Serialize)]
pub struct VulkanInfo { pub struct VulkanInfo {
@ -35,8 +38,8 @@ fn parse_uuid(bytes: &[u8; 16]) -> String {
) )
} }
pub fn get_vulkan_gpus(lib_path: &str) -> Vec<GpuInfo> { pub fn get_vulkan_gpus() -> Vec<GpuInfo> {
match get_vulkan_gpus_internal(lib_path) { match get_vulkan_gpus_internal() {
Ok(gpus) => gpus, Ok(gpus) => gpus,
Err(e) => { Err(e) => {
log::error!("Failed to get Vulkan GPUs: {:?}", e); log::error!("Failed to get Vulkan GPUs: {:?}", e);
@ -45,86 +48,59 @@ pub fn get_vulkan_gpus(lib_path: &str) -> Vec<GpuInfo> {
} }
} }
fn parse_c_string(buf: &[i8]) -> String { fn get_vulkan_gpus_internal() -> Result<Vec<GpuInfo>, Box<dyn std::error::Error>> {
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) } let library = VulkanLibrary::new()?;
.to_str()
.unwrap_or_default()
.to_string()
}
fn get_vulkan_gpus_internal(lib_path: &str) -> Result<Vec<GpuInfo>, Box<dyn std::error::Error>> { let instance = Instance::new(
let entry = if lib_path.is_empty() { library,
unsafe { Entry::load()? } InstanceCreateInfo {
} else { application_name: Some("Jan GPU Detection".into()),
unsafe { Entry::load_from(lib_path)? } application_version: vulkano::Version::V1_1,
}; ..Default::default()
let app_info = vk::ApplicationInfo { },
api_version: vk::make_api_version(0, 1, 1, 0), )?;
..Default::default()
};
let create_info = vk::InstanceCreateInfo {
p_application_info: &app_info,
..Default::default()
};
let instance = unsafe { entry.create_instance(&create_info, None)? };
let mut device_info_list = vec![]; let mut device_info_list = vec![];
for (i, device) in unsafe { instance.enumerate_physical_devices()? } for (i, physical_device) in instance.enumerate_physical_devices()?.enumerate() {
.iter() let properties = physical_device.properties();
.enumerate()
{
// create a chain of properties struct for VkPhysicalDeviceProperties2(3)
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDeviceProperties2.html
// props2 -> driver_props -> id_props
let mut id_props = vk::PhysicalDeviceIDProperties::default();
let mut driver_props = vk::PhysicalDeviceDriverProperties {
p_next: &mut id_props as *mut _ as *mut std::ffi::c_void,
..Default::default()
};
let mut props2 = vk::PhysicalDeviceProperties2 {
p_next: &mut driver_props as *mut _ as *mut std::ffi::c_void,
..Default::default()
};
unsafe {
instance.get_physical_device_properties2(*device, &mut props2);
}
let props = props2.properties; if properties.device_type == PhysicalDeviceType::Cpu {
if props.device_type == vk::PhysicalDeviceType::CPU {
continue; continue;
} }
let memory_properties = physical_device.memory_properties();
let total_memory: u64 = memory_properties
.memory_heaps
.iter()
.filter(|heap| heap.flags.intersects(MemoryHeapFlags::DEVICE_LOCAL))
.map(|heap| heap.size / (1024 * 1024))
.sum();
let device_uuid = physical_device.properties().device_uuid.unwrap_or([0; 16]);
let driver_version = format!("{}", properties.driver_version);
let device_info = GpuInfo { let device_info = GpuInfo {
name: parse_c_string(&props.device_name), name: properties.device_name.clone(),
total_memory: unsafe { instance.get_physical_device_memory_properties(*device) } total_memory,
.memory_heaps vendor: Vendor::from_vendor_id(properties.vendor_id),
.iter() uuid: parse_uuid(&device_uuid),
.filter(|heap| heap.flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL)) driver_version,
.map(|heap| heap.size / (1024 * 1024))
.sum(),
vendor: Vendor::from_vendor_id(props.vendor_id),
uuid: parse_uuid(&id_props.device_uuid),
driver_version: parse_c_string(&driver_props.driver_info),
nvidia_info: None, nvidia_info: None,
vulkan_info: Some(VulkanInfo { vulkan_info: Some(VulkanInfo {
index: i as u64, index: i as u64,
device_type: format!("{:?}", props.device_type), device_type: format!("{:?}", properties.device_type),
api_version: format!( api_version: format!(
"{}.{}.{}", "{}.{}.{}",
vk::api_version_major(props.api_version), properties.api_version.major,
vk::api_version_minor(props.api_version), properties.api_version.minor,
vk::api_version_patch(props.api_version) properties.api_version.patch
), ),
device_id: props.device_id, device_id: properties.device_id,
}), }),
}; };
device_info_list.push(device_info); device_info_list.push(device_info);
} }
unsafe {
instance.destroy_instance(None);
}
Ok(device_info_list) Ok(device_info_list)
} }

View File

@ -3,7 +3,6 @@ use super::utils::{estimate_kv_cache_internal, read_gguf_metadata_internal};
use crate::gguf::types::{KVCacheError, KVCacheEstimate, ModelSupportStatus}; use crate::gguf::types::{KVCacheError, KVCacheEstimate, ModelSupportStatus};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use tauri::Runtime;
use tauri_plugin_hardware::get_system_info; use tauri_plugin_hardware::get_system_info;
/// Read GGUF metadata from a model file /// Read GGUF metadata from a model file
#[tauri::command] #[tauri::command]
@ -49,16 +48,15 @@ pub async fn get_model_size(path: String) -> Result<u64, String> {
} }
#[tauri::command] #[tauri::command]
pub async fn is_model_supported<R: Runtime>( pub async fn is_model_supported(
path: String, path: String,
ctx_size: Option<u32>, ctx_size: Option<u32>,
app_handle: tauri::AppHandle<R>,
) -> Result<ModelSupportStatus, String> { ) -> Result<ModelSupportStatus, String> {
// Get model size // Get model size
let model_size = get_model_size(path.clone()).await?; let model_size = get_model_size(path.clone()).await?;
// Get system info // Get system info
let system_info = get_system_info(app_handle.clone()); let system_info = get_system_info();
log::info!("modelSize: {}", model_size); log::info!("modelSize: {}", model_size);

View File

@ -3,7 +3,6 @@ use crate::gguf::utils::estimate_kv_cache_internal;
use crate::gguf::utils::read_gguf_metadata_internal; use crate::gguf::utils::read_gguf_metadata_internal;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use tauri::Runtime;
use tauri_plugin_hardware::get_system_info; use tauri_plugin_hardware::get_system_info;
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@ -27,15 +26,14 @@ pub enum ModelMode {
} }
#[tauri::command] #[tauri::command]
pub async fn plan_model_load<R: Runtime>( pub async fn plan_model_load(
path: String, path: String,
memory_mode: String, memory_mode: String,
mmproj_path: Option<String>, mmproj_path: Option<String>,
requested_ctx: Option<u64>, requested_ctx: Option<u64>,
app: tauri::AppHandle<R>,
) -> Result<ModelPlan, String> { ) -> Result<ModelPlan, String> {
let model_size = get_model_size(path.clone()).await?; let model_size = get_model_size(path.clone()).await?;
let sys_info = get_system_info(app.clone()); let sys_info = get_system_info();
let gguf = read_gguf_metadata_internal(path.clone()).await?; let gguf = read_gguf_metadata_internal(path.clone()).await?;
let mut mmproj_size: u64 = 0; let mut mmproj_size: u64 = 0;

View File

@ -25,7 +25,7 @@ use crate::core::{
mcp::models::McpServerConfig, mcp::models::McpServerConfig,
state::{AppState, RunningServiceEnum, SharedMcpServers}, state::{AppState, RunningServiceEnum, SharedMcpServers},
}; };
use jan_utils::can_override_npx; use jan_utils::{can_override_npx, can_override_uvx};
/// Calculate exponential backoff delay with jitter /// Calculate exponential backoff delay with jitter
/// ///
@ -627,19 +627,20 @@ async fn schedule_mcp_start_task<R: Runtime>(
} }
} else { } else {
let mut cmd = Command::new(config_params.command.clone()); let mut cmd = Command::new(config_params.command.clone());
if config_params.command.clone() == "npx" && can_override_npx() { let bun_x_path = format!("{}/bun", bin_path.display());
if config_params.command.clone() == "npx" && can_override_npx(bun_x_path.clone()) {
let mut cache_dir = app_path.clone(); let mut cache_dir = app_path.clone();
cache_dir.push(".npx"); cache_dir.push(".npx");
let bun_x_path = format!("{}/bun", bin_path.display());
cmd = Command::new(bun_x_path); cmd = Command::new(bun_x_path);
cmd.arg("x"); cmd.arg("x");
cmd.env("BUN_INSTALL", cache_dir.to_str().unwrap().to_string()); cmd.env("BUN_INSTALL", cache_dir.to_str().unwrap().to_string());
} }
if config_params.command.clone() == "uvx" {
let uv_path = format!("{}/uv", bin_path.display());
if config_params.command.clone() == "uvx" && can_override_uvx(uv_path.clone()) {
let mut cache_dir = app_path.clone(); let mut cache_dir = app_path.clone();
cache_dir.push(".uvx"); cache_dir.push(".uvx");
let bun_x_path = format!("{}/uv", bin_path.display()); cmd = Command::new(uv_path);
cmd = Command::new(bun_x_path);
cmd.arg("tool"); cmd.arg("tool");
cmd.arg("run"); cmd.arg("run");
cmd.env("UV_CACHE_DIR", cache_dir.to_str().unwrap().to_string()); cmd.env("UV_CACHE_DIR", cache_dir.to_str().unwrap().to_string());

View File

@ -6,13 +6,11 @@
"linux": { "linux": {
"appimage": { "appimage": {
"bundleMediaFramework": false, "bundleMediaFramework": false,
"files": { "files": {}
}
}, },
"deb": { "deb": {
"files": { "files": {
"usr/bin/bun": "resources/bin/bun", "usr/bin/bun": "resources/bin/bun"
"usr/lib/Jan/resources/lib/libvulkan.so": "resources/lib/libvulkan.so"
} }
} }
} }

View File

@ -1,7 +1,11 @@
{ {
"bundle": { "bundle": {
"targets": ["nsis"], "targets": ["nsis"],
"resources": ["resources/pre-install/**/*", "resources/lib/vulkan-1.dll", "resources/lib/vc_redist.x64.exe", "resources/LICENSE"], "resources": [
"resources/pre-install/**/*",
"resources/lib/vc_redist.x64.exe",
"resources/LICENSE"
],
"externalBin": ["resources/bin/bun", "resources/bin/uv"], "externalBin": ["resources/bin/bun", "resources/bin/uv"],
"windows": { "windows": {
"nsis": { "nsis": {

View File

@ -1,5 +1,5 @@
/// Checks AVX2 CPU support for npx override with bun binary /// Checks if npx can be overridden with bun binary
pub fn can_override_npx() -> bool { pub fn can_override_npx(bun_path: String) -> bool {
// We need to check the CPU for the AVX2 instruction support if we are running under MacOS // We need to check the CPU for the AVX2 instruction support if we are running under MacOS
// with Intel CPU. We can override `npx` command with `bun` only if CPU is // with Intel CPU. We can override `npx` command with `bun` only if CPU is
// supporting AVX2, otherwise we need to use default `npx` binary // supporting AVX2, otherwise we need to use default `npx` binary
@ -13,10 +13,31 @@ pub fn can_override_npx() -> bool {
return false; // we cannot override npx with bun binary return false; // we cannot override npx with bun binary
} }
} }
// Check if bun_path exists
if !std::path::Path::new(bun_path.as_str()).exists() {
#[cfg(feature = "logging")]
log::warn!(
"bun binary not found at '{}', default npx binary will be used",
bun_path
);
return false;
}
true // by default, we can override npx with bun binary true // by default, we can override npx with bun binary
} }
/// Checks if uv_path exists and determines if uvx can be overridden with the uv binary
pub fn can_override_uvx(uv_path: String) -> bool {
if !std::path::Path::new(uv_path.as_str()).exists() {
#[cfg(feature = "logging")]
log::warn!(
"uv binary not found at '{}', default uvx binary will be used",
uv_path
);
return false;
}
true // by default, we can override uvx with uv binary
}
/// Setup library paths for different operating systems /// Setup library paths for different operating systems
pub fn setup_library_path(library_path: Option<&str>, command: &mut tokio::process::Command) { pub fn setup_library_path(library_path: Option<&str>, command: &mut tokio::process::Command) {
if let Some(lib_path) = library_path { if let Some(lib_path) = library_path {