feature: @janhq/plugin-core module & plugins update (#321)

* @janhq/plugin-core module

* refactor web to use exported services from module

* refactor data-plugin to provide DAL & move model logics to model management plugin

* model-management in TS

* add ci auto package, increate version, and publish to npm repository

* chore: storage operations

* chore: hybrid data-plugin esm & cjs module

* chore: PouchDB Driver

* chore: documentation

---------

Co-authored-by: Hien To <hien@jan.ai>
Co-authored-by: Service Account <service@jan.ai>
This commit is contained in:
Louis 2023-10-14 15:59:28 +07:00 committed by GitHub
parent 5732ccec94
commit 5fc1ba7067
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 2632 additions and 2327 deletions

View File

@ -43,6 +43,7 @@ jobs:
- name: Linter and test
run: |
yarn config set network-timeout 300000
yarn build:core
yarn install
yarn lint
yarn build:plugins
@ -68,6 +69,7 @@ jobs:
- name: Linter and test
run: |
yarn config set network-timeout 300000
yarn build:core
yarn install
yarn lint
yarn build:plugins
@ -96,6 +98,7 @@ jobs:
export DISPLAY=$(w -h | awk 'NR==1 {print $2}')
echo -e "Display ID: $DISPLAY"
yarn config set network-timeout 300000
yarn build:core
yarn install
yarn lint
yarn build:plugins

View File

@ -0,0 +1,66 @@
name: Publish Plugin-core Package to npmjs
on:
push:
branches:
- main
paths:
- "plugin-core/**"
- ".github/workflows/publish-plugin-core.yml"
- "!plugin-core/package.json"
jobs:
build:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
token: ${{ secrets.PAT_SERVICE_ACCOUNT }}
- name: Install jq
uses: dcarbone/install-jq-action@v2.0.1
- name: "Auto Increase package Version"
run: |
# Extract current version
current_version=$(jq -r '.version' plugin-core/package.json)
# Break the version into its components
major_version=$(echo $current_version | cut -d "." -f 1)
minor_version=$(echo $current_version | cut -d "." -f 2)
patch_version=$(echo $current_version | cut -d "." -f 3)
# Increment the patch version by one
new_patch_version=$((patch_version+1))
# Construct the new version
new_version="$major_version.$minor_version.$new_patch_version"
# Replace the old version with the new version in package.json
jq --arg version "$new_version" '.version = $version' plugin-core/package.json > /tmp/package.json && mv /tmp/package.json plugin-core/package.json
# Print the new version
echo "Updated package.json version to: $new_version"
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
with:
node-version: "20.x"
registry-url: "https://registry.npmjs.org"
- run: npm install && npm run build
working-directory: ./plugin-core
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
working-directory: ./plugin-core
- name: "Commit new version to main and create tag"
run: |
version=$(jq -r '.version' plugin-core/package.json)
git config --global user.email "service@jan.ai"
git config --global user.name "Service Account"
git add plugin-core/package.json
git commit -m "${GITHUB_REPOSITORY}: Update tag build $version"
git -c http.extraheader="AUTHORIZATION: bearer ${{ secrets.PAT_SERVICE_ACCOUNT }}" push origin HEAD:feat/#306-plugin-core-module
git tag -a plugin-core-$version -m "${GITHUB_REPOSITORY}: Update tag build $version for plugin-core"
git -c http.extraheader="AUTHORIZATION: bearer ${{ secrets.PAT_SERVICE_ACCOUNT }}" push origin plugin-core-$version

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ build
electron/renderer
*.log
plugin-core/lib

View File

@ -0,0 +1,8 @@
{
"extends": "./../tsconfig.json",
"compilerOptions": {
"outDir": "./../dist/cjs",
"module": "commonjs"
},
"files": ["../module.ts"]
}

View File

@ -0,0 +1,8 @@
{
"extends": "./../tsconfig.json",
"compilerOptions": {
"outDir": "./../dist/esm",
"module": "esnext"
},
"files": ["../index.ts"]
}

View File

@ -1,169 +1,180 @@
import { core, store, RegisterExtensionPoint, StoreService, DataService } from "@janhq/plugin-core";
// Provide an async method to manipulate the price provided by the extension point
const MODULE_PATH = "data-plugin/dist/module.js";
const MODULE_PATH = "data-plugin/dist/cjs/module.js";
const storeModel = (model: any) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "storeModel", model)
.then((res: any) => resolve(res));
}
});
/**
* Create a collection on data store
*
* @param name name of the collection to create
* @param schema schema of the collection to create, include fields and their types
* @returns Promise<void>
*
*/
function createCollection({ name, schema }: { name: string; schema?: { [key: string]: any } }): Promise<void> {
console.log("renderer: creating collection:", name, schema);
return core.invokePluginFunc(MODULE_PATH, "createCollection", name, schema);
}
const getFinishedDownloadModels = () =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getFinishedDownloadModels")
.then((res: any) => resolve(res));
}
});
/**
* Delete a collection
*
* @param name name of the collection to delete
* @returns Promise<void>
*
*/
function deleteCollection(name: string): Promise<void> {
return core.invokePluginFunc(MODULE_PATH, "deleteCollection", name);
}
const getModelById = (modelId: string) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getModelById", modelId)
.then((res: any) => resolve(res));
}
});
/**
* Insert a value to a collection
*
* @param collectionName name of the collection
* @param value value to insert
* @returns Promise<any>
*
*/
function insertOne({ collectionName, value }: { collectionName: string; value: any }): Promise<any> {
return core.invokePluginFunc(MODULE_PATH, "insertOne", collectionName, value);
}
const updateFinishedDownloadAt = (fileName: string) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(
MODULE_PATH,
"updateFinishedDownloadAt",
fileName,
Date.now()
)
.then((res: any) => resolve(res));
}
});
/**
* Update value of a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to update
* @param value value to update
* @returns Promise<void>
*
*/
function updateOne({ collectionName, key, value }: { collectionName: string; key: string; value: any }): Promise<void> {
return core.invokePluginFunc(MODULE_PATH, "updateOne", collectionName, key, value);
}
const getUnfinishedDownloadModels = () =>
new Promise<any>((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getUnfinishedDownloadModels")
.then((res: any[]) => resolve(res));
} else {
resolve([]);
}
});
/**
* Updates all records that match a selector in a collection in the data store.
* @param collectionName - The name of the collection containing the records to update.
* @param selector - The selector to use to get the records to update.
* @param value - The new value for the records.
* @returns {Promise<void>} A promise that resolves when the records are updated.
*/
function updateMany({
collectionName,
value,
selector,
}: {
collectionName: string;
value: any;
selector?: { [key: string]: any };
}): Promise<void> {
return core.invokePluginFunc(MODULE_PATH, "updateMany", collectionName, value, selector);
}
const deleteDownloadModel = (modelId: string) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "deleteDownloadModel", modelId)
.then((res: any) => resolve(res));
}
});
/**
* Delete a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to delete
* @returns Promise<void>
*
*/
function deleteOne({ collectionName, key }: { collectionName: string; key: string }): Promise<void> {
return core.invokePluginFunc(MODULE_PATH, "deleteOne", collectionName, key);
}
const getConversations = () =>
new Promise<any>((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getConversations")
.then((res: any[]) => resolve(res));
} else {
resolve([]);
}
});
const getConversationMessages = (id: any) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getConversationMessages", id)
.then((res: any[]) => resolve(res));
} else {
resolve([]);
}
});
/**
* Deletes all records with a matching key from a collection in the data store.
*
* @param collectionName name of the collection
* @param selector selector to use to get the records to delete.
* @returns {Promise<void>}
*
*/
function deleteMany({
collectionName,
selector,
}: {
collectionName: string;
selector?: { [key: string]: any };
}): Promise<void> {
return core.invokePluginFunc(MODULE_PATH, "deleteMany", collectionName, selector);
}
const createConversation = (conversation: any) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "storeConversation", conversation)
.then((res: any) => {
resolve(res);
});
} else {
resolve(undefined);
}
});
/**
* Retrieve a record from a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to retrieve.
* @param {string} key - The key of the record to retrieve.
* @returns {Promise<any>} A promise that resolves when the record is retrieved.
*/
function findOne({ collectionName, key }: { collectionName: string; key: string }): Promise<any> {
return core.invokePluginFunc(MODULE_PATH, "findOne", collectionName, key);
}
const createMessage = (message: any) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "storeMessage", message)
.then((res: any) => {
resolve(res);
});
} else {
resolve(undefined);
}
});
/**
* Gets records in a collection in the data store using a selector.
* @param {string} collectionName - The name of the collection containing the record to get the value from.
* @param {{ [key: string]: any }} selector - The selector to use to get the value from the record.
* @param {[{ [key: string]: any }]} sort - The sort options to use to retrieve records.
* @returns {Promise<any>} A promise that resolves with the selected value.
*/
function findMany({
collectionName,
selector,
sort,
}: {
collectionName: string;
selector: { [key: string]: any };
sort?: [{ [key: string]: any }];
}): Promise<any> {
return core.invokePluginFunc(MODULE_PATH, "findMany", collectionName, selector, sort);
}
const updateMessage = (message: any) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "updateMessage", message)
.then((res: any) => {
resolve(res);
});
} else {
resolve(undefined);
}
});
const deleteConversation = (id: any) =>
new Promise((resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "deleteConversation", id)
.then((res: any) => {
resolve(res);
});
} else {
resolve("-");
}
});
const setupDb = () => {
window.electronAPI.invokePluginFunc(MODULE_PATH, "init");
};
function onStart() {
createCollection({ name: "conversations", schema: {} });
createCollection({ name: "messages", schema: {} });
}
// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: any }) {
setupDb();
register("getConversations", "getConv", getConversations, 1);
register("createConversation", "insertConv", createConversation);
register("updateMessage", "updateMessage", updateMessage);
register("deleteConversation", "deleteConv", deleteConversation);
register("createMessage", "insertMessage", createMessage);
register("getConversationMessages", "getMessages", getConversationMessages);
register("storeModel", "storeModel", storeModel);
register(
"updateFinishedDownloadAt",
"updateFinishedDownloadAt",
updateFinishedDownloadAt
);
register(
"getUnfinishedDownloadModels",
"getUnfinishedDownloadModels",
getUnfinishedDownloadModels
);
register("deleteDownloadModel", "deleteDownloadModel", deleteDownloadModel);
register("getModelById", "getModelById", getModelById);
register(
"getFinishedDownloadModels",
"getFinishedDownloadModels",
getFinishedDownloadModels
);
export function init({ register }: { register: RegisterExtensionPoint }) {
onStart();
register(StoreService.CreateCollection, createCollection.name, createCollection);
register(StoreService.DeleteCollection, deleteCollection.name, deleteCollection);
register(StoreService.InsertOne, insertOne.name, insertOne);
register(StoreService.UpdateOne, updateOne.name, updateOne);
register(StoreService.UpdateMany, updateMany.name, updateMany);
register(StoreService.DeleteOne, deleteOne.name, deleteOne);
register(StoreService.DeleteMany, deleteMany.name, deleteMany);
register(StoreService.FindOne, findOne.name, findOne);
register(StoreService.FindMany, findMany.name, findMany);
register(DataService.GetConversations, getConversations.name, getConversations);
register(DataService.CreateConversation, createConversation.name, createConversation);
register(DataService.UpdateMessage, updateMessage.name, updateMessage);
register(DataService.DeleteConversation, deleteConversation.name, deleteConversation);
register(DataService.CreateMessage, createMessage.name, createMessage);
register(DataService.GetConversationMessages, getConversationMessages.name, getConversationMessages);
}
function getConversations(): Promise<any> {
return store.findMany("conversations", {}, [{ updatedAt: "desc" }]);
}
function createConversation(conversation: any): Promise<number | undefined> {
return store.insertOne("conversations", conversation);
}
function createMessage(message: any): Promise<number | undefined> {
return store.insertOne("messages", message);
}
function updateMessage(message: any): Promise<void> {
return store.updateOne("messages", message._id, message);
}
function deleteConversation(id: any) {
return store.deleteOne("conversations", id).then(() => store.deleteMany("messages", { conversationId: id }));
}
function getConversationMessages(conversationId: any) {
return store.findMany("messages", { conversationId }, [{ createdAt: "desc" }]);
}

View File

@ -1,497 +1,234 @@
const sqlite3 = require("sqlite3").verbose();
const path = require("path");
const { app } = require("electron");
var PouchDB = require("pouchdb-node");
PouchDB.plugin(require("pouchdb-find"));
var path = require("path");
var { app } = require("electron");
var fs = require("fs");
const MODEL_TABLE_CREATION = `
CREATE TABLE IF NOT EXISTS models (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
short_description TEXT NOT NULL,
avatar_url TEXT,
long_description TEXT NOT NULL,
author TEXT NOT NULL,
version TEXT NOT NULL,
model_url TEXT NOT NULL,
nsfw INTEGER NOT NULL,
tags TEXT NOT NULL,
default_greeting TEXT NOT NULL,
type TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`;
const MODEL_VERSION_TABLE_CREATION = `
CREATE TABLE IF NOT EXISTS model_versions (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
quant_method TEXT NOT NULL,
bits INTEGER NOT NULL,
size INTEGER NOT NULL,
max_ram_required INTEGER NOT NULL,
usecase TEXT NOT NULL,
download_link TEXT NOT NULL,
model_id TEXT NOT NULL,
start_download_at INTEGER DEFAULT -1,
finish_download_at INTEGER DEFAULT -1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`;
const MODEL_TABLE_INSERTION = `
INSERT OR IGNORE INTO models (
id,
name,
short_description,
avatar_url,
long_description,
author,
version,
model_url,
nsfw,
tags,
default_greeting,
type
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`;
const MODEL_VERSION_TABLE_INSERTION = `
INSERT INTO model_versions (
id,
name,
quant_method,
bits,
size,
max_ram_required,
usecase,
download_link,
model_id,
start_download_at
) VALUES (?,?,?,?,?,?,?,?,?,?)`;
const getDbPath = () => {
return path.join(app.getPath("userData"), "jan.db");
};
function init() {
const db = new sqlite3.Database(getDbPath());
console.debug(`Database located at ${getDbPath()}`);
db.serialize(() => {
db.run(MODEL_TABLE_CREATION);
db.run(MODEL_VERSION_TABLE_CREATION);
db.run(
"CREATE TABLE IF NOT EXISTS conversations ( id INTEGER PRIMARY KEY, name TEXT, model_id TEXT, image TEXT, message TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);"
);
db.run(
"CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY, name TEXT, conversation_id INTEGER, user TEXT, message TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP);"
);
});
db.close();
}
const dbs: Record<string, any> = {};
/**
* Store a model in the database when user start downloading it
* Create a collection on data store
*
* @param params: { model, modelVersion }
*/
function storeModel(params: any) {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
console.debug("Inserting", JSON.stringify(params));
const model = params.model;
const modelTags = model.tags.join(",");
const modelVersion = params.modelVersion;
db.serialize(() => {
const stmt = db.prepare(MODEL_TABLE_INSERTION);
stmt.run(
model.id,
model.name,
model.shortDescription,
model.avatarUrl,
model.longDescription,
model.author,
model.version,
model.modelUrl,
model.nsfw,
modelTags,
model.greeting,
model.type
);
stmt.finalize();
const stmt2 = db.prepare(MODEL_VERSION_TABLE_INSERTION);
stmt2.run(
modelVersion.id,
modelVersion.name,
modelVersion.quantMethod,
modelVersion.bits,
modelVersion.size,
modelVersion.maxRamRequired,
modelVersion.usecase,
modelVersion.downloadLink,
model.id,
modelVersion.startDownloadAt
);
stmt2.finalize();
});
db.close();
res(undefined);
});
}
/**
* Update the finished download time of a model
* @param name name of the collection to create
* @param schema schema of the collection to create, include fields and their types
* @returns Promise<void>
*
* @param model Product
*/
function updateFinishedDownloadAt(modelVersionId: string) {
return new Promise((res, rej) => {
const db = new sqlite3.Database(getDbPath());
const time = Date.now();
console.debug(
`Updating finished downloaded model version ${modelVersionId}`
);
const stmt = `UPDATE model_versions SET finish_download_at = ? WHERE id = ?`;
db.run(stmt, [time, modelVersionId], (err: any) => {
if (err) {
console.log(err);
rej(err);
} else {
console.log("Updated 1 row");
res("Updated");
}
});
db.close();
function createCollection(name: string, schema?: { [key: string]: any }): Promise<void> {
return new Promise<void>((resolve) => {
const dbPath = path.join(app.getPath("userData"), "databases");
if (!fs.existsSync(dbPath)) fs.mkdirSync(dbPath);
const db = new PouchDB(`${path.join(dbPath, name)}`);
dbs[name] = db;
resolve();
});
}
/**
* Get all unfinished models from the database
* Delete a collection
*
* @param name name of the collection to delete
* @returns Promise<void>
*
*/
function getUnfinishedDownloadModels() {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
function deleteCollection(name: string): Promise<void> {
// Do nothing with Unstructured Database
return dbs[name].destroy();
}
const query = `SELECT * FROM model_versions WHERE finish_download_at = -1 ORDER BY start_download_at DESC`;
db.all(query, (err: Error, row: any) => {
if (row) {
res(row);
} else {
res([]);
}
/**
* Insert a value to a collection
*
* @param collectionName name of the collection
* @param value value to insert
* @returns Promise<any>
*
*/
function insertOne(collectionName: string, value: any): Promise<any> {
if (!value._id) return dbs[collectionName].post(value).then((doc) => doc.id);
return dbs[collectionName].put(value).then((doc) => doc.id);
}
/**
* Update value of a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to update
* @param value value to update
* @returns Promise<void>
*
*/
function updateOne(collectionName: string, key: string, value: any): Promise<void> {
return dbs[collectionName].get(key).then((doc) => {
return dbs[collectionName].put({
_id: key,
_rev: doc._rev,
force: true,
...value,
});
db.close();
});
}
async function getFinishedDownloadModels() {
const db = new sqlite3.Database(getDbPath());
try {
const query = `SELECT * FROM model_versions WHERE finish_download_at != -1 ORDER BY finish_download_at DESC`;
const modelVersions: any = await new Promise((resolve, reject) => {
db.all(query, (err: Error, rows: any[]) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
/**
* Update value of a collection's records
*
* @param collectionName name of the collection
* @param selector selector of records to update
* @param value value to update
* @returns Promise<void>
*
*/
function updateMany(collectionName: string, value: any, selector?: { [key: string]: any }): Promise<any> {
// Creates keys from selector for indexing
const keys = selector ? Object.keys(selector) : [];
const models = await Promise.all(
modelVersions.map(async (modelVersion) => {
const modelQuery = `SELECT * FROM models WHERE id = ?`;
return new Promise((resolve, reject) => {
db.get(
modelQuery,
[modelVersion.model_id],
(err: Error, row: any) => {
if (err) {
reject(err);
} else {
resolve(row);
}
}
);
});
// At a basic level, there are two steps to running a query: createIndex()
// (to define which fields to index) and find() (to query the index).
return (
keys.length > 0
? dbs[collectionName].createIndex({
// There is selector so we need to create index
index: { fields: keys },
})
: Promise.resolve()
) // No selector, so no need to create index
.then(() =>
dbs[collectionName].find({
// Find documents using Mango queries
selector,
})
);
const downloadedModels = [];
modelVersions.forEach((modelVersion: any) => {
const model = models.find((m: any) => m.id === modelVersion.model_id);
if (!model) {
return;
}
const assistantModel = {
id: modelVersion.id,
name: modelVersion.name,
quantMethod: modelVersion.quant_method,
bits: modelVersion.bits,
size: modelVersion.size,
maxRamRequired: modelVersion.max_ram_required,
usecase: modelVersion.usecase,
downloadLink: modelVersion.download_link,
startDownloadAt: modelVersion.start_download_at,
finishDownloadAt: modelVersion.finish_download_at,
productId: model.id,
productName: model.name,
shortDescription: model.short_description,
longDescription: model.long_description,
avatarUrl: model.avatar_url,
author: model.author,
version: model.version,
modelUrl: model.model_url,
nsfw: model.nsfw === 0 ? false : true,
greeting: model.default_greeting,
type: model.type,
createdAt: new Date(model.created_at).getTime(),
updatedAt: new Date(model.updated_at ?? "").getTime(),
status: "",
releaseDate: -1,
tags: model.tags.split(","),
};
downloadedModels.push(assistantModel);
)
.then((data) => {
const docs = data.docs.map((doc) => {
// Update doc with new value
return (doc = {
...doc,
...value,
});
});
return dbs[collectionName].bulkDocs(docs);
});
}
db.close();
/**
* Delete a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to delete
* @returns Promise<void>
*
*/
function deleteOne(collectionName: string, key: string): Promise<void> {
return findOne(collectionName, key).then((doc) => dbs[collectionName].remove(doc));
}
return downloadedModels;
} catch (err) {
console.error(err);
return [];
/**
* Delete a collection records by selector
*
* @param {string} collectionName name of the collection
* @param {{ [key: string]: any }} selector selector for retrieving records.
* @returns Promise<void>
*
*/
function deleteMany(collectionName: string, selector?: { [key: string]: any }): Promise<void> {
// Creates keys from selector for indexing
const keys = selector ? Object.keys(selector) : [];
// At a basic level, there are two steps to running a query: createIndex()
// (to define which fields to index) and find() (to query the index).
return (
keys.length > 0
? dbs[collectionName].createIndex({
// There is selector so we need to create index
index: { fields: keys },
})
: Promise.resolve()
) // No selector, so no need to create index
.then(() =>
dbs[collectionName].find({
// Find documents using Mango queries
selector,
})
)
.then((data) => {
return Promise.all(
// Remove documents
data.docs.map((doc) => {
return dbs[collectionName].remove(doc);
})
);
});
}
/**
* Retrieve a record from a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to retrieve.
* @param {string} key - The key of the record to retrieve.
* @returns {Promise<any>} A promise that resolves when the record is retrieved.
*/
function findOne(collectionName: string, key: string): Promise<any> {
return dbs[collectionName].get(key).catch(() => undefined);
}
/**
* Gets records in a collection in the data store using a selector.
* @param {string} collectionName - The name of the collection containing records to retrieve.
* @param {{ [key: string]: any }} selector - The selector to use to retrieve records.
* @param {[{ [key: string]: any }]} sort - The sort options to use to retrieve records.
* @returns {Promise<any>} A promise that resolves with the selected records.
*/
function findMany(
collectionName: string,
selector?: { [key: string]: any },
sort?: [{ [key: string]: any }]
): Promise<any> {
const keys = selector ? Object.keys(selector) : [];
const sortKeys = sort ? sort.flatMap((e) => (e ? Object.keys(e) : undefined)) : [];
// Note that we are specifying that the field must be greater than or equal to null
// which is a workaround for the fact that the Mango query language requires us to have a selector.
// In CouchDB collation order, null is the "lowest" value, and so this will return all documents regardless of their field value.
sortKeys.forEach((key) => {
if (!keys.includes(key)) {
selector = { ...selector, [key]: { $gt: null } };
}
});
// There is no selector & sort, so we can just use allDocs() to get all the documents.
if (sortKeys.concat(keys).length === 0) {
return dbs[collectionName]
.allDocs({
include_docs: true,
endkey: "_design",
inclusive_end: false,
})
.then((data) => data.rows.map((row) => row.doc));
}
}
function deleteDownloadModel(modelId: string) {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
console.debug(`Deleting ${modelId}`);
db.serialize(() => {
const stmt = db.prepare("DELETE FROM model_versions WHERE id = ?");
stmt.run(modelId);
stmt.finalize();
res(modelId);
});
db.close();
});
}
function fetchModelVersion(db: any, versionId: string) {
return new Promise((resolve, reject) => {
db.get(
"SELECT * FROM model_versions WHERE id = ?",
[versionId],
(err, row) => {
if (err) {
reject(err);
} else {
resolve(row);
}
}
);
});
}
async function fetchModel(db: any, modelId: string) {
return new Promise((resolve, reject) => {
db.get("SELECT * FROM models WHERE id = ?", [modelId], (err, row) => {
if (err) {
reject(err);
} else {
resolve(row);
}
});
});
}
const getModelById = async (versionId: string): Promise<any | undefined> => {
const db = new sqlite3.Database(getDbPath());
const modelVersion: any | undefined = await fetchModelVersion(db, versionId);
if (!modelVersion) return undefined;
const model: any | undefined = await fetchModel(db, modelVersion.model_id);
if (!model) return undefined;
const assistantModel = {
id: modelVersion.id,
name: modelVersion.name,
quantMethod: modelVersion.quant_method,
bits: modelVersion.bits,
size: modelVersion.size,
maxRamRequired: modelVersion.max_ram_required,
usecase: modelVersion.usecase,
downloadLink: modelVersion.download_link,
startDownloadAt: modelVersion.start_download_at,
finishDownloadAt: modelVersion.finish_download_at,
productId: model.id,
productName: model.name,
shortDescription: model.short_description,
longDescription: model.long_description,
avatarUrl: model.avatar_url,
author: model.author,
version: model.version,
modelUrl: model.model_url,
nsfw: model.nsfw === 0 ? false : true,
greeting: model.default_greeting,
type: model.type,
createdAt: new Date(model.created_at).getTime(),
updatedAt: new Date(model.updated_at ?? "").getTime(),
status: "",
releaseDate: -1,
tags: model.tags.split(","),
};
return assistantModel;
};
function getConversations() {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
db.all(
"SELECT * FROM conversations ORDER BY updated_at DESC",
(err: any, row: any) => {
res(row);
}
);
db.close();
});
}
function storeConversation(conversation: any): Promise<number | undefined> {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
db.serialize(() => {
const stmt = db.prepare(
"INSERT INTO conversations (name, model_id, image, message) VALUES (?, ?, ?, ?)"
);
stmt.run(
conversation.name,
conversation.model_id,
conversation.image,
conversation.message,
function (err: any) {
if (err) {
// Handle the insertion error here
console.error(err.message);
res(undefined);
return;
}
// @ts-ignoreF
const id = this.lastID;
res(id);
return;
}
);
stmt.finalize();
});
db.close();
});
}
function storeMessage(message: any): Promise<number | undefined> {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
db.serialize(() => {
const stmt = db.prepare(
"INSERT INTO messages (name, conversation_id, user, message) VALUES (?, ?, ?, ?)"
);
stmt.run(
message.name,
message.conversation_id,
message.user,
message.message,
function (err: any) {
if (err) {
// Handle the insertion error here
console.error(err.message);
res(undefined);
return;
}
//@ts-ignore
const id = this.lastID;
res(id);
return;
}
);
stmt.finalize();
});
db.close();
});
}
function updateMessage(message: any): Promise<number | undefined> {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
db.serialize(() => {
const stmt = db.prepare(
"UPDATE messages SET message = ?, updated_at = ? WHERE id = ?"
);
stmt.run(message.message, message.updated_at, message.id);
stmt.finalize();
res(message.id);
});
db.close();
});
}
function deleteConversation(id: any) {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
db.serialize(() => {
const deleteConv = db.prepare("DELETE FROM conversations WHERE id = ?");
deleteConv.run(id);
deleteConv.finalize();
const deleteMessages = db.prepare(
"DELETE FROM messages WHERE conversation_id = ?"
);
deleteMessages.run(id);
deleteMessages.finalize();
res(id);
});
db.close();
});
}
function getConversationMessages(conversation_id: any) {
return new Promise((res) => {
const db = new sqlite3.Database(getDbPath());
const query = `SELECT * FROM messages WHERE conversation_id = ${conversation_id} ORDER BY id DESC`;
db.all(query, (err: Error, row: any) => {
res(row);
});
db.close();
});
// At a basic level, there are two steps to running a query: createIndex()
// (to define which fields to index) and find() (to query the index).
return dbs[collectionName]
.createIndex({
// Create index for selector & sort
index: { fields: sortKeys.concat(keys) },
})
.then(() => {
// Find documents using Mango queries
return dbs[collectionName].find({
selector,
sort,
});
})
.then((data) => data.docs); // Return documents
}
module.exports = {
init,
getConversations,
deleteConversation,
storeConversation,
storeMessage,
updateMessage,
getConversationMessages,
storeModel,
updateFinishedDownloadAt,
getUnfinishedDownloadModels,
getFinishedDownloadModels,
deleteDownloadModel,
getModelById,
createCollection,
deleteCollection,
insertOne,
findOne,
findMany,
updateOne,
updateMany,
deleteOne,
deleteMany,
};

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,27 @@
{
"name": "data-plugin",
"version": "1.0.2",
"description": "Jan Database Plugin efficiently stores conversation and model data using SQLite, providing accessible data management",
"version": "1.0.3",
"description": "The Data Connector provides easy access to a data API using the PouchDB engine. It offers accessible data management capabilities.",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/circle-stack.svg",
"main": "dist/index.js",
"main": "dist/esm/index.js",
"author": "Jan",
"license": "MIT",
"activationPoints": [
"init"
],
"scripts": {
"build": "tsc -b . && webpack --config webpack.config.js",
"postinstall": "rimraf ./data-plugin*.tgz && node-pre-gyp install --directory=./node_modules/sqlite3 --target_platform=darwin --target_libc=unknown --target_arch=x64 && node-pre-gyp install --directory=./node_modules/sqlite3 --target_platform=darwin --target_libc=unknown --target_arch=arm64 && node-pre-gyp install --directory=./node_modules/sqlite3 --target_platform=linux --target_libc=glibc --target_arch=x64 && node-pre-gyp install --directory=./node_modules/sqlite3 --target_platform=linux --target_libc=musl --target_arch=x64 && node-pre-gyp install --directory=./node_modules/sqlite3 --target_platform=win32 --target_libc=unknown --target_arch=x64 && npm run build",
"build": "tsc --project ./config/tsconfig.esm.json && tsc --project ./config/tsconfig.cjs.json && webpack --config webpack.config.js",
"postinstall": "rimraf ./data-plugin*.tgz && npm run build",
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
},
"exports": {
".": "./dist/index.js",
"./main": "./dist/module.js"
"import": "./dist/esm/index.js",
"require": "./dist/cjs/module.js",
"default": "./dist/esm/index.js"
},
"devDependencies": {
"cpx": "^1.5.0",
"node-pre-gyp": "^0.17.0",
"rimraf": "^3.0.2",
"ts-loader": "^9.4.4",
"ts-node": "^10.9.1",
@ -28,8 +30,8 @@
"webpack-cli": "^5.1.4"
},
"bundledDependencies": [
"sql.js",
"sqlite3"
"pouchdb-node",
"pouchdb-find"
],
"files": [
"dist/**",
@ -37,7 +39,8 @@
"node_modules"
],
"dependencies": {
"node-pre-gyp": "^0.17.0",
"sqlite3": "^5.1.6"
"@janhq/plugin-core": "file:../../../../plugin-core",
"pouchdb-find": "^8.0.1",
"pouchdb-node": "^8.0.1"
}
}

View File

@ -1,22 +1,12 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
/* Modules */
"module": "ES6" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "." /* Specify the base directory to resolve non-relative module names. */,
// "paths": {} /* Specify a set of entries that re-map imports to additional lookup locations. */,
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "resolveJsonModule": true, /* Enable importing .json files. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": false /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"target": "es2016",
"module": "ES6",
"moduleResolution": "node",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true
}
}

View File

@ -1,7 +0,0 @@
export {};
declare global {
interface Window {
electronAPI?: any | undefined;
}
}

View File

@ -14,12 +14,15 @@ module.exports = {
],
},
output: {
filename: "index.js", // Adjust the output file name as needed
filename: "esm/index.js", // Adjust the output file name as needed
path: path.resolve(__dirname, "dist"),
library: { type: "module" }, // Specify ESM output format
},
resolve: {
extensions: [".ts", ".js"],
},
optimization: {
minimize: false
},
// Add loaders and other configuration as needed for your project
};

View File

@ -1,67 +0,0 @@
const MODULE_PATH = "model-management-plugin/dist/module.js";
const getDownloadedModels = () =>
new Promise(async (resolve) => {
if (window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getDownloadedModels")
.then((res) => resolve(res));
}
});
const getAvailableModels = () =>
new Promise(async (resolve) => {
if (window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getAvailableModels")
.then((res) => resolve(res));
}
});
const downloadModel = (product) =>
new Promise(async (resolve) => {
if (window && window.electronAPI) {
window.electronAPI
.downloadFile(product.downloadUrl, product.fileName)
.then((res) => resolve(res));
} else {
resolve("-");
}
});
const deleteModel = (path) =>
new Promise(async (resolve) => {
if (window.electronAPI) {
console.debug(`Delete model model management plugin: ${path}`);
const response = await window.electronAPI.deleteFile(path);
resolve(response);
}
});
const searchModels = (params) =>
new Promise(async (resolve) => {
if (window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "searchModels", params)
.then((res) => resolve(res));
}
});
const getConfiguredModels = () =>
new Promise(async (resolve) => {
if (window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "getConfiguredModels")
.then((res) => resolve(res));
}
});
// Register all the above functions and objects with the relevant extension points
export function init({ register }) {
register("getDownloadedModels", "getDownloadedModels", getDownloadedModels);
register("getAvailableModels", "getAvailableModels", getAvailableModels);
register("downloadModel", "downloadModel", downloadModel);
register("deleteModel", "deleteModel", deleteModel);
register("searchModels", "searchModels", searchModels);
register("getConfiguredModels", "getConfiguredModels", getConfiguredModels);
}

View File

@ -0,0 +1,103 @@
import { ModelManagementService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";
const MODULE_PATH = "model-management-plugin/dist/module.js";
const getDownloadedModels = () => core.invokePluginFunc(MODULE_PATH, "getDownloadedModels");
const getAvailableModels = () => core.invokePluginFunc(MODULE_PATH, "getAvailableModels");
const downloadModel = (product) => core.downloadFile(product.downloadUrl, product.fileName);
const deleteModel = (path) => core.deleteFile(path);
const searchModels = (params) => core.invokePluginFunc(MODULE_PATH, "searchModels", params);
const getConfiguredModels = () => core.invokePluginFunc(MODULE_PATH, "getConfiguredModels");
/**
* Store a model in the database when user start downloading it
*
* @param model Product
*/
function storeModel(model: any) {
return store.findOne("models", model._id).then((doc) => {
if (doc) {
return store.updateOne("models", model._id, model);
} else {
return store.insertOne("models", model);
}
});
}
/**
* Update the finished download time of a model
*
* @param model Product
*/
function updateFinishedDownloadAt(_id: string): Promise<any> {
return store.updateMany("models", { _id }, { time: Date.now(), finishDownloadAt: 1 });
}
/**
* Retrieves all unfinished models from the database.
*
* @returns A promise that resolves with an array of unfinished models.
*/
function getUnfinishedDownloadModels(): Promise<any> {
return store.findMany("models", { finishDownloadAt: -1 }, [{ startDownloadAt: "desc" }]);
}
/**
* Retrieves all finished models from the database.
*
* @returns A promise that resolves with an array of finished models.
*/
function getFinishedDownloadModels(): Promise<any> {
return store.findMany("models", { finishDownloadAt: 1 });
}
/**
* Deletes a model from the database.
*
* @param modelId The ID of the model to delete.
* @returns A promise that resolves when the model is deleted.
*/
function deleteDownloadModel(modelId: string): Promise<any> {
return store.deleteOne("models", modelId);
}
/**
* Retrieves a model from the database by ID.
*
* @param modelId The ID of the model to retrieve.
* @returns A promise that resolves with the model.
*/
function getModelById(modelId: string): Promise<any> {
return store.findOne("models", modelId);
}
function onStart() {
store.createCollection("models", {});
}
// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: RegisterExtensionPoint }) {
onStart();
register(ModelManagementService.GetDownloadedModels, getDownloadedModels.name, getDownloadedModels);
register(ModelManagementService.GetAvailableModels, getAvailableModels.name, getAvailableModels);
register(ModelManagementService.DownloadModel, downloadModel.name, downloadModel);
register(ModelManagementService.DeleteModel, deleteModel.name, deleteModel);
register(ModelManagementService.SearchModels, searchModels.name, searchModels);
register(ModelManagementService.GetConfiguredModels, getConfiguredModels.name, getConfiguredModels);
register(ModelManagementService.StoreModel, storeModel.name, storeModel);
register(ModelManagementService.UpdateFinishedDownloadAt, updateFinishedDownloadAt.name, updateFinishedDownloadAt);
register(
ModelManagementService.GetUnfinishedDownloadModels,
getUnfinishedDownloadModels.name,
getUnfinishedDownloadModels
);
register(ModelManagementService.DeleteDownloadModel, deleteDownloadModel.name, deleteDownloadModel);
register(ModelManagementService.GetModelById, getModelById.name, getModelById);
register(ModelManagementService.GetFinishedDownloadModels, getFinishedDownloadModels.name, getFinishedDownloadModels);
}

View File

@ -83,7 +83,7 @@ const listFilesByName = async (modelName) => {
};
async function getConfiguredModels() {
const files = await getModelFiles();
const files: any = await getModelFiles();
const promises = files.map((file) => getContent(file));
const response = await Promise.all(promises);
@ -100,7 +100,7 @@ const parseToModel = (model) => {
const modelVersions = [];
model.versions.forEach((v) => {
const version = {
id: `${model.author}-${v.name}`,
_id: `${model.author}-${v.name}`,
name: v.name,
quantMethod: v.quantMethod,
bits: v.bits,
@ -114,7 +114,7 @@ const parseToModel = (model) => {
});
const product = {
id: model.id,
_id: model.id,
name: model.name,
shortDescription: model.shortDescription,
avatarUrl: model.avatarUrl,

View File

@ -13,7 +13,9 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@huggingface/hub": "^0.8.5"
"@huggingface/hub": "^0.8.5",
"@janhq/plugin-core": "file:../../../../plugin-core",
"ts-loader": "^9.5.0"
},
"devDependencies": {
"cpx": "^1.5.0",
@ -22,6 +24,14 @@
"webpack-cli": "^5.1.4"
}
},
"../../../../plugin-core": {
"name": "@janhq/plugin-core",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.0.2"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@ -43,11 +53,14 @@
"node": ">=18"
}
},
"node_modules/@janhq/plugin-core": {
"resolved": "../../../../plugin-core",
"link": true
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
"dev": true,
"dependencies": {
"@jridgewell/set-array": "^1.0.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
@ -61,7 +74,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@ -70,7 +82,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@ -79,7 +90,6 @@
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
@ -88,14 +98,12 @@
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
"integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
@ -105,7 +113,6 @@
"version": "8.44.3",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz",
"integrity": "sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==",
"dev": true,
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@ -115,7 +122,6 @@
"version": "3.7.5",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.5.tgz",
"integrity": "sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==",
"dev": true,
"dependencies": {
"@types/eslint": "*",
"@types/estree": "*"
@ -124,26 +130,22 @@
"node_modules/@types/estree": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.2.tgz",
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==",
"dev": true
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA=="
},
"node_modules/@types/json-schema": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
"integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==",
"dev": true
"integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ=="
},
"node_modules/@types/node": {
"version": "20.8.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.2.tgz",
"integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w==",
"dev": true
"integrity": "sha512-Vvycsc9FQdwhxE3y3DzeIxuEJbWGDsnrxvMADzTDF/lcdR9/K+AQIeAghTQsHtotg/q0j3WEOYS/jQgSdWue3w=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz",
"integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==",
"dev": true,
"dependencies": {
"@webassemblyjs/helper-numbers": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
@ -152,26 +154,22 @@
"node_modules/@webassemblyjs/floating-point-hex-parser": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
"dev": true
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
},
"node_modules/@webassemblyjs/helper-api-error": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
"dev": true
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
},
"node_modules/@webassemblyjs/helper-buffer": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz",
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==",
"dev": true
"integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA=="
},
"node_modules/@webassemblyjs/helper-numbers": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
"dev": true,
"dependencies": {
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
@ -181,14 +179,12 @@
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
"dev": true
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
},
"node_modules/@webassemblyjs/helper-wasm-section": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz",
"integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
@ -200,7 +196,6 @@
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
"dev": true,
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
@ -209,7 +204,6 @@
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
"dev": true,
"dependencies": {
"@xtuc/long": "4.2.2"
}
@ -217,14 +211,12 @@
"node_modules/@webassemblyjs/utf8": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
"dev": true
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
},
"node_modules/@webassemblyjs/wasm-edit": {
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz",
"integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
@ -240,7 +232,6 @@
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz",
"integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
@ -253,7 +244,6 @@
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz",
"integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-buffer": "1.11.6",
@ -265,7 +255,6 @@
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz",
"integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@webassemblyjs/helper-api-error": "1.11.6",
@ -279,7 +268,6 @@
"version": "1.11.6",
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz",
"integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==",
"dev": true,
"dependencies": {
"@webassemblyjs/ast": "1.11.6",
"@xtuc/long": "4.2.2"
@ -332,20 +320,17 @@
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
"dev": true
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
},
"node_modules/@xtuc/long": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
"node_modules/acorn": {
"version": "8.10.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
@ -357,7 +342,6 @@
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
"integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
"dev": true,
"peerDependencies": {
"acorn": "^8"
}
@ -366,7 +350,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -382,11 +365,24 @@
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true,
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/anymatch": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
@ -571,7 +567,6 @@
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
"integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -602,8 +597,7 @@
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
},
"node_modules/cache-base": {
"version": "1.0.1",
@ -638,7 +632,6 @@
"version": "1.0.30001543",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001543.tgz",
"integrity": "sha512-qxdO8KPWPQ+Zk6bvNpPeQIOH47qZSYdFZd6dXQzb2KzhnSXju4Kd7H1PkSJx6NICSMgo/IhRZRhhfPTHYpJUCA==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -654,6 +647,32 @@
}
]
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
@ -678,7 +697,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
"dev": true,
"engines": {
"node": ">=6.0"
}
@ -802,6 +820,22 @@
"node": ">=0.10.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
@ -811,8 +845,7 @@
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
"node_modules/component-emitter": {
"version": "1.3.0",
@ -934,14 +967,12 @@
"node_modules/electron-to-chromium": {
"version": "1.4.542",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.542.tgz",
"integrity": "sha512-6+cpa00G09N3sfh2joln4VUXHquWrOFx3FLZqiVQvl45+zS9DskDBTPvob+BhvFRmTBkyDSk0vvLMMRo/qc6mQ==",
"dev": true
"integrity": "sha512-6+cpa00G09N3sfh2joln4VUXHquWrOFx3FLZqiVQvl45+zS9DskDBTPvob+BhvFRmTBkyDSk0vvLMMRo/qc6mQ=="
},
"node_modules/enhanced-resolve": {
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@ -965,14 +996,12 @@
"node_modules/es-module-lexer": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz",
"integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==",
"dev": true
"integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q=="
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -981,7 +1010,6 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^4.1.1"
@ -994,7 +1022,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true,
"dependencies": {
"estraverse": "^5.2.0"
},
@ -1006,7 +1033,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
}
@ -1015,7 +1041,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true,
"engines": {
"node": ">=4.0"
}
@ -1024,7 +1049,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"dev": true,
"engines": {
"node": ">=0.8.x"
}
@ -1093,14 +1117,12 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
@ -1274,8 +1296,7 @@
"node_modules/glob-to-regexp": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
"dev": true
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"node_modules/glob2base": {
"version": "0.0.12",
@ -1292,8 +1313,7 @@
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/has": {
"version": "1.0.4",
@ -1308,7 +1328,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@ -1669,7 +1688,6 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@ -1682,14 +1700,12 @@
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"node_modules/kind-of": {
"version": "3.2.2",
@ -1707,7 +1723,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
"dev": true,
"engines": {
"node": ">=6.11.5"
}
@ -1724,6 +1739,17 @@
"node": ">=8"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/map-cache": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
@ -1754,8 +1780,7 @@
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"dev": true
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"node_modules/micromatch": {
"version": "2.3.11",
@ -1785,7 +1810,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@ -1794,7 +1818,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
@ -1925,14 +1948,12 @@
"node_modules/neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
"node_modules/node-releases": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
"dev": true
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
},
"node_modules/normalize-path": {
"version": "2.1.1",
@ -2179,8 +2200,18 @@
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
@ -2222,7 +2253,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -2263,7 +2293,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"dependencies": {
"safe-buffer": "^5.1.0"
}
@ -2743,7 +2772,6 @@
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
@ -2772,7 +2800,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.8",
"ajv": "^6.12.5",
@ -2786,11 +2813,24 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/serialize-javascript": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
"dev": true,
"dependencies": {
"randombytes": "^2.1.0"
}
@ -3023,7 +3063,6 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@ -3046,7 +3085,6 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
@ -3171,7 +3209,6 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@ -3198,7 +3235,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"dev": true,
"engines": {
"node": ">=6"
}
@ -3207,7 +3243,6 @@
"version": "5.21.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz",
"integrity": "sha512-WtnFKrxu9kaoXuiZFSGrcAvvBqAdmKx0SFNmVNYdJamMu9yyN3I/QF0FbH4QcqJQ+y1CJnzxGIKH0cSj+FGYRw==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
@ -3225,7 +3260,6 @@
"version": "5.3.9",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz",
"integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==",
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.17",
"jest-worker": "^27.4.5",
@ -3307,6 +3341,99 @@
"node": ">=0.10.0"
}
},
"node_modules/ts-loader": {
"version": "9.5.0",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz",
"integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==",
"dependencies": {
"chalk": "^4.1.0",
"enhanced-resolve": "^5.0.0",
"micromatch": "^4.0.0",
"semver": "^7.3.4",
"source-map": "^0.7.4"
},
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"typescript": "*",
"webpack": "^5.0.0"
}
},
"node_modules/ts-loader/node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dependencies": {
"fill-range": "^7.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ts-loader/node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ts-loader/node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/ts-loader/node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dependencies": {
"braces": "^3.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/ts-loader/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"engines": {
"node": ">= 8"
}
},
"node_modules/ts-loader/node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/union-value": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
@ -3383,7 +3510,6 @@
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -3413,7 +3539,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"dependencies": {
"punycode": "^2.1.0"
}
@ -3444,7 +3569,6 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"dev": true,
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
@ -3457,7 +3581,6 @@
"version": "5.88.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
"integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
"dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",
@ -3571,7 +3694,6 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"dev": true,
"engines": {
"node": ">=10.13.0"
}
@ -3602,6 +3724,11 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
}

View File

@ -1,7 +1,7 @@
{
"name": "model-management-plugin",
"version": "1.0.0",
"description": "Model Management Plugin leverages the HuggingFace API for model exploration and seamless downloads",
"description": "Model Management Plugin provides model exploration and seamless downloads",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/queue-list.svg",
"main": "dist/index.js",
"author": "James",
@ -10,8 +10,8 @@
"init"
],
"scripts": {
"build": "webpack --config webpack.config.js",
"postinstall": "rimraf ./*.tgz && npm run build && cpx \"module.js\" \"dist\"",
"build": "tsc -b . && webpack --config webpack.config.js",
"postinstall": "rimraf ./*.tgz && npm run build",
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
},
"devDependencies": {
@ -26,7 +26,9 @@
"README.md"
],
"dependencies": {
"@huggingface/hub": "^0.8.5"
"@huggingface/hub": "^0.8.5",
"@janhq/plugin-core": "file:../../../../plugin-core",
"ts-loader": "^9.5.0"
},
"bundledDependencies": [
"@huggingface/hub"

View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2016",
"module": "ES6",
"moduleResolution": "node",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true
}
}

View File

@ -2,7 +2,7 @@ const path = require("path");
module.exports = {
experiments: { outputModule: true },
entry: "./index.js", // Adjust the entry point to match your project's main file
entry: "./index.ts", // Adjust the entry point to match your project's main file
mode: "production",
module: {
rules: [
@ -19,7 +19,10 @@ module.exports = {
library: { type: "module" }, // Specify ESM output format
},
resolve: {
extensions: [".js"],
extensions: [".ts", ".js"],
},
optimization: {
minimize: false
},
// Add loaders and other configuration as needed for your project
};

View File

@ -19,6 +19,7 @@
"dev:electron": "yarn workspace jan dev",
"dev:web": "yarn workspace jan-web dev",
"dev": "concurrently --kill-others \"yarn dev:web\" \"wait-on http://localhost:3000 && yarn dev:electron\"",
"build:core": "cd plugin-core && yarn install && yarn run build",
"build:web": "yarn workspace jan-web build && cpx \"web/out/**\" \"electron/renderer/\"",
"build:electron": "yarn workspace jan build",
"build:plugins": "rimraf ./electron/core/pre-install/*.tgz && concurrently \"cd ./electron/core/plugins/data-plugin && npm ci\" \"cd ./electron/core/plugins/inference-plugin && npm ci\" \"cd ./electron/core/plugins/model-management-plugin && npm ci\" \"cd ./electron/core/plugins/monitoring-plugin && npm ci\" && concurrently \"cd ./electron/core/plugins/data-plugin && npm run build:publish\" \"cd ./electron/core/plugins/inference-plugin && npm run build:publish\" \"cd ./electron/core/plugins/model-management-plugin && npm run build:publish\" \"cd ./electron/core/plugins/monitoring-plugin && npm run build:publish\"",

261
plugin-core/README.md Normal file
View File

@ -0,0 +1,261 @@
## @janhq/plugin-core
> The module includes functions for communicating with core APIs, registering plugin extensions, and exporting type definitions.
## Usage
### Import the package
```js
// javascript
const core = require("@janhq/plugin-core");
// typescript
import { core } from "@janhq/plugin-core";
```
### Register Plugin Extensions
Every plugin must define an `init` function in its main entry file to initialize the plugin and register its extensions with the Jan platform.
You can `register` any function as a plugin extension using `CoreServiceAPI` below. For example, the `DataService.GetConversations` entry name can be used to register a function that retrieves conversations.
Once the extension is registered, it can be used by other plugins or components in the Jan platform. For example, a UI component might use the DataService.GetConversations extension to retrieve a list of conversations to display to the user.
```js
import { RegisterExtensionPoint, DataService } from "@janhq/plugin-core";
function getConversations() {
// Your logic here
}
export function init({ register }: { register: RegisterExtensionPoint }) {
register(DataService.GetConversations, getConversations.name, getConversations);
}
```
### Access Core API
To access the Core API in your plugin, you can follow the code examples and explanations provided below.
##### Import Core API and Store Module
In your main entry code (e.g., `index.ts`), start by importing the necessary modules and functions from the `@janhq/plugin-core` library.
```js
// index.ts
import { store, core } from "@janhq/plugin-core";
```
#### Interact with Local Data Storage
The Core API allows you to interact with local data storage. Here are a couple of examples of how you can use it:
#### Insert Data
You can use the store.insertOne function to insert data into a specific collection in the local data store.
```js
function insertData() {
store.insertOne("conversations", { name: "meow" });
// Insert a new document with { name: "meow" } into the "conversations" collection.
}
```
#### Get Data
To retrieve data from a collection in the local data store, you can use the `store.findOne` or `store.findMany` function. It allows you to filter and retrieve documents based on specific criteria.
store.getOne(collectionName, key) retrieves a single document that matches the provided key in the specified collection.
store.getMany(collectionName, selector, sort) retrieves multiple documents that match the provided selector in the specified collection.
```js
function getData() {
const selector = { name: "meow" };
const data = store.findMany("conversations", selector);
// Retrieve documents from the "conversations" collection that match the filter.
}
```
#### Update Data
You can update data in the local store using these functions:
store.updateOne(collectionName, key, update) updates a single document that matches the provided key in the specified collection.
store.updateMany(collectionName, selector, update) updates multiple documents that match the provided selector in the specified collection.
```js
function updateData() {
const selector = { name: "meow" };
const update = { name: "newName" };
store.updateOne("conversations", selector, update);
// Update a document in the "conversations" collection.
}
```
#### Delete Data
You can delete data from the local data store using these functions:
store.deleteOne(collectionName, key) deletes a single document that matches the provided key in the specified collection.
store.deleteMany(collectionName, selector) deletes multiple documents that match the provided selector in the specified collection.
```js
function deleteData() {
const selector = { name: "meow" };
store.deleteOne("conversations", selector);
// Delete a document from the "conversations" collection.
}
```
#### Perform File Operations
The Core API also provides functions to perform file operations. Here are a couple of examples:
#### Download a File
You can download a file from a specified URL and save it with a given file name using the core.downloadFile function.
```js
function downloadModel(url: string, fileName: string) {
core.downloadFile(url, fileName);
}
```
#### Delete a File
To delete a file, you can use the core.deleteFile function, providing the path to the file you want to delete.
```js
function deleteModel(filePath: string) {
core.deleteFile(path);
}
```
### Execute plugin module in main process
To execute a plugin module in the main process of your application, you can follow the steps outlined below.
##### Import the `core` Object
In your main process code (e.g., `index.ts`), start by importing the `core` object from the `@janhq/plugin-core` library.
```js
// index.ts
import { core } from "@janhq/plugin-core";
```
##### Define the Module Path
Specify the path to the plugin module you want to execute. This path should lead to the module file (e.g., module.js) that contains the functions you wish to call.
```js
// index.ts
const MODULE_PATH = "data-plugin/dist/module.js";
```
##### Define the Function to Execute
Create a function that will execute a function defined in your plugin module. In the example provided, the function `getConversationMessages` is created to invoke the `getConvMessages` function from the plugin module.
```js
// index.ts
function getConversationMessages(id: number) {
return core.invokePluginFunc(MODULE_PATH, "getConvMessages", id);
}
export function init({ register }: { register: RegisterExtensionPoint }) {
register(DataService.GetConversationMessages, getConversationMessages.name, getConversationMessages);
}
```
##### Define Your Plugin Module
In your plugin module (e.g., module.ts), define the logic for the function you wish to execute. In the example, the function getConvMessages is defined with a placeholder comment indicating where your logic should be implemented.
```js
// module.ts
function getConvMessages(id: number) {
// Your logic here
}
module.exports = {
getConvMessages
}
```
## CoreService API
The `CoreService` type is an exported union type that includes:
- `StoreService`
- `DataService`
- `InferenceService`
- `ModelManagementService`
- `SystemMonitoringService`
- `PreferenceService`
## StoreService
The `StoreService` enum represents available methods for managing the database store. It includes the following methods:
- `CreateCollection`: Creates a new collection in the data store.
- `DeleteCollection`: Deletes an existing collection from the data store.
- `InsertOne`: Inserts a new value into an existing collection in the data store.
- `UpdateOne`: Updates an existing value in an existing collection in the data store.
- `UpdateMany`: Updates multiple records in a collection in the data store.
- `DeleteOne`: Deletes an existing value from an existing collection in the data store.
- `DeleteMany`: Deletes multiple records in a collection in the data store.
- `FindMany`: Retrieves multiple records from a collection in the data store.
- `FindOne`: Retrieves a single record from a collection in the data store.
## DataService
The `DataService` enum represents methods related to managing conversations and messages. It includes the following methods:
- `GetConversations`: Gets a list of conversations from the data store.
- `CreateConversation`: Creates a new conversation in the data store.
- `DeleteConversation`: Deletes an existing conversation from the data store.
- `CreateMessage`: Creates a new message in an existing conversation in the data store.
- `UpdateMessage`: Updates an existing message in an existing conversation in the data store.
- `GetConversationMessages`: Gets a list of messages for an existing conversation from the data store.
## InferenceService
The `InferenceService` enum exports:
- `InferenceUrl`: The URL for the inference server.
- `InitModel`: Initializes a model for inference.
- `StopModel`: Stops a running inference model.
## ModelManagementService
The `ModelManagementService` enum provides methods for managing models:
- `GetDownloadedModels`: Gets a list of downloaded models.
- `GetAvailableModels`: Gets a list of available models from data store.
- `DeleteModel`: Deletes a downloaded model.
- `DownloadModel`: Downloads a model from the server.
- `SearchModels`: Searches for models on the server.
- `GetConfiguredModels`: Gets configured models from the data store.
- `StoreModel`: Stores a model in the data store.
- `UpdateFinishedDownloadAt`: Updates the finished download time for a model in the data store.
- `GetUnfinishedDownloadModels`: Gets a list of unfinished download models from the data store.
- `GetFinishedDownloadModels`: Gets a list of finished download models from the data store.
- `DeleteDownloadModel`: Deletes a downloaded model from the data store.
- `GetModelById`: Gets a model by its ID from the data store.
## PreferenceService
The `PreferenceService` enum provides methods for managing plugin preferences:
- `ExperimentComponent`: Represents the UI experiment component for a testing function.
## SystemMonitoringService
The `SystemMonitoringService` enum includes methods for monitoring system resources:
- `GetResourcesInfo`: Gets information about system resources.
- `GetCurrentLoad`: Gets the current system load.
For more detailed information on each of these components, please refer to the source code.

51
plugin-core/core.ts Normal file
View File

@ -0,0 +1,51 @@
/**
* Execute a plugin module function in main process
*
* @param plugin plugin name to import
* @param method function name to execute
* @param args arguments to pass to the function
* @returns Promise<any>
*
*/
const invokePluginFunc: (
plugin: string,
method: string,
...args: any[]
) => Promise<any> = (plugin, method, ...args) =>
window.coreAPI?.invokePluginFunc(plugin, method, ...args) ??
window.electronAPI?.invokePluginFunc(plugin, method, ...args);
/**
* Downloads a file from a URL and saves it to the local file system.
* @param {string} url - The URL of the file to download.
* @param {string} fileName - The name to use for the downloaded file.
* @returns {Promise<any>} A promise that resolves when the file is downloaded.
*/
const downloadFile: (url: string, fileName: string) => Promise<any> = (url, fileName) =>
window.coreAPI?.downloadFile(url, fileName) ?? window.electronAPI?.downloadFile(url, fileName);
/**
* Deletes a file from the local file system.
* @param {string} path - The path of the file to delete.
* @returns {Promise<any>} A promise that resolves when the file is deleted.
*/
const deleteFile: (path: string) => Promise<any> = (path) =>
window.coreAPI?.deleteFile(path) ?? window.electronAPI?.deleteFile(path);
/** Register extension point function type definition
*
*/
export type RegisterExtensionPoint = (
extensionName: string,
extensionId: string,
method: Function,
priority?: number
) => void;
/**
* Core exports
*/
export const core = {
invokePluginFunc,
downloadFile,
deleteFile,
};

224
plugin-core/index.ts Normal file
View File

@ -0,0 +1,224 @@
/**
* CoreService exports
*/
export type CoreService =
| StoreService
| DataService
| InferenceService
| ModelManagementService
| SystemMonitoringService
| PreferenceService;
/**
* Represents the available methods for the StoreService.
* @enum {string}
*/
export enum StoreService {
/**
* Creates a new collection in the database store.
*/
CreateCollection = "createCollection",
/**
* Deletes an existing collection from the database store.
*/
DeleteCollection = "deleteCollection",
/**
* Inserts a new value into an existing collection in the database store.
*/
InsertOne = "insertOne",
/**
* Updates an existing value in an existing collection in the database store.
*/
UpdateOne = "updateOne",
/**
* Updates multiple records in a collection in the database store.
*/
UpdateMany = "updateMany",
/**
* Deletes an existing value from an existing collection in the database store.
*/
DeleteOne = "deleteOne",
/**
* Delete multiple records in a collection in the database store.
*/
DeleteMany = "deleteMany",
/**
* Retrieve multiple records from a collection in the data store
*/
FindMany = "findMany",
/**
* Retrieve a record from a collection in the data store.
*/
FindOne = "findOne",
}
/**
* DataService exports.
* @enum {string}
*/
export enum DataService {
/**
* Gets a list of conversations from the server.
*/
GetConversations = "getConversations",
/**
* Creates a new conversation on the server.
*/
CreateConversation = "createConversation",
/**
* Deletes an existing conversation from the server.
*/
DeleteConversation = "deleteConversation",
/**
* Creates a new message in an existing conversation on the server.
*/
CreateMessage = "createMessage",
/**
* Updates an existing message in an existing conversation on the server.
*/
UpdateMessage = "updateMessage",
/**
* Gets a list of messages for an existing conversation from the server.
*/
GetConversationMessages = "getConversationMessages",
}
/**
* InferenceService exports.
* @enum {string}
*/
export enum InferenceService {
/**
* The URL for the inference server.
*/
InferenceUrl = "inferenceUrl",
/**
* Initializes a model for inference.
*/
InitModel = "initModel",
/**
* Stops a running inference model.
*/
StopModel = "stopModel",
}
/**
* ModelManagementService exports.
* @enum {string}
*/
export enum ModelManagementService {
/**
* Gets a list of downloaded models.
*/
GetDownloadedModels = "getDownloadedModels",
/**
* Gets a list of available models from the server.
*/
GetAvailableModels = "getAvailableModels",
/**
* Deletes a downloaded model.
*/
DeleteModel = "deleteModel",
/**
* Downloads a model from the server.
*/
DownloadModel = "downloadModel",
/**
* Searches for models on the server.
*/
SearchModels = "searchModels",
/**
* Gets configued models from the database.
*/
GetConfiguredModels = "getConfiguredModels",
/**
* Stores a model in the database.
*/
StoreModel = "storeModel",
/**
* Updates the finished download time for a model in the database.
*/
UpdateFinishedDownloadAt = "updateFinishedDownloadAt",
/**
* Gets a list of unfinished download models from the database.
*/
GetUnfinishedDownloadModels = "getUnfinishedDownloadModels",
/**
* Gets a list of finished download models from the database.
*/
GetFinishedDownloadModels = "getFinishedDownloadModels",
/**
* Deletes a download model from the database.
*/
DeleteDownloadModel = "deleteDownloadModel",
/**
* Gets a model by its ID from the database.
*/
GetModelById = "getModelById",
}
/**
* PreferenceService exports.
* @enum {string}
*/
export enum PreferenceService {
/**
* The experiment component for which preferences are being managed.
*/
ExperimentComponent = "experimentComponent",
}
/**
* SystemMonitoringService exports.
* @enum {string}
*/
export enum SystemMonitoringService {
/**
* Gets information about system resources.
*/
GetResourcesInfo = "getResourcesInfo",
/**
* Gets the current system load.
*/
GetCurrentLoad = "getCurrentLoad",
}
/**
* Store module exports.
* @module
*/
export { store } from "./store";
/**
* Core module exports.
* @module
*/
export { core, RegisterExtensionPoint } from "./core";

22
plugin-core/package-lock.json generated Normal file
View File

@ -0,0 +1,22 @@
{
"name": "@janhq/plugin-core",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@janhq/plugin-core",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.0.2"
}
},
"node_modules/@types/node": {
"version": "12.20.55",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
"dev": true
}
}
}

37
plugin-core/package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "@janhq/plugin-core",
"version": "0.1.0",
"description": "Plugin core lib",
"keywords": [
"jan",
"plugin",
"core"
],
"homepage": "https://github.com/janhq",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"exports": {
".": "./lib/index.js",
"./store": "./lib/store.js"
},
"files": [
"lib",
"README.md",
"LICENSE.md",
"package.json",
"!.DS_Store"
],
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
"build": "tsc"
},
"devDependencies": {
"@types/node": "^12.0.2",
"typescript": "^5.2.2"
}
}

129
plugin-core/store.ts Normal file
View File

@ -0,0 +1,129 @@
/**
* Creates, reads, updates, and deletes data in a data store.
* @module
*/
/**
* Creates a new collection in the data store.
* @param {string} name - The name of the collection to create.
* @param { [key: string]: any } schema - schema of the collection to create, include fields and their types
* @returns {Promise<void>} A promise that resolves when the collection is created.
*/
function createCollection(
name: string,
schema: { [key: string]: any }
): Promise<void> {
return window.corePlugin?.store?.createCollection(name, schema);
}
/**
* Deletes a collection from the data store.
* @param {string} name - The name of the collection to delete.
* @returns {Promise<void>} A promise that resolves when the collection is deleted.
*/
function deleteCollection(name: string): Promise<void> {
return window.corePlugin?.store?.deleteCollection(name);
}
/**
* Inserts a value into a collection in the data store.
* @param {string} collectionName - The name of the collection to insert the value into.
* @param {any} value - The value to insert into the collection.
* @returns {Promise<any>} A promise that resolves with the inserted value.
*/
function insertOne(collectionName: string, value: any): Promise<any> {
return window.corePlugin?.store?.insertOne(collectionName, value);
}
/**
* Retrieve a record from a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to retrieve.
* @param {string} key - The key of the record to retrieve.
* @returns {Promise<any>} A promise that resolves when the record is retrieved.
*/
function findOne(collectionName: string, key: string): Promise<any> {
return window.corePlugin?.store?.findOne(collectionName, key);
}
/**
* Retrieves all records that match a selector in a collection in the data store.
* @param {string} collectionName - The name of the collection to retrieve.
* @param {{ [key: string]: any }} selector - The selector to use to get records from the collection.
* @param {[{ [key: string]: any }]} sort - The sort options to use to retrieve records.
* @returns {Promise<any>} A promise that resolves when all records are retrieved.
*/
function findMany(
collectionName: string,
selector?: { [key: string]: any },
sort?: [{ [key: string]: any }]
): Promise<any> {
return window.corePlugin?.store?.findMany(collectionName, selector, sort);
}
/**
* Updates the value of a record in a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to update.
* @param {string} key - The key of the record to update.
* @param {any} value - The new value for the record.
* @returns {Promise<void>} A promise that resolves when the record is updated.
*/
function updateOne(
collectionName: string,
key: string,
value: any
): Promise<void> {
return window.corePlugin?.store?.updateOne(collectionName, key, value);
}
/**
* Updates all records that match a selector in a collection in the data store.
* @param {string} collectionName - The name of the collection containing the records to update.
* @param {{ [key: string]: any }} selector - The selector to use to get the records to update.
* @param {any} value - The new value for the records.
* @returns {Promise<void>} A promise that resolves when the records are updated.
*/
function updateMany(
collectionName: string,
value: any,
selector?: { [key: string]: any }
): Promise<void> {
return window.corePlugin?.store?.updateMany(collectionName, selector, value);
}
/**
* Deletes a single record from a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to delete.
* @param {string} key - The key of the record to delete.
* @returns {Promise<void>} A promise that resolves when the record is deleted.
*/
function deleteOne(collectionName: string, key: string): Promise<void> {
return window.corePlugin?.store?.deleteOne(collectionName, key);
}
/**
* Deletes all records with a matching key from a collection in the data store.
* @param {string} collectionName - The name of the collection to delete the records from.
* @param {{ [key: string]: any }} selector - The selector to use to get the records to delete.
* @returns {Promise<void>} A promise that resolves when the records are deleted.
*/
function deleteMany(
collectionName: string,
selector?: { [key: string]: any }
): Promise<void> {
return window.corePlugin?.store?.deleteMany(collectionName, selector);
}
/**
* Exports the data store operations as an object.
*/
export const store = {
createCollection,
deleteCollection,
insertOne,
findOne,
findMany,
updateOne,
updateMany,
deleteOne,
deleteMany,
};

13
plugin-core/tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2016",
"module": "ES6",
"outDir": "./lib",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"declaration": true
},
"exclude": ["lib", "node_modules", "**/*.test.ts", "**/__mocks__/*"]
}

12
plugin-core/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
export {};
declare global {
interface CorePlugin {
store?: any | undefined;
}
interface Window {
corePlugin?: CorePlugin;
coreAPI?: any | undefined;
electronAPI?: any | undefined;
}
}

View File

@ -24,14 +24,14 @@ const AvailableModelCard: React.FC<Props> = ({
let total = 0;
let transferred = 0;
if (model.id && downloadState[model.id]) {
if (model._id && downloadState[model._id]) {
isDownloading =
downloadState[model.id].error == null &&
downloadState[model.id].percent < 1;
downloadState[model._id].error == null &&
downloadState[model._id].percent < 1;
if (isDownloading) {
total = downloadState[model.id].size.total;
transferred = downloadState[model.id].size.transferred;
total = downloadState[model._id].size.total;
transferred = downloadState[model._id].size.transferred;
}
}

View File

@ -16,7 +16,7 @@ const ConversationalList: React.FC<Props> = ({ models }) => (
</div>
<div className="mt-2 pl-6 flex w-full gap-2 overflow-x-scroll scroll overflow-hidden">
{models.map((item) => (
<ConversationalCard key={item.id} model={item} />
<ConversationalCard key={item._id} model={item} />
))}
</div>
</>

View File

@ -133,7 +133,7 @@ const ExploreModelItem = forwardRef<HTMLDivElement, Props>(({ model }, ref) => {
<ModelVersionList
model={model}
versions={model.availableVersions}
recommendedVersion={suitableModel?.id ?? ""}
recommendedVersion={suitableModel?._id ?? ""}
/>
)}
<button

View File

@ -29,8 +29,8 @@ const ExploreModelItemHeader: React.FC<Props> = ({
const { performanceTag, title, getPerformanceForModel } =
useGetPerformanceTag();
const downloadAtom = useMemo(
() => atom((get) => get(modelDownloadStateAtom)[suitableModel.id]),
[suitableModel.id]
() => atom((get) => get(modelDownloadStateAtom)[suitableModel._id]),
[suitableModel._id]
);
const downloadState = useAtomValue(downloadAtom);
const setMainViewState = useSetAtom(setMainViewStateAtom);
@ -44,7 +44,7 @@ const ExploreModelItemHeader: React.FC<Props> = ({
}, [exploreModel, suitableModel]);
const isDownloaded =
downloadedModels.find((model) => model.id === suitableModel.id) != null;
downloadedModels.find((model) => model._id === suitableModel._id) != null;
let downloadButton = (
<PrimaryButton

View File

@ -19,7 +19,7 @@ const ExploreModelList: React.FC = () => {
</div>
)}
{models.map((item) => (
<ExploreModelItem key={item.id} model={item} />
<ExploreModelItem key={item._id} model={item} />
))}
</div>
);

View File

@ -3,7 +3,7 @@ import JanImage from "../JanImage";
import { useAtomValue, useSetAtom } from "jotai";
import Image from "next/image";
import { Conversation } from "@/_models/Conversation";
import { DataService } from "../../../shared/coreService";
import { ModelManagementService } from "@janhq/plugin-core";
import { executeSerial } from "../../../../electron/core/plugin-manager/execution/extension-manager";
import {
conversationStatesAtom,
@ -39,28 +39,28 @@ const HistoryItem: React.FC<Props> = ({
updateConversationWaitingForResponseAtom
);
const updateConvError = useSetAtom(updateConversationErrorAtom);
const isSelected = activeConvoId === conversation.id;
const isSelected = activeConvoId === conversation._id;
const { initModel } = useInitModel();
const onClick = async () => {
const model = await executeSerial(
DataService.GET_MODEL_BY_ID,
conversation.model_id
ModelManagementService.GetModelById,
conversation.modelId
);
if (conversation.id) updateConvWaiting(conversation.id, true);
if (conversation._id) updateConvWaiting(conversation._id, true);
initModel(model).then((res: any) => {
if (conversation.id) updateConvWaiting(conversation.id, false);
if (conversation._id) updateConvWaiting(conversation._id, false);
if (res?.error && conversation.id) {
updateConvError(conversation.id, res.error);
if (res?.error && conversation._id) {
updateConvError(conversation._id, res.error);
}
});
if (activeConvoId !== conversation.id) {
if (activeConvoId !== conversation._id) {
setMainViewState(MainViewState.Conversation);
setActiveConvoId(conversation.id);
setActiveConvoId(conversation._id);
}
};
@ -69,7 +69,7 @@ const HistoryItem: React.FC<Props> = ({
: "bg-white dark:bg-gray-500";
let rightImageUrl: string | undefined;
if (conversationStates[conversation.id ?? ""]?.waitingForResponse === true) {
if (conversationStates[conversation._id ?? ""]?.waitingForResponse === true) {
rightImageUrl = "icons/loading.svg";
}

View File

@ -36,11 +36,11 @@ const HistoryList: React.FC = () => {
)
.map((convo) => (
<HistoryItem
key={convo.id}
key={convo._id}
conversation={convo}
avatarUrl={convo.image}
name={convo.name || "Jan"}
updatedAt={convo.updated_at ?? ""}
updatedAt={convo.updatedAt ?? ""}
/>
))
) : (

View File

@ -19,20 +19,20 @@ const ModelRow: React.FC<Props> = ({ model }) => {
const { deleteModel } = useDeleteModel();
let status = ModelStatus.Installed;
if (activeModel && activeModel.id === model.id) {
if (activeModel && activeModel._id === model._id) {
status = ModelStatus.Active;
}
let actionButtonType = ModelActionType.Start;
if (activeModel && activeModel.id === model.id) {
if (activeModel && activeModel._id === model._id) {
actionButtonType = ModelActionType.Stop;
}
const onModelActionClick = (action: ModelActionType) => {
if (action === ModelActionType.Start) {
startModel(model.id);
startModel(model._id);
} else {
stopModel(model.id);
stopModel(model._id);
}
};

View File

@ -65,7 +65,7 @@ const SelectModels: React.FC = () => {
<Listbox.Options className="absolute z-10 mt-1 max-h-[188px] w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{downloadedModels.map((model) => (
<Listbox.Option
key={model.id}
key={model._id}
className={({ active }) =>
classNames(
active ? "bg-blue-600 text-white" : "text-gray-900",

View File

@ -24,7 +24,7 @@ const ModelTable: React.FC<Props> = ({ models }) => (
</thead>
<tbody>
{models.map((model) => (
<ModelRow key={model.id} model={model} />
<ModelRow key={model._id} model={model} />
))}
</tbody>
</table>

View File

@ -24,11 +24,11 @@ const ModelVersionItem: React.FC<Props> = ({
const { downloadModel } = useDownloadModel();
const { downloadedModels } = useGetDownloadedModels();
const isDownloaded =
downloadedModels.find((model) => model.id === modelVersion.id) != null;
downloadedModels.find((model) => model._id === modelVersion._id) != null;
const downloadAtom = useMemo(
() => atom((get) => get(modelDownloadStateAtom)[modelVersion.id ?? ""]),
[modelVersion.id ?? ""]
() => atom((get) => get(modelDownloadStateAtom)[modelVersion._id ?? ""]),
[modelVersion._id]
);
const downloadState = useAtomValue(downloadAtom);

View File

@ -22,10 +22,10 @@ const ModelVersionList: React.FC<Props> = ({
<div className="border border-gray-200 rounded-lg overflow-hidden">
{versions.map((item) => (
<ModelVersionItem
key={item.id}
key={item._id}
model={model}
modelVersion={item}
isRecommended={item.id === recommendedVersion}
isRecommended={item._id === recommendedVersion}
/>
))}
</div>

View File

@ -105,7 +105,7 @@ export const Preferences = () => {
if (typeof window !== "undefined") {
// @ts-ignore
await window.pluggableElectronIpc.update([plugin], true);
window.electronAPI.relaunch();
window.electronAPI.reloadPlugins();
}
// plugins.update(active.map((plg) => plg.name));
};

View File

@ -5,7 +5,7 @@ import { ReactNode, useEffect } from "react";
import { appDownloadProgress } from "./JotaiWrapper";
import { DownloadState } from "@/_models/DownloadState";
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
import { DataService } from "../../shared/coreService";
import { ModelManagementService } from "@janhq/plugin-core";
import {
setDownloadStateAtom,
setDownloadStateSuccessAtom,
@ -44,7 +44,7 @@ export default function EventListenerWrapper({ children }: Props) {
setDownloadStateSuccess(callback.fileName);
executeSerial(
DataService.UPDATE_FINISHED_DOWNLOAD,
ModelManagementService.UpdateFinishedDownloadAt,
callback.fileName
).then(() => {
getDownloadedModels().then((models) => {

View File

@ -85,17 +85,17 @@ export const updateConversationHasMoreAtom = atom(
*/
export const userConversationsAtom = atom<Conversation[]>([]);
export const currentConversationAtom = atom<Conversation | undefined>((get) =>
get(userConversationsAtom).find((c) => c.id === get(getActiveConvoIdAtom))
get(userConversationsAtom).find((c) => c._id === get(getActiveConvoIdAtom))
);
export const setConvoUpdatedAtAtom = atom(null, (get, set, convoId: string) => {
const convo = get(userConversationsAtom).find((c) => c.id === convoId);
const convo = get(userConversationsAtom).find((c) => c._id === convoId);
if (!convo) return;
const newConvo: Conversation = {
...convo,
updated_at: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
const newConversations: Conversation[] = get(userConversationsAtom).map((c) =>
c.id === convoId ? newConvo : c
c._id === convoId ? newConvo : c
);
set(userConversationsAtom, newConversations);
@ -104,11 +104,11 @@ export const setConvoUpdatedAtAtom = atom(null, (get, set, convoId: string) => {
export const setConvoLastImageAtom = atom(
null,
(get, set, convoId: string, lastImageUrl: string) => {
const convo = get(userConversationsAtom).find((c) => c.id === convoId);
const convo = get(userConversationsAtom).find((c) => c._id === convoId);
if (!convo) return;
const newConvo: Conversation = { ...convo };
const newConversations: Conversation[] = get(userConversationsAtom).map(
(c) => (c.id === convoId ? newConvo : c)
(c) => (c._id === convoId ? newConvo : c)
);
set(userConversationsAtom, newConversations);

View File

@ -2,7 +2,7 @@ import { ChatMessage, RawMessage, toChatMessage } from "@/_models/ChatMessage";
import { executeSerial } from "@/_services/pluginService";
import { useAtomValue, useSetAtom } from "jotai";
import { useEffect, useState } from "react";
import { DataService } from "../../shared/coreService";
import { DataService } from "@janhq/plugin-core";
import { addOldMessagesAtom } from "@/_helpers/atoms/ChatMessage.atom";
import {
currentConversationAtom,
@ -27,20 +27,20 @@ const useChatMessages = (offset = 0) => {
if (!currentConvo) {
return;
}
const hasMore = convoStates[currentConvo.id ?? ""]?.hasMore ?? true;
const hasMore = convoStates[currentConvo._id ?? ""]?.hasMore ?? true;
if (!hasMore) return;
const getMessages = async () => {
executeSerial(
DataService.GET_CONVERSATION_MESSAGES,
currentConvo.id
DataService.GetConversationMessages,
currentConvo._id
).then((data: any) => {
if (!data) {
return;
}
parseMessages(data ?? []).then((newMessages) => {
addOldChatMessages(newMessages);
updateConvoHasMore(currentConvo.id ?? "", false);
updateConvoHasMore(currentConvo._id ?? "", false);
setLoading(false);
});
});
@ -48,7 +48,7 @@ const useChatMessages = (offset = 0) => {
getMessages();
}, [
offset,
currentConvo?.id,
currentConvo?._id,
convoStates,
addOldChatMessages,
updateConvoHasMore,
@ -57,7 +57,7 @@ const useChatMessages = (offset = 0) => {
return {
loading: loading,
error: undefined,
hasMore: convoStates[currentConvo?.id ?? ""]?.hasMore ?? true,
hasMore: convoStates[currentConvo?._id ?? ""]?.hasMore ?? true,
};
};

View File

@ -1,7 +1,7 @@
import { useAtom, useSetAtom } from "jotai";
import { Conversation } from "@/_models/Conversation";
import { executeSerial } from "@/_services/pluginService";
import { DataService } from "../../shared/coreService";
import { DataService } from "@janhq/plugin-core";
import {
userConversationsAtom,
setActiveConvoIdAtom,
@ -27,12 +27,12 @@ const useCreateConversation = () => {
const requestCreateConvo = async (model: AssistantModel) => {
const conversationName = model.name;
const conv: Conversation = {
model_id: model.id,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
modelId: model._id,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: conversationName,
};
const id = await executeSerial(DataService.CREATE_CONVERSATION, conv);
const id = await executeSerial(DataService.CreateConversation, conv);
if (id) updateConvWaiting(id, true);
initModel(model).then((res: any) => {
@ -43,11 +43,11 @@ const useCreateConversation = () => {
});
const mappedConvo: Conversation = {
id,
model_id: model.id,
_id: id,
modelId: model._id,
name: conversationName,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
addNewConvoState(id ?? "", {

View File

@ -1,7 +1,7 @@
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
import { execute } from "@/_services/pluginService";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { DataService } from "../../shared/coreService";
import { DataService } from "@janhq/plugin-core";
import { deleteConversationMessage } from "@/_helpers/atoms/ChatMessage.atom";
import {
userConversationsAtom,
@ -33,15 +33,15 @@ export default function useDeleteConversation() {
const deleteConvo = async () => {
if (activeConvoId) {
try {
await execute(DataService.DELETE_CONVERSATION, activeConvoId);
await execute(DataService.DeleteConversation, activeConvoId);
const currentConversations = userConversations.filter(
(c) => c.id !== activeConvoId,
(c) => c._id !== activeConvoId,
);
setUserConversations(currentConversations);
deleteMessages(activeConvoId);
if (currentConversations.length > 0) {
setActiveConvoId(currentConversations[0].id);
setActiveConvoId(currentConversations[0]._id);
} else {
setMainViewState(MainViewState.Welcome);
setActiveConvoId(undefined);

View File

@ -1,5 +1,5 @@
import { execute, executeSerial } from "@/_services/pluginService";
import { DataService, ModelManagementService } from "../../shared/coreService";
import { ModelManagementService } from "@janhq/plugin-core";
import { useSetAtom } from "jotai";
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
import { getDownloadedModels } from "./useGetDownloadedModels";
@ -9,8 +9,8 @@ export default function useDeleteModel() {
const setDownloadedModels = useSetAtom(downloadedModelAtom);
const deleteModel = async (model: AssistantModel) => {
execute(DataService.DELETE_DOWNLOAD_MODEL, model.id);
await executeSerial(ModelManagementService.DELETE_MODEL, model.id);
execute(ModelManagementService.DeleteDownloadModel, model._id);
await executeSerial(ModelManagementService.DeleteModel, model._id);
// reload models
const downloadedModels = await getDownloadedModels();

View File

@ -1,16 +1,51 @@
import { executeSerial } from "@/_services/pluginService";
import { DataService, ModelManagementService } from "../../shared/coreService";
import { Product } from "@/_models/Product";
import { DataService, ModelManagementService } from "@janhq/plugin-core";
import { ModelVersion } from "@/_models/ModelVersion";
import { Product } from "@/_models/Product";
import { AssistantModel } from "@/_models/AssistantModel";
export default function useDownloadModel() {
const assistanModel = (
model: Product,
modelVersion: ModelVersion
): AssistantModel => {
return {
_id: modelVersion._id,
name: modelVersion.name,
quantMethod: modelVersion.quantMethod,
bits: modelVersion.bits,
size: modelVersion.size,
maxRamRequired: modelVersion.maxRamRequired,
usecase: modelVersion.usecase,
downloadLink: modelVersion.downloadLink,
startDownloadAt: modelVersion.startDownloadAt,
finishDownloadAt: modelVersion.finishDownloadAt,
productId: model._id,
productName: model.name,
shortDescription: model.shortDescription,
longDescription: model.longDescription,
avatarUrl: model.avatarUrl,
author: model.author,
version: model.version,
modelUrl: model.modelUrl,
nsfw: model.nsfw === true ? false : true,
greeting: model.greeting,
type: model.type,
createdAt: new Date(model.createdAt).getTime(),
updatedAt: new Date(model.updatedAt ?? "").getTime(),
status: "",
releaseDate: -1,
tags: model.tags,
};
};
const downloadModel = async (model: Product, modelVersion: ModelVersion) => {
modelVersion.startDownloadAt = Date.now();
await executeSerial(DataService.STORE_MODEL, { model, modelVersion });
await executeSerial(ModelManagementService.DOWNLOAD_MODEL, {
const assistantModel = assistanModel(model, modelVersion);
await executeSerial(ModelManagementService.StoreModel, assistantModel);
await executeSerial(ModelManagementService.DownloadModel, {
downloadUrl: modelVersion.downloadLink,
fileName: modelVersion.id,
fileName: modelVersion._id,
});
};

View File

@ -1,7 +1,7 @@
import { Product } from "@/_models/Product";
import { useEffect } from "react";
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
import { DataService, ModelManagementService } from "../../shared/coreService";
import { ModelManagementService } from "@janhq/plugin-core";
import { useAtom } from "jotai";
import { downloadedModelAtom } from "@/_helpers/atoms/DownloadedModel.atom";
import { AssistantModel } from "@/_models/AssistantModel";
@ -20,11 +20,11 @@ export function useGetDownloadedModels() {
export async function getDownloadedModels(): Promise<AssistantModel[]> {
const downloadedModels: AssistantModel[] = await executeSerial(
DataService.GET_FINISHED_DOWNLOAD_MODELS
ModelManagementService.GetFinishedDownloadModels
);
return downloadedModels ?? [];
}
export async function getConfiguredModels(): Promise<Product[]> {
return executeSerial(ModelManagementService.GET_CONFIGURED_MODELS);
return executeSerial(ModelManagementService.GetConfiguredModels);
}

View File

@ -1,5 +1,5 @@
import { executeSerial } from "@/_services/pluginService";
import { SystemMonitoringService } from "../../shared/coreService";
import { SystemMonitoringService } from "@janhq/plugin-core";
import { ModelVersion } from "@/_models/ModelVersion";
import { useState } from "react";
@ -8,7 +8,7 @@ export default function useGetMostSuitableModelVersion() {
const getMostSuitableModelVersion = async (modelVersions: ModelVersion[]) => {
const resourceInfo = await executeSerial(
SystemMonitoringService.GET_RESOURCES_INFORMATION
SystemMonitoringService.GetResourcesInfo
);
const totalRam = resourceInfo.mem.total;

View File

@ -1,5 +1,5 @@
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
import { SystemMonitoringService } from "../../shared/coreService";
import { SystemMonitoringService } from "@janhq/plugin-core";
import { useState } from "react";
import { ModelVersion } from "@/_models/ModelVersion";
import { ModelPerformance, TagType } from "@/_components/SimpleTag/TagType";
@ -14,7 +14,7 @@ export default function useGetPerformanceTag() {
const getPerformanceForModel = async (modelVersion: ModelVersion) => {
const resourceInfo = await executeSerial(
SystemMonitoringService.GET_RESOURCES_INFORMATION
SystemMonitoringService.GetResourcesInfo
);
const totalRam = resourceInfo.mem.total;
const requiredRam = modelVersion.maxRamRequired;

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
import { SystemMonitoringService } from "../../shared/coreService";
import { SystemMonitoringService } from "@janhq/plugin-core";
export default function useGetSystemResources() {
const [ram, setRam] = useState<number>(0);
@ -8,10 +8,10 @@ export default function useGetSystemResources() {
const getSystemResources = async () => {
const resourceInfor = await executeSerial(
SystemMonitoringService.GET_RESOURCES_INFORMATION
SystemMonitoringService.GetResourcesInfo
);
const currentLoadInfor = await executeSerial(
SystemMonitoringService.GET_CURRENT_LOAD_INFORMATION
SystemMonitoringService.GetCurrentLoad
);
const ram =
(resourceInfor?.mem?.active ?? 0) / (resourceInfor?.mem?.total ?? 1);

View File

@ -1,7 +1,7 @@
import { Conversation, ConversationState } from "@/_models/Conversation";
import { useSetAtom } from "jotai";
import { executeSerial } from "@/_services/pluginService";
import { DataService } from "../../shared/coreService";
import { DataService } from "@janhq/plugin-core";
import {
conversationStatesAtom,
userConversationsAtom,
@ -14,11 +14,11 @@ const useGetUserConversations = () => {
const getUserConversations = async () => {
try {
const convos: Conversation[] | undefined = await executeSerial(
DataService.GET_CONVERSATIONS
DataService.GetConversations
);
const convoStates: Record<string, ConversationState> = {};
convos?.forEach((convo) => {
convoStates[convo.id ?? ""] = {
convoStates[convo._id ?? ""] = {
hasMore: true,
waitingForResponse: false,
};

View File

@ -1,5 +1,5 @@
import { executeSerial } from "@/_services/pluginService";
import { InferenceService } from "../../shared/coreService";
import { InferenceService } from "@janhq/plugin-core";
import { useAtom } from "jotai";
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
import { AssistantModel } from "@/_models/AssistantModel";
@ -8,12 +8,12 @@ export default function useInitModel() {
const [activeModel, setActiveModel] = useAtom(activeAssistantModelAtom);
const initModel = async (model: AssistantModel) => {
if (activeModel && activeModel.id === model.id) {
console.debug(`Model ${model.id} is already init. Ignore..`);
if (activeModel && activeModel._id === model._id) {
console.debug(`Model ${model._id} is already init. Ignore..`);
return;
}
const res = await executeSerial(InferenceService.INIT_MODEL, model.id);
const res = await executeSerial(InferenceService.InitModel, model._id);
if (res?.error) {
console.log("error occured: ", res);
return res;

View File

@ -1,7 +1,7 @@
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { selectAtom } from "jotai/utils";
import { DataService, InferenceService } from "../../shared/coreService";
import { DataService, InferenceService } from "@janhq/plugin-core";
import {
MessageSenderType,
RawMessage,
@ -45,13 +45,13 @@ export default function useSendChatMessage() {
updateConvWaiting(conversationId, true);
const prompt = currentPrompt.trim();
const newMessage: RawMessage = {
conversation_id: parseInt(currentConvo?.id ?? "0") ?? 0,
conversationId: currentConvo?._id,
message: prompt,
user: "user",
created_at: new Date().toISOString(),
createdAt: new Date().toISOString(),
};
const id = await executeSerial(DataService.CREATE_MESSAGE, newMessage);
newMessage.id = id;
const id = await executeSerial(DataService.CreateMessage, newMessage);
newMessage._id = id;
const newChatMessage = await toChatMessage(newMessage);
addNewMessage(newChatMessage);
@ -70,7 +70,7 @@ export default function useSendChatMessage() {
: "assistant",
};
});
const url = await executeSerial(InferenceService.INFERENCE_URL);
const url = await executeSerial(InferenceService.InferenceUrl);
const response = await fetch(url, {
method: "POST",
headers: {
@ -93,13 +93,13 @@ export default function useSendChatMessage() {
// Cache received response
const newResponse: RawMessage = {
conversation_id: parseInt(currentConvo?.id ?? "0") ?? 0,
conversationId: currentConvo?._id,
message: answer,
user: "assistant",
created_at: new Date().toISOString(),
createdAt: new Date().toISOString(),
};
const respId = await executeSerial(DataService.CREATE_MESSAGE, newResponse);
newResponse.id = respId;
const respId = await executeSerial(DataService.CreateMessage, newResponse);
newResponse._id = respId;
const responseChatMessage = await toChatMessage(newResponse);
addNewMessage(responseChatMessage);
@ -136,10 +136,10 @@ export default function useSendChatMessage() {
responseChatMessage.conversationId,
answer.trimEnd()
);
await executeSerial(DataService.UPDATE_MESSAGE, {
await executeSerial(DataService.UpdateMessage, {
...newResponse,
message: answer.trimEnd(),
updated_at: new Date()
updatedAt: new Date()
.toISOString()
.replace("T", " ")
.replace(/\.\d+Z$/, ""),

View File

@ -1,5 +1,5 @@
import { executeSerial } from "@/_services/pluginService";
import { DataService, InferenceService } from "../../shared/coreService";
import { ModelManagementService, InferenceService } from "@janhq/plugin-core";
import useInitModel from "./useInitModel";
import { useSetAtom } from "jotai";
import { activeAssistantModelAtom } from "@/_helpers/atoms/Model.atom";
@ -9,7 +9,7 @@ export default function useStartStopModel() {
const setActiveModel = useSetAtom(activeAssistantModelAtom);
const startModel = async (modelId: string) => {
const model = await executeSerial(DataService.GET_MODEL_BY_ID, modelId);
const model = await executeSerial(ModelManagementService.GetModelById, modelId);
if (!model) {
alert(`Model ${modelId} not found! Please re-download the model first.`);
} else {
@ -18,7 +18,7 @@ export default function useStartStopModel() {
};
const stopModel = async (modelId: string) => {
await executeSerial(InferenceService.STOP_MODEL, modelId);
await executeSerial(InferenceService.StopModel, modelId);
setActiveModel(undefined);
};

View File

@ -8,7 +8,7 @@ export type AssistantModel = {
* Combination of owner and model name.
* Being used as file name. MUST be unique.
*/
id: string;
_id: string;
name: string;

View File

@ -34,16 +34,16 @@ export interface ChatMessage {
}
export interface RawMessage {
id?: number;
conversation_id: number;
_id?: string;
conversationId?: string;
user?: string;
message?: string;
created_at?: string;
updated_at?: string;
createdAt?: string;
updatedAt?: string;
}
export const toChatMessage = async (m: RawMessage): Promise<ChatMessage> => {
const createdAt = new Date(m.created_at ?? "").getTime();
const createdAt = new Date(m.createdAt ?? "").getTime();
const imageUrls: string[] = [];
const imageUrl = undefined;
if (imageUrl) {
@ -59,8 +59,8 @@ export const toChatMessage = async (m: RawMessage): Promise<ChatMessage> => {
const contentHtml = processedContent.toString();
return {
id: (m.id ?? 0).toString(),
conversationId: (m.conversation_id ?? 0).toString(),
id: (m._id ?? 0).toString(),
conversationId: (m.conversationId ?? 0).toString(),
messageType: messageType,
messageSenderType: messageSenderType,
senderUid: m.user?.toString() || "0",

View File

@ -1,11 +1,11 @@
export interface Conversation {
id?: string;
model_id?: string;
_id?: string;
modelId?: string;
name?: string;
image?: string;
message?: string;
created_at?: string;
updated_at?: string;
createdAt?: string;
updatedAt?: string;
}
/**

View File

@ -6,7 +6,7 @@ export type ModelVersion = {
* Combination of owner and model name.
* Being used as file name. Should be unique.
*/
id: string;
_id: string;
name: string;
quantMethod: string;
bits: number;

View File

@ -9,7 +9,7 @@ export enum ProductType {
}
export interface Product {
id: string;
_id: string;
name: string;
shortDescription: string;
avatarUrl: string;

View File

@ -0,0 +1,8 @@
import { store } from "./storeService";
export const setupCoreServices = () => {
if (!window.corePlugin) {
window.corePlugin = {
store,
};
}
};

View File

@ -1,40 +1,29 @@
"use client";
import {
extensionPoints,
plugins,
} from "../../../electron/core/plugin-manager/execution/index";
import {
CoreService,
DataService,
InferenceService,
ModelManagementService,
} from "../../shared/coreService";
import { extensionPoints, plugins } from "../../../electron/core/plugin-manager/execution/index";
import { CoreService, DataService, InferenceService, ModelManagementService } from "@janhq/plugin-core";
export const isCorePluginInstalled = () => {
if (!extensionPoints.get(DataService.GET_CONVERSATIONS)) {
if (!extensionPoints.get(DataService.GetConversations)) {
return false;
}
if (!extensionPoints.get(InferenceService.INIT_MODEL)) {
if (!extensionPoints.get(InferenceService.InitModel)) {
return false;
}
if (!extensionPoints.get(ModelManagementService.DOWNLOAD_MODEL)) {
if (!extensionPoints.get(ModelManagementService.DownloadModel)) {
return false;
}
return true;
};
export const setupBasePlugins = async () => {
if (
typeof window === "undefined" ||
typeof window.electronAPI === "undefined"
) {
if (typeof window === "undefined" || typeof window.electronAPI === "undefined") {
return;
}
const basePlugins = await window.electronAPI.basePlugins();
if (
!extensionPoints.get(DataService.GET_CONVERSATIONS) ||
!extensionPoints.get(InferenceService.INIT_MODEL) ||
!extensionPoints.get(ModelManagementService.DOWNLOAD_MODEL)
!extensionPoints.get(DataService.GetConversations) ||
!extensionPoints.get(InferenceService.InitModel) ||
!extensionPoints.get(ModelManagementService.DownloadModel)
) {
const installed = await plugins.install(basePlugins);
if (installed) {

View File

@ -0,0 +1,138 @@
import { StoreService } from "@janhq/plugin-core";
import { executeSerial } from "./pluginService";
/**
* Create a collection on data store
*
* @param name name of the collection to create
* @param schema schema of the collection to create, include fields and their types, optional for relational database engine
* @returns {Promise<void>}
*
*/
function createCollection(name: string, schema?: { [key: string]: any }): Promise<void> {
return executeSerial(StoreService.CreateCollection, { name, schema });
}
/**
* Delete a collection
*
* @param name name of the collection to delete
* @returns {Promise<void>}
*
*/
function deleteCollection(name: string): Promise<void> {
return executeSerial(StoreService.DeleteCollection, name);
}
/**
* Insert a value to a collection
*
* @param collectionName name of the collection
* @param value value to insert
* @returns {Promise<any>}
*
*/
function insertOne(collectionName: string, value: any): Promise<any> {
return executeSerial(StoreService.InsertOne, {
collectionName,
value,
});
}
/**
* Retrieve a record from a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to retrieve.
* @param {string} key - The key of the record to retrieve.
* @returns {Promise<any>} A promise that resolves when the record is retrieved.
*/
function findOne(collectionName: string, key: string): Promise<any> {
return executeSerial(StoreService.FindOne, { collectionName, key });
}
/**
* Retrieves all records that match a selector in a collection in the data store.
* @param collectionName - The name of the collection to retrieve.
* @param selector - The selector to use to get records from the collection.
* @param sort - The sort options to use to retrieve records.
* @returns {Promise<any>}
*/
function findMany(
collectionName: string,
selector?: { [key: string]: any },
sort?: [{ [key: string]: any }]
): Promise<any> {
return executeSerial(StoreService.FindMany, {
collectionName,
selector,
sort,
});
}
/**
* Update value of a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to update
* @param value value to update
* @returns Promise<void>
*
*/
function updateOne(collectionName: string, key: string, value: any): Promise<void> {
return executeSerial(StoreService.UpdateOne, {
collectionName,
key,
value,
});
}
/**
* Updates all records that match a selector in a collection in the data store.
* @param collectionName - The name of the collection containing the records to update.
* @param selector - The selector to use to get the records to update.
* @param value - The new value for the records.
* @returns {Promise<void>} A promise that resolves when the records are updated.
*/
function updateMany(collectionName: string, value: any, selector?: { [key: string]: any }): Promise<void> {
return executeSerial(StoreService.UpdateMany, {
collectionName,
value,
selector,
});
}
/**
* Delete a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to delete
* @returns Promise<void>
*
*/
function deleteOne(collectionName: string, key: string): Promise<void> {
return executeSerial(StoreService.DeleteOne, { collectionName, key });
}
/**
* Deletes all records with a matching key from a collection in the data store.
* @param {string} collectionName - The name of the collection to delete the records from.
* @param {{ [key: string]: any }} selector - The selector to use to get the records to delete.
* @returns {Promise<void>} A promise that resolves when the records are deleted.
*/
function deleteMany(collectionName: string, selector?: { [key: string]: any }): Promise<void> {
return executeSerial(StoreService.DeleteMany, {
collectionName,
selector,
});
}
export const store = {
createCollection,
deleteCollection,
insertOne,
updateOne,
updateMany,
deleteOne,
deleteMany,
findOne,
findMany,
};

View File

@ -18,10 +18,16 @@ import {
} from "./_services/pluginService";
import LeftContainer from "./_components/LeftContainer";
import EventListenerWrapper from "./_helpers/EventListenerWrapper";
import { setupCoreServices } from "./_services/coreService";
const Page: React.FC = () => {
const [activated, setActivated] = useState(false);
// Services Setup
useEffect(() => {
// Setup Core Service
setupCoreServices();
async function setupPE() {
// Enable activation point management
setup({

View File

@ -13,6 +13,7 @@
"dependencies": {
"@headlessui/react": "^1.7.15",
"@heroicons/react": "^2.0.18",
"@janhq/plugin-core": "file:../plugin-core",
"@tailwindcss/typography": "^0.5.9",
"@types/react": "18.2.15",
"@types/react-dom": "18.2.7",

View File

@ -1,50 +0,0 @@
export type CoreService =
| DataService
| ModelService
| InferenceService
| ModelManagementService
| SystemMonitoringService
| PreferenceService;
export enum DataService {
GET_CONVERSATIONS = "getConversations",
CREATE_CONVERSATION = "createConversation",
DELETE_CONVERSATION = "deleteConversation",
CREATE_MESSAGE = "createMessage",
UPDATE_MESSAGE = "updateMessage",
GET_CONVERSATION_MESSAGES = "getConversationMessages",
STORE_MODEL = "storeModel",
UPDATE_FINISHED_DOWNLOAD = "updateFinishedDownloadAt",
GET_UNFINISHED_DOWNLOAD_MODELS = "getUnfinishedDownloadModels",
GET_FINISHED_DOWNLOAD_MODELS = "getFinishedDownloadModels",
DELETE_DOWNLOAD_MODEL = "deleteDownloadModel",
GET_MODEL_BY_ID = "getModelById",
}
export enum ModelService {
GET_MODELS = "getModels",
}
export enum InferenceService {
INFERENCE_URL = "inferenceUrl",
INIT_MODEL = "initModel",
STOP_MODEL = "stopModel",
}
export enum ModelManagementService {
DELETE_MODEL = "deleteModel",
DOWNLOAD_MODEL = "downloadModel",
SEARCH_MODELS = "searchModels",
GET_CONFIGURED_MODELS = "getConfiguredModels",
}
export enum PreferenceService {
GET_EXPERIMENT_COMPONENT = "experimentComponent",
}
export enum SystemMonitoringService {
GET_RESOURCES_INFORMATION = "getResourcesInfo",
GET_CURRENT_LOAD_INFORMATION = "getCurrentLoad",
}

View File

@ -3,5 +3,6 @@ export {};
declare global {
interface Window {
electronAPI?: any | undefined;
corePlugin?: any | undefined;
}
}