* feature: event based plugin * chore: update README.md * Update yarn script for build plugins (#363) * Update yarn script for build plugins * Plugin-core install from npmjs instead of from local --------- Co-authored-by: Hien To <> * #360 plugin preferences (#361) * feature: #360 plugin preferences * chore: update core-plugin README.md * chore: create collections on start * chore: bumb core version * chore: update README * chore: notify preferences update * fix: preference update --------- Co-authored-by: hiento09 <136591877+hiento09@users.noreply.github.com>
This commit is contained in:
parent
237d94ed1a
commit
27258433d1
@ -1,6 +1,6 @@
|
||||
import { core, store, RegisterExtensionPoint, StoreService, DataService } from "@janhq/plugin-core";
|
||||
import { core, store, RegisterExtensionPoint, StoreService, DataService, PluginService } from "@janhq/plugin-core";
|
||||
|
||||
// Provide an async method to manipulate the price provided by the extension point
|
||||
const PluginName = "data-plugin";
|
||||
const MODULE_PATH = "data-plugin/dist/cjs/module.js";
|
||||
|
||||
/**
|
||||
@ -12,7 +12,6 @@ const MODULE_PATH = "data-plugin/dist/cjs/module.js";
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -137,8 +136,7 @@ function onStart() {
|
||||
|
||||
// Register all the above functions and objects with the relevant extension points
|
||||
export function init({ register }: { register: RegisterExtensionPoint }) {
|
||||
onStart();
|
||||
|
||||
register(PluginService.OnStart, PluginName, onStart);
|
||||
register(StoreService.CreateCollection, createCollection.name, createCollection);
|
||||
register(StoreService.DeleteCollection, deleteCollection.name, deleteCollection);
|
||||
register(StoreService.InsertOne, insertOne.name, insertOne);
|
||||
|
||||
168
electron/core/plugins/data-plugin/package-lock.json
generated
168
electron/core/plugins/data-plugin/package-lock.json
generated
@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "data-plugin",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "data-plugin",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.4",
|
||||
"bundleDependencies": [
|
||||
"rxdb",
|
||||
"rxjs"
|
||||
"pouchdb-node",
|
||||
"pouchdb-find"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@janhq/plugin-core": "file:../../../../plugin-core",
|
||||
"@janhq/plugin-core": "^0.1.5",
|
||||
"pouchdb-find": "^8.0.1",
|
||||
"pouchdb-node": "^8.0.1"
|
||||
},
|
||||
@ -31,10 +31,11 @@
|
||||
},
|
||||
"../../../../plugin-core": {
|
||||
"name": "@janhq/plugin-core",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.6",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.2"
|
||||
"@types/node": "^12.0.2",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@ -177,9 +178,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
|
||||
"integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
|
||||
"version": "20.8.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.6.tgz",
|
||||
"integrity": "sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.25.1"
|
||||
@ -397,6 +398,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
@ -408,6 +410,7 @@
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
|
||||
"integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"immediate": "^3.2.3",
|
||||
@ -674,7 +677,8 @@
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
],
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "1.13.1",
|
||||
@ -769,6 +773,7 @@
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
@ -777,7 +782,8 @@
|
||||
"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=="
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/cache-base": {
|
||||
"version": "1.0.1",
|
||||
@ -809,9 +815,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001547",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz",
|
||||
"integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==",
|
||||
"version": "1.0.30001549",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz",
|
||||
"integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -966,6 +972,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
|
||||
"integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==",
|
||||
"inBundle": true,
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
@ -1083,7 +1090,8 @@
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/cpx": {
|
||||
"version": "1.5.0",
|
||||
@ -1158,6 +1166,7 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz",
|
||||
"integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"abstract-leveldown": "~6.2.1",
|
||||
"inherits": "^2.0.3"
|
||||
@ -1218,7 +1227,8 @@
|
||||
"node_modules/double-ended-queue": {
|
||||
"version": "2.1.0-0",
|
||||
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
|
||||
"integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ=="
|
||||
"integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/duplexer": {
|
||||
"version": "0.1.2",
|
||||
@ -1227,15 +1237,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.551",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz",
|
||||
"integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==",
|
||||
"version": "1.4.554",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.554.tgz",
|
||||
"integrity": "sha512-Q0umzPJjfBrrj8unkONTgbKQXzXRrH7sVV7D9ea2yBV3Oaogz991yhbpfvo2LMNkJItmruXTEzVpP9cp7vaIiQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/encoding-down": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz",
|
||||
"integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"abstract-leveldown": "^6.2.1",
|
||||
"inherits": "^2.0.3",
|
||||
@ -1250,6 +1261,7 @@
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/end-stream/-/end-stream-0.1.0.tgz",
|
||||
"integrity": "sha512-Brl10T8kYnc75IepKizW6Y9liyW8ikz1B7n/xoHrJxoVSSjoqPn30sb7XVFfQERK4QfUMYRGs9dhWwtt2eu6uA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"write-stream": "~0.4.3"
|
||||
}
|
||||
@ -1283,6 +1295,7 @@
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
|
||||
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"prr": "~1.0.1"
|
||||
},
|
||||
@ -1352,6 +1365,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"inBundle": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -1451,6 +1465,7 @@
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz",
|
||||
"integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0"
|
||||
},
|
||||
@ -1792,7 +1807,8 @@
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
],
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/ignore-walk": {
|
||||
"version": "3.0.4",
|
||||
@ -1806,7 +1822,8 @@
|
||||
"node_modules/immediate": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
|
||||
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q=="
|
||||
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/import-local": {
|
||||
"version": "3.1.0",
|
||||
@ -1840,7 +1857,8 @@
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/ini": {
|
||||
"version": "1.3.8",
|
||||
@ -2078,7 +2096,8 @@
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
@ -2155,6 +2174,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz",
|
||||
"integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"level-js": "^5.0.0",
|
||||
"level-packager": "^5.1.0",
|
||||
@ -2172,6 +2192,7 @@
|
||||
"version": "9.0.2",
|
||||
"resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz",
|
||||
"integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"buffer": "^5.6.0"
|
||||
},
|
||||
@ -2183,6 +2204,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz",
|
||||
"integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==",
|
||||
"inBundle": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -2191,6 +2213,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"errno": "~0.1.1"
|
||||
},
|
||||
@ -2202,6 +2225,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz",
|
||||
"integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0",
|
||||
@ -2215,6 +2239,7 @@
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
@ -2228,6 +2253,7 @@
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz",
|
||||
"integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"abstract-leveldown": "~6.2.3",
|
||||
"buffer": "^5.5.0",
|
||||
@ -2239,6 +2265,7 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz",
|
||||
"integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"encoding-down": "^6.3.0",
|
||||
"levelup": "^4.3.2"
|
||||
@ -2251,6 +2278,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz",
|
||||
"integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.2"
|
||||
},
|
||||
@ -2262,6 +2290,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/level-write-stream/-/level-write-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-bBNKOEOMl8msO+uIM9YX/gUO6ckokZ/4pCwTm/lwvs46x6Xs8Zy0sn3Vh37eDqse4mhy4fOMIb/JsSM2nyQFtw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"end-stream": "~0.1.0"
|
||||
}
|
||||
@ -2271,6 +2300,7 @@
|
||||
"resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz",
|
||||
"integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==",
|
||||
"hasInstallScript": true,
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"abstract-leveldown": "~6.2.1",
|
||||
"napi-macros": "~2.0.0",
|
||||
@ -2284,6 +2314,7 @@
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz",
|
||||
"integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"deferred-leveldown": "~5.3.0",
|
||||
"level-errors": "~2.0.0",
|
||||
@ -2337,7 +2368,8 @@
|
||||
"node_modules/ltgt": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
|
||||
"integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA=="
|
||||
"integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
@ -2565,7 +2597,8 @@
|
||||
"node_modules/napi-macros": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
|
||||
"integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg=="
|
||||
"integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/needle": {
|
||||
"version": "2.9.1",
|
||||
@ -2594,6 +2627,7 @@
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
@ -2613,6 +2647,7 @@
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz",
|
||||
"integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==",
|
||||
"inBundle": true,
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
@ -3041,6 +3076,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-8.0.1.tgz",
|
||||
"integrity": "sha512-BxJRHdfiC8gID8h4DPS0Xy6wsa2VBHRHMv9hsm0BhGTWTqS4k8ivItVSeU2dMoXiTBYp+7SerYmovUQNGSX1GA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"pouchdb-binary-utils": "8.0.1",
|
||||
"pouchdb-collate": "8.0.1",
|
||||
@ -3056,6 +3092,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-8.0.1.tgz",
|
||||
"integrity": "sha512-WsuR/S0aoUlcA0Alt99czkXsfuXWcrYXAcvGiTW02zawVXOafCnb/qHjA09TUaV0oy5HeHmYaNnDckoOUqspeA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "1.1.2"
|
||||
}
|
||||
@ -3063,22 +3100,26 @@
|
||||
"node_modules/pouchdb-collate": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-collate/-/pouchdb-collate-8.0.1.tgz",
|
||||
"integrity": "sha512-DTuNz1UJjBTGZMUlWS1klSE1rPsmHy8IIDie3MFH1ZTz/C+SwGgGwkiAyUDv/n00D18EMLgXq5mu+r7L6K1BwQ=="
|
||||
"integrity": "sha512-DTuNz1UJjBTGZMUlWS1klSE1rPsmHy8IIDie3MFH1ZTz/C+SwGgGwkiAyUDv/n00D18EMLgXq5mu+r7L6K1BwQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/pouchdb-collections": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-8.0.1.tgz",
|
||||
"integrity": "sha512-TlkQ2GGHJApJgL0b7bJMQcwX6eMfVenLeoK9mqHfC2fJssui+HWJJ5LYKHOWan11SeB90BQVFbO6rHN6CJQeDg=="
|
||||
"integrity": "sha512-TlkQ2GGHJApJgL0b7bJMQcwX6eMfVenLeoK9mqHfC2fJssui+HWJJ5LYKHOWan11SeB90BQVFbO6rHN6CJQeDg==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/pouchdb-errors": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-8.0.1.tgz",
|
||||
"integrity": "sha512-H+ZsQxcG/JV3Tn29gnM6c9+lRPCN91ZYOkoIICsLjVRYgOTzN1AvNUD/G5JCB+81aI/u3fxZec0LEaZh6g6NHA=="
|
||||
"integrity": "sha512-H+ZsQxcG/JV3Tn29gnM6c9+lRPCN91ZYOkoIICsLjVRYgOTzN1AvNUD/G5JCB+81aI/u3fxZec0LEaZh6g6NHA==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/pouchdb-fetch": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-fetch/-/pouchdb-fetch-8.0.1.tgz",
|
||||
"integrity": "sha512-Px5HLT8MxqTujc8bpPRKoouznDTJa9XBGqCbhl95q6rhjWRfwZEvXjV92z0B5BALAM6D6avMyG0DjuNfUWnMuA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"abort-controller": "3.0.0",
|
||||
"fetch-cookie": "0.11.0",
|
||||
@ -3089,6 +3130,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-find/-/pouchdb-find-8.0.1.tgz",
|
||||
"integrity": "sha512-i5criYXMOXlbeRrCrXonqaOY+xiMiOyTLybqvtX/NkUsiD4BxJxkq5AxdSlHdJ9703nWJ0k6S+5C8VrpEj8tsQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"pouchdb-abstract-mapreduce": "8.0.1",
|
||||
"pouchdb-collate": "8.0.1",
|
||||
@ -3103,6 +3145,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-8.0.1.tgz",
|
||||
"integrity": "sha512-asZcFLy1DA3oe5CeXIRCpfVrBHaHRvSb3Tc/LPD1dZDDtpEkeCuXGtJm+praN0jl41jTBEm0uMdD/YI0J5ZFXw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"pouchdb-collections": "8.0.1",
|
||||
"pouchdb-utils": "8.0.1"
|
||||
@ -3112,6 +3155,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-8.0.1.tgz",
|
||||
"integrity": "sha512-shVcs/K/iilrcAhDEERpLIrGm/cnDVsXiocOzs7kycJEuBqYnLD9nj58VwWDcum26wfa8T9cznvEGE1jlYVNPQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"pouchdb-binary-utils": "8.0.1",
|
||||
"spark-md5": "3.0.2"
|
||||
@ -3121,6 +3165,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-node/-/pouchdb-node-8.0.1.tgz",
|
||||
"integrity": "sha512-xYvVmBB/nEZltBYkslE0RKciL7l359BFbG27gyGJlpPHeMUhtVlYHoEiI2gb2x2pEn7hG9B7Odo8keuq4/ot1w==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"abort-controller": "3.0.0",
|
||||
"buffer-from": "1.1.2",
|
||||
@ -3143,12 +3188,14 @@
|
||||
"node_modules/pouchdb-node/node_modules/isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
|
||||
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/pouchdb-node/node_modules/readable-stream": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
@ -3159,12 +3206,14 @@
|
||||
"node_modules/pouchdb-node/node_modules/string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/pouchdb-selector-core": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-selector-core/-/pouchdb-selector-core-8.0.1.tgz",
|
||||
"integrity": "sha512-dHWsnR+mLGyfVld1vSHJI1xKTwS1xk1G2dggjfXfUrLehI+wysjTUOwiSNytyPzG6DpT+o86wyUpwzPwsDCLBw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"pouchdb-collate": "8.0.1",
|
||||
"pouchdb-utils": "8.0.1"
|
||||
@ -3174,6 +3223,7 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-8.0.1.tgz",
|
||||
"integrity": "sha512-pWgxdk9EHVWJmjQoEvTe+ZlPXyjcuQ/vgLITN+RjGwcYhoQYUE1M0PksQd2dUP3V8lGS4+wrg9lEM/qSJPYcpw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"clone-buffer": "1.0.0",
|
||||
"immediate": "3.3.0",
|
||||
@ -3195,22 +3245,26 @@
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
|
||||
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
||||
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
|
||||
"inBundle": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@ -3218,7 +3272,8 @@
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/randomatic": {
|
||||
"version": "3.1.1",
|
||||
@ -3280,6 +3335,7 @@
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
@ -3293,7 +3349,8 @@
|
||||
"node_modules/readable-stream/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "2.2.1",
|
||||
@ -3694,7 +3751,8 @@
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
@ -4145,7 +4203,8 @@
|
||||
"node_modules/spark-md5": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz",
|
||||
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="
|
||||
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/split-string": {
|
||||
"version": "3.1.0",
|
||||
@ -4235,6 +4294,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
@ -4242,7 +4302,8 @@
|
||||
"node_modules/string_decoder/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "1.0.2",
|
||||
@ -4405,6 +4466,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
|
||||
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "2 || 3"
|
||||
@ -4466,6 +4528,7 @@
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
@ -4479,7 +4542,8 @@
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/ts-loader": {
|
||||
"version": "9.5.0",
|
||||
@ -4655,6 +4719,7 @@
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
||||
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
||||
"inBundle": true,
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
@ -4766,6 +4831,7 @@
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
@ -4783,12 +4849,14 @@
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"inBundle": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
@ -4802,7 +4870,8 @@
|
||||
"node_modules/vuvuzela": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vuvuzela/-/vuvuzela-1.0.3.tgz",
|
||||
"integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ=="
|
||||
"integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
@ -4820,12 +4889,13 @@
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.88.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
|
||||
"integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
|
||||
"version": "5.89.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
|
||||
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
@ -4949,6 +5019,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
@ -4994,6 +5065,7 @@
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/write-stream/-/write-stream-0.4.3.tgz",
|
||||
"integrity": "sha512-IJrvkhbAnj89W/GAVdVgbnPiVw5Ntg/B4tc/MUCIEwj/g6JIww1DWJyB/yBMT3yw2/TkT6IUZ0+IYef3flEw8A==",
|
||||
"inBundle": true,
|
||||
"dependencies": {
|
||||
"readable-stream": "~0.0.2"
|
||||
}
|
||||
@ -5001,12 +5073,14 @@
|
||||
"node_modules/write-stream/node_modules/readable-stream": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-0.0.4.tgz",
|
||||
"integrity": "sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw=="
|
||||
"integrity": "sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw==",
|
||||
"inBundle": true
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"inBundle": true,
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
"node_modules"
|
||||
],
|
||||
"dependencies": {
|
||||
"@janhq/plugin-core": "file:../../../../plugin-core",
|
||||
"@janhq/plugin-core": "^0.1.7",
|
||||
"pouchdb-find": "^8.0.1",
|
||||
"pouchdb-node": "^8.0.1"
|
||||
}
|
||||
|
||||
@ -1,23 +1,97 @@
|
||||
const MODULE_PATH = "inference-plugin/dist/module.js";
|
||||
import { EventName, InferenceService, NewMessageRequest, PluginService, core, events, store } from "@janhq/plugin-core";
|
||||
|
||||
const initModel = async (product) =>
|
||||
new Promise(async (resolve) => {
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI
|
||||
.invokePluginFunc(MODULE_PATH, "initModel", product)
|
||||
.then((res) => resolve(res));
|
||||
}
|
||||
});
|
||||
const PluginName = "inference-plugin";
|
||||
const MODULE_PATH = `${PluginName}/dist/module.js`;
|
||||
const inferenceUrl = "http://localhost:3928/llama/chat_completion";
|
||||
|
||||
const inferenceUrl = () => "http://localhost:3928/llama/chat_completion";
|
||||
const initModel = async (product) => core.invokePluginFunc(MODULE_PATH, "initModel", product);
|
||||
|
||||
const stopModel = () => {
|
||||
window.electronAPI.invokePluginFunc(MODULE_PATH, "killSubprocess");
|
||||
core.invokePluginFunc(MODULE_PATH, "killSubprocess");
|
||||
};
|
||||
|
||||
async function handleMessageRequest(data: NewMessageRequest) {
|
||||
// TODO: Common collections should be able to access via core functions instead of store
|
||||
const messageHistory =
|
||||
(await store.findMany("messages", { conversationId: data.conversationId }, [{ createdAt: "asc" }])) ?? [];
|
||||
const recentMessages = messageHistory
|
||||
.filter((e) => e.message !== "" && (e.user === "user" || e.user === "assistant"))
|
||||
.slice(-10)
|
||||
.map((message) => {
|
||||
return {
|
||||
content: message.message.trim(),
|
||||
role: message.user === "user" ? "user" : "assistant",
|
||||
};
|
||||
});
|
||||
|
||||
const message = {
|
||||
...data,
|
||||
message: "",
|
||||
user: "assistant",
|
||||
createdAt: new Date().toISOString(),
|
||||
_id: undefined,
|
||||
};
|
||||
// TODO: Common collections should be able to access via core functions instead of store
|
||||
const id = await store.insertOne("messages", message);
|
||||
|
||||
message._id = id;
|
||||
events.emit(EventName.OnNewMessageResponse, message);
|
||||
|
||||
const response = await fetch(inferenceUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "text/event-stream",
|
||||
"Access-Control-Allow-Origi": "*",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: recentMessages,
|
||||
stream: true,
|
||||
model: "gpt-3.5-turbo",
|
||||
max_tokens: 500,
|
||||
}),
|
||||
});
|
||||
const stream = response.body;
|
||||
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
const reader = stream?.getReader();
|
||||
let answer = "";
|
||||
|
||||
while (true && reader) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
console.log("SSE stream closed");
|
||||
break;
|
||||
}
|
||||
const text = decoder.decode(value);
|
||||
const lines = text.trim().split("\n");
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ") && !line.includes("data: [DONE]")) {
|
||||
const data = JSON.parse(line.replace("data: ", ""));
|
||||
answer += data.choices[0]?.delta?.content ?? "";
|
||||
if (answer.startsWith("assistant: ")) {
|
||||
answer = answer.replace("assistant: ", "");
|
||||
}
|
||||
message.message = answer;
|
||||
events.emit(EventName.OnMessageResponseUpdate, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
message.message = answer.trim();
|
||||
// TODO: Common collections should be able to access via core functions instead of store
|
||||
await store.updateOne("messages", message._id, message);
|
||||
}
|
||||
|
||||
const registerListener = () => {
|
||||
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
|
||||
};
|
||||
|
||||
const onStart = async () => {
|
||||
registerListener();
|
||||
};
|
||||
// Register all the above functions and objects with the relevant extension points
|
||||
export function init({ register }) {
|
||||
register("initModel", "initModel", initModel);
|
||||
register("inferenceUrl", "inferenceUrl", inferenceUrl);
|
||||
register("stopModel", "stopModel", stopModel);
|
||||
register(PluginService.OnStart, PluginName, onStart);
|
||||
register(InferenceService.InitModel, initModel.name, initModel);
|
||||
register(InferenceService.StopModel, stopModel.name, stopModel);
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@janhq/plugin-core": "^0.1.5",
|
||||
"kill-port-process": "^3.2.0",
|
||||
"tcp-port-used": "^1.0.2",
|
||||
"ts-loader": "^9.5.0"
|
||||
@ -28,6 +29,15 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"../../../../plugin-core": {
|
||||
"name": "@janhq/plugin-core",
|
||||
"version": "0.1.6",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.2",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@ -37,6 +47,10 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@janhq/plugin-core": "^0.1.7",
|
||||
"kill-port-process": "^3.2.0",
|
||||
"tcp-port-used": "^1.0.2",
|
||||
"ts-loader": "^9.5.0"
|
||||
|
||||
@ -1,22 +1,13 @@
|
||||
{
|
||||
"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. */
|
||||
"target": "es2016",
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
|
||||
"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. */
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": false,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
export {};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI?: any | undefined;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
import { ModelManagementService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";
|
||||
import { ModelManagementService, PluginService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";
|
||||
|
||||
const PluginName = "model-management-plugin";
|
||||
const MODULE_PATH = "model-management-plugin/dist/module.js";
|
||||
|
||||
const getDownloadedModels = () => core.invokePluginFunc(MODULE_PATH, "getDownloadedModels");
|
||||
@ -81,7 +83,7 @@ function onStart() {
|
||||
|
||||
// Register all the above functions and objects with the relevant extension points
|
||||
export function init({ register }: { register: RegisterExtensionPoint }) {
|
||||
onStart();
|
||||
register(PluginService.OnStart, PluginName, onStart);
|
||||
|
||||
register(ModelManagementService.GetDownloadedModels, getDownloadedModels.name, getDownloadedModels);
|
||||
register(ModelManagementService.GetAvailableModels, getAvailableModels.name, getAvailableModels);
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@huggingface/hub": "^0.8.5",
|
||||
"@janhq/plugin-core": "file:../../../../plugin-core",
|
||||
"@janhq/plugin-core": "^0.1.5",
|
||||
"ts-loader": "^9.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -26,10 +26,11 @@
|
||||
},
|
||||
"../../../../plugin-core": {
|
||||
"name": "@janhq/plugin-core",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.6",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.2"
|
||||
"@types/node": "^12.0.2",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@huggingface/hub": "^0.8.5",
|
||||
"@janhq/plugin-core": "file:../../../../plugin-core",
|
||||
"@janhq/plugin-core": "^0.1.7",
|
||||
"ts-loader": "^9.5.0"
|
||||
},
|
||||
"bundledDependencies": [
|
||||
|
||||
115
electron/core/plugins/openai-plugin/index.ts
Normal file
115
electron/core/plugins/openai-plugin/index.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import {
|
||||
PluginService,
|
||||
EventName,
|
||||
NewMessageRequest,
|
||||
events,
|
||||
store,
|
||||
preferences,
|
||||
RegisterExtensionPoint,
|
||||
} from "@janhq/plugin-core";
|
||||
import { Configuration, OpenAIApi } from "azure-openai";
|
||||
|
||||
const PluginName = "openai-plugin";
|
||||
|
||||
const setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
||||
XMLHttpRequest.prototype.setRequestHeader = function newSetRequestHeader(key: string, val: string) {
|
||||
if (key.toLocaleLowerCase() === "user-agent") {
|
||||
return;
|
||||
}
|
||||
setRequestHeader.apply(this, [key, val]);
|
||||
};
|
||||
|
||||
var openai: OpenAIApi | undefined = undefined;
|
||||
|
||||
const setup = async () => {
|
||||
const apiKey: string = (await preferences.get(PluginName, "apiKey")) ?? "";
|
||||
const endpoint: string = (await preferences.get(PluginName, "endpoint")) ?? "";
|
||||
const deploymentName: string = (await preferences.get(PluginName, "deploymentName")) ?? "";
|
||||
try {
|
||||
openai = new OpenAIApi(
|
||||
new Configuration({
|
||||
azure: {
|
||||
apiKey, //Your API key goes here
|
||||
endpoint, //Your endpoint goes here. It is like: "https://endpointname.openai.azure.com/"
|
||||
deploymentName, //Your deployment name goes here. It is like "chatgpt"
|
||||
},
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
openai = undefined;
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
async function onStart() {
|
||||
setup();
|
||||
registerListener();
|
||||
}
|
||||
|
||||
async function handleMessageRequest(data: NewMessageRequest) {
|
||||
if (!openai) {
|
||||
const message = {
|
||||
...data,
|
||||
message: "Your API key is not set. Please set it in the plugin preferences.",
|
||||
user: "GPT-3",
|
||||
avatar: "https://static-assets.jan.ai/openai-icon.jpg",
|
||||
createdAt: new Date().toISOString(),
|
||||
_id: undefined,
|
||||
};
|
||||
const id = await store.insertOne("messages", message);
|
||||
message._id = id;
|
||||
events.emit(EventName.OnNewMessageResponse, message);
|
||||
return;
|
||||
}
|
||||
|
||||
const message = {
|
||||
...data,
|
||||
message: "",
|
||||
user: "GPT-3",
|
||||
avatar: "https://static-assets.jan.ai/openai-icon.jpg",
|
||||
createdAt: new Date().toISOString(),
|
||||
_id: undefined,
|
||||
};
|
||||
const id = await store.insertOne("messages", message);
|
||||
|
||||
message._id = id;
|
||||
events.emit(EventName.OnNewMessageResponse, message);
|
||||
const response = await openai.createChatCompletion({
|
||||
messages: [{ role: "user", content: data.message }],
|
||||
model: "gpt-3.5-turbo",
|
||||
});
|
||||
message.message = response.data.choices[0].message.content;
|
||||
events.emit(EventName.OnMessageResponseUpdate, message);
|
||||
await store.updateOne("messages", message._id, message);
|
||||
}
|
||||
|
||||
const registerListener = () => {
|
||||
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
|
||||
};
|
||||
|
||||
const onPreferencesUpdate = () => {
|
||||
setup();
|
||||
};
|
||||
// Register all the above functions and objects with the relevant extension points
|
||||
export function init({ register }: { register: RegisterExtensionPoint }) {
|
||||
register(PluginService.OnStart, PluginName, onStart);
|
||||
register(PluginService.OnPreferencesUpdate, PluginName, onPreferencesUpdate);
|
||||
|
||||
preferences.registerPreferences<string>(register, PluginName, "apiKey", "API Key", "Azure Project API Key", "");
|
||||
preferences.registerPreferences<string>(
|
||||
register,
|
||||
PluginName,
|
||||
"endpoint",
|
||||
"API Endpoint",
|
||||
"Azure Deployment Endpoint API",
|
||||
""
|
||||
);
|
||||
preferences.registerPreferences<string>(
|
||||
register,
|
||||
PluginName,
|
||||
"deploymentName",
|
||||
"Deployment Name",
|
||||
"The deployment name you chose when you deployed the model",
|
||||
""
|
||||
);
|
||||
}
|
||||
3708
electron/core/plugins/openai-plugin/package-lock.json
generated
Normal file
3708
electron/core/plugins/openai-plugin/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
46
electron/core/plugins/openai-plugin/package.json
Normal file
46
electron/core/plugins/openai-plugin/package.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "azure-openai-plugin",
|
||||
"version": "1.0.0",
|
||||
"description": "Inference plugin for Azure OpenAI",
|
||||
"icon": "https://static-assets.jan.ai/openai-icon.jpg",
|
||||
"main": "dist/index.js",
|
||||
"author": "Jan",
|
||||
"license": "MIT",
|
||||
"activationPoints": [
|
||||
"init"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc -b . && webpack --config webpack.config.js",
|
||||
"postinstall": "rimraf ./*.tgz && npm run build && rimraf dist/nitro/* && cpx \"nitro/**\" \"dist/nitro\"",
|
||||
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
|
||||
},
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./main": "./dist/module.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cpx": "^1.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"webpack": "^5.88.2",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@janhq/plugin-core": "^0.1.7",
|
||||
"azure-openai": "^0.9.4",
|
||||
"kill-port-process": "^3.2.0",
|
||||
"tcp-port-used": "^1.0.2",
|
||||
"ts-loader": "^9.5.0"
|
||||
},
|
||||
"bundledDependencies": [
|
||||
"tcp-port-used",
|
||||
"kill-port-process"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist/*",
|
||||
"package.json",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
13
electron/core/plugins/openai-plugin/tsconfig.json
Normal file
13
electron/core/plugins/openai-plugin/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
|
||||
"outDir": "./dist",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": false,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
25
electron/core/plugins/openai-plugin/webpack.config.js
Normal file
25
electron/core/plugins/openai-plugin/webpack.config.js
Normal file
@ -0,0 +1,25 @@
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
experiments: { outputModule: true },
|
||||
entry: "./index.ts", // Adjust the entry point to match your project's main file
|
||||
mode: "production",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: "ts-loader",
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
],
|
||||
},
|
||||
output: {
|
||||
filename: "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"],
|
||||
},
|
||||
// Add loaders and other configuration as needed for your project
|
||||
};
|
||||
@ -22,8 +22,8 @@
|
||||
"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\"",
|
||||
"build:plugins-darwin": "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\" && chmod +x ./electron/auto-sign.sh && ./electron/auto-sign.sh && 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\"",
|
||||
"build:plugins": "rimraf ./electron/core/pre-install/*.tgz && concurrently --kill-others-on-fail \"cd ./electron/core/plugins/data-plugin && npm install && npm run postinstall\" \"cd ./electron/core/plugins/inference-plugin && npm install && npm run postinstall\" \"cd ./electron/core/plugins/model-management-plugin && npm install && npm run postinstall\" \"cd ./electron/core/plugins/monitoring-plugin && npm install && npm run postinstall\" && concurrently --kill-others-on-fail \"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\"",
|
||||
"build:plugins-darwin": "rimraf ./electron/core/pre-install/*.tgz && concurrently \"cd ./electron/core/plugins/data-plugin && npm install && npm run postinstall\" \"cd ./electron/core/plugins/inference-plugin && npm install && npm run postinstall\" \"cd ./electron/core/plugins/model-management-plugin && npm install && npm run postinstall\" \"cd ./electron/core/plugins/monitoring-plugin && npm install && npm run postinstall\" && chmod +x ./electron/auto-sign.sh && ./electron/auto-sign.sh && 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\"",
|
||||
"build": "yarn build:web && yarn build:electron",
|
||||
"build:darwin": "yarn build:web && yarn workspace jan build:darwin",
|
||||
"build:win32": "yarn build:web && yarn workspace jan build:win32",
|
||||
|
||||
@ -143,15 +143,20 @@ To register plugin preferences, you can use the preferences object from the @jan
|
||||
```js
|
||||
import { PluginService, preferences } from "@janhq/plugin-core";
|
||||
|
||||
const PluginName = "your-first-plugin";
|
||||
const pluginName = "your-first-plugin";
|
||||
const preferenceKey = "";
|
||||
const preferenceName = "Your First Preference";
|
||||
const preferenceDescription = "This is for example only";
|
||||
const defaultValue = "";
|
||||
|
||||
export function init({ register }: { register: RegisterExtensionPoint }) {
|
||||
// Register preference update handlers. E.g. update plugin instance with new configuration
|
||||
register(PluginService.OnPreferencesUpdate, PluginName, onPreferencesUpdate);
|
||||
register(PluginService.OnPreferencesUpdate, pluginName, onPreferencesUpdate);
|
||||
|
||||
// Register plugin preferences. E.g. Plugin need apiKey and endpoint to connect to your service
|
||||
preferences.registerPreferences<string>(register, PluginName, "apiKey", "");
|
||||
preferences.registerPreferences<string>(register, PluginName, "endpoint", "");
|
||||
// Register plugin preferences. E.g. Plugin need apiKey to connect to your service
|
||||
preferences.registerPreferences <
|
||||
string >
|
||||
(register, pluginName, preferenceKey, preferenceName, preferenceDescription, defaultValue);
|
||||
}
|
||||
```
|
||||
|
||||
@ -162,15 +167,13 @@ To retrieve the values of the registered preferences, we're using the get method
|
||||
```js
|
||||
import { preferences } from "@janhq/plugin-core";
|
||||
|
||||
const PluginName = "your-first-plugin";
|
||||
const pluginName = "your-first-plugin";
|
||||
const preferenceKey = "apiKey";
|
||||
|
||||
const setup = async () => {
|
||||
// Retrieve apiKey
|
||||
const apiKey: string = (await preferences.get(PluginName, "apiKey")) ?? "";
|
||||
|
||||
// Retrieve endpoint
|
||||
const endpoint: string = (await preferences.get(PluginName, "endpoint")) ?? "";
|
||||
}
|
||||
const apiKey: string = (await preferences.get(pluginName, preferenceKey)) ?? "";
|
||||
};
|
||||
```
|
||||
|
||||
### Access Core API
|
||||
|
||||
@ -8,7 +8,8 @@ export type CoreService =
|
||||
| InferenceService
|
||||
| ModelManagementService
|
||||
| SystemMonitoringService
|
||||
| PreferenceService;
|
||||
| PreferenceService
|
||||
| PluginService;
|
||||
|
||||
/**
|
||||
* Represents the available methods for the StoreService.
|
||||
|
||||
@ -4,13 +4,13 @@ import { store } from "./store";
|
||||
* Returns the value of the specified preference for the specified plugin.
|
||||
*
|
||||
* @param pluginName The name of the plugin.
|
||||
* @param preferenceName The name of the preference.
|
||||
* @param preferenceKey The key of the preference.
|
||||
* @returns A promise that resolves to the value of the preference.
|
||||
*/
|
||||
function get(pluginName: string, preferenceName: string): Promise<any> {
|
||||
function get(pluginName: string, preferenceKey: string): Promise<any> {
|
||||
return store
|
||||
.createCollection("preferences", {})
|
||||
.then(() => store.findOne("preferences", `${pluginName}.${preferenceName}`))
|
||||
.then(() => store.findOne("preferences", `${pluginName}.${preferenceKey}`))
|
||||
.then((doc) => doc?.value ?? "");
|
||||
}
|
||||
|
||||
@ -18,20 +18,20 @@ function get(pluginName: string, preferenceName: string): Promise<any> {
|
||||
* Sets the value of the specified preference for the specified plugin.
|
||||
*
|
||||
* @param pluginName The name of the plugin.
|
||||
* @param preferenceName The name of the preference.
|
||||
* @param preferenceKey The key of the preference.
|
||||
* @param value The value of the preference.
|
||||
* @returns A promise that resolves when the preference has been set.
|
||||
*/
|
||||
function set(pluginName: string, preferenceName: string, value: any): Promise<any> {
|
||||
function set(pluginName: string, preferenceKey: string, value: any): Promise<any> {
|
||||
return store
|
||||
.createCollection("preferences", {})
|
||||
.then(() =>
|
||||
store
|
||||
.findOne("preferences", `${pluginName}.${preferenceName}`)
|
||||
.findOne("preferences", `${pluginName}.${preferenceKey}`)
|
||||
.then((doc) =>
|
||||
doc
|
||||
? store.updateOne("preferences", `${pluginName}.${preferenceName}`, { value })
|
||||
: store.insertOne("preferences", { _id: `${pluginName}.${preferenceName}`, value })
|
||||
? store.updateOne("preferences", `${pluginName}.${preferenceKey}`, { value })
|
||||
: store.insertOne("preferences", { _id: `${pluginName}.${preferenceKey}`, value })
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -51,7 +51,9 @@ function clear(pluginName: string): Promise<void> {
|
||||
*
|
||||
* @param register The function to use for registering the preference.
|
||||
* @param pluginName The name of the plugin.
|
||||
* @param preferenceKey The key of the preference.
|
||||
* @param preferenceName The name of the preference.
|
||||
* @param preferenceDescription The description of the preference.
|
||||
* @param defaultValue The default value of the preference.
|
||||
*/
|
||||
function registerPreferences<T>(
|
||||
|
||||
@ -6,21 +6,22 @@ import {
|
||||
extensionPoints,
|
||||
activationPoints,
|
||||
} from "@/../../electron/core/plugin-manager/execution/index";
|
||||
import {
|
||||
ChartPieIcon,
|
||||
CommandLineIcon,
|
||||
PlayIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { ChartPieIcon, CommandLineIcon, PlayIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
|
||||
import classNames from "classnames";
|
||||
import { PluginService, preferences } from "@janhq/plugin-core";
|
||||
import { execute } from "../../../electron/core/plugin-manager/execution/extension-manager";
|
||||
|
||||
/* eslint-disable @next/next/no-sync-scripts */
|
||||
export const Preferences = () => {
|
||||
const [search, setSearch] = useState<string>("");
|
||||
const [activePlugins, setActivePlugins] = useState<any[]>([]);
|
||||
const [preferenceItems, setPreferenceItems] = useState<any[]>([]);
|
||||
const [preferenceValues, setPreferenceValues] = useState<any[]>([]);
|
||||
const [isTestAvailable, setIsTestAvailable] = useState(false);
|
||||
const [fileName, setFileName] = useState("");
|
||||
const experimentRef = useRef(null);
|
||||
const preferenceRef = useRef(null);
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = event.target.files?.[0];
|
||||
@ -31,7 +32,6 @@ export const Preferences = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const preferenceRef = useRef(null);
|
||||
useEffect(() => {
|
||||
async function setupPE() {
|
||||
// Enable activation point management
|
||||
@ -54,19 +54,22 @@ export const Preferences = () => {
|
||||
setTimeout(async () => {
|
||||
await activationPoints.trigger("init");
|
||||
if (extensionPoints.get("experimentComponent")) {
|
||||
const components = await Promise.all(
|
||||
extensionPoints.execute("experimentComponent")
|
||||
);
|
||||
const components = await Promise.all(extensionPoints.execute("experimentComponent"));
|
||||
if (components.length > 0) {
|
||||
setIsTestAvailable(true);
|
||||
}
|
||||
components.forEach((e) => {
|
||||
if (preferenceRef.current) {
|
||||
if (experimentRef.current) {
|
||||
// @ts-ignore
|
||||
preferenceRef.current.appendChild(e);
|
||||
experimentRef.current.appendChild(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (extensionPoints.get("PluginPreferences")) {
|
||||
const data = await Promise.all(extensionPoints.execute("PluginPreferences"));
|
||||
setPreferenceItems(Array.isArray(data) ? data : []);
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
setupPE().then(() => activePlugins());
|
||||
@ -86,17 +89,9 @@ export const Preferences = () => {
|
||||
|
||||
// Uninstall a plugin on clicking uninstall
|
||||
const uninstall = async (name: string) => {
|
||||
//@ts-ignore
|
||||
|
||||
// Send the filename of the to be uninstalled plugin
|
||||
// to the main process for removal
|
||||
//@ts-ignore
|
||||
const res = await plugins.uninstall([name]);
|
||||
console.log(
|
||||
res
|
||||
? "Plugin successfully uninstalled"
|
||||
: "Plugin could not be uninstalled"
|
||||
);
|
||||
if (res) window.electronAPI.relaunch();
|
||||
};
|
||||
|
||||
@ -110,6 +105,26 @@ export const Preferences = () => {
|
||||
// plugins.update(active.map((plg) => plg.name));
|
||||
};
|
||||
|
||||
let timeout: any | undefined = undefined;
|
||||
function notifyPreferenceUpdate() {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => execute(PluginService.OnPreferencesUpdate), 100);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (preferenceItems) {
|
||||
Promise.all(
|
||||
preferenceItems.map((e) =>
|
||||
preferences.get(e.pluginName, e.preferenceKey).then((k) => ({ key: e.preferenceKey, value: k }))
|
||||
)
|
||||
).then((data) => {
|
||||
setPreferenceValues(data);
|
||||
});
|
||||
}
|
||||
}, [preferenceItems]);
|
||||
|
||||
return (
|
||||
<div className="w-full h-screen overflow-scroll">
|
||||
<div className="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 border-b border-gray-200 bg-white shadow-sm sm:gap-x-6 sm:px-6 px-8">
|
||||
@ -152,12 +167,9 @@ export const Preferences = () => {
|
||||
{!fileName ? (
|
||||
<div className="flex flex-col items-center justify-center pt-5 pb-6">
|
||||
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
<span className="font-semibold">Click to upload</span>{" "}
|
||||
or drag and drop
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
TGZ (MAX 50MB)
|
||||
<span className="font-semibold">Click to upload</span> or drag and drop
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">TGZ (MAX 50MB)</p>
|
||||
</div>
|
||||
) : (
|
||||
<>{fileName}</>
|
||||
@ -177,9 +189,7 @@ export const Preferences = () => {
|
||||
type="submit"
|
||||
className={classNames(
|
||||
"rounded-md px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
|
||||
fileName
|
||||
? "bg-blue-500 hover:bg-blue-300"
|
||||
: "bg-gray-500"
|
||||
fileName ? "bg-blue-500 hover:bg-blue-300" : "bg-gray-500"
|
||||
)}
|
||||
>
|
||||
Install Plugin
|
||||
@ -205,11 +215,7 @@ export const Preferences = () => {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-stretch gap-4">
|
||||
{activePlugins
|
||||
.filter(
|
||||
(e) =>
|
||||
search.trim() === "" ||
|
||||
e.name.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
.filter((e) => search.trim() === "" || e.name.toLowerCase().includes(search.toLowerCase()))
|
||||
.map((e) => (
|
||||
<div
|
||||
key={e.name}
|
||||
@ -218,19 +224,13 @@ export const Preferences = () => {
|
||||
>
|
||||
<div className="flex flex-row space-x-2 items-center">
|
||||
<span className="relative inline-block mt-1">
|
||||
<img
|
||||
className="h-14 w-14 rounded-md"
|
||||
src={e.icon ?? "icons/app_icon.svg"}
|
||||
alt=""
|
||||
/>
|
||||
<img className="h-14 w-14 rounded-md" src={e.icon ?? "icons/app_icon.svg"} alt="" />
|
||||
</span>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-xl font-bold tracking-tight text-gray-900 dark:text-white capitalize">
|
||||
{e.name.replaceAll("-", " ")}
|
||||
</p>
|
||||
<p className="font-normal text-gray-700 dark:text-gray-400">
|
||||
Version: {e.version}
|
||||
</p>
|
||||
<p className="font-normal text-gray-700 dark:text-gray-400">Version: {e.version}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -266,8 +266,34 @@ export const Preferences = () => {
|
||||
Test Plugins
|
||||
</div>
|
||||
)}
|
||||
<div className="h-full w-full" ref={preferenceRef}></div>
|
||||
{/* Content */}
|
||||
<div className="h-full w-full" ref={experimentRef}></div>
|
||||
|
||||
<div className="flex flex-row items-center my-4">
|
||||
<PlayIcon width={30} />
|
||||
Preferences
|
||||
</div>
|
||||
<div className="h-full w-full flex flex-col" ref={preferenceRef}>
|
||||
{preferenceItems?.map((e) => (
|
||||
<div key={e.preferenceKey} className="flex flex-col mb-4">
|
||||
<div>
|
||||
<span className="text-[16px] text-gray-600">Setting:</span>{" "}
|
||||
<span className="text-[16px] text-gray-900">{e.preferenceName}</span>
|
||||
</div>
|
||||
<span className="text-[14px] text-gray-400">{e.preferenceDescription}</span>
|
||||
<div className="flex flex-row space-x-4 items-center mt-2">
|
||||
<input
|
||||
className="text-gray-500 w-1/3 rounded-sm border-gray-300 border-[1px] h-8"
|
||||
defaultValue={preferenceValues.filter((v) => v.key === e.preferenceKey)[0]?.value}
|
||||
onChange={(event) => {
|
||||
preferences
|
||||
.set(e.pluginName, e.preferenceKey, event.target.value)
|
||||
.then(() => notifyPreferenceUpdate());
|
||||
}}
|
||||
></input>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
34
web/app/_helpers/EventHandler.tsx
Normal file
34
web/app/_helpers/EventHandler.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { addNewMessageAtom, updateMessageAtom } from "@/_helpers/atoms/ChatMessage.atom";
|
||||
import { toChatMessage } from "@/_models/ChatMessage";
|
||||
import { events, EventName, NewMessageResponse } from "@janhq/plugin-core";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { ReactNode, useEffect } from "react";
|
||||
|
||||
export default function EventHandler({ children }: { children: ReactNode }) {
|
||||
const addNewMessage = useSetAtom(addNewMessageAtom);
|
||||
const updateMessage = useSetAtom(updateMessageAtom);
|
||||
|
||||
function handleNewMessageResponse(message: NewMessageResponse) {
|
||||
const newResponse = toChatMessage(message);
|
||||
addNewMessage(newResponse);
|
||||
}
|
||||
async function handleMessageResponseUpdate(messageResponse: NewMessageResponse) {
|
||||
if (messageResponse.conversationId && messageResponse._id && messageResponse.message)
|
||||
updateMessage(messageResponse._id, messageResponse.conversationId, messageResponse.message);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (window.corePlugin.events) {
|
||||
events.on(EventName.OnNewMessageResponse, handleNewMessageResponse);
|
||||
events.on(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
events.off(EventName.OnNewMessageResponse, handleNewMessageResponse);
|
||||
events.off(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate);
|
||||
};
|
||||
}, []);
|
||||
return <> {children}</>;
|
||||
}
|
||||
@ -6,12 +6,10 @@ import { appDownloadProgress } from "./JotaiWrapper";
|
||||
import { DownloadState } from "@/_models/DownloadState";
|
||||
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
|
||||
import { ModelManagementService } from "@janhq/plugin-core";
|
||||
import {
|
||||
setDownloadStateAtom,
|
||||
setDownloadStateSuccessAtom,
|
||||
} from "./atoms/DownloadState.atom";
|
||||
import { setDownloadStateAtom, setDownloadStateSuccessAtom } from "./atoms/DownloadState.atom";
|
||||
import { getDownloadedModels } from "@/_hooks/useGetDownloadedModels";
|
||||
import { downloadedModelAtom } from "./atoms/DownloadedModel.atom";
|
||||
import EventHandler from "./EventHandler";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
@ -25,57 +23,46 @@ export default function EventListenerWrapper({ children }: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
if (window && window.electronAPI) {
|
||||
window.electronAPI.onFileDownloadUpdate(
|
||||
(_event: string, state: DownloadState | undefined) => {
|
||||
if (!state) return;
|
||||
setDownloadState(state);
|
||||
}
|
||||
);
|
||||
window.electronAPI.onFileDownloadUpdate((_event: string, state: DownloadState | undefined) => {
|
||||
if (!state) return;
|
||||
setDownloadState(state);
|
||||
});
|
||||
|
||||
window.electronAPI.onFileDownloadError(
|
||||
(_event: string, callback: any) => {
|
||||
console.log("Download error", callback);
|
||||
}
|
||||
);
|
||||
window.electronAPI.onFileDownloadError((_event: string, callback: any) => {
|
||||
console.log("Download error", callback);
|
||||
});
|
||||
|
||||
window.electronAPI.onFileDownloadSuccess(
|
||||
(_event: string, callback: any) => {
|
||||
if (callback && callback.fileName) {
|
||||
setDownloadStateSuccess(callback.fileName);
|
||||
window.electronAPI.onFileDownloadSuccess((_event: string, callback: any) => {
|
||||
if (callback && callback.fileName) {
|
||||
setDownloadStateSuccess(callback.fileName);
|
||||
|
||||
executeSerial(
|
||||
ModelManagementService.UpdateFinishedDownloadAt,
|
||||
callback.fileName
|
||||
).then(() => {
|
||||
getDownloadedModels().then((models) => {
|
||||
setDownloadedModels(models);
|
||||
});
|
||||
executeSerial(ModelManagementService.UpdateFinishedDownloadAt, callback.fileName).then(() => {
|
||||
getDownloadedModels().then((models) => {
|
||||
setDownloadedModels(models);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
window.electronAPI.onAppUpdateDownloadUpdate(
|
||||
(_event: string, progress: any) => {
|
||||
setProgress(progress.percent);
|
||||
console.log("app update progress:", progress.percent);
|
||||
}
|
||||
);
|
||||
window.electronAPI.onAppUpdateDownloadUpdate((_event: string, progress: any) => {
|
||||
setProgress(progress.percent);
|
||||
console.log("app update progress:", progress.percent);
|
||||
});
|
||||
|
||||
window.electronAPI.onAppUpdateDownloadError(
|
||||
(_event: string, callback: any) => {
|
||||
console.log("Download error", callback);
|
||||
setProgress(-1);
|
||||
}
|
||||
);
|
||||
window.electronAPI.onAppUpdateDownloadError((_event: string, callback: any) => {
|
||||
console.log("Download error", callback);
|
||||
setProgress(-1);
|
||||
});
|
||||
|
||||
window.electronAPI.onAppUpdateDownloadSuccess(
|
||||
(_event: string, callback: any) => {
|
||||
setProgress(-1);
|
||||
}
|
||||
);
|
||||
window.electronAPI.onAppUpdateDownloadSuccess((_event: string, callback: any) => {
|
||||
setProgress(-1);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <div id="eventlistener">{children}</div>;
|
||||
return (
|
||||
<div id="eventlistener">
|
||||
<EventHandler>{children}</EventHandler>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -31,28 +31,18 @@ const useChatMessages = (offset = 0) => {
|
||||
if (!hasMore) return;
|
||||
|
||||
const getMessages = async () => {
|
||||
executeSerial(
|
||||
DataService.GetConversationMessages,
|
||||
currentConvo._id
|
||||
).then((data: any) => {
|
||||
executeSerial(DataService.GetConversationMessages, currentConvo._id).then((data: any) => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
parseMessages(data ?? []).then((newMessages) => {
|
||||
addOldChatMessages(newMessages);
|
||||
updateConvoHasMore(currentConvo._id ?? "", false);
|
||||
setLoading(false);
|
||||
});
|
||||
const newMessages = parseMessages(data ?? []);
|
||||
addOldChatMessages(newMessages);
|
||||
updateConvoHasMore(currentConvo._id ?? "", false);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
getMessages();
|
||||
}, [
|
||||
offset,
|
||||
currentConvo?._id,
|
||||
convoStates,
|
||||
addOldChatMessages,
|
||||
updateConvoHasMore,
|
||||
]);
|
||||
}, [offset, currentConvo?._id, convoStates, addOldChatMessages, updateConvoHasMore]);
|
||||
|
||||
return {
|
||||
loading: loading,
|
||||
@ -61,10 +51,10 @@ const useChatMessages = (offset = 0) => {
|
||||
};
|
||||
};
|
||||
|
||||
async function parseMessages(messages: RawMessage[]): Promise<ChatMessage[]> {
|
||||
function parseMessages(messages: RawMessage[]): ChatMessage[] {
|
||||
const newMessages: ChatMessage[] = [];
|
||||
for (const m of messages) {
|
||||
const chatMessage = await toChatMessage(m);
|
||||
const chatMessage = toChatMessage(m);
|
||||
newMessages.push(chatMessage);
|
||||
}
|
||||
return newMessages;
|
||||
|
||||
@ -1,51 +1,19 @@
|
||||
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
|
||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||
import { selectAtom } from "jotai/utils";
|
||||
import { DataService, InferenceService } from "@janhq/plugin-core";
|
||||
import {
|
||||
ChatMessage,
|
||||
MessageSenderType,
|
||||
RawMessage,
|
||||
toChatMessage,
|
||||
} from "@/_models/ChatMessage";
|
||||
import { DataService, EventName, events } from "@janhq/plugin-core";
|
||||
import { RawMessage, toChatMessage } from "@/_models/ChatMessage";
|
||||
import { executeSerial } from "@/_services/pluginService";
|
||||
import { useCallback } from "react";
|
||||
import {
|
||||
addNewMessageAtom,
|
||||
updateMessageAtom,
|
||||
chatMessages,
|
||||
currentChatMessagesAtom,
|
||||
} from "@/_helpers/atoms/ChatMessage.atom";
|
||||
import {
|
||||
currentConversationAtom,
|
||||
getActiveConvoIdAtom,
|
||||
updateConversationAtom,
|
||||
updateConversationWaitingForResponseAtom,
|
||||
} from "@/_helpers/atoms/Conversation.atom";
|
||||
import { Conversation } from "@/_models/Conversation";
|
||||
import { addNewMessageAtom } from "@/_helpers/atoms/ChatMessage.atom";
|
||||
import { currentConversationAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||
|
||||
export default function useSendChatMessage() {
|
||||
const currentConvo = useAtomValue(currentConversationAtom);
|
||||
const updateConversation = useSetAtom(updateConversationAtom);
|
||||
const addNewMessage = useSetAtom(addNewMessageAtom);
|
||||
const updateMessage = useSetAtom(updateMessageAtom);
|
||||
const activeConversationId = useAtomValue(getActiveConvoIdAtom) ?? "";
|
||||
const updateConvWaiting = useSetAtom(
|
||||
updateConversationWaitingForResponseAtom
|
||||
);
|
||||
|
||||
const chatMessagesHistory = useAtomValue(
|
||||
selectAtom(
|
||||
chatMessages,
|
||||
useCallback((v) => v[activeConversationId], [activeConversationId])
|
||||
)
|
||||
);
|
||||
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom);
|
||||
|
||||
const sendChatMessage = async () => {
|
||||
setCurrentPrompt("");
|
||||
const conversationId = activeConversationId;
|
||||
updateConvWaiting(conversationId, true);
|
||||
const prompt = currentPrompt.trim();
|
||||
const newMessage: RawMessage = {
|
||||
conversationId: currentConvo?._id,
|
||||
@ -56,188 +24,9 @@ export default function useSendChatMessage() {
|
||||
const id = await executeSerial(DataService.CreateMessage, newMessage);
|
||||
newMessage._id = id;
|
||||
|
||||
const newChatMessage = await toChatMessage(newMessage);
|
||||
addNewMessage(newChatMessage);
|
||||
const messageHistory = chatMessagesHistory ?? [];
|
||||
const recentMessages = [
|
||||
...messageHistory.sort((a, b) => parseInt(a.id) - parseInt(b.id)),
|
||||
newChatMessage,
|
||||
]
|
||||
.slice(-10)
|
||||
.map((message) => {
|
||||
return {
|
||||
content: message.text,
|
||||
role:
|
||||
message.messageSenderType === MessageSenderType.User
|
||||
? "user"
|
||||
: "assistant",
|
||||
};
|
||||
});
|
||||
const url = await executeSerial(InferenceService.InferenceUrl);
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "text/event-stream",
|
||||
"Access-Control-Allow-Origi": "*",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: recentMessages,
|
||||
stream: true,
|
||||
model: "gpt-3.5-turbo",
|
||||
max_tokens: 500,
|
||||
}),
|
||||
});
|
||||
const stream = response.body;
|
||||
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
const reader = stream?.getReader();
|
||||
let answer = "";
|
||||
|
||||
// Cache received response
|
||||
const newResponse: RawMessage = {
|
||||
conversationId: currentConvo?._id,
|
||||
message: answer,
|
||||
user: "assistant",
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
const respId = await executeSerial(DataService.CreateMessage, newResponse);
|
||||
newResponse._id = respId;
|
||||
const responseChatMessage = toChatMessage(newResponse);
|
||||
addNewMessage(responseChatMessage);
|
||||
|
||||
while (true && reader) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
console.log("SSE stream closed");
|
||||
break;
|
||||
}
|
||||
const text = decoder.decode(value);
|
||||
const lines = text.trim().split("\n");
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ") && !line.includes("data: [DONE]")) {
|
||||
updateConvWaiting(conversationId, false);
|
||||
const data = JSON.parse(line.replace("data: ", ""));
|
||||
answer += data.choices[0]?.delta?.content ?? "";
|
||||
if (answer.startsWith("assistant: ")) {
|
||||
answer = answer.replace("assistant: ", "");
|
||||
}
|
||||
updateMessage(
|
||||
responseChatMessage.id,
|
||||
responseChatMessage.conversationId,
|
||||
answer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateMessage(
|
||||
responseChatMessage.id,
|
||||
responseChatMessage.conversationId,
|
||||
answer.trimEnd()
|
||||
);
|
||||
await executeSerial(DataService.UpdateMessage, {
|
||||
...newResponse,
|
||||
message: answer.trimEnd(),
|
||||
updatedAt: new Date()
|
||||
.toISOString()
|
||||
.replace("T", " ")
|
||||
.replace(/\.\d+Z$/, ""),
|
||||
});
|
||||
|
||||
const updatedConvo: Conversation = {
|
||||
...currentConvo,
|
||||
lastMessage: answer.trim(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
await executeSerial(DataService.UpdateConversation, updatedConvo);
|
||||
updateConversation(updatedConvo);
|
||||
updateConvWaiting(conversationId, false);
|
||||
|
||||
newResponse.message = answer.trim();
|
||||
const messages: RawMessage[] = [newMessage, newResponse];
|
||||
|
||||
inferConvoSummary(updatedConvo, messages);
|
||||
};
|
||||
|
||||
const inferConvoSummary = async (
|
||||
convo: Conversation,
|
||||
lastMessages: RawMessage[]
|
||||
) => {
|
||||
if (convo.summary) return;
|
||||
const newMessage: RawMessage = {
|
||||
conversationId: currentConvo?._id,
|
||||
message: "summary this conversation in 5 words",
|
||||
user: "user",
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
const messageHistory = lastMessages.map((m) => toChatMessage(m));
|
||||
const newChatMessage = toChatMessage(newMessage);
|
||||
|
||||
const recentMessages = [...messageHistory, newChatMessage]
|
||||
.slice(-10)
|
||||
.map((message) => ({
|
||||
content: message.text,
|
||||
role: message.messageSenderType,
|
||||
}));
|
||||
|
||||
console.debug(`Sending ${JSON.stringify(recentMessages)}`);
|
||||
const url = await executeSerial(InferenceService.InferenceUrl);
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "text/event-stream",
|
||||
"Access-Control-Allow-Origi": "*",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: recentMessages,
|
||||
stream: true,
|
||||
model: "gpt-3.5-turbo",
|
||||
max_tokens: 500,
|
||||
}),
|
||||
});
|
||||
const stream = response.body;
|
||||
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
const reader = stream?.getReader();
|
||||
let answer = "";
|
||||
|
||||
// Cache received response
|
||||
const newResponse: RawMessage = {
|
||||
conversationId: currentConvo?._id,
|
||||
message: answer,
|
||||
user: "assistant",
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
while (reader) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
console.log("SSE stream closed");
|
||||
break;
|
||||
}
|
||||
const text = decoder.decode(value);
|
||||
const lines = text.trim().split("\n");
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("data: ") && !line.includes("data: [DONE]")) {
|
||||
const data = JSON.parse(line.replace("data: ", ""));
|
||||
answer += data.choices[0]?.delta?.content ?? "";
|
||||
if (answer.startsWith("assistant: ")) {
|
||||
answer = answer.replace("assistant: ", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updatedConvo: Conversation = {
|
||||
...convo,
|
||||
summary: answer.trim(),
|
||||
};
|
||||
|
||||
console.debug(`Update convo: ${JSON.stringify(updatedConvo)}`);
|
||||
await executeSerial(DataService.UpdateConversation, updatedConvo);
|
||||
updateConversation(updatedConvo);
|
||||
addNewMessage(newChatMessage);
|
||||
events.emit(EventName.OnNewMessageRequest, newMessage);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { NewMessageResponse } from "@janhq/plugin-core";
|
||||
export enum MessageType {
|
||||
Text = "Text",
|
||||
Image = "Image",
|
||||
@ -33,12 +34,13 @@ export interface RawMessage {
|
||||
_id?: string;
|
||||
conversationId?: string;
|
||||
user?: string;
|
||||
avatar?: string;
|
||||
message?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export const toChatMessage = (m: RawMessage): ChatMessage => {
|
||||
export const toChatMessage = (m: RawMessage | NewMessageResponse): ChatMessage => {
|
||||
const createdAt = new Date(m.createdAt ?? "").getTime();
|
||||
const imageUrls: string[] = [];
|
||||
const imageUrl = undefined;
|
||||
@ -47,8 +49,7 @@ export const toChatMessage = (m: RawMessage): ChatMessage => {
|
||||
}
|
||||
|
||||
const messageType = MessageType.Text;
|
||||
const messageSenderType =
|
||||
m.user === "user" ? MessageSenderType.User : MessageSenderType.Ai;
|
||||
const messageSenderType = m.user === "user" ? MessageSenderType.User : MessageSenderType.Ai;
|
||||
|
||||
const content = m.message ?? "";
|
||||
|
||||
@ -58,9 +59,8 @@ export const toChatMessage = (m: RawMessage): ChatMessage => {
|
||||
messageType: messageType,
|
||||
messageSenderType: messageSenderType,
|
||||
senderUid: m.user?.toString() || "0",
|
||||
senderName: m.user === "user" ? "You" : "Assistant",
|
||||
senderAvatarUrl:
|
||||
m.user === "user" ? "icons/avatar.svg" : "icons/app_icon.svg",
|
||||
senderName: m.user === "user" ? "You" : m.user && m.user !== "ai" && m.user !== "assistant" ? m.user : "Assistant",
|
||||
senderAvatarUrl: m.avatar ? m.avatar : m.user === "user" ? "icons/avatar.svg" : "icons/app_icon.svg",
|
||||
text: content,
|
||||
imageUrls: imageUrls,
|
||||
createdAt: createdAt,
|
||||
|
||||
@ -1,8 +1,21 @@
|
||||
import { store } from "./storeService";
|
||||
import { EventEmitter } from "./eventsService";
|
||||
|
||||
export const setupCoreServices = () => {
|
||||
if (typeof window === "undefined") {
|
||||
console.log("undefine", window);
|
||||
return;
|
||||
} else {
|
||||
console.log("Setting up core services");
|
||||
}
|
||||
if (!window.corePlugin) {
|
||||
window.corePlugin = {
|
||||
store,
|
||||
events: new EventEmitter(),
|
||||
};
|
||||
}
|
||||
if (!window.coreAPI) {
|
||||
// fallback electron API
|
||||
window.coreAPI = window.electronAPI;
|
||||
}
|
||||
};
|
||||
|
||||
40
web/app/_services/eventsService.ts
Normal file
40
web/app/_services/eventsService.ts
Normal file
@ -0,0 +1,40 @@
|
||||
export class EventEmitter {
|
||||
private handlers: Map<string, Function[]>;
|
||||
|
||||
constructor() {
|
||||
this.handlers = new Map<string, Function[]>();
|
||||
}
|
||||
|
||||
public on(eventName: string, handler: Function): void {
|
||||
if (!this.handlers.has(eventName)) {
|
||||
this.handlers.set(eventName, []);
|
||||
}
|
||||
|
||||
this.handlers.get(eventName)?.push(handler);
|
||||
}
|
||||
|
||||
public off(eventName: string, handler: Function): void {
|
||||
if (!this.handlers.has(eventName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlers = this.handlers.get(eventName);
|
||||
const index = handlers?.indexOf(handler);
|
||||
|
||||
if (index !== undefined && index !== -1) {
|
||||
handlers?.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public emit(eventName: string, args: any): void {
|
||||
if (!this.handlers.has(eventName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlers = this.handlers.get(eventName);
|
||||
|
||||
handlers?.forEach((handler) => {
|
||||
handler(args);
|
||||
});
|
||||
}
|
||||
}
|
||||
111
web/app/page.tsx
111
web/app/page.tsx
@ -1,76 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import { PluginService } from "@janhq/plugin-core";
|
||||
import { ThemeWrapper } from "./_helpers/ThemeWrapper";
|
||||
import JotaiWrapper from "./_helpers/JotaiWrapper";
|
||||
import { ModalWrapper } from "./_helpers/ModalWrapper";
|
||||
import { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import {
|
||||
setup,
|
||||
plugins,
|
||||
activationPoints,
|
||||
} from "../../electron/core/plugin-manager/execution/index";
|
||||
import {
|
||||
isCorePluginInstalled,
|
||||
setupBasePlugins,
|
||||
} from "./_services/pluginService";
|
||||
import { setup, plugins, activationPoints, extensionPoints } from "../../electron/core/plugin-manager/execution/index";
|
||||
import { isCorePluginInstalled, setupBasePlugins } from "./_services/pluginService";
|
||||
import EventListenerWrapper from "./_helpers/EventListenerWrapper";
|
||||
import { setupCoreServices } from "./_services/coreService";
|
||||
import MainContainer from "./_components/MainContainer";
|
||||
import { executeSerial } from "../../electron/core/plugin-manager/execution/extension-manager";
|
||||
|
||||
const Page: React.FC = () => {
|
||||
const [setupCore, setSetupCore] = useState(false);
|
||||
const [activated, setActivated] = useState(false);
|
||||
|
||||
async function setupPE() {
|
||||
// Enable activation point management
|
||||
setup({
|
||||
importer: (plugin: string) =>
|
||||
import(/* webpackIgnore: true */ plugin).catch((err) => {
|
||||
console.log(err);
|
||||
}),
|
||||
});
|
||||
|
||||
// Register all active plugins with their activation points
|
||||
await plugins.registerActive();
|
||||
setTimeout(async () => {
|
||||
// Trigger activation points
|
||||
await activationPoints.trigger("init");
|
||||
if (!isCorePluginInstalled()) {
|
||||
setupBasePlugins();
|
||||
return;
|
||||
}
|
||||
if (extensionPoints.get(PluginService.OnStart)) {
|
||||
await executeSerial(PluginService.OnStart);
|
||||
}
|
||||
setActivated(true);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Services Setup
|
||||
useEffect(() => {
|
||||
// Setup Core Service
|
||||
setupCoreServices();
|
||||
|
||||
async function setupPE() {
|
||||
// Enable activation point management
|
||||
setup({
|
||||
importer: (plugin: string) =>
|
||||
import(/* webpackIgnore: true */ plugin).catch((err) => {
|
||||
console.log(err);
|
||||
}),
|
||||
});
|
||||
|
||||
// Register all active plugins with their activation points
|
||||
await plugins.registerActive();
|
||||
setTimeout(async () => {
|
||||
// Trigger activation points
|
||||
await activationPoints.trigger("init");
|
||||
if (!isCorePluginInstalled()) {
|
||||
setupBasePlugins();
|
||||
return;
|
||||
}
|
||||
setActivated(true);
|
||||
}, 500);
|
||||
}
|
||||
// Electron
|
||||
if (window && window.electronAPI) {
|
||||
setupPE();
|
||||
} else {
|
||||
// Host
|
||||
setActivated(true);
|
||||
}
|
||||
setSetupCore(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (setupCore) {
|
||||
// Electron
|
||||
if (window && window.electronAPI) {
|
||||
setupPE();
|
||||
} else {
|
||||
// Host
|
||||
setActivated(true);
|
||||
}
|
||||
}
|
||||
}, [setupCore]);
|
||||
|
||||
return (
|
||||
<JotaiWrapper>
|
||||
<EventListenerWrapper>
|
||||
<ThemeWrapper>
|
||||
<ModalWrapper>
|
||||
{activated ? (
|
||||
<MainContainer />
|
||||
) : (
|
||||
<div className="bg-white w-screen h-screen items-center justify-center flex">
|
||||
<Image width={50} height={50} src="icons/app_icon.svg" alt="" />
|
||||
</div>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
</ThemeWrapper>
|
||||
</EventListenerWrapper>
|
||||
{setupCore && (
|
||||
<EventListenerWrapper>
|
||||
<ThemeWrapper>
|
||||
<ModalWrapper>
|
||||
{activated ? (
|
||||
<MainContainer />
|
||||
) : (
|
||||
<div className="bg-white w-screen h-screen items-center justify-center flex">
|
||||
<Image width={50} height={50} src="icons/app_icon.svg" alt="" />
|
||||
</div>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
</ThemeWrapper>
|
||||
</EventListenerWrapper>
|
||||
)}
|
||||
</JotaiWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
1
web/types/index.d.ts
vendored
1
web/types/index.d.ts
vendored
@ -4,5 +4,6 @@ declare global {
|
||||
interface Window {
|
||||
electronAPI?: any | undefined;
|
||||
corePlugin?: any | undefined;
|
||||
coreAPI?: any | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user