Merge pull request #4468 from janhq/dev
release: Jan 0.5.14 release cut
1
.github/scripts/rename-uninstaller.sh
vendored
@ -18,6 +18,7 @@ if [ ! -f "$FILE_PATH" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Perform the replacements
|
# Perform the replacements
|
||||||
|
sed -i -e "s#Jan#Jan-$CHANNEL#g" "$FILE_PATH"
|
||||||
sed -i -e "s#jan#jan-$CHANNEL#g" "$FILE_PATH"
|
sed -i -e "s#jan#jan-$CHANNEL#g" "$FILE_PATH"
|
||||||
|
|
||||||
# Notify completion
|
# Notify completion
|
||||||
|
|||||||
@ -44,16 +44,17 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.base_ref }}
|
ref: ${{ github.base_ref }}
|
||||||
- name: Use Node.js v20.9.0
|
- name: Use Node.js 20.x
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: v20.9.0
|
node-version: 20
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
make config-yarn
|
||||||
yarn
|
yarn
|
||||||
yarn build:core
|
|
||||||
yarn build:joi
|
yarn build:joi
|
||||||
|
yarn build:core
|
||||||
|
|
||||||
- name: Run test coverage
|
- name: Run test coverage
|
||||||
run: yarn test:coverage
|
run: yarn test:coverage
|
||||||
@ -66,7 +67,7 @@ jobs:
|
|||||||
|
|
||||||
test-on-macos:
|
test-on-macos:
|
||||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||||
runs-on: [self-hosted, macOS, macos-desktop]
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Getting the repo
|
- name: Getting the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -78,6 +79,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Set IS_TEST environment variable
|
||||||
|
run: |
|
||||||
|
echo "IS_TEST=true" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 'Cleanup cache'
|
- name: 'Cleanup cache'
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
@ -94,24 +99,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}})" >> $GITHUB_ENV
|
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}})" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 'Config report portal'
|
# - name: 'Config report portal'
|
||||||
run: |
|
# run: |
|
||||||
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App macos" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App macos" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
run: |
|
run: |
|
||||||
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
make test
|
make test
|
||||||
env:
|
env:
|
||||||
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
||||||
# TURBO_API: '${{ secrets.TURBO_API }}'
|
|
||||||
# TURBO_TEAM: 'macos'
|
|
||||||
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
|
||||||
|
|
||||||
test-on-macos-pr-target:
|
test-on-macos-pr-target:
|
||||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||||
runs-on: [self-hosted, macOS, macos-desktop]
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Getting the repo
|
- name: Getting the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -131,8 +131,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
run: |
|
run: |
|
||||||
npm config set registry https://registry.npmjs.org --global
|
|
||||||
yarn config set registry https://registry.npmjs.org --global
|
|
||||||
make test
|
make test
|
||||||
env:
|
env:
|
||||||
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
||||||
@ -174,24 +172,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}}" >> $GITHUB_ENV
|
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 'Config report portal'
|
# - name: 'Config report portal'
|
||||||
shell: bash
|
# shell: bash
|
||||||
run: |
|
# run: |
|
||||||
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows ${{ matrix.antivirus-tools }}" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows ${{ matrix.antivirus-tools }}" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
make test
|
make test
|
||||||
# env:
|
|
||||||
# TURBO_API: '${{ secrets.TURBO_API }}'
|
|
||||||
# TURBO_TEAM: 'windows'
|
|
||||||
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
|
||||||
test-on-windows-pr:
|
test-on-windows-pr:
|
||||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'workflow_dispatch'
|
||||||
runs-on: windows-desktop-default-windows-security
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Getting the repo
|
- name: Getting the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -199,7 +192,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Installing node
|
- name: Installing node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
@ -222,25 +215,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "REPORT_PORTAL_DESCRIPTION=${{github.event.after}}" >> $GITHUB_ENV
|
echo "REPORT_PORTAL_DESCRIPTION=${{github.event.after}}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 'Config report portal'
|
# - name: 'Config report portal'
|
||||||
shell: bash
|
# shell: bash
|
||||||
run: |
|
# run: |
|
||||||
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Windows" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
make test
|
make test
|
||||||
# env:
|
|
||||||
# TURBO_API: '${{ secrets.TURBO_API }}'
|
|
||||||
# TURBO_TEAM: 'windows'
|
|
||||||
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
|
||||||
|
|
||||||
test-on-windows-pr-target:
|
test-on-windows-pr-target:
|
||||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||||
runs-on: windows-desktop-default-windows-security
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Getting the repo
|
- name: Getting the repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -268,12 +255,10 @@ jobs:
|
|||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
shell: powershell
|
shell: powershell
|
||||||
run: |
|
run: |
|
||||||
npm config set registry https://registry.npmjs.org --global
|
|
||||||
yarn config set registry https://registry.npmjs.org --global
|
|
||||||
make test
|
make test
|
||||||
|
|
||||||
test-on-ubuntu:
|
test-on-ubuntu:
|
||||||
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
runs-on: ubuntu-latest
|
||||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||||
steps:
|
steps:
|
||||||
- name: Getting the repo
|
- name: Getting the repo
|
||||||
@ -302,22 +287,16 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}}" >> $GITHUB_ENV
|
echo "REPORT_PORTAL_DESCRIPTION=${{github.sha}}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 'Config report portal'
|
# - name: 'Config report portal'
|
||||||
shell: bash
|
# shell: bash
|
||||||
run: |
|
# run: |
|
||||||
make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Linux" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
# make update-playwright-config REPORT_PORTAL_URL=${{ secrets.REPORT_PORTAL_URL }} REPORT_PORTAL_API_KEY=${{ secrets.REPORT_PORTAL_API_KEY }} REPORT_PORTAL_PROJECT_NAME=${{ secrets.REPORT_PORTAL_PROJECT_NAME }} REPORT_PORTAL_LAUNCH_NAME="Jan App Linux" REPORT_PORTAL_DESCRIPTION="${{env.REPORT_PORTAL_DESCRIPTION}}"
|
||||||
|
|
||||||
- name: Linter and test
|
- name: Linter and test
|
||||||
run: |
|
run: |
|
||||||
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
||||||
echo -e "Display ID: $DISPLAY"
|
echo -e "Display ID: $DISPLAY"
|
||||||
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
make test
|
make test
|
||||||
# env:
|
|
||||||
# TURBO_API: '${{ secrets.TURBO_API }}'
|
|
||||||
# TURBO_TEAM: 'linux'
|
|
||||||
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
@ -327,7 +306,7 @@ jobs:
|
|||||||
retention-days: 2
|
retention-days: 2
|
||||||
|
|
||||||
coverage-check:
|
coverage-check:
|
||||||
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
runs-on: ubuntu-latest
|
||||||
needs: base_branch_cov
|
needs: base_branch_cov
|
||||||
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||||
steps:
|
steps:
|
||||||
@ -341,6 +320,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install yarn
|
||||||
|
run: npm install -g yarn
|
||||||
|
|
||||||
- name: 'Cleanup cache'
|
- name: 'Cleanup cache'
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
@ -356,15 +338,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
||||||
echo -e "Display ID: $DISPLAY"
|
echo -e "Display ID: $DISPLAY"
|
||||||
npm config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
yarn config set registry ${{ secrets.NPM_PROXY }} --global
|
|
||||||
make lint
|
make lint
|
||||||
yarn build:test
|
yarn build:test
|
||||||
yarn test:coverage
|
yarn test:coverage
|
||||||
# env:
|
|
||||||
# TURBO_API: '${{ secrets.TURBO_API }}'
|
|
||||||
# TURBO_TEAM: 'linux'
|
|
||||||
# TURBO_TOKEN: '${{ secrets.TURBO_TOKEN }}'
|
|
||||||
|
|
||||||
- name: Generate Code Coverage report
|
- name: Generate Code Coverage report
|
||||||
id: code-coverage
|
id: code-coverage
|
||||||
@ -377,7 +353,7 @@ jobs:
|
|||||||
show-annotations: 'warning'
|
show-annotations: 'warning'
|
||||||
|
|
||||||
test-on-ubuntu-pr-target:
|
test-on-ubuntu-pr-target:
|
||||||
runs-on: [self-hosted, Linux, ubuntu-desktop]
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
|
||||||
steps:
|
steps:
|
||||||
- name: Getting the repo
|
- name: Getting the repo
|
||||||
@ -400,6 +376,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
|
||||||
echo -e "Display ID: $DISPLAY"
|
echo -e "Display ID: $DISPLAY"
|
||||||
npm config set registry https://registry.npmjs.org --global
|
|
||||||
yarn config set registry https://registry.npmjs.org --global
|
|
||||||
make test
|
make test
|
||||||
|
|||||||
8
.github/workflows/publish-npm-core.yml
vendored
@ -1,10 +1,10 @@
|
|||||||
name: Publish plugin models Package to npmjs
|
name: Publish core Package to npmjs
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["v[0-9]+.[0-9]+.[0-9]+-core"]
|
tags: ["v[0-9]+.[0-9]+.[0-9]+-core"]
|
||||||
paths: ["core/**"]
|
paths: ["core/**", ".github/workflows/publish-npm-core.yml"]
|
||||||
pull_request:
|
pull_request:
|
||||||
paths: ["core/**"]
|
paths: ["core/**", ".github/workflows/publish-npm-core.yml"]
|
||||||
jobs:
|
jobs:
|
||||||
build-and-publish-plugins:
|
build-and-publish-plugins:
|
||||||
environment: production
|
environment: production
|
||||||
@ -45,7 +45,7 @@ jobs:
|
|||||||
node-version: "20.x"
|
node-version: "20.x"
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
- run: cd core && yarn install && yarn build
|
- run: cd core && corepack enable && corepack prepare yarn@4.5.3 --activate && yarn --version && yarn config set -H enableImmutableInstalls false && yarn install && yarn build
|
||||||
|
|
||||||
- run: cd core && yarn publish --access public
|
- run: cd core && yarn publish --access public
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|||||||
8
.github/workflows/publish-npm-joi.yml
vendored
@ -1,10 +1,10 @@
|
|||||||
name: Publish plugin models Package to npmjs
|
name: Publish joi Package to npmjs
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["v[0-9]+.[0-9]+.[0-9]+-joi"]
|
tags: ["v[0-9]+.[0-9]+.[0-9]+-joi"]
|
||||||
paths: ["joi/**"]
|
paths: ["joi/**", ".github/workflows/publish-npm-joi.yml"]
|
||||||
pull_request:
|
pull_request:
|
||||||
paths: ["joi/**"]
|
paths: ["joi/**", ".github/workflows/publish-npm-joi.yml"]
|
||||||
jobs:
|
jobs:
|
||||||
build-and-publish-plugins:
|
build-and-publish-plugins:
|
||||||
environment: production
|
environment: production
|
||||||
@ -45,7 +45,7 @@ jobs:
|
|||||||
node-version: "20.x"
|
node-version: "20.x"
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
- run: cd joi && yarn install && yarn build
|
- run: cd joi && corepack enable && corepack prepare yarn@4.5.3 --activate && yarn --version && yarn config set -H enableImmutableInstalls false && yarn install && yarn build
|
||||||
|
|
||||||
- run: cd joi && yarn publish --access public
|
- run: cd joi && yarn publish --access public
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|||||||
5
.gitignore
vendored
@ -8,7 +8,6 @@ error.log
|
|||||||
node_modules
|
node_modules
|
||||||
*.tgz
|
*.tgz
|
||||||
!charts/server/charts/*.tgz
|
!charts/server/charts/*.tgz
|
||||||
yarn.lock
|
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@ -48,3 +47,7 @@ coverage
|
|||||||
test_results.html
|
test_results.html
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
electron/shared/**
|
electron/shared/**
|
||||||
|
|
||||||
|
# docs
|
||||||
|
docs/yarn.lock
|
||||||
|
electron/.version.bak
|
||||||
|
|||||||
3
.yarnrc.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
nmHoistingLimits: workspaces
|
||||||
|
nodeLinker: node-modules
|
||||||
|
checksumBehavior: update
|
||||||
31
Makefile
@ -10,23 +10,23 @@ REPORT_PORTAL_DESCRIPTION ?= "Jan App report"
|
|||||||
all:
|
all:
|
||||||
@echo "Specify a target to run"
|
@echo "Specify a target to run"
|
||||||
|
|
||||||
# Builds the UI kit
|
# Config yarn version
|
||||||
build-joi:
|
|
||||||
ifeq ($(OS),Windows_NT)
|
config-yarn:
|
||||||
cd joi && yarn config set network-timeout 300000 && yarn install && yarn build
|
corepack enable
|
||||||
else
|
corepack prepare yarn@4.5.3 --activate
|
||||||
cd joi && yarn install && yarn build
|
yarn --version
|
||||||
endif
|
yarn config set -H enableImmutableInstalls false
|
||||||
|
|
||||||
# Installs yarn dependencies and builds core and extensions
|
# Installs yarn dependencies and builds core and extensions
|
||||||
install-and-build: build-joi
|
install-and-build: config-yarn
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
yarn config set network-timeout 300000
|
echo "skip"
|
||||||
endif
|
endif
|
||||||
yarn global add turbo@1.13.2
|
yarn install
|
||||||
|
yarn build:joi
|
||||||
yarn build:core
|
yarn build:core
|
||||||
yarn build:server
|
yarn build:server
|
||||||
yarn install
|
|
||||||
yarn build:extensions
|
yarn build:extensions
|
||||||
|
|
||||||
check-file-counts: install-and-build
|
check-file-counts: install-and-build
|
||||||
@ -117,9 +117,8 @@ build: check-file-counts
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
-powershell -Command "Get-ChildItem -Path . -Include node_modules, .next, dist, build, out, .turbo -Recurse -Directory | Remove-Item -Recurse -Force"
|
-powershell -Command "Get-ChildItem -Path . -Include node_modules, .next, dist, build, out, .turbo, .yarn -Recurse -Directory | Remove-Item -Recurse -Force"
|
||||||
-powershell -Command "Get-ChildItem -Path . -Include package-lock.json -Recurse -File | Remove-Item -Recurse -Force"
|
-powershell -Command "Get-ChildItem -Path . -Include package-lock.json, tsconfig.tsbuildinfo -Recurse -File | Remove-Item -Recurse -Force"
|
||||||
-powershell -Command "Get-ChildItem -Path . -Include yarn.lock -Recurse -File | Remove-Item -Recurse -Force"
|
|
||||||
-powershell -Command "Remove-Item -Recurse -Force ./pre-install/*.tgz"
|
-powershell -Command "Remove-Item -Recurse -Force ./pre-install/*.tgz"
|
||||||
-powershell -Command "Remove-Item -Recurse -Force ./extensions/*/*.tgz"
|
-powershell -Command "Remove-Item -Recurse -Force ./extensions/*/*.tgz"
|
||||||
-powershell -Command "Remove-Item -Recurse -Force ./electron/pre-install/*.tgz"
|
-powershell -Command "Remove-Item -Recurse -Force ./electron/pre-install/*.tgz"
|
||||||
@ -131,8 +130,8 @@ else ifeq ($(shell uname -s),Linux)
|
|||||||
find . -name "build" -type d -exec rm -rf '{}' +
|
find . -name "build" -type d -exec rm -rf '{}' +
|
||||||
find . -name "out" -type d -exec rm -rf '{}' +
|
find . -name "out" -type d -exec rm -rf '{}' +
|
||||||
find . -name ".turbo" -type d -exec rm -rf '{}' +
|
find . -name ".turbo" -type d -exec rm -rf '{}' +
|
||||||
|
find . -name ".yarn" -type d -exec rm -rf '{}' +
|
||||||
find . -name "packake-lock.json" -type f -exec rm -rf '{}' +
|
find . -name "packake-lock.json" -type f -exec rm -rf '{}' +
|
||||||
find . -name "yarn.lock" -type f -exec rm -rf '{}' +
|
|
||||||
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
|
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
|
||||||
rm -rf ./pre-install/*.tgz
|
rm -rf ./pre-install/*.tgz
|
||||||
rm -rf ./extensions/*/*.tgz
|
rm -rf ./extensions/*/*.tgz
|
||||||
@ -146,8 +145,8 @@ else
|
|||||||
find . -name "build" -type d -exec rm -rf '{}' +
|
find . -name "build" -type d -exec rm -rf '{}' +
|
||||||
find . -name "out" -type d -exec rm -rf '{}' +
|
find . -name "out" -type d -exec rm -rf '{}' +
|
||||||
find . -name ".turbo" -type d -exec rm -rf '{}' +
|
find . -name ".turbo" -type d -exec rm -rf '{}' +
|
||||||
|
find . -name ".yarn" -type d -exec rm -rf '{}' +
|
||||||
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
|
find . -name "package-lock.json" -type f -exec rm -rf '{}' +
|
||||||
find . -name "yarn.lock" -type f -exec rm -rf '{}' +
|
|
||||||
rm -rf ./pre-install/*.tgz
|
rm -rf ./pre-install/*.tgz
|
||||||
rm -rf ./extensions/*/*.tgz
|
rm -rf ./extensions/*/*.tgz
|
||||||
rm -rf ./electron/pre-install/*.tgz
|
rm -rf ./electron/pre-install/*.tgz
|
||||||
|
|||||||
9
ai.menlo.jan.desktop
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=Jan
|
||||||
|
Comment=Local AI Assistant that runs 100% offline
|
||||||
|
Exec=run.sh
|
||||||
|
Icon=ai.menlo.jan
|
||||||
|
Type=Application
|
||||||
|
Categories=Development;
|
||||||
|
Keywords=AI;Assistant;LLM;ChatGPT;Local;Offline;
|
||||||
|
StartupNotify=true
|
||||||
42
ai.menlo.jan.metainfo.xml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>ai.menlo.jan</id>
|
||||||
|
<metadata_license>FSFAP</metadata_license>
|
||||||
|
<project_license>AGPL-3.0-only</project_license>
|
||||||
|
<name>Jan</name>
|
||||||
|
<summary>Local AI Assistant that runs 100% offline on your device</summary>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
Jan is a ChatGPT-alternative that runs 100% offline on your device. Our goal is to make it easy for anyone to download and run LLMs and use AI with full control and privacy.
|
||||||
|
</p>
|
||||||
|
<p>Features:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Model Library with popular LLMs like Llama, Gemma, Mistral, or Qwen</li>
|
||||||
|
<li>Connect to Remote AI APIs like Groq and OpenRouter</li>
|
||||||
|
<li>Local API Server with OpenAI-equivalent API</li>
|
||||||
|
<li>Extensions for customizing Jan</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<launchable type="desktop-id">ai.menlo.jan.desktop</launchable>
|
||||||
|
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<image>https://catalog.jan.ai/flatpak/demo.gif</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
|
||||||
|
<url type="homepage">https://jan.ai/</url>
|
||||||
|
<url type="bugtracker">https://github.com/janhq/jan/issues</url>
|
||||||
|
|
||||||
|
<content_rating type="oars-1.1" />
|
||||||
|
|
||||||
|
<releases>
|
||||||
|
<release version="0.5.12" date="2024-01-02">
|
||||||
|
<description>
|
||||||
|
<p>Latest stable release of Jan AI</p>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
|
</releases>
|
||||||
|
</component>
|
||||||
@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "@janhq/core",
|
"name": "@janhq/core",
|
||||||
"version": "0.1.10",
|
"version": "0.1.10",
|
||||||
"description": "Jan app core lib",
|
"description": "Core library for the Jan AI application framework",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"jan",
|
"jan",
|
||||||
"core"
|
"core"
|
||||||
],
|
],
|
||||||
"homepage": "https://jan.ai",
|
"homepage": "https://jan.ai",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "dist/core.es5.js",
|
"browser": "dist/index.js",
|
||||||
"module": "dist/core.cjs.js",
|
"main": "dist/index.js",
|
||||||
|
"module": "dist/node/index.cjs.js",
|
||||||
"typings": "dist/types/index.d.ts",
|
"typings": "dist/types/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
@ -17,13 +18,13 @@
|
|||||||
],
|
],
|
||||||
"author": "Jan <service@jan.ai>",
|
"author": "Jan <service@jan.ai>",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/core.es5.js",
|
".": "./dist/index.js",
|
||||||
"./node": "./dist/node/index.cjs.js"
|
"./node": "./dist/node/index.cjs.js"
|
||||||
},
|
},
|
||||||
"typesVersions": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
".": [
|
".": [
|
||||||
"./dist/core.es5.js.map",
|
"./dist/index.js.map",
|
||||||
"./dist/types/index.d.ts"
|
"./dist/types/index.d.ts"
|
||||||
],
|
],
|
||||||
"node": [
|
"node": [
|
||||||
@ -36,25 +37,25 @@
|
|||||||
"lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
|
"lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "tsc --module commonjs && rollup -c rollup.config.ts",
|
"build": "tsc -p . && rolldown -c rolldown.config.mjs"
|
||||||
"start": "rollup -c rollup.config.ts -w"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
"@npmcli/arborist": "^7.1.0",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^20.11.4",
|
"@types/node": "^22.10.0",
|
||||||
|
"@types/pacote": "^11.1.7",
|
||||||
|
"@types/request": "^2.48.12",
|
||||||
|
"electron": "33.2.1",
|
||||||
"eslint": "8.57.0",
|
"eslint": "8.57.0",
|
||||||
"eslint-plugin-jest": "^27.9.0",
|
"eslint-plugin-jest": "^27.9.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-junit": "^16.0.0",
|
"jest-junit": "^16.0.0",
|
||||||
"jest-runner": "^29.7.0",
|
"jest-runner": "^29.7.0",
|
||||||
|
"pacote": "^21.0.0",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"request-progress": "^3.0.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^2.38.5",
|
"rolldown": "1.0.0-beta.1",
|
||||||
"rollup-plugin-commonjs": "^9.1.8",
|
|
||||||
"rollup-plugin-json": "^3.1.0",
|
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
|
||||||
"rollup-plugin-sourcemaps": "^0.6.3",
|
|
||||||
"rollup-plugin-typescript2": "^0.36.0",
|
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.2.5",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
@ -62,5 +63,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"ulidx": "^2.3.0"
|
"ulidx": "^2.3.0"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@4.5.3"
|
||||||
}
|
}
|
||||||
|
|||||||
51
core/rolldown.config.mjs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { defineConfig } from 'rolldown'
|
||||||
|
import pkgJson from './package.json' with { type: 'json' }
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{
|
||||||
|
input: 'src/index.ts',
|
||||||
|
output: {
|
||||||
|
format: 'esm',
|
||||||
|
file: 'dist/index.js',
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
platform: 'browser',
|
||||||
|
external: ['path'],
|
||||||
|
define: {
|
||||||
|
NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`),
|
||||||
|
VERSION: JSON.stringify(pkgJson.version),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'src/node/index.ts',
|
||||||
|
external: [
|
||||||
|
'fs/promises',
|
||||||
|
'path',
|
||||||
|
'pacote',
|
||||||
|
'@types/pacote',
|
||||||
|
'@npmcli/arborist',
|
||||||
|
'ulidx',
|
||||||
|
'node-fetch',
|
||||||
|
'fs',
|
||||||
|
'request',
|
||||||
|
'crypto',
|
||||||
|
'url',
|
||||||
|
'http',
|
||||||
|
'os',
|
||||||
|
'util',
|
||||||
|
'child_process',
|
||||||
|
'electron',
|
||||||
|
'request-progress',
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
format: 'cjs',
|
||||||
|
file: 'dist/node/index.cjs.js',
|
||||||
|
sourcemap: true,
|
||||||
|
inlineDynamicImports: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.ts'],
|
||||||
|
},
|
||||||
|
platform: 'node',
|
||||||
|
},
|
||||||
|
])
|
||||||
@ -1,85 +0,0 @@
|
|||||||
import resolve from 'rollup-plugin-node-resolve'
|
|
||||||
import commonjs from 'rollup-plugin-commonjs'
|
|
||||||
import sourceMaps from 'rollup-plugin-sourcemaps'
|
|
||||||
import typescript from 'rollup-plugin-typescript2'
|
|
||||||
import json from 'rollup-plugin-json'
|
|
||||||
import replace from '@rollup/plugin-replace'
|
|
||||||
|
|
||||||
const pkg = require('./package.json')
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
input: `src/index.ts`,
|
|
||||||
output: [
|
|
||||||
// { file: pkg.main, name: libraryName, format: 'umd', sourcemap: true },
|
|
||||||
{ file: pkg.main, format: 'es', sourcemap: true },
|
|
||||||
],
|
|
||||||
// Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
|
|
||||||
external: ['path'],
|
|
||||||
watch: {
|
|
||||||
include: 'src/**',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
// Allow json resolution
|
|
||||||
json(),
|
|
||||||
// Compile TypeScript files
|
|
||||||
typescript({ useTsconfigDeclarationDir: true }),
|
|
||||||
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
|
|
||||||
commonjs(),
|
|
||||||
// Allow node_modules resolution, so you can use 'external' to control
|
|
||||||
// which external modules to include in the bundle
|
|
||||||
// https://github.com/rollup/rollup-plugin-node-resolve#usage
|
|
||||||
replace({
|
|
||||||
'preventAssignment': true,
|
|
||||||
'node:crypto': 'crypto',
|
|
||||||
'delimiters': ['"', '"'],
|
|
||||||
}),
|
|
||||||
resolve({
|
|
||||||
browser: true,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Resolve source maps to the original source
|
|
||||||
sourceMaps(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: `src/node/index.ts`,
|
|
||||||
output: [{ file: 'dist/node/index.cjs.js', format: 'cjs', sourcemap: true }],
|
|
||||||
// Indicate here external modules you don't wanna include in your bundle (i.e.: 'lodash')
|
|
||||||
external: [
|
|
||||||
'fs/promises',
|
|
||||||
'path',
|
|
||||||
'pacote',
|
|
||||||
'@types/pacote',
|
|
||||||
'@npmcli/arborist',
|
|
||||||
'ulidx',
|
|
||||||
'node-fetch',
|
|
||||||
'fs',
|
|
||||||
'request',
|
|
||||||
'crypto',
|
|
||||||
'url',
|
|
||||||
'http',
|
|
||||||
'os',
|
|
||||||
'util',
|
|
||||||
'child_process',
|
|
||||||
],
|
|
||||||
watch: {
|
|
||||||
include: 'src/node/**',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
// Allow json resolution
|
|
||||||
json(),
|
|
||||||
// Compile TypeScript files
|
|
||||||
typescript({ useTsconfigDeclarationDir: true }),
|
|
||||||
// Allow bundling cjs modules (unlike webpack, rollup doesn't understand cjs)
|
|
||||||
commonjs(),
|
|
||||||
// Allow node_modules resolution, so you can use 'external' to control
|
|
||||||
// which external modules to include in the bundle
|
|
||||||
// https://github.com/rollup/rollup-plugin-node-resolve#usage
|
|
||||||
resolve(),
|
|
||||||
|
|
||||||
// Resolve source maps to the original source
|
|
||||||
sourceMaps(),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@ -1,4 +1,9 @@
|
|||||||
import { DownloadRequest, FileStat, NetworkConfig, SystemInformation } from '../types'
|
import {
|
||||||
|
DownloadRequest,
|
||||||
|
FileStat,
|
||||||
|
NetworkConfig,
|
||||||
|
SystemInformation,
|
||||||
|
} from '../types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a extension module function in main process
|
* Execute a extension module function in main process
|
||||||
@ -9,11 +14,12 @@ import { DownloadRequest, FileStat, NetworkConfig, SystemInformation } from '../
|
|||||||
* @returns Promise<any>
|
* @returns Promise<any>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
const executeOnMain: (extension: string, method: string, ...args: any[]) => Promise<any> = (
|
const executeOnMain: (
|
||||||
extension,
|
extension: string,
|
||||||
method,
|
method: string,
|
||||||
...args
|
...args: any[]
|
||||||
) => globalThis.core?.api?.invokeExtensionFunc(extension, method, ...args)
|
) => Promise<any> = (extension, method, ...args) =>
|
||||||
|
globalThis.core?.api?.invokeExtensionFunc(extension, method, ...args)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads a file from a URL and saves it to the local file system.
|
* Downloads a file from a URL and saves it to the local file system.
|
||||||
@ -23,10 +29,11 @@ const executeOnMain: (extension: string, method: string, ...args: any[]) => Prom
|
|||||||
*
|
*
|
||||||
* @returns {Promise<any>} A promise that resolves when the file is downloaded.
|
* @returns {Promise<any>} A promise that resolves when the file is downloaded.
|
||||||
*/
|
*/
|
||||||
const downloadFile: (downloadRequest: DownloadRequest, network?: NetworkConfig) => Promise<any> = (
|
const downloadFile: (
|
||||||
downloadRequest,
|
downloadRequest: DownloadRequest,
|
||||||
network
|
network?: NetworkConfig
|
||||||
) => globalThis.core?.api?.downloadFile(downloadRequest, network)
|
) => Promise<any> = (downloadRequest, network) =>
|
||||||
|
globalThis.core?.api?.downloadFile(downloadRequest, network)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aborts the download of a specific file.
|
* Aborts the download of a specific file.
|
||||||
@ -41,7 +48,8 @@ const abortDownload: (fileName: string) => Promise<any> = (fileName) =>
|
|||||||
*
|
*
|
||||||
* @returns {Promise<string>} A Promise that resolves with Jan's data folder path.
|
* @returns {Promise<string>} A Promise that resolves with Jan's data folder path.
|
||||||
*/
|
*/
|
||||||
const getJanDataFolderPath = (): Promise<string> => globalThis.core.api?.getJanDataFolderPath()
|
const getJanDataFolderPath = (): Promise<string> =>
|
||||||
|
globalThis.core.api?.getJanDataFolderPath()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the file explorer at a specific path.
|
* Opens the file explorer at a specific path.
|
||||||
@ -64,14 +72,16 @@ const joinPath: (paths: string[]) => Promise<string> = (paths) =>
|
|||||||
* @param path - The file path to retrieve dirname.
|
* @param path - The file path to retrieve dirname.
|
||||||
* @returns {Promise<string>} A promise that resolves the dirname.
|
* @returns {Promise<string>} A promise that resolves the dirname.
|
||||||
*/
|
*/
|
||||||
const dirName: (path: string) => Promise<string> = (path) => globalThis.core.api?.dirName(path)
|
const dirName: (path: string) => Promise<string> = (path) =>
|
||||||
|
globalThis.core.api?.dirName(path)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the basename from an url.
|
* Retrieve the basename from an url.
|
||||||
* @param path - The path to retrieve.
|
* @param path - The path to retrieve.
|
||||||
* @returns {Promise<string>} A promise that resolves with the basename.
|
* @returns {Promise<string>} A promise that resolves with the basename.
|
||||||
*/
|
*/
|
||||||
const baseName: (paths: string) => Promise<string> = (path) => globalThis.core.api?.baseName(path)
|
const baseName: (paths: string) => Promise<string> = (path) =>
|
||||||
|
globalThis.core.api?.baseName(path)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens an external URL in the default web browser.
|
* Opens an external URL in the default web browser.
|
||||||
@ -87,13 +97,15 @@ const openExternalUrl: (url: string) => Promise<any> = (url) =>
|
|||||||
*
|
*
|
||||||
* @returns {Promise<string>} - A promise that resolves with the resource path.
|
* @returns {Promise<string>} - A promise that resolves with the resource path.
|
||||||
*/
|
*/
|
||||||
const getResourcePath: () => Promise<string> = () => globalThis.core.api?.getResourcePath()
|
const getResourcePath: () => Promise<string> = () =>
|
||||||
|
globalThis.core.api?.getResourcePath()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the user's home path.
|
* Gets the user's home path.
|
||||||
* @returns return user's home path
|
* @returns return user's home path
|
||||||
*/
|
*/
|
||||||
const getUserHomePath = (): Promise<string> => globalThis.core.api?.getUserHomePath()
|
const getUserHomePath = (): Promise<string> =>
|
||||||
|
globalThis.core.api?.getUserHomePath()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log to file from browser processes.
|
* Log to file from browser processes.
|
||||||
@ -111,8 +123,10 @@ const log: (message: string, fileName?: string) => void = (message, fileName) =>
|
|||||||
*
|
*
|
||||||
* @returns {Promise<boolean>} - A promise that resolves with a boolean indicating whether the path is a subdirectory.
|
* @returns {Promise<boolean>} - A promise that resolves with a boolean indicating whether the path is a subdirectory.
|
||||||
*/
|
*/
|
||||||
const isSubdirectory: (from: string, to: string) => Promise<boolean> = (from: string, to: string) =>
|
const isSubdirectory: (from: string, to: string) => Promise<boolean> = (
|
||||||
globalThis.core.api?.isSubdirectory(from, to)
|
from: string,
|
||||||
|
to: string
|
||||||
|
) => globalThis.core.api?.isSubdirectory(from, to)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get system information
|
* Get system information
|
||||||
@ -159,5 +173,4 @@ export {
|
|||||||
systemInformation,
|
systemInformation,
|
||||||
showToast,
|
showToast,
|
||||||
dirName,
|
dirName,
|
||||||
FileStat,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export enum ExtensionTypeEnum {
|
|||||||
Model = 'model',
|
Model = 'model',
|
||||||
SystemMonitoring = 'systemMonitoring',
|
SystemMonitoring = 'systemMonitoring',
|
||||||
HuggingFace = 'huggingFace',
|
HuggingFace = 'huggingFace',
|
||||||
|
Engine = 'engine',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtensionType {
|
export interface ExtensionType {
|
||||||
|
|||||||
@ -22,7 +22,9 @@ export function requestInference(
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Accept': model.parameters?.stream ? 'text/event-stream' : 'application/json',
|
'Accept': model.parameters?.stream
|
||||||
|
? 'text/event-stream'
|
||||||
|
: 'application/json',
|
||||||
...headers,
|
...headers,
|
||||||
},
|
},
|
||||||
body: JSON.stringify(requestBody),
|
body: JSON.stringify(requestBody),
|
||||||
@ -47,12 +49,24 @@ export function requestInference(
|
|||||||
}
|
}
|
||||||
// There could be overriden stream parameter in the model
|
// There could be overriden stream parameter in the model
|
||||||
// that is set in request body (transformed payload)
|
// that is set in request body (transformed payload)
|
||||||
if (requestBody?.stream === false || model.parameters?.stream === false) {
|
if (
|
||||||
|
requestBody?.stream === false ||
|
||||||
|
model.parameters?.stream === false
|
||||||
|
) {
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|
if (data.error || data.message) {
|
||||||
|
subscriber.error(data.error ?? data)
|
||||||
|
subscriber.complete()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (transformResponse) {
|
if (transformResponse) {
|
||||||
subscriber.next(transformResponse(data))
|
subscriber.next(transformResponse(data))
|
||||||
} else {
|
} else {
|
||||||
subscriber.next(data.choices[0]?.message?.content ?? '')
|
subscriber.next(
|
||||||
|
data.choices
|
||||||
|
? data.choices[0]?.message?.content
|
||||||
|
: (data.content[0]?.text ?? '')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const stream = response.body
|
const stream = response.body
|
||||||
|
|||||||
110
core/src/browser/extensions/enginesManagement.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import {
|
||||||
|
InferenceEngine,
|
||||||
|
Engines,
|
||||||
|
EngineVariant,
|
||||||
|
EngineReleased,
|
||||||
|
EngineConfig,
|
||||||
|
DefaultEngineVariant,
|
||||||
|
} from '../../types'
|
||||||
|
import { BaseExtension, ExtensionTypeEnum } from '../extension'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Engine management extension. Persists and retrieves engine management.
|
||||||
|
* @abstract
|
||||||
|
* @extends BaseExtension
|
||||||
|
*/
|
||||||
|
export abstract class EngineManagementExtension extends BaseExtension {
|
||||||
|
type(): ExtensionTypeEnum | undefined {
|
||||||
|
return ExtensionTypeEnum.Engine
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns A Promise that resolves to an object of list engines.
|
||||||
|
*/
|
||||||
|
abstract getEngines(): Promise<Engines>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name - Inference engine name.
|
||||||
|
* @returns A Promise that resolves to an array of installed engine.
|
||||||
|
*/
|
||||||
|
abstract getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name - Inference engine name.
|
||||||
|
* @param version - Version of the engine.
|
||||||
|
* @param platform - Optional to sort by operating system. macOS, linux, windows.
|
||||||
|
* @returns A Promise that resolves to an array of latest released engine by version.
|
||||||
|
*/
|
||||||
|
abstract getReleasedEnginesByVersion(
|
||||||
|
name: InferenceEngine,
|
||||||
|
version: string,
|
||||||
|
platform?: string
|
||||||
|
): Promise<EngineReleased[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name - Inference engine name.
|
||||||
|
* @param platform - Optional to sort by operating system. macOS, linux, windows.
|
||||||
|
* @returns A Promise that resolves to an array of latest released engine.
|
||||||
|
*/
|
||||||
|
abstract getLatestReleasedEngine(
|
||||||
|
name: InferenceEngine,
|
||||||
|
platform?: string
|
||||||
|
): Promise<EngineReleased[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name - Inference engine name.
|
||||||
|
* @returns A Promise that resolves to intall of engine.
|
||||||
|
*/
|
||||||
|
abstract installEngine(
|
||||||
|
name: string,
|
||||||
|
engineConfig: EngineConfig
|
||||||
|
): Promise<{ messages: string }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new remote engine
|
||||||
|
* @returns A Promise that resolves to intall of engine.
|
||||||
|
*/
|
||||||
|
abstract addRemoteEngine(
|
||||||
|
engineConfig: EngineConfig
|
||||||
|
): Promise<{ messages: string }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name - Inference engine name.
|
||||||
|
* @returns A Promise that resolves to unintall of engine.
|
||||||
|
*/
|
||||||
|
abstract uninstallEngine(
|
||||||
|
name: InferenceEngine,
|
||||||
|
engineConfig: EngineConfig
|
||||||
|
): Promise<{ messages: string }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name - Inference engine name.
|
||||||
|
* @returns A Promise that resolves to an object of default engine.
|
||||||
|
*/
|
||||||
|
abstract getDefaultEngineVariant(
|
||||||
|
name: InferenceEngine
|
||||||
|
): Promise<DefaultEngineVariant>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @body variant - string
|
||||||
|
* @body version - string
|
||||||
|
* @returns A Promise that resolves to set default engine.
|
||||||
|
*/
|
||||||
|
abstract setDefaultEngineVariant(
|
||||||
|
name: InferenceEngine,
|
||||||
|
engineConfig: EngineConfig
|
||||||
|
): Promise<{ messages: string }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns A Promise that resolves to update engine.
|
||||||
|
*/
|
||||||
|
abstract updateEngine(
|
||||||
|
name: InferenceEngine,
|
||||||
|
engineConfig?: EngineConfig
|
||||||
|
): Promise<{ messages: string }>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns A Promise that resolves to an object of remote models list .
|
||||||
|
*/
|
||||||
|
abstract getRemoteModels(name: InferenceEngine | string): Promise<any>
|
||||||
|
}
|
||||||
@ -28,3 +28,8 @@ export { ModelExtension } from './model'
|
|||||||
* Base AI Engines.
|
* Base AI Engines.
|
||||||
*/
|
*/
|
||||||
export * from './engines'
|
export * from './engines'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Engines Management
|
||||||
|
*/
|
||||||
|
export * from './enginesManagement'
|
||||||
|
|||||||
@ -4,16 +4,6 @@ jest.mock('../../helper', () => ({
|
|||||||
}))
|
}))
|
||||||
import { App } from './app'
|
import { App } from './app'
|
||||||
|
|
||||||
it('should call stopServer', () => {
|
|
||||||
const app = new App()
|
|
||||||
const stopServerMock = jest.fn().mockResolvedValue('Server stopped')
|
|
||||||
jest.mock('@janhq/server', () => ({
|
|
||||||
stopServer: stopServerMock,
|
|
||||||
}))
|
|
||||||
app.stopServer()
|
|
||||||
expect(stopServerMock).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly retrieve basename', () => {
|
it('should correctly retrieve basename', () => {
|
||||||
const app = new App()
|
const app = new App()
|
||||||
const result = app.baseName('/path/to/file.txt')
|
const result = app.baseName('/path/to/file.txt')
|
||||||
@ -23,7 +13,8 @@ it('should correctly retrieve basename', () => {
|
|||||||
it('should correctly identify subdirectories', () => {
|
it('should correctly identify subdirectories', () => {
|
||||||
const app = new App()
|
const app = new App()
|
||||||
const basePath = process.platform === 'win32' ? 'C:\\path\\to' : '/path/to'
|
const basePath = process.platform === 'win32' ? 'C:\\path\\to' : '/path/to'
|
||||||
const subPath = process.platform === 'win32' ? 'C:\\path\\to\\subdir' : '/path/to/subdir'
|
const subPath =
|
||||||
|
process.platform === 'win32' ? 'C:\\path\\to\\subdir' : '/path/to/subdir'
|
||||||
const result = app.isSubdirectory(basePath, subPath)
|
const result = app.isSubdirectory(basePath, subPath)
|
||||||
expect(result).toBe(true)
|
expect(result).toBe(true)
|
||||||
})
|
})
|
||||||
@ -31,7 +22,8 @@ it('should correctly identify subdirectories', () => {
|
|||||||
it('should correctly join multiple paths', () => {
|
it('should correctly join multiple paths', () => {
|
||||||
const app = new App()
|
const app = new App()
|
||||||
const result = app.joinPath(['path', 'to', 'file'])
|
const result = app.joinPath(['path', 'to', 'file'])
|
||||||
const expectedPath = process.platform === 'win32' ? 'path\\to\\file' : 'path/to/file'
|
const expectedPath =
|
||||||
|
process.platform === 'win32' ? 'path\\to\\file' : 'path/to/file'
|
||||||
expect(result).toBe(expectedPath)
|
expect(result).toBe(expectedPath)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -52,5 +44,7 @@ it('should retrieve the directory name from a file path (Unix/Windows)', async (
|
|||||||
it('should retrieve the directory name when using file protocol', async () => {
|
it('should retrieve the directory name when using file protocol', async () => {
|
||||||
const app = new App()
|
const app = new App()
|
||||||
const path = 'file:/models/file.txt'
|
const path = 'file:/models/file.txt'
|
||||||
expect(await app.dirName(path)).toBe(process.platform === 'win32' ? 'app\\models' : 'app/models')
|
expect(await app.dirName(path)).toBe(
|
||||||
|
process.platform === 'win32' ? 'app\\models' : 'app/models'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -44,11 +44,8 @@ export class App implements Processor {
|
|||||||
/**
|
/**
|
||||||
* Checks if the given path is a subdirectory of the given directory.
|
* Checks if the given path is a subdirectory of the given directory.
|
||||||
*
|
*
|
||||||
* @param _event - The IPC event object.
|
|
||||||
* @param from - The path to check.
|
* @param from - The path to check.
|
||||||
* @param to - The directory to check against.
|
* @param to - The directory to check against.
|
||||||
*
|
|
||||||
* @returns {Promise<boolean>} - A promise that resolves with the result.
|
|
||||||
*/
|
*/
|
||||||
isSubdirectory(from: any, to: any) {
|
isSubdirectory(from: any, to: any) {
|
||||||
const rel = relative(from, to)
|
const rel = relative(from, to)
|
||||||
@ -79,26 +76,4 @@ export class App implements Processor {
|
|||||||
async updateAppConfiguration(args: any) {
|
async updateAppConfiguration(args: any) {
|
||||||
await updateAppConfiguration(args)
|
await updateAppConfiguration(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Start Jan API Server.
|
|
||||||
*/
|
|
||||||
async startServer(args?: any) {
|
|
||||||
const { startServer } = require('@janhq/server')
|
|
||||||
return startServer({
|
|
||||||
host: args?.host,
|
|
||||||
port: args?.port,
|
|
||||||
isCorsEnabled: args?.isCorsEnabled,
|
|
||||||
isVerboseEnabled: args?.isVerboseEnabled,
|
|
||||||
prefix: args?.prefix,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop Jan API Server.
|
|
||||||
*/
|
|
||||||
stopServer() {
|
|
||||||
const { stopServer } = require('@janhq/server')
|
|
||||||
return stopServer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,10 @@ export default class Extension {
|
|||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
get specifier() {
|
get specifier() {
|
||||||
return this.origin + (this.installOptions.version ? '@' + this.installOptions.version : '')
|
return (
|
||||||
|
this.origin +
|
||||||
|
(this.installOptions.version ? '@' + this.installOptions.version : '')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,8 +78,10 @@ export default class Extension {
|
|||||||
async getManifest() {
|
async getManifest() {
|
||||||
// Get the package's manifest (package.json object)
|
// Get the package's manifest (package.json object)
|
||||||
try {
|
try {
|
||||||
await import('pacote').then((pacote) => {
|
const pacote = require('pacote')
|
||||||
return pacote.manifest(this.specifier, this.installOptions).then((mnf) => {
|
return pacote
|
||||||
|
.manifest(this.specifier, this.installOptions)
|
||||||
|
.then((mnf: any) => {
|
||||||
// set the Package properties based on the it's manifest
|
// set the Package properties based on the it's manifest
|
||||||
this.name = mnf.name
|
this.name = mnf.name
|
||||||
this.productName = mnf.productName as string | undefined
|
this.productName = mnf.productName as string | undefined
|
||||||
@ -84,9 +89,10 @@ export default class Extension {
|
|||||||
this.main = mnf.main
|
this.main = mnf.main
|
||||||
this.description = mnf.description
|
this.description = mnf.description
|
||||||
})
|
})
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Package ${this.origin} does not contain a valid manifest: ${error}`)
|
throw new Error(
|
||||||
|
`Package ${this.origin} does not contain a valid manifest: ${error}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -103,10 +109,13 @@ export default class Extension {
|
|||||||
await this.getManifest()
|
await this.getManifest()
|
||||||
|
|
||||||
// Install the package in a child folder of the given folder
|
// Install the package in a child folder of the given folder
|
||||||
const pacote = await import('pacote')
|
const pacote = require('pacote')
|
||||||
await pacote.extract(
|
await pacote.extract(
|
||||||
this.specifier,
|
this.specifier,
|
||||||
join(ExtensionManager.instance.getExtensionsPath() ?? '', this.name ?? ''),
|
join(
|
||||||
|
ExtensionManager.instance.getExtensionsPath() ?? '',
|
||||||
|
this.name ?? ''
|
||||||
|
),
|
||||||
this.installOptions
|
this.installOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -169,13 +178,12 @@ export default class Extension {
|
|||||||
* @returns the latest available version if a new version is available or false if not.
|
* @returns the latest available version if a new version is available or false if not.
|
||||||
*/
|
*/
|
||||||
async isUpdateAvailable() {
|
async isUpdateAvailable() {
|
||||||
return import('pacote').then((pacote) => {
|
const pacote = require('pacote')
|
||||||
if (this.origin) {
|
if (this.origin) {
|
||||||
return pacote.manifest(this.origin).then((mnf) => {
|
return pacote.manifest(this.origin).then((mnf: any) => {
|
||||||
return mnf.version !== this.version ? mnf.version : false
|
return mnf.version !== this.version ? mnf.version : false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,28 +1,19 @@
|
|||||||
import { getEngineConfiguration } from './config';
|
import { getAppConfigurations, defaultAppConfig } from './config'
|
||||||
import { getAppConfigurations, defaultAppConfig } from './config';
|
|
||||||
|
|
||||||
import { getJanExtensionsPath } from './config';
|
|
||||||
import { getJanDataFolderPath } from './config';
|
|
||||||
it('should return undefined for invalid engine ID', async () => {
|
|
||||||
const config = await getEngineConfiguration('invalid_engine');
|
|
||||||
expect(config).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
import { getJanExtensionsPath, getJanDataFolderPath } from './config'
|
||||||
|
|
||||||
it('should return default config when CI is e2e', () => {
|
it('should return default config when CI is e2e', () => {
|
||||||
process.env.CI = 'e2e';
|
process.env.CI = 'e2e'
|
||||||
const config = getAppConfigurations();
|
const config = getAppConfigurations()
|
||||||
expect(config).toEqual(defaultAppConfig());
|
expect(config).toEqual(defaultAppConfig())
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
||||||
it('should return extensions path when retrieved successfully', () => {
|
it('should return extensions path when retrieved successfully', () => {
|
||||||
const extensionsPath = getJanExtensionsPath();
|
const extensionsPath = getJanExtensionsPath()
|
||||||
expect(extensionsPath).not.toBeUndefined();
|
expect(extensionsPath).not.toBeUndefined()
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
||||||
it('should return data folder path when retrieved successfully', () => {
|
it('should return data folder path when retrieved successfully', () => {
|
||||||
const dataFolderPath = getJanDataFolderPath();
|
const dataFolderPath = getJanDataFolderPath()
|
||||||
expect(dataFolderPath).not.toBeUndefined();
|
expect(dataFolderPath).not.toBeUndefined()
|
||||||
});
|
})
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { AppConfiguration, SettingComponentProps } from '../../types'
|
import { AppConfiguration } from '../../types'
|
||||||
import { join, resolve } from 'path'
|
import { join, resolve } from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import os from 'os'
|
import os from 'os'
|
||||||
import childProcess from 'child_process'
|
|
||||||
const configurationFileName = 'settings.json'
|
const configurationFileName = 'settings.json'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,7 +18,9 @@ export const getAppConfigurations = (): AppConfiguration => {
|
|||||||
|
|
||||||
if (!fs.existsSync(configurationFile)) {
|
if (!fs.existsSync(configurationFile)) {
|
||||||
// create default app config if we don't have one
|
// create default app config if we don't have one
|
||||||
console.debug(`App config not found, creating default config at ${configurationFile}`)
|
console.debug(
|
||||||
|
`App config not found, creating default config at ${configurationFile}`
|
||||||
|
)
|
||||||
fs.writeFileSync(configurationFile, JSON.stringify(appDefaultConfiguration))
|
fs.writeFileSync(configurationFile, JSON.stringify(appDefaultConfiguration))
|
||||||
return appDefaultConfiguration
|
return appDefaultConfiguration
|
||||||
}
|
}
|
||||||
@ -30,20 +31,28 @@ export const getAppConfigurations = (): AppConfiguration => {
|
|||||||
)
|
)
|
||||||
return appConfigurations
|
return appConfigurations
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to read app config, return default config instead! Err: ${err}`)
|
console.error(
|
||||||
|
`Failed to read app config, return default config instead! Err: ${err}`
|
||||||
|
)
|
||||||
return defaultAppConfig()
|
return defaultAppConfig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getConfigurationFilePath = () =>
|
const getConfigurationFilePath = () =>
|
||||||
join(
|
join(
|
||||||
global.core?.appPath() || process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME'],
|
global.core?.appPath() ||
|
||||||
|
process.env[process.platform == 'win32' ? 'USERPROFILE' : 'HOME'],
|
||||||
configurationFileName
|
configurationFileName
|
||||||
)
|
)
|
||||||
|
|
||||||
export const updateAppConfiguration = (configuration: AppConfiguration): Promise<void> => {
|
export const updateAppConfiguration = (
|
||||||
|
configuration: AppConfiguration
|
||||||
|
): Promise<void> => {
|
||||||
const configurationFile = getConfigurationFilePath()
|
const configurationFile = getConfigurationFilePath()
|
||||||
console.debug('updateAppConfiguration, configurationFile: ', configurationFile)
|
console.debug(
|
||||||
|
'updateAppConfiguration, configurationFile: ',
|
||||||
|
configurationFile
|
||||||
|
)
|
||||||
|
|
||||||
fs.writeFileSync(configurationFile, JSON.stringify(configuration))
|
fs.writeFileSync(configurationFile, JSON.stringify(configuration))
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@ -69,86 +78,6 @@ export const getJanExtensionsPath = (): string => {
|
|||||||
return join(appConfigurations.data_folder, 'extensions')
|
return join(appConfigurations.data_folder, 'extensions')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility function to physical cpu count
|
|
||||||
*
|
|
||||||
* @returns {number} The physical cpu count.
|
|
||||||
*/
|
|
||||||
export const physicalCpuCount = async (): Promise<number> => {
|
|
||||||
const platform = os.platform()
|
|
||||||
try {
|
|
||||||
if (platform === 'linux') {
|
|
||||||
const output = await exec('lscpu -p | egrep -v "^#" | sort -u -t, -k 2,4 | wc -l')
|
|
||||||
return parseInt(output.trim(), 10)
|
|
||||||
} else if (platform === 'darwin') {
|
|
||||||
const output = await exec('sysctl -n hw.physicalcpu_max')
|
|
||||||
return parseInt(output.trim(), 10)
|
|
||||||
} else if (platform === 'win32') {
|
|
||||||
const output = await exec('WMIC CPU Get NumberOfCores')
|
|
||||||
return output
|
|
||||||
.split(os.EOL)
|
|
||||||
.map((line: string) => parseInt(line))
|
|
||||||
.filter((value: number) => !isNaN(value))
|
|
||||||
.reduce((sum: number, number: number) => sum + number, 1)
|
|
||||||
} else {
|
|
||||||
const cores = os.cpus().filter((cpu: any, index: number) => {
|
|
||||||
const hasHyperthreading = cpu.model.includes('Intel')
|
|
||||||
const isOdd = index % 2 === 1
|
|
||||||
return !hasHyperthreading || isOdd
|
|
||||||
})
|
|
||||||
return cores.length
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('Failed to get physical CPU count', err)
|
|
||||||
// Divide by 2 to get rid of hyper threading
|
|
||||||
const coreCount = Math.ceil(os.cpus().length / 2)
|
|
||||||
console.debug('Using node API to get physical CPU count:', coreCount)
|
|
||||||
return coreCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const exec = async (command: string): Promise<string> => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
childProcess.exec(command, { encoding: 'utf8' }, (error, stdout) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
resolve(stdout)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// a hacky way to get the api key. we should comes up with a better
|
|
||||||
// way to handle this
|
|
||||||
export const getEngineConfiguration = async (engineId: string) => {
|
|
||||||
if (engineId !== 'openai' && engineId !== 'groq') return undefined
|
|
||||||
|
|
||||||
const settingDirectoryPath = join(
|
|
||||||
getJanDataFolderPath(),
|
|
||||||
'settings',
|
|
||||||
'@janhq',
|
|
||||||
engineId === 'openai' ? 'inference-openai-extension' : 'inference-groq-extension',
|
|
||||||
'settings.json'
|
|
||||||
)
|
|
||||||
|
|
||||||
const content = fs.readFileSync(settingDirectoryPath, 'utf-8')
|
|
||||||
const settings: SettingComponentProps[] = JSON.parse(content)
|
|
||||||
const apiKeyId = engineId === 'openai' ? 'openai-api-key' : 'groq-api-key'
|
|
||||||
const keySetting = settings.find((setting) => setting.key === apiKeyId)
|
|
||||||
let fullUrl = settings.find((setting) => setting.key === 'chat-completions-endpoint')
|
|
||||||
?.controllerProps.value
|
|
||||||
|
|
||||||
let apiKey = keySetting?.controllerProps.value
|
|
||||||
if (typeof apiKey !== 'string') apiKey = ''
|
|
||||||
if (typeof fullUrl !== 'string') fullUrl = ''
|
|
||||||
|
|
||||||
return {
|
|
||||||
api_key: apiKey,
|
|
||||||
full_url: fullUrl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default app configurations
|
* Default app configurations
|
||||||
* App Data Folder default to Electron's userData
|
* App Data Folder default to Electron's userData
|
||||||
@ -158,7 +87,10 @@ export const getEngineConfiguration = async (engineId: string) => {
|
|||||||
*/
|
*/
|
||||||
export const defaultAppConfig = (): AppConfiguration => {
|
export const defaultAppConfig = (): AppConfiguration => {
|
||||||
const { app } = require('electron')
|
const { app } = require('electron')
|
||||||
const defaultJanDataFolder = join(app?.getPath('userData') ?? os?.homedir() ?? '', 'data')
|
const defaultJanDataFolder = join(
|
||||||
|
app?.getPath('userData') ?? os?.homedir() ?? '',
|
||||||
|
'data'
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
data_folder:
|
data_folder:
|
||||||
process.env.CI === 'e2e'
|
process.env.CI === 'e2e'
|
||||||
|
|||||||
@ -1,15 +1,9 @@
|
|||||||
import { getSystemResourceInfo } from './resource';
|
import { getSystemResourceInfo } from './resource'
|
||||||
|
|
||||||
it('should return the correct system resource information with a valid CPU count', async () => {
|
it('should return the correct system resource information with a valid CPU count', async () => {
|
||||||
const mockCpuCount = 4;
|
const result = await getSystemResourceInfo()
|
||||||
jest.spyOn(require('./config'), 'physicalCpuCount').mockResolvedValue(mockCpuCount);
|
|
||||||
const logSpy = jest.spyOn(require('./logger'), 'log').mockImplementation(() => {});
|
|
||||||
|
|
||||||
const result = await getSystemResourceInfo();
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
numCpuPhysicalCore: mockCpuCount,
|
|
||||||
memAvailable: 0,
|
memAvailable: 0,
|
||||||
});
|
})
|
||||||
expect(logSpy).toHaveBeenCalledWith(`[CORTEX]::CPU information - ${mockCpuCount}`);
|
})
|
||||||
});
|
|
||||||
|
|||||||
@ -1,13 +1,7 @@
|
|||||||
import { SystemResourceInfo } from '../../types'
|
import { SystemResourceInfo } from '../../types'
|
||||||
import { physicalCpuCount } from './config'
|
|
||||||
import { log } from './logger'
|
|
||||||
|
|
||||||
export const getSystemResourceInfo = async (): Promise<SystemResourceInfo> => {
|
export const getSystemResourceInfo = async (): Promise<SystemResourceInfo> => {
|
||||||
const cpu = await physicalCpuCount()
|
|
||||||
log(`[CORTEX]::CPU information - ${cpu}`)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
numCpuPhysicalCore: cpu,
|
|
||||||
memAvailable: 0, // TODO: this should not be 0
|
memAvailable: 0, // TODO: this should not be 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,10 @@ export enum NativeRoute {
|
|||||||
|
|
||||||
quickAskSizeUpdated = 'quickAskSizeUpdated',
|
quickAskSizeUpdated = 'quickAskSizeUpdated',
|
||||||
ackDeepLink = 'ackDeepLink',
|
ackDeepLink = 'ackDeepLink',
|
||||||
factoryReset = 'factoryReset'
|
factoryReset = 'factoryReset',
|
||||||
|
|
||||||
|
startServer = 'startServer',
|
||||||
|
stopServer = 'stopServer',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,8 +44,6 @@ export enum AppRoute {
|
|||||||
dirName = 'dirName',
|
dirName = 'dirName',
|
||||||
isSubdirectory = 'isSubdirectory',
|
isSubdirectory = 'isSubdirectory',
|
||||||
baseName = 'baseName',
|
baseName = 'baseName',
|
||||||
startServer = 'startServer',
|
|
||||||
stopServer = 'stopServer',
|
|
||||||
log = 'log',
|
log = 'log',
|
||||||
systemInformation = 'systemInformation',
|
systemInformation = 'systemInformation',
|
||||||
showToast = 'showToast',
|
showToast = 'showToast',
|
||||||
|
|||||||
54
core/src/types/engine/index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { InferenceEngine } from '../../types'
|
||||||
|
|
||||||
|
export type Engines = {
|
||||||
|
[key in InferenceEngine]: (EngineVariant & EngineConfig)[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EngineMetadata = {
|
||||||
|
get_models_url?: string
|
||||||
|
header_template?: string
|
||||||
|
transform_req?: {
|
||||||
|
chat_completions?: {
|
||||||
|
url?: string
|
||||||
|
template?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transform_resp?: {
|
||||||
|
chat_completions?: {
|
||||||
|
template?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EngineVariant = {
|
||||||
|
engine: InferenceEngine
|
||||||
|
name: string
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DefaultEngineVariant = {
|
||||||
|
engine: InferenceEngine
|
||||||
|
variant: string
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EngineReleased = {
|
||||||
|
created_at: string
|
||||||
|
download_count: number
|
||||||
|
name: string
|
||||||
|
size: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EngineConfig = {
|
||||||
|
engine?: string
|
||||||
|
version?: string
|
||||||
|
variant?: string
|
||||||
|
type?: string
|
||||||
|
url?: string
|
||||||
|
api_key?: string
|
||||||
|
metadata?: EngineMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EngineEvent {
|
||||||
|
OnEngineUpdate = 'OnEngineUpdate',
|
||||||
|
}
|
||||||
@ -10,3 +10,4 @@ export * from './huggingface'
|
|||||||
export * from './miscellaneous'
|
export * from './miscellaneous'
|
||||||
export * from './api'
|
export * from './api'
|
||||||
export * from './setting'
|
export * from './setting'
|
||||||
|
export * from './engine'
|
||||||
|
|||||||
@ -32,9 +32,8 @@ export type ThreadMessage = {
|
|||||||
completed_at: number
|
completed_at: number
|
||||||
/** The additional metadata of this message. **/
|
/** The additional metadata of this message. **/
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
|
/** Type of the message */
|
||||||
type?: string
|
type?: string
|
||||||
|
|
||||||
/** The error code which explain what error type. Used in conjunction with MessageStatus.Error */
|
/** The error code which explain what error type. Used in conjunction with MessageStatus.Error */
|
||||||
error_code?: ErrorCode
|
error_code?: ErrorCode
|
||||||
}
|
}
|
||||||
@ -72,6 +71,10 @@ export type MessageRequest = {
|
|||||||
// TODO: deprecate threadId field
|
// TODO: deprecate threadId field
|
||||||
thread?: Thread
|
thread?: Thread
|
||||||
|
|
||||||
|
/** Engine name to process */
|
||||||
|
engine?: string
|
||||||
|
|
||||||
|
/** Message type */
|
||||||
type?: string
|
type?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +150,9 @@ export interface Attachment {
|
|||||||
/**
|
/**
|
||||||
* The tools to add this file to.
|
* The tools to add this file to.
|
||||||
*/
|
*/
|
||||||
tools?: Array<CodeInterpreterTool | Attachment.AssistantToolsFileSearchTypeOnly>
|
tools?: Array<
|
||||||
|
CodeInterpreterTool | Attachment.AssistantToolsFileSearchTypeOnly
|
||||||
|
>
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace Attachment {
|
export namespace Attachment {
|
||||||
@ -166,5 +171,10 @@ export interface IncompleteDetails {
|
|||||||
/**
|
/**
|
||||||
* The reason the message is incomplete.
|
* The reason the message is incomplete.
|
||||||
*/
|
*/
|
||||||
reason: 'content_filter' | 'max_tokens' | 'run_cancelled' | 'run_expired' | 'run_failed'
|
reason:
|
||||||
|
| 'content_filter'
|
||||||
|
| 'max_tokens'
|
||||||
|
| 'run_cancelled'
|
||||||
|
| 'run_expired'
|
||||||
|
| 'run_failed'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
export type SystemResourceInfo = {
|
export type SystemResourceInfo = {
|
||||||
numCpuPhysicalCore: number
|
|
||||||
memAvailable: number
|
memAvailable: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,11 @@ export type SettingComponentProps = {
|
|||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
controllerType: ControllerType
|
controllerType: ControllerType
|
||||||
controllerProps: SliderComponentProps | CheckboxComponentProps | InputComponentProps
|
controllerProps:
|
||||||
|
| SliderComponentProps
|
||||||
|
| CheckboxComponentProps
|
||||||
|
| InputComponentProps
|
||||||
|
| DropdownComponentProps
|
||||||
|
|
||||||
extensionName?: string
|
extensionName?: string
|
||||||
requireModelReload?: boolean
|
requireModelReload?: boolean
|
||||||
@ -12,13 +16,26 @@ export type SettingComponentProps = {
|
|||||||
|
|
||||||
export type ConfigType = 'runtime' | 'setting'
|
export type ConfigType = 'runtime' | 'setting'
|
||||||
|
|
||||||
export type ControllerType = 'slider' | 'checkbox' | 'input' | 'tag'
|
export type ControllerType =
|
||||||
|
| 'slider'
|
||||||
|
| 'checkbox'
|
||||||
|
| 'input'
|
||||||
|
| 'tag'
|
||||||
|
| 'dropdown'
|
||||||
|
|
||||||
export type InputType = 'password' | 'text' | 'email' | 'number' | 'tel' | 'url'
|
export type InputType =
|
||||||
|
| 'password'
|
||||||
|
| 'text'
|
||||||
|
| 'email'
|
||||||
|
| 'number'
|
||||||
|
| 'tel'
|
||||||
|
| 'url'
|
||||||
|
| 'dropdown'
|
||||||
|
|
||||||
const InputActions = ['unobscure', 'copy'] as const
|
const InputActions = ['unobscure', 'copy'] as const
|
||||||
export type InputActionsTuple = typeof InputActions
|
export type InputActionsTuple = typeof InputActions
|
||||||
export type InputAction = InputActionsTuple[number]
|
export type InputAction = InputActionsTuple[number]
|
||||||
|
export type DropdownOption = { name: string; value: string }
|
||||||
|
|
||||||
export type InputComponentProps = {
|
export type InputComponentProps = {
|
||||||
placeholder: string
|
placeholder: string
|
||||||
@ -38,3 +55,9 @@ export type SliderComponentProps = {
|
|||||||
export type CheckboxComponentProps = {
|
export type CheckboxComponentProps = {
|
||||||
value: boolean
|
value: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DropdownComponentProps = {
|
||||||
|
value: string
|
||||||
|
type?: InputType
|
||||||
|
options?: DropdownOption[]
|
||||||
|
}
|
||||||
|
|||||||
@ -11,11 +11,10 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"declarationDir": "dist/types",
|
"declarationDir": "dist/types",
|
||||||
"outDir": "dist/lib",
|
"outDir": "dist",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"types": ["@types/jest"],
|
"types": ["jest", "node"]
|
||||||
"resolveJsonModule": true
|
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["**/*.test.ts"]
|
"exclude": ["src/**/*.test.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,5 +58,5 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
"packageManager": "yarn@1.22.22"
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
docs/public/assets/images/changelog/jan-v0.5.10.gif
Normal file
|
After Width: | Height: | Size: 4.9 MiB |
BIN
docs/public/assets/images/changelog/jan-v0.5.11.gif
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
docs/public/assets/images/changelog/jan-v0.5.12.gif
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
docs/public/assets/images/changelog/jan-v0.5.8.gif
Normal file
|
After Width: | Height: | Size: 7.3 MiB |
|
Before Width: | Height: | Size: 20 MiB After Width: | Height: | Size: 18 MiB |
@ -93,7 +93,7 @@
|
|||||||
<url><loc>https://jan.ai/docs/desktop/windows</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/desktop/windows</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
<url><loc>https://jan.ai/docs/error-codes</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/error-codes</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
<url><loc>https://jan.ai/docs/extensions</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/extensions</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
<url><loc>https://jan.ai/docs/installing-extension</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/install-extensions</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
<url><loc>https://jan.ai/docs/models</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/models</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
<url><loc>https://jan.ai/docs/models/manage-models</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/models/manage-models</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
<url><loc>https://jan.ai/docs/models/model-parameters</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
<url><loc>https://jan.ai/docs/models/model-parameters</loc><lastmod>2024-09-09T08:19:45.722Z</lastmod><changefreq>daily</changefreq><priority>1</priority></url>
|
||||||
|
|||||||
25
docs/src/pages/changelog/2024-11-22-jan-bugs.mdx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "Model downloads & running issues fixed"
|
||||||
|
version: 0.5.9
|
||||||
|
description: "Jan v0.5.9 is here: fixing what needed fixing."
|
||||||
|
date: 2024-11-22
|
||||||
|
ogImage: "/assets/images/changelog/jan-v0.5.9.gif"
|
||||||
|
---
|
||||||
|
|
||||||
|
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
|
||||||
|
|
||||||
|
<ChangelogHeader title= "Jan v0.5.9 is here:fixing what needed fixing" date="2024-11-22" ogImage= "/assets/images/changelog/jan-v0.5.9.gif" />
|
||||||
|
|
||||||
|
Jan v0.5.9 is here: fixing what needed fixing
|
||||||
|
|
||||||
|
### Highlights 🎉
|
||||||
|
|
||||||
|
- Model downloads & running issues fixed
|
||||||
|
- Document upload bugs resolved
|
||||||
|
- System glitches addressed: Factory Reset, HTTP Proxy, Hugging Face tokens
|
||||||
|
- Fixed issues with code blocks in streaming responses
|
||||||
|
- Improved the UX of the Local API Server page
|
||||||
|
|
||||||
|
Update your product or download the latest: https://jan.ai
|
||||||
|
|
||||||
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.9).
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "Jan supports Qwen2.5-Coder 14B & 32B"
|
||||||
|
version: 0.5.8
|
||||||
|
description: "Jan v0.5.8 is out: Jan supports Qwen2.5-Coder 14B & 32B through Cortex"
|
||||||
|
date: 2024-11-14
|
||||||
|
ogImage: "/assets/images/changelog/jan-v0.5.8.gif"
|
||||||
|
---
|
||||||
|
|
||||||
|
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
|
||||||
|
|
||||||
|
<ChangelogHeader title= "Jan supports Qwen2.5-Coder 14B & 32B" date="2024-11-14" ogImage= "/assets/images/changelog/jan-v0.5.7.gif" />
|
||||||
|
|
||||||
|
Jan v0.5.8 is out: Jan supports Qwen2.5-Coder 14B & 32B through Cortex
|
||||||
|
|
||||||
|
### Highlights 🎉
|
||||||
|
|
||||||
|
- A new engine: Jan now run models via [Cortex](https://cortex.so)
|
||||||
|
- SupportsAlibaba_Qwen's Coder 14B & 32B Support
|
||||||
|
- Supports markdown rendering on user messages
|
||||||
|
|
||||||
|
and various UI/UX enhancements 💫
|
||||||
|
|
||||||
|
Update your product or download the latest: https://jan.ai
|
||||||
|
|
||||||
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.8).
|
||||||
22
docs/src/pages/changelog/2024-12-03-jan-is-faster.mdx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
title: "Jan v0.5.10 is live"
|
||||||
|
version: 0.5.10
|
||||||
|
description: "Jan is faster, smoother, and more reliable."
|
||||||
|
date: 2024-12-03
|
||||||
|
ogImage: "/assets/images/changelog/jan-v0.5.10.gif"
|
||||||
|
---
|
||||||
|
|
||||||
|
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
|
||||||
|
|
||||||
|
<ChangelogHeader title= "Jan v0.5.10: Jan is faster, smoother, and more reliable." date="2024-12-03" ogImage= "/assets/images/changelog/jan-v0.5.10.gif" />
|
||||||
|
|
||||||
|
Jan v0.5.10 is live: Jan is faster, smoother, and more reliable.
|
||||||
|
|
||||||
|
### Highlights 🎉
|
||||||
|
|
||||||
|
- Resolved model startup issues, memory leaks, and improved token limits
|
||||||
|
- Clearer error messages and subtle UX improvements
|
||||||
|
|
||||||
|
Update your product or download the latest: https://jan.ai
|
||||||
|
|
||||||
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.10).
|
||||||
26
docs/src/pages/changelog/2024-12-05-jan-hot-fix-mac.mdx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
title: "Jan v0.5.11 is here!"
|
||||||
|
version: 0.5.11
|
||||||
|
description: "Critical issues fixed, Mac installation updated."
|
||||||
|
date: 2024-12-05
|
||||||
|
ogImage: "/assets/images/changelog/jan-v0.5.11.gif"
|
||||||
|
---
|
||||||
|
|
||||||
|
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
|
||||||
|
|
||||||
|
<ChangelogHeader title= "Jan v0.5.11: Jan is faster, smoother, and more reliable." date="2024-12-05" ogImage= "/assets/images/changelog/jan-v0.5.11.gif" />
|
||||||
|
|
||||||
|
Jan v0.5.11 is here - critical issues fixed, Mac installation updated.
|
||||||
|
|
||||||
|
### Highlights 🎉
|
||||||
|
|
||||||
|
- Crashes (markdown & code highlighting)
|
||||||
|
- Thread switching & auto-scroll
|
||||||
|
- Syntax highlighting bugs
|
||||||
|
- API issues (Anthropic, OpenRouter)
|
||||||
|
- Title glitches with special characters
|
||||||
|
- Model settings inconsistencies
|
||||||
|
|
||||||
|
Update your product or download the latest: https://jan.ai
|
||||||
|
|
||||||
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.11).
|
||||||
28
docs/src/pages/changelog/2024-12-30-jan-new-privacy.mdx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
title: "Jan gives you full control over your privacy"
|
||||||
|
version: 0.5.12
|
||||||
|
description: "Improved Privacy settings to give full control over analytics."
|
||||||
|
date: 2024-12-30
|
||||||
|
ogImage: "/assets/images/changelog/jan-v0.5.12.gif"
|
||||||
|
---
|
||||||
|
|
||||||
|
import ChangelogHeader from "@/components/Changelog/ChangelogHeader"
|
||||||
|
|
||||||
|
<ChangelogHeader title= "Jan v0.5.12: Improved Privacy settings to give full control over analytics." date="2024-12-30" ogImage= "/assets/images/changelog/jan-v0.5.12.gif" />
|
||||||
|
|
||||||
|
Jan v0.5.11 is here - critical issues fixed, Mac installation updated.
|
||||||
|
|
||||||
|
### Highlights 🎉
|
||||||
|
|
||||||
|
- Updated privacy settings with opt-in/out options for Jan Analytics
|
||||||
|
- Adjustable chat width
|
||||||
|
- The right sidebar and input box are now optimized for new users
|
||||||
|
|
||||||
|
### Fixes 💫
|
||||||
|
- Updated privacy settings with opt-in/out options for Jan Analytics
|
||||||
|
- Adjustable chat width
|
||||||
|
- The right sidebar and input box are now optimized for new users
|
||||||
|
|
||||||
|
Update your product or download the latest: https://jan.ai
|
||||||
|
|
||||||
|
For more details, see the [GitHub release notes](https://github.com/janhq/jan/releases/tag/v0.5.12).
|
||||||
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 972 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 721 KiB |
|
Before Width: | Height: | Size: 900 KiB |
|
Before Width: | Height: | Size: 658 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 763 KiB |
|
Before Width: | Height: | Size: 1000 KiB |
|
Before Width: | Height: | Size: 788 KiB |
|
Before Width: | Height: | Size: 908 KiB |
|
Before Width: | Height: | Size: 732 KiB |
|
Before Width: | Height: | Size: 627 KiB |
|
Before Width: | Height: | Size: 345 KiB |
|
Before Width: | Height: | Size: 782 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 907 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
BIN
docs/src/pages/docs/_assets/anthropic.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
docs/src/pages/docs/_assets/api-server.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 106 KiB |
BIN
docs/src/pages/docs/_assets/assistant-01.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 271 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 462 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
BIN
docs/src/pages/docs/_assets/cohere.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 876 KiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 733 KiB |
|
Before Width: | Height: | Size: 588 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 3.4 MiB |
|
Before Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 928 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 714 KiB |
|
Before Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 151 KiB |