feat: rotate api token for each run (#4820)

* feat: rotate api token for each run

* chore: correct github repo url

* chore: correct github api url
This commit is contained in:
Louis 2025-03-20 21:41:41 +07:00 committed by GitHub
parent 431e4b00dc
commit fd2d23869c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 122 additions and 118 deletions

View File

@ -33,6 +33,8 @@ export enum NativeRoute {
stopServer = 'stopServer', stopServer = 'stopServer',
appUpdateDownload = 'appUpdateDownload', appUpdateDownload = 'appUpdateDownload',
appToken = 'appToken',
} }
/** /**

View File

@ -317,4 +317,11 @@ export function handleAppIPCs() {
const { stopServer } = require('@janhq/server') const { stopServer } = require('@janhq/server')
return stopServer() return stopServer()
}) })
/**
* Handles the "appToken" IPC message to generate a random app ID.
*/
ipcMain.handle(NativeRoute.appToken, async (_event): Promise<string> => {
return process.env.appToken ?? 'cortex.cpp'
})
} }

View File

@ -29,6 +29,7 @@ import { trayManager } from './managers/tray'
import { logSystemInfo } from './utils/system' import { logSystemInfo } from './utils/system'
import { registerGlobalShortcuts } from './utils/shortcut' import { registerGlobalShortcuts } from './utils/shortcut'
import { registerLogger } from './utils/logger' import { registerLogger } from './utils/logger'
import { randomBytes } from 'crypto'
const preloadPath = join(__dirname, 'preload.js') const preloadPath = join(__dirname, 'preload.js')
const preloadQuickAskPath = join(__dirname, 'preload.quickask.js') const preloadQuickAskPath = join(__dirname, 'preload.quickask.js')
@ -56,6 +57,10 @@ const createMainWindow = () => {
windowManager.createMainWindow(preloadPath, startUrl) windowManager.createMainWindow(preloadPath, startUrl)
} }
// Generate a random token for the app
// This token is used for authentication when making request to cortex.cpp server
process.env.appToken = randomBytes(16).toString('hex')
app app
.whenReady() .whenReady()
.then(() => { .then(() => {

View File

@ -28,7 +28,6 @@ export default defineConfig([
}, },
define: { define: {
CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
platform: 'node', platform: 'node',
}, },

View File

@ -1,4 +1,3 @@
declare const NODE: string declare const NODE: string
declare const VERSION: string declare const VERSION: string
declare const CORTEX_API_URL: string declare const CORTEX_API_URL: string
declare const CORTEX_API_KEY: string

View File

@ -23,11 +23,16 @@ export class Retrieval {
constructor(chunkSize: number = 4000, chunkOverlap: number = 200) { constructor(chunkSize: number = 4000, chunkOverlap: number = 200) {
this.updateTextSplitter(chunkSize, chunkOverlap) this.updateTextSplitter(chunkSize, chunkOverlap)
this.initialize()
}
private async initialize() {
const apiKey = await window.core?.api.appToken() ?? 'cortex.cpp'
// declare time-weighted retriever and storage // declare time-weighted retriever and storage
this.timeWeightedVectorStore = new MemoryVectorStore( this.timeWeightedVectorStore = new MemoryVectorStore(
new OpenAIEmbeddings( new OpenAIEmbeddings(
{ openAIApiKey: CORTEX_API_KEY }, { openAIApiKey: apiKey },
{ basePath: `${CORTEX_API_URL}/v1` } { basePath: `${CORTEX_API_URL}/v1` }
) )
) )
@ -47,9 +52,10 @@ export class Retrieval {
}) })
} }
public updateEmbeddingEngine(model: string, engine: string): void { public async updateEmbeddingEngine(model: string, engine: string) {
const apiKey = await window.core?.api.appToken() ?? 'cortex.cpp'
this.embeddingModel = new OpenAIEmbeddings( this.embeddingModel = new OpenAIEmbeddings(
{ openAIApiKey: CORTEX_API_KEY, model }, { openAIApiKey: apiKey, model },
// TODO: Raw settings // TODO: Raw settings
{ basePath: `${CORTEX_API_URL}/v1` } { basePath: `${CORTEX_API_URL}/v1` }
) )

View File

@ -9,6 +9,5 @@ export default defineConfig({
platform: 'browser', platform: 'browser',
define: { define: {
API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}) })

View File

@ -1,5 +1,4 @@
declare const API_URL: string declare const API_URL: string
declare const CORTEX_API_KEY: string
interface Core { interface Core {
api: APIFunctions api: APIFunctions

View File

@ -4,7 +4,7 @@ import {
ThreadAssistantInfo, ThreadAssistantInfo,
ThreadMessage, ThreadMessage,
} from '@janhq/core' } from '@janhq/core'
import ky from 'ky' import ky, { KyInstance } from 'ky'
import PQueue from 'p-queue' import PQueue from 'p-queue'
type ThreadList = { type ThreadList = {
@ -26,17 +26,18 @@ export default class CortexConversationalExtension extends ConversationalExtensi
* Extended API instance for making requests to the Cortex API. * Extended API instance for making requests to the Cortex API.
* @returns * @returns
*/ */
api = () => api: KyInstance
ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
*/ */
async onLoad() { async onLoad() {
const apiKey = await window.core?.api.appToken() ?? 'cortex.cpp'
this.api = ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
this.queue.add(() => this.healthz()) this.queue.add(() => this.healthz())
} }
@ -50,7 +51,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async listThreads(): Promise<Thread[]> { async listThreads(): Promise<Thread[]> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get('v1/threads?limit=-1') .get('v1/threads?limit=-1')
.json<ThreadList>() .json<ThreadList>()
.then((e) => e.data) .then((e) => e.data)
@ -63,7 +64,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async createThread(thread: Thread): Promise<Thread> { async createThread(thread: Thread): Promise<Thread> {
return this.queue.add(() => return this.queue.add(() =>
this.api().post('v1/threads', { json: thread }).json<Thread>() this.api.post('v1/threads', { json: thread }).json<Thread>()
) as Promise<Thread> ) as Promise<Thread>
} }
@ -74,7 +75,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
async modifyThread(thread: Thread): Promise<void> { async modifyThread(thread: Thread): Promise<void> {
return this.queue return this.queue
.add(() => .add(() =>
this.api().patch(`v1/threads/${thread.id}`, { json: thread }) this.api.patch(`v1/threads/${thread.id}`, { json: thread })
) )
.then() .then()
} }
@ -85,7 +86,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async deleteThread(threadId: string): Promise<void> { async deleteThread(threadId: string): Promise<void> {
return this.queue return this.queue
.add(() => this.api().delete(`v1/threads/${threadId}`)) .add(() => this.api.delete(`v1/threads/${threadId}`))
.then() .then()
} }
@ -96,7 +97,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async createMessage(message: ThreadMessage): Promise<ThreadMessage> { async createMessage(message: ThreadMessage): Promise<ThreadMessage> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post(`v1/threads/${message.thread_id}/messages`, { .post(`v1/threads/${message.thread_id}/messages`, {
json: message, json: message,
}) })
@ -111,7 +112,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async modifyMessage(message: ThreadMessage): Promise<ThreadMessage> { async modifyMessage(message: ThreadMessage): Promise<ThreadMessage> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.patch( .patch(
`v1/threads/${message.thread_id}/messages/${message.id}`, `v1/threads/${message.thread_id}/messages/${message.id}`,
{ {
@ -131,7 +132,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
async deleteMessage(threadId: string, messageId: string): Promise<void> { async deleteMessage(threadId: string, messageId: string): Promise<void> {
return this.queue return this.queue
.add(() => .add(() =>
this.api().delete(`v1/threads/${threadId}/messages/${messageId}`) this.api.delete(`v1/threads/${threadId}/messages/${messageId}`)
) )
.then() .then()
} }
@ -143,7 +144,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async listMessages(threadId: string): Promise<ThreadMessage[]> { async listMessages(threadId: string): Promise<ThreadMessage[]> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/threads/${threadId}/messages?order=asc&limit=-1`) .get(`v1/threads/${threadId}/messages?order=asc&limit=-1`)
.json<MessageList>() .json<MessageList>()
.then((e) => e.data) .then((e) => e.data)
@ -158,7 +159,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
*/ */
async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> { async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/assistants/${threadId}?limit=-1`) .get(`v1/assistants/${threadId}?limit=-1`)
.json<ThreadAssistantInfo>() .json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo> ) as Promise<ThreadAssistantInfo>
@ -174,7 +175,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
assistant: ThreadAssistantInfo assistant: ThreadAssistantInfo
): Promise<ThreadAssistantInfo> { ): Promise<ThreadAssistantInfo> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post(`v1/assistants/${threadId}`, { json: assistant }) .post(`v1/assistants/${threadId}`, { json: assistant })
.json<ThreadAssistantInfo>() .json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo> ) as Promise<ThreadAssistantInfo>
@ -191,7 +192,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
assistant: ThreadAssistantInfo assistant: ThreadAssistantInfo
): Promise<ThreadAssistantInfo> { ): Promise<ThreadAssistantInfo> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.patch(`v1/assistants/${threadId}`, { json: assistant }) .patch(`v1/assistants/${threadId}`, { json: assistant })
.json<ThreadAssistantInfo>() .json<ThreadAssistantInfo>()
) as Promise<ThreadAssistantInfo> ) as Promise<ThreadAssistantInfo>
@ -202,7 +203,7 @@ export default class CortexConversationalExtension extends ConversationalExtensi
* @returns * @returns
*/ */
async healthz(): Promise<void> { async healthz(): Promise<void> {
return this.api() return this.api
.get('healthz', { .get('healthz', {
retry: { limit: 20, delay: () => 500, methods: ['get'] }, retry: { limit: 20, delay: () => 500, methods: ['get'] },
}) })

View File

@ -28,7 +28,6 @@ export default defineConfig([
'Authorization: Bearer {{api_key}}' 'Authorization: Bearer {{api_key}}'
), ),
VERSION: JSON.stringify(pkgJson.version ?? '0.0.0'), VERSION: JSON.stringify(pkgJson.version ?? '0.0.0'),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}, },
{ {

View File

@ -6,7 +6,6 @@ declare const DEFAULT_REQUEST_PAYLOAD_TRANSFORM: string
declare const DEFAULT_RESPONSE_BODY_TRANSFORM: string declare const DEFAULT_RESPONSE_BODY_TRANSFORM: string
declare const DEFAULT_REQUEST_HEADERS_TRANSFORM: string declare const DEFAULT_REQUEST_HEADERS_TRANSFORM: string
declare const VERSION: string declare const VERSION: string
declare const CORTEX_API_KEY: string
declare const DEFAULT_REMOTE_ENGINES: ({ declare const DEFAULT_REMOTE_ENGINES: ({
id: string id: string

View File

@ -15,7 +15,7 @@ import {
ModelEvent, ModelEvent,
EngineEvent, EngineEvent,
} from '@janhq/core' } from '@janhq/core'
import ky, { HTTPError } from 'ky' import ky, { HTTPError, KyInstance } from 'ky'
import PQueue from 'p-queue' import PQueue from 'p-queue'
import { EngineError } from './error' import { EngineError } from './error'
import { getJanDataFolderPath } from '@janhq/core' import { getJanDataFolderPath } from '@janhq/core'
@ -35,17 +35,18 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
* Extended API instance for making requests to the Cortex API. * Extended API instance for making requests to the Cortex API.
* @returns * @returns
*/ */
api = () => api: KyInstance
ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
*/ */
async onLoad() { async onLoad() {
const apiKey = (await window.core?.api.appToken()) ?? 'cortex.cpp'
this.api = ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
// Symlink Engines Directory // Symlink Engines Directory
await executeOnMain(NODE, 'symlinkEngines') await executeOnMain(NODE, 'symlinkEngines')
// Run Healthcheck // Run Healthcheck
@ -70,7 +71,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getEngines(): Promise<Engines> { async getEngines(): Promise<Engines> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get('v1/engines') .get('v1/engines')
.json<Engines>() .json<Engines>()
.then((e) => e) .then((e) => e)
@ -81,7 +82,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
* @returns A Promise that resolves to an object of list engines. * @returns A Promise that resolves to an object of list engines.
*/ */
async getRemoteModels(name: string): Promise<any> { async getRemoteModels(name: string): Promise<any> {
return this.api() return this.api
.get(`v1/models/remote/${name}`) .get(`v1/models/remote/${name}`)
.json<ModelList>() .json<ModelList>()
.catch(() => ({ .catch(() => ({
@ -95,7 +96,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]> { async getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/engines/${name}`) .get(`v1/engines/${name}`)
.json<EngineVariant[]>() .json<EngineVariant[]>()
.then((e) => e) .then((e) => e)
@ -114,7 +115,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
platform?: string platform?: string
) { ) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/engines/${name}/releases/${version}`) .get(`v1/engines/${name}/releases/${version}`)
.json<EngineReleased[]>() .json<EngineReleased[]>()
.then((e) => .then((e) =>
@ -130,7 +131,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getLatestReleasedEngine(name: InferenceEngine, platform?: string) { async getLatestReleasedEngine(name: InferenceEngine, platform?: string) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/engines/${name}/releases/latest`) .get(`v1/engines/${name}/releases/latest`)
.json<EngineReleased[]>() .json<EngineReleased[]>()
.then((e) => .then((e) =>
@ -145,7 +146,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async installEngine(name: string, engineConfig: EngineConfig) { async installEngine(name: string, engineConfig: EngineConfig) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post(`v1/engines/${name}/install`, { json: engineConfig }) .post(`v1/engines/${name}/install`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
@ -178,7 +179,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
engineConfig.metadata.header_template = DEFAULT_REQUEST_HEADERS_TRANSFORM engineConfig.metadata.header_template = DEFAULT_REQUEST_HEADERS_TRANSFORM
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post('v1/engines', { json: engineConfig }) .post('v1/engines', { json: engineConfig })
.then((e) => { .then((e) => {
if (persistModels && engineConfig.metadata?.get_models_url) { if (persistModels && engineConfig.metadata?.get_models_url) {
@ -198,7 +199,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async uninstallEngine(name: InferenceEngine, engineConfig: EngineConfig) { async uninstallEngine(name: InferenceEngine, engineConfig: EngineConfig) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.delete(`v1/engines/${name}/install`, { json: engineConfig }) .delete(`v1/engines/${name}/install`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
@ -211,7 +212,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
async addRemoteModel(model: Model) { async addRemoteModel(model: Model) {
return this.queue return this.queue
.add(() => .add(() =>
this.api() this.api
.post('v1/models/add', { .post('v1/models/add', {
json: { json: {
inference_params: { inference_params: {
@ -236,7 +237,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async getDefaultEngineVariant(name: InferenceEngine) { async getDefaultEngineVariant(name: InferenceEngine) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/engines/${name}/default`) .get(`v1/engines/${name}/default`)
.json<{ messages: string }>() .json<{ messages: string }>()
.then((e) => e) .then((e) => e)
@ -253,7 +254,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
engineConfig: EngineConfig engineConfig: EngineConfig
) { ) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post(`v1/engines/${name}/default`, { json: engineConfig }) .post(`v1/engines/${name}/default`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
@ -264,7 +265,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
*/ */
async updateEngine(name: InferenceEngine, engineConfig?: EngineConfig) { async updateEngine(name: InferenceEngine, engineConfig?: EngineConfig) {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post(`v1/engines/${name}/update`, { json: engineConfig }) .post(`v1/engines/${name}/update`, { json: engineConfig })
.then((e) => e) .then((e) => e)
) as Promise<{ messages: string }> ) as Promise<{ messages: string }>
@ -275,7 +276,7 @@ export default class JanEngineManagementExtension extends EngineManagementExtens
* @returns * @returns
*/ */
async healthz(): Promise<void> { async healthz(): Promise<void> {
return this.api() return this.api
.get('healthz', { .get('healthz', {
retry: { limit: 20, delay: () => 500, methods: ['get'] }, retry: { limit: 20, delay: () => 500, methods: ['get'] },
}) })

View File

@ -11,7 +11,6 @@ export default defineConfig([
define: { define: {
NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`), NODE: JSON.stringify(`${pkgJson.name}/${pkgJson.node}`),
API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}, },
]) ])

View File

@ -1,6 +1,5 @@
declare const API_URL: string declare const API_URL: string
declare const NODE: string declare const NODE: string
declare const CORTEX_API_KEY: string
interface Core { interface Core {
api: APIFunctions api: APIFunctions

View File

@ -1,5 +1,5 @@
import { HardwareManagementExtension, HardwareInformation } from '@janhq/core' import { HardwareManagementExtension, HardwareInformation } from '@janhq/core'
import ky from 'ky' import ky, { KyInstance } from 'ky'
import PQueue from 'p-queue' import PQueue from 'p-queue'
/** /**
@ -13,17 +13,18 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
* Extended API instance for making requests to the Cortex API. * Extended API instance for making requests to the Cortex API.
* @returns * @returns
*/ */
api = () => api: KyInstance
ky.extend({
prefixUrl: API_URL,
headers: {
'Authorization': `Bearer ${CORTEX_API_KEY}`,
},
});
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
*/ */
async onLoad() { async onLoad() {
const apiKey = await window.core?.api.appToken() ?? 'cortex.cpp'
this.api = ky.extend({
prefixUrl: API_URL,
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
// Run Healthcheck // Run Healthcheck
this.queue.add(() => this.healthz()) this.queue.add(() => this.healthz())
} }
@ -38,7 +39,7 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
* @returns * @returns
*/ */
async healthz(): Promise<void> { async healthz(): Promise<void> {
return this.api() return this.api
.get('healthz', { .get('healthz', {
retry: { limit: 20, delay: () => 500, methods: ['get'] }, retry: { limit: 20, delay: () => 500, methods: ['get'] },
}) })
@ -50,7 +51,7 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
*/ */
async getHardware(): Promise<HardwareInformation> { async getHardware(): Promise<HardwareInformation> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get('v1/hardware') .get('v1/hardware')
.json<HardwareInformation>() .json<HardwareInformation>()
.then((e) => e) .then((e) => e)
@ -65,7 +66,7 @@ export default class JSONHardwareManagementExtension extends HardwareManagementE
activated_gpus: number[] activated_gpus: number[]
}> { }> {
return this.queue.add(() => return this.queue.add(() =>
this.api().post('v1/hardware/activate', { json: data }).then((e) => e) this.api.post('v1/hardware/activate', { json: data }).then((e) => e)
) as Promise<{ ) as Promise<{
message: string message: string
activated_gpus: number[] activated_gpus: number[]

View File

@ -19,7 +19,6 @@ export default defineConfig([
CORTEX_SOCKET_URL: JSON.stringify( CORTEX_SOCKET_URL: JSON.stringify(
`ws://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}` `ws://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
), ),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
CORTEX_ENGINE_VERSION: JSON.stringify('v0.1.55'), CORTEX_ENGINE_VERSION: JSON.stringify('v0.1.55'),
}, },
}, },
@ -36,7 +35,6 @@ export default defineConfig([
extensions: ['.js', '.ts', '.json'], extensions: ['.js', '.ts', '.json'],
}, },
define: { define: {
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
CORTEX_API_URL: JSON.stringify( CORTEX_API_URL: JSON.stringify(
`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}` `http://127.0.0.1:${process.env.CORTEX_API_PORT ?? '39291'}`
), ),

View File

@ -3,4 +3,3 @@ declare const CORTEX_API_URL: string
declare const CORTEX_SOCKET_URL: string declare const CORTEX_SOCKET_URL: string
declare const CORTEX_ENGINE_VERSION: string declare const CORTEX_ENGINE_VERSION: string
declare const SETTINGS: any declare const SETTINGS: any
declare const CORTEX_API_KEY: string

View File

@ -17,7 +17,7 @@ import {
ModelEvent, ModelEvent,
} from '@janhq/core' } from '@janhq/core'
import PQueue from 'p-queue' import PQueue from 'p-queue'
import ky from 'ky' import ky, { KyInstance } from 'ky'
/** /**
* Event subscription types of Downloader * Event subscription types of Downloader
@ -75,33 +75,30 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
abortControllers = new Map<string, AbortController>() abortControllers = new Map<string, AbortController>()
/** api!: KyInstance
* Extended API instance for making requests to the Cortex API.
* @returns
*/
api = () =>
ky.extend({
prefixUrl: CORTEX_API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Authorization headers for the API requests. * Authorization headers for the API requests.
* @returns * @returns
*/ */
headers(): Promise<HeadersInit> { headers(): Promise<HeadersInit> {
return Promise.resolve({ return window.core?.api.appToken().then((token: string) => ({
Authorization: `Bearer ${CORTEX_API_KEY}`, Authorization: `Bearer ${token}`,
}) }))
} }
/** /**
* Subscribes to events emitted by the @janhq/core package. * Called when the extension is loaded.
*/ */
async onLoad() { async onLoad() {
super.onLoad() super.onLoad()
const apiKey = (await window.core?.api.appToken()) ?? 'cortex.cpp'
this.api = ky.extend({
prefixUrl: CORTEX_API_URL,
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
// Register Settings // Register Settings
this.registerSettings(SETTINGS) this.registerSettings(SETTINGS)
@ -175,7 +172,7 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
this.abortControllers.set(model.id, controller) this.abortControllers.set(model.id, controller)
return await this.queue.add(() => return await this.queue.add(() =>
this.api() this.api
.post('v1/models/start', { .post('v1/models/start', {
json: { json: {
...extractModelLoadParams(model.settings), ...extractModelLoadParams(model.settings),
@ -205,7 +202,7 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
} }
override async unloadModel(model: Model): Promise<void> { override async unloadModel(model: Model): Promise<void> {
return this.api() return this.api
.post('v1/models/stop', { .post('v1/models/stop', {
json: { model: model.id }, json: { model: model.id },
}) })
@ -221,7 +218,7 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
* @returns * @returns
*/ */
private async healthz(): Promise<void> { private async healthz(): Promise<void> {
return this.api() return this.api
.get('healthz', { .get('healthz', {
retry: { retry: {
limit: 20, limit: 20,
@ -237,7 +234,7 @@ export default class JanInferenceCortexExtension extends LocalOAIEngine {
* @returns * @returns
*/ */
private async clean(): Promise<any> { private async clean(): Promise<any> {
return this.api() return this.api
.delete('processmanager/destroy', { .delete('processmanager/destroy', {
timeout: 2000, // maximum 2 seconds timeout: 2000, // maximum 2 seconds
retry: { retry: {

View File

@ -46,7 +46,7 @@ function run(): Promise<any> {
dataFolderPath, dataFolderPath,
'config', 'config',
'--api_keys', '--api_keys',
CORTEX_API_KEY ?? 'cortexserver', process.env.appToken ?? 'cortex.cpp',
], ],
{ {
env: { env: {

View File

@ -13,6 +13,5 @@ export default defineConfig({
SETTINGS: JSON.stringify(settingJson), SETTINGS: JSON.stringify(settingJson),
CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
DEFAULT_MODEL_SOURCES: JSON.stringify(modelSources), DEFAULT_MODEL_SOURCES: JSON.stringify(modelSources),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
}, },
}) })

View File

@ -2,7 +2,6 @@ declare const NODE: string
declare const CORTEX_API_URL: string declare const CORTEX_API_URL: string
declare const SETTINGS: SettingComponentProps[] declare const SETTINGS: SettingComponentProps[]
declare const DEFAULT_MODEL_SOURCES: any declare const DEFAULT_MODEL_SOURCES: any
declare const CORTEX_API_KEY: string
interface Core { interface Core {
api: APIFunctions api: APIFunctions

View File

@ -13,7 +13,7 @@ import {
import { scanModelsFolder } from './legacy/model-json' import { scanModelsFolder } from './legacy/model-json'
import { deleteModelFiles } from './legacy/delete' import { deleteModelFiles } from './legacy/delete'
import PQueue from 'p-queue' import PQueue from 'p-queue'
import ky from 'ky' import ky, { KyInstance } from 'ky'
/** /**
* cortex.cpp setting keys * cortex.cpp setting keys
@ -37,19 +37,18 @@ export default class JanModelExtension extends ModelExtension {
* Extended API instance for making requests to the Cortex API. * Extended API instance for making requests to the Cortex API.
* @returns * @returns
*/ */
api = () => api: KyInstance
ky.extend({
prefixUrl: CORTEX_API_URL,
headers: {
Authorization: `Bearer ${CORTEX_API_KEY}`,
},
})
/** /**
* Called when the extension is loaded. * Called when the extension is loaded.
* @override
*/ */
async onLoad() { async onLoad() {
const apiKey = (await window.core?.api.appToken()) ?? 'cortex.cpp'
this.api = ky.extend({
prefixUrl: CORTEX_API_URL,
headers: {
Authorization: `Bearer ${apiKey}`,
},
})
this.queue.add(() => this.healthz()) this.queue.add(() => this.healthz())
this.registerSettings(SETTINGS) this.registerSettings(SETTINGS)
@ -95,7 +94,7 @@ export default class JanModelExtension extends ModelExtension {
* Sending POST to /models/pull/{id} endpoint to pull the model * Sending POST to /models/pull/{id} endpoint to pull the model
*/ */
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post('v1/models/pull', { json: { model, id, name } }) .post('v1/models/pull', { json: { model, id, name } })
.json() .json()
.catch(async (e) => { .catch(async (e) => {
@ -116,7 +115,7 @@ export default class JanModelExtension extends ModelExtension {
* Sending DELETE to /models/pull/{id} endpoint to cancel a model pull * Sending DELETE to /models/pull/{id} endpoint to cancel a model pull
*/ */
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.delete('v1/models/pull', { json: { taskId: model } }) .delete('v1/models/pull', { json: { taskId: model } })
.json() .json()
.then() .then()
@ -130,7 +129,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async deleteModel(model: string): Promise<void> { async deleteModel(model: string): Promise<void> {
return this.queue return this.queue
.add(() => this.api().delete(`v1/models/${model}`).json().then()) .add(() => this.api.delete(`v1/models/${model}`).json().then())
.catch((e) => console.debug(e)) .catch((e) => console.debug(e))
.finally(async () => { .finally(async () => {
// Delete legacy model files // Delete legacy model files
@ -232,7 +231,7 @@ export default class JanModelExtension extends ModelExtension {
async updateModel(model: Partial<Model>): Promise<Model> { async updateModel(model: Partial<Model>): Promise<Model> {
return this.queue return this.queue
.add(() => .add(() =>
this.api() this.api
.patch(`v1/models/${model.id}`, { json: { ...model } }) .patch(`v1/models/${model.id}`, { json: { ...model } })
.json() .json()
.then() .then()
@ -246,7 +245,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async getModel(model: string): Promise<Model> { async getModel(model: string): Promise<Model> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get(`v1/models/${model}`) .get(`v1/models/${model}`)
.json() .json()
.then((e) => this.transformModel(e)) .then((e) => this.transformModel(e))
@ -265,7 +264,7 @@ export default class JanModelExtension extends ModelExtension {
option?: OptionType option?: OptionType
): Promise<void> { ): Promise<void> {
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.post('v1/models/import', { .post('v1/models/import', {
json: { model, modelPath, name, option }, json: { model, modelPath, name, option },
}) })
@ -282,7 +281,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async getSources(): Promise<ModelSource[]> { async getSources(): Promise<ModelSource[]> {
const sources = await this.queue const sources = await this.queue
.add(() => this.api().get('v1/models/sources').json<Data<ModelSource>>()) .add(() => this.api.get('v1/models/sources').json<Data<ModelSource>>())
.then((e) => (typeof e === 'object' ? (e.data as ModelSource[]) : [])) .then((e) => (typeof e === 'object' ? (e.data as ModelSource[]) : []))
.catch(() => []) .catch(() => [])
return sources.concat( return sources.concat(
@ -296,7 +295,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async addSource(source: string): Promise<any> { async addSource(source: string): Promise<any> {
return this.queue.add(() => return this.queue.add(() =>
this.api().post('v1/models/sources', { this.api.post('v1/models/sources', {
json: { json: {
source, source,
}, },
@ -310,7 +309,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async deleteSource(source: string): Promise<any> { async deleteSource(source: string): Promise<any> {
return this.queue.add(() => return this.queue.add(() =>
this.api().delete('v1/models/sources', { this.api.delete('v1/models/sources', {
json: { json: {
source, source,
}, },
@ -325,7 +324,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async isModelLoaded(model: string): Promise<boolean> { async isModelLoaded(model: string): Promise<boolean> {
return this.queue return this.queue
.add(() => this.api().get(`v1/models/status/${model}`)) .add(() => this.api.get(`v1/models/status/${model}`))
.then((e) => true) .then((e) => true)
.catch(() => false) .catch(() => false)
} }
@ -344,7 +343,7 @@ export default class JanModelExtension extends ModelExtension {
*/ */
async fetchModels(): Promise<Model[]> { async fetchModels(): Promise<Model[]> {
return this.queue return this.queue
.add(() => this.api().get('v1/models?limit=-1').json<Data<Model>>()) .add(() => this.api.get('v1/models?limit=-1').json<Data<Model>>())
.then((e) => .then((e) =>
typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : [] typeof e === 'object' ? e.data.map((e) => this.transformModel(e)) : []
) )
@ -384,7 +383,7 @@ export default class JanModelExtension extends ModelExtension {
}): Promise<void> { }): Promise<void> {
return this.queue return this.queue
.add(() => .add(() =>
this.api() this.api
.patch('v1/configs', { json: body }) .patch('v1/configs', { json: body })
.then(() => {}) .then(() => {})
) )
@ -396,7 +395,7 @@ export default class JanModelExtension extends ModelExtension {
* @returns * @returns
*/ */
private healthz(): Promise<void> { private healthz(): Promise<void> {
return this.api() return this.api
.get('healthz', { .get('healthz', {
retry: { retry: {
limit: 20, limit: 20,
@ -416,7 +415,7 @@ export default class JanModelExtension extends ModelExtension {
const models = await this.fetchModels() const models = await this.fetchModels()
return this.queue.add(() => return this.queue.add(() =>
this.api() this.api
.get('v1/models/hub?author=cortexso&tag=cortex.cpp') .get('v1/models/hub?author=cortexso&tag=cortex.cpp')
.json<Data<string>>() .json<Data<string>>()
.then((e) => { .then((e) => {

3
server/global.d.ts vendored
View File

@ -1,2 +1 @@
declare const CORTEX_API_URL: string declare const CORTEX_API_URL: string
declare const CORTEX_API_KEY: string

View File

@ -90,7 +90,7 @@ export const startServer = async (configs?: ServerConfig): Promise<boolean> => {
if (req.url.includes('/configs')) return headers if (req.url.includes('/configs')) return headers
return { return {
...headers, ...headers,
authorization: `Bearer ${CORTEX_API_KEY}`, // Add or modify Authorization header authorization: `Bearer ${process.env.appToken}`, // Add or modify Authorization header
} }
} }

View File

@ -16,7 +16,6 @@ export default defineConfig([
platform: 'node', platform: 'node',
define: { define: {
CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`), CORTEX_API_URL: JSON.stringify(`http://127.0.0.1:${process.env.CORTEX_API_PORT ?? "39291"}`),
CORTEX_API_KEY: JSON.stringify(process.env.CORTEX_API_KEY ?? 'cortexserver'),
} }
}, },
]) ])

View File

@ -3,7 +3,7 @@ import useSWR from 'swr'
const fetchLatestRelease = async (includeBeta: boolean) => { const fetchLatestRelease = async (includeBeta: boolean) => {
const res = await fetch( const res = await fetch(
'https://api.github.com/menloresearch/janhq/jan/releases' 'https://api.github.com/repos/menloresearch/jan/releases'
) )
if (!res.ok) throw new Error('Failed to fetch releases') if (!res.ok) throw new Error('Failed to fetch releases')