Skip to content

Commit

Permalink
refactor: add app and nitro log - resolve dependencies issue (janhq#1447
Browse files Browse the repository at this point in the history
)

* refactor: add app and nitro log - resolve dependencies issue

* fix: update guidance message on inference error

* chore: add timestamp to log files

* chore: add clear logs action
  • Loading branch information
louis-jan authored Jan 10, 2024
1 parent a3f14d5 commit 74ed081
Show file tree
Hide file tree
Showing 26 changed files with 356 additions and 267 deletions.
2 changes: 2 additions & 0 deletions core/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export default [
'crypto',
'url',
'http',
'os',
'util'
],
watch: {
include: 'src/node/**',
Expand Down
1 change: 1 addition & 0 deletions core/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum AppRoute {
baseName = 'baseName',
startServer = 'startServer',
stopServer = 'stopServer',
log = 'log'
}

export enum AppEvent {
Expand Down
12 changes: 6 additions & 6 deletions core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,12 @@ const openExternalUrl: (url: string) => Promise<any> = (url) =>
const getResourcePath: () => Promise<string> = () => global.core.api?.getResourcePath()

/**
* Gets the file's stats.
* Log to file from browser processes.
*
* @param path - The path to the file.
* @returns {Promise<FileStat>} - A promise that resolves with the file's stats.
* @param message - Message to log.
*/
const fileStat: (path: string) => Promise<FileStat | undefined> = (path) =>
global.core.api?.fileStat(path)
const log: (message: string, fileName?: string) => void = (message, fileName) =>
global.core.api?.log(message, fileName)

/**
* Register extension point function type definition
Expand All @@ -108,5 +107,6 @@ export {
joinPath,
openExternalUrl,
baseName,
fileStat,
log,
FileStat
}
14 changes: 14 additions & 0 deletions core/src/fs.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FileStat } from "./types"

/**
* Writes data to a file at the specified path.
* @returns {Promise<any>} A Promise that resolves when the file is written successfully.
Expand Down Expand Up @@ -58,6 +60,17 @@ const syncFile: (src: string, dest: string) => Promise<any> = (src, dest) =>
*/
const copyFileSync = (...args: any[]) => global.core.api?.copyFileSync(...args)


/**
* Gets the file's stats.
*
* @param path - The path to the file.
* @returns {Promise<FileStat>} - A promise that resolves with the file's stats.
*/
const fileStat: (path: string) => Promise<FileStat | undefined> = (path) =>
global.core.api?.fileStat(path)


// TODO: Export `dummy` fs functions automatically
// Currently adding these manually
export const fs = {
Expand All @@ -71,4 +84,5 @@ export const fs = {
appendFileSync,
copyFileSync,
syncFile,
fileStat
}
11 changes: 5 additions & 6 deletions core/src/node/api/common/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ import { JanApiRouteConfiguration, RouteConfiguration } from './configuration'
import { join } from 'path'
import { ContentType, MessageStatus, Model, ThreadMessage } from './../../../index'

import fetch from 'node-fetch'
import { ulid } from 'ulid'
import request from 'request'

const progress = require('request-progress')
const os = require('os')

const path = join(os.homedir(), 'jan')
Expand Down Expand Up @@ -209,6 +204,7 @@ export const createMessage = async (threadId: string, message: any) => {
const threadMessagesFileName = 'messages.jsonl'

try {
const { ulid } = require('ulid')
const msgId = ulid()
const createdAt = Date.now()
const threadMessage: ThreadMessage = {
Expand Down Expand Up @@ -260,8 +256,10 @@ export const downloadModel = async (modelId: string) => {

// path to model binary
const modelBinaryPath = join(directoryPath, modelId)
const rq = request(model.source_url)

const request = require('request')
const rq = request(model.source_url)
const progress = require('request-progress')
progress(rq, {})
.on('progress', function (state: any) {
console.log('progress', JSON.stringify(state, null, 2))
Expand Down Expand Up @@ -324,6 +322,7 @@ export const chatCompletions = async (request: any, reply: any) => {
}
console.debug(apiUrl)
console.debug(JSON.stringify(headers))
const fetch = require('node-fetch')
const response = await fetch(apiUrl, {
method: 'POST',
headers: headers,
Expand Down
10 changes: 6 additions & 4 deletions core/src/node/api/routes/download.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { DownloadRoute } from '../../../api'
import { join } from 'path'
import { userSpacePath, DownloadManager, HttpServer } from '../../index'
import { userSpacePath } from '../../extension/manager'
import { DownloadManager } from '../../download'
import { HttpServer } from '../HttpServer'
import { createWriteStream } from 'fs'

const request = require('request')
const progress = require('request-progress')

export const downloadRouter = async (app: HttpServer) => {
app.post(`/${DownloadRoute.downloadFile}`, async (req, res) => {
const body = JSON.parse(req.body as any)
Expand All @@ -19,6 +18,9 @@ export const downloadRouter = async (app: HttpServer) => {
const localPath = normalizedArgs[1]
const fileName = localPath.split('/').pop() ?? ''

const request = require('request')
const progress = require('request-progress')

const rq = request(normalizedArgs[0])
progress(rq, {})
.on('progress', function (state: any) {
Expand Down
14 changes: 6 additions & 8 deletions core/src/node/api/routes/extension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { join, extname } from 'path'
import { ExtensionRoute } from '../../../api'
import {
userSpacePath,
ModuleManager,
getActiveExtensions,
installExtensions,
HttpServer,
} from '../../index'
import { ExtensionRoute } from '../../../api/index'
import { userSpacePath } from '../../extension/manager'
import { ModuleManager } from '../../module'
import { getActiveExtensions, installExtensions } from '../../extension/store'
import { HttpServer } from '../HttpServer'

import { readdirSync } from 'fs'

export const extensionRouter = async (app: HttpServer) => {
Expand Down
3 changes: 2 additions & 1 deletion core/src/node/api/routes/fs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FileSystemRoute } from '../../../api'
import { join } from 'path'
import { HttpServer, userSpacePath } from '../../index'
import { HttpServer } from '../HttpServer'
import { userSpacePath } from '../../extension/manager'

export const fsRouter = async (app: HttpServer) => {
const moduleName = 'fs'
Expand Down
6 changes: 5 additions & 1 deletion core/src/node/api/routes/v1.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { HttpServer } from '../HttpServer'
import { commonRouter, threadRouter, fsRouter, extensionRouter, downloadRouter } from './index'
import { commonRouter } from './common'
import { threadRouter } from './thread'
import { fsRouter } from './fs'
import { extensionRouter } from './extension'
import { downloadRouter } from './download'

export const v1Router = async (app: HttpServer) => {
// MARK: External Routes
Expand Down
3 changes: 1 addition & 2 deletions core/src/node/download.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Request } from "request";

/**
* Manages file downloads and network requests.
Expand All @@ -18,7 +17,7 @@ export class DownloadManager {
* @param {string} fileName - The name of the file.
* @param {Request | undefined} request - The network request to set, or undefined to clear the request.
*/
setRequest(fileName: string, request: Request | undefined) {
setRequest(fileName: string, request: any | undefined) {
this.networkRequests[fileName] = request;
}
}
33 changes: 19 additions & 14 deletions core/src/node/extension/extension.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { rmdirSync } from 'fs'
import { resolve, join } from 'path'
import { manifest, extract } from 'pacote'
import * as Arborist from '@npmcli/arborist'
import { ExtensionManager } from './manager'

/**
Expand Down Expand Up @@ -41,6 +39,7 @@ export default class Extension {
* @param {Object} [options] Options provided to pacote when fetching the manifest.
*/
constructor(origin?: string, options = {}) {
const Arborist = require('@npmcli/arborist')
const defaultOpts = {
version: false,
fullMetadata: false,
Expand Down Expand Up @@ -74,13 +73,15 @@ export default class Extension {
async getManifest() {
// Get the package's manifest (package.json object)
try {
const mnf = await manifest(this.specifier, this.installOptions)

// set the Package properties based on the it's manifest
this.name = mnf.name
this.version = mnf.version
this.main = mnf.main
this.description = mnf.description
await import('pacote').then((pacote) => {
return pacote.manifest(this.specifier, this.installOptions).then((mnf) => {
// set the Package properties based on the it's manifest
this.name = mnf.name
this.version = mnf.version
this.main = mnf.main
this.description = mnf.description
})
})
} catch (error) {
throw new Error(`Package ${this.origin} does not contain a valid manifest: ${error}`)
}
Expand All @@ -99,7 +100,8 @@ export default class Extension {
await this.getManifest()

// Install the package in a child folder of the given folder
await extract(
const pacote = await import('pacote')
await pacote.extract(
this.specifier,
join(ExtensionManager.instance.extensionsPath ?? '', this.name ?? ''),
this.installOptions,
Expand Down Expand Up @@ -164,10 +166,13 @@ export default class Extension {
* @returns the latest available version if a new version is available or false if not.
*/
async isUpdateAvailable() {
if (this.origin) {
const mnf = await manifest(this.origin)
return mnf.version !== this.version ? mnf.version : false
}
return import('pacote').then((pacote) => {
if (this.origin) {
return pacote.manifest(this.origin).then((mnf) => {
return mnf.version !== this.version ? mnf.version : false
})
}
})
}

/**
Expand Down
17 changes: 0 additions & 17 deletions core/src/node/extension/manager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { join, resolve } from "path";

import { existsSync, mkdirSync, writeFileSync } from "fs";
import { init } from "./index";
import { homedir } from "os"
/**
* Manages extension installation and migration.
Expand All @@ -20,22 +19,6 @@ export class ExtensionManager {
}
}

/**
* Sets up the extensions by initializing the `extensions` module with the `confirmInstall` and `extensionsPath` options.
* The `confirmInstall` function always returns `true` to allow extension installation.
* The `extensionsPath` option specifies the path to install extensions to.
*/
setupExtensions() {
init({
// Function to check from the main process that user wants to install a extension
confirmInstall: async (_extensions: string[]) => {
return true;
},
// Path to install extension to
extensionsPath: join(userSpacePath, "extensions"),
});
}

setExtensionsPath(extPath: string) {
// Create folder if it does not exist
let extDir;
Expand Down
20 changes: 14 additions & 6 deletions core/src/node/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,24 @@ import util from 'util'
import path from 'path'
import os from 'os'

const appDir = path.join(os.homedir(), 'jan')
export const logDir = path.join(os.homedir(), 'jan', 'logs')

export const logPath = path.join(appDir, 'app.log')
export const log = function (message: string, fileName: string = 'app.log') {
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true })
}
if (!message.startsWith('[')) {
message = `[APP]::${message}`
}

message = `${new Date().toISOString()} ${message}`

export const log = function (d: any) {
if (fs.existsSync(appDir)) {
var log_file = fs.createWriteStream(logPath, {
if (fs.existsSync(logDir)) {
var log_file = fs.createWriteStream(path.join(logDir, fileName), {
flags: 'a',
})
log_file.write(util.format(d) + '\n')
log_file.write(util.format(message) + '\n')
log_file.close()
console.debug(message)
}
}
22 changes: 18 additions & 4 deletions electron/handlers/app.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { app, ipcMain, shell, nativeTheme } from 'electron'
import { app, ipcMain, shell } from 'electron'
import { join, basename } from 'path'
import { WindowManager } from './../managers/window'
import { getResourcePath, userSpacePath } from './../utils/path'
import { AppRoute } from '@janhq/core'
import { ExtensionManager, ModuleManager } from '@janhq/core/node'
import { ModuleManager, init, log } from '@janhq/core/node'
import { startServer, stopServer } from '@janhq/server'

export function handleAppIPCs() {
Expand Down Expand Up @@ -59,7 +59,7 @@ export function handleAppIPCs() {
app.isPackaged ? join(getResourcePath(), 'docs', 'openapi') : undefined
)
)

/**
* Stop Jan API Server.
*/
Expand All @@ -82,8 +82,22 @@ export function handleAppIPCs() {
require.resolve(join(userSpacePath, 'extensions', modulePath))
]
}
ExtensionManager.instance.setupExtensions()
init({
// Function to check from the main process that user wants to install a extension
confirmInstall: async (_extensions: string[]) => {
return true
},
// Path to install extension to
extensionsPath: join(userSpacePath, 'extensions'),
})
WindowManager.instance.currentWindow?.reload()
}
})

/**
* Log message to log file.
*/
ipcMain.handle(AppRoute.log, async (_event, message, fileName) =>
log(message, fileName)
)
}
2 changes: 1 addition & 1 deletion electron/handlers/fileManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FileManagerRoute } from '@janhq/core'
import { userSpacePath, getResourcePath } from './../utils/path'
import fs from 'fs'
import { join } from 'path'
import { FileStat } from '@janhq/core/.'
import { FileStat } from '@janhq/core'

/**
* Handles file system extensions operations.
Expand Down
Loading

0 comments on commit 74ed081

Please sign in to comment.