Merge branch 'dev' into LazyYuuki-patch-1

This commit is contained in:
Bui Quang Huy 2025-09-15 13:07:11 +08:00 committed by GitHub
commit f02f001266
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 30 additions and 38 deletions

1
.gitignore vendored
View File

@ -13,6 +13,7 @@ core/lib/**
.yarnrc .yarnrc
*.tsbuildinfo *.tsbuildinfo
test_results.html test_results.html
pre-install
# docs # docs
docs/yarn.lock docs/yarn.lock

View File

@ -3,7 +3,7 @@
* Handles API requests to Jan backend for models and chat completions * Handles API requests to Jan backend for models and chat completions
*/ */
import { JanAuthService } from '../shared/auth' import { getSharedAuthService, JanAuthService } from '../shared'
import { JanModel, janProviderStore } from './store' import { JanModel, janProviderStore } from './store'
// JAN_API_BASE is defined in vite.config.ts // JAN_API_BASE is defined in vite.config.ts
@ -77,7 +77,7 @@ export class JanApiClient {
private authService: JanAuthService private authService: JanAuthService
private constructor() { private constructor() {
this.authService = JanAuthService.getInstance() this.authService = getSharedAuthService()
} }
static getInstance(): JanApiClient { static getInstance(): JanApiClient {
@ -216,12 +216,9 @@ export class JanApiClient {
async initialize(): Promise<void> { async initialize(): Promise<void> {
try { try {
await this.authService.initialize()
janProviderStore.setAuthenticated(true) janProviderStore.setAuthenticated(true)
// Fetch initial models // Fetch initial models
await this.getModels() await this.getModels()
console.log('Jan API client initialized successfully') console.log('Jan API client initialized successfully')
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to initialize API client' const errorMessage = error instanceof Error ? error.message : 'Failed to initialize API client'

View File

@ -5,7 +5,7 @@
*/ */
import { MCPExtension, MCPTool, MCPToolCallResult } from '@janhq/core' import { MCPExtension, MCPTool, MCPToolCallResult } from '@janhq/core'
import { JanAuthService } from '../shared/auth' import { getSharedAuthService, JanAuthService } from '../shared'
import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js' import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
import { JanMCPOAuthProvider } from './oauth-provider' import { JanMCPOAuthProvider } from './oauth-provider'
@ -30,14 +30,12 @@ export default class MCPExtensionWeb extends MCPExtension {
version?: string version?: string
) { ) {
super(url, name, productName, active, description, version) super(url, name, productName, active, description, version)
this.authService = JanAuthService.getInstance() this.authService = getSharedAuthService()
this.oauthProvider = new JanMCPOAuthProvider(this.authService) this.oauthProvider = new JanMCPOAuthProvider(this.authService)
} }
async onLoad(): Promise<void> { async onLoad(): Promise<void> {
try { try {
// Initialize authentication first
await this.authService.initialize()
// Initialize MCP client with OAuth // Initialize MCP client with OAuth
await this.initializeMCPClient() await this.initializeMCPClient()
// Then fetch tools // Then fetch tools

View File

@ -20,21 +20,13 @@ const AUTH_STORAGE_KEY = 'jan_auth_tokens'
const TOKEN_EXPIRY_BUFFER = 60 * 1000 // 1 minute buffer before actual expiry const TOKEN_EXPIRY_BUFFER = 60 * 1000 // 1 minute buffer before actual expiry
export class JanAuthService { export class JanAuthService {
private static instance: JanAuthService
private tokens: AuthTokens | null = null private tokens: AuthTokens | null = null
private tokenExpiryTime: number = 0 private tokenExpiryTime: number = 0
private constructor() { constructor() {
this.loadTokensFromStorage() this.loadTokensFromStorage()
} }
static getInstance(): JanAuthService {
if (!JanAuthService.instance) {
JanAuthService.instance = new JanAuthService()
}
return JanAuthService.instance
}
private loadTokensFromStorage(): void { private loadTokensFromStorage(): void {
try { try {
const storedTokens = localStorage.getItem(AUTH_STORAGE_KEY) const storedTokens = localStorage.getItem(AUTH_STORAGE_KEY)
@ -169,16 +161,6 @@ export class JanAuthService {
return this.tokens.access_token return this.tokens.access_token
} }
async initialize(): Promise<void> {
try {
await this.getValidAccessToken()
console.log('Jan auth service initialized successfully')
} catch (error) {
console.error('Failed to initialize Jan auth service:', error)
throw error
}
}
async getAuthHeader(): Promise<{ Authorization: string }> { async getAuthHeader(): Promise<{ Authorization: string }> {
const token = await this.getValidAccessToken() const token = await this.getValidAccessToken()
return { return {
@ -218,3 +200,20 @@ export class JanAuthService {
this.clearTokens() this.clearTokens()
} }
} }
declare global {
interface Window {
janAuthService?: JanAuthService
}
}
/**
* Gets or creates the shared JanAuthService instance on the window object
* This ensures all extensions use the same auth service instance
*/
export function getSharedAuthService(): JanAuthService {
if (!window.janAuthService) {
window.janAuthService = new JanAuthService()
}
return window.janAuthService
}

View File

@ -1,3 +1,3 @@
export { getSharedDB } from './db' export { getSharedDB } from './db'
export { JanAuthService } from './auth' export { JanAuthService, getSharedAuthService } from './auth'
export type { AuthTokens, AuthResponse } from './auth' export type { AuthTokens, AuthResponse } from './auth'

View File

View File

@ -231,11 +231,6 @@ async function main() {
console.log('Downloads completed.') console.log('Downloads completed.')
} }
// Ensure the downloads directory exists
if (!fs.existsSync('downloads')) {
fs.mkdirSync('downloads')
}
main().catch((err) => { main().catch((err) => {
console.error('Error:', err) console.error('Error:', err)
process.exit(1) process.exit(1)

View File

@ -193,6 +193,7 @@ export const sendCompletion = async (
if ( if (
thread.model.id && thread.model.id &&
models[providerName]?.models !== true && // Skip if provider accepts any model (models: true)
!Object.values(models[providerName]).flat().includes(thread.model.id) && !Object.values(models[providerName]).flat().includes(thread.model.id) &&
!tokenJS.extendedModelExist(providerName as any, thread.model.id) && !tokenJS.extendedModelExist(providerName as any, thread.model.id) &&
provider.provider !== 'llamacpp' provider.provider !== 'llamacpp'
@ -396,9 +397,12 @@ export const postMessageProcessing = async (
let toolParameters = {} let toolParameters = {}
if (toolCall.function.arguments.length) { if (toolCall.function.arguments.length) {
try { try {
console.log('Raw tool arguments:', toolCall.function.arguments)
toolParameters = JSON.parse(toolCall.function.arguments) toolParameters = JSON.parse(toolCall.function.arguments)
console.log('Parsed tool parameters:', toolParameters)
} catch (error) { } catch (error) {
console.error('Failed to parse tool arguments:', error) console.error('Failed to parse tool arguments:', error)
console.error('Raw arguments that failed:', toolCall.function.arguments)
} }
} }
const approved = const approved =
@ -414,9 +418,7 @@ export const postMessageProcessing = async (
const { promise, cancel } = getServiceHub().mcp().callToolWithCancellation({ const { promise, cancel } = getServiceHub().mcp().callToolWithCancellation({
toolName: toolCall.function.name, toolName: toolCall.function.name,
arguments: toolCall.function.arguments.length arguments: toolCall.function.arguments.length ? toolParameters : {},
? JSON.parse(toolCall.function.arguments)
: {},
}) })
useAppState.getState().setCancelToolCall(cancel) useAppState.getState().setCancelToolCall(cancel)

View File

@ -114,7 +114,7 @@ const AppLayout = () => {
{/* Fake absolute panel top to enable window drag */} {/* Fake absolute panel top to enable window drag */}
<div className="absolute w-full h-10 z-10" data-tauri-drag-region /> <div className="absolute w-full h-10 z-10" data-tauri-drag-region />
<DialogAppUpdater /> <DialogAppUpdater />
<BackendUpdater /> {PlatformFeatures[PlatformFeature.LOCAL_INFERENCE] && <BackendUpdater />}
{/* Use ResizablePanelGroup only on larger screens */} {/* Use ResizablePanelGroup only on larger screens */}
{!isSmallScreen && isLeftPanelOpen ? ( {!isSmallScreen && isLeftPanelOpen ? (