Compare commits
25 Commits
Author | SHA1 | Date |
---|---|---|
Wizzard | 045f78d50d | |
Wizzard | ee6f775b5b | |
Wizzard | bdf5715868 | |
Wizzard | d2470b2dd6 | |
Wizzard | 05e5495fad | |
Wizzard | 5298482708 | |
Wizzard | 113e413167 | |
Wizzard | 5bc5fb7cad | |
Wizzard | 9a92b47a02 | |
Wizzard | 81a8f6b9e3 | |
Wizzard | 35309ee2d1 | |
Wizzard | 445eda821d | |
Wizzard | 437831aea5 | |
Wizzard | e5419b0108 | |
Wizzard | 424a1b5ad1 | |
Wizzard | 1a00e34837 | |
Wizzard | 2d9f8649a7 | |
Wizzard | 16c1d680cb | |
Wizzard | 0ab585943f | |
Wizzard | 226f4bf409 | |
Wizzard | 4bc3b013bb | |
Wizzard | 97402a8b1f | |
Wizzard | 61cf0bd49e | |
Wizzard | af39ad7094 | |
Wizzard | fd8bba8199 |
|
@ -0,0 +1,51 @@
|
||||||
|
name: CLI Build
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-cli:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install dependencies (CLI)
|
||||||
|
run: cd cli && npm install
|
||||||
|
|
||||||
|
- name: Install pkg globally
|
||||||
|
run: npm install -g pkg
|
||||||
|
|
||||||
|
- name: Build CLI executables
|
||||||
|
run: |
|
||||||
|
cd cli
|
||||||
|
pkg kuzco-cli.js --targets node18-linux-x64,node18-macos-x64,node18-win-x64
|
||||||
|
|
||||||
|
- name: Rename executables
|
||||||
|
run: |
|
||||||
|
cd cli
|
||||||
|
mv kuzco-cli-linux kuzco-cli-linux-x64
|
||||||
|
mv kuzco-cli-macos kuzco-cli-macos-x64
|
||||||
|
mv kuzco-cli-win.exe kuzco-cli-win-x64.exe
|
||||||
|
|
||||||
|
- name: Upload Linux Executable
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: kuzco-cli-linux-x64
|
||||||
|
path: cli/kuzco-cli-linux-x64
|
||||||
|
|
||||||
|
- name: Upload macOS Executable
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: kuzco-cli-macos-x64
|
||||||
|
path: cli/kuzco-cli-macos-x64
|
||||||
|
|
||||||
|
- name: Upload Windows Executable
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: kuzco-cli-win-x64.exe
|
||||||
|
path: cli/kuzco-cli-win-x64.exe
|
|
@ -0,0 +1,75 @@
|
||||||
|
name: GUI Build
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-linux-gui:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install dependencies (GUI)
|
||||||
|
run: cd gui && npm install
|
||||||
|
|
||||||
|
- name: Build Linux Application
|
||||||
|
run: cd gui && npm run dist-linux
|
||||||
|
|
||||||
|
- name: Archive Linux production build
|
||||||
|
run: |
|
||||||
|
cd gui/dist
|
||||||
|
tar czf linux-unpacked.tar.gz linux-unpacked
|
||||||
|
|
||||||
|
- name: Upload Linux AppImage
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: KuzcoChat-Linux-Appimage
|
||||||
|
path: gui/dist/*.AppImage
|
||||||
|
|
||||||
|
- name: Upload Linux DEB
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: KuzcoChat-DEB
|
||||||
|
path: gui/dist/*.deb
|
||||||
|
|
||||||
|
- name: Upload Linux Unpacked Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: KuzcoChat-Linux-Unpacked
|
||||||
|
path: gui/dist/linux-unpacked.tar.gz
|
||||||
|
|
||||||
|
build-windows-gui:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
|
||||||
|
- name: Install dependencies (GUI)
|
||||||
|
run: cd gui && npm install
|
||||||
|
|
||||||
|
- name: Build Windows Application
|
||||||
|
run: cd gui && npm run dist-win
|
||||||
|
|
||||||
|
- name: Zip Windows production build
|
||||||
|
run: |
|
||||||
|
Compress-Archive -Path gui/dist/win-unpacked/* -DestinationPath gui/dist/win-unpacked.zip
|
||||||
|
|
||||||
|
- name: Upload Windows Executable
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: KuzcoChat-Windows-EXE
|
||||||
|
path: gui/dist/*.exe
|
||||||
|
|
||||||
|
- name: Upload Windows Unpacked Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: KuzcoChat-Windows-Unpacked
|
||||||
|
path: gui/dist/win-unpacked.zip
|
|
@ -1,4 +1,4 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
main-linux
|
gui/dist/
|
||||||
main-macos
|
*.exe
|
||||||
*.exe
|
|
||||||
|
|
29
README.md
29
README.md
|
@ -19,7 +19,7 @@ Welcome to Kuzco CLI, a sleek Node.js-based interface for interacting with Kuzco
|
||||||
1. Clone this repository and navigate into the directory:
|
1. Clone this repository and navigate into the directory:
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.deadzone.lol/Wizzard/kuzco-cli.git
|
git clone https://git.deadzone.lol/Wizzard/kuzco-cli.git
|
||||||
cd kuzco-cli
|
cd kuzco-cli/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Install the necessary Node.js dependencies:
|
2. Install the necessary Node.js dependencies:
|
||||||
|
@ -27,6 +27,16 @@ Welcome to Kuzco CLI, a sleek Node.js-based interface for interacting with Kuzco
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
3. Installing the GUI:
|
||||||
|
```bash
|
||||||
|
cd kuzco-cli/gui
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Install the necessary Node.js dependencies:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
On first run, you'll be prompted to enter your API key for accessing Kuzco's network. This is stored securely and ensures your interactions are authenticated.
|
On first run, you'll be prompted to enter your API key for accessing Kuzco's network. This is stored securely and ensures your interactions are authenticated.
|
||||||
|
@ -35,17 +45,26 @@ On first run, you'll be prompted to enter your API key for accessing Kuzco's net
|
||||||
|
|
||||||
Run Kuzco CLI with:
|
Run Kuzco CLI with:
|
||||||
```bash
|
```bash
|
||||||
node main.js
|
cd kuzco-cli/cli
|
||||||
|
node kuzco-cli.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Run Kuzco GUI with:
|
||||||
|
```bash
|
||||||
|
cd kuzco-cli/gui
|
||||||
|
electron19 kuzco-gui.js
|
||||||
```
|
```
|
||||||
|
|
||||||
Follow the on-screen prompts to send your AI prompts to the network.
|
Follow the on-screen prompts to send your AI prompts to the network.
|
||||||
|
|
||||||
|
## Prebuilt Binaries
|
||||||
|
|
||||||
|
You can download prebuilt versions of this application from the [GitHub Actions page](https://github.com/CODJointOps/kuzco-cli/actions).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Your contributions are welcome!
|
Your contributions are welcome!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Kuzco CLI is released under the MIT license. Feel free to use, modify, and distribute it as you see fit.
|
Kuzco CLI is released under the MIT license. Feel free to use, modify, and distribute it as you see fit.
|
||||||
|
|
||||||
Enjoy harnessing the power of decentralized AI with Kuzco!
|
|
|
@ -52,9 +52,27 @@ const askQuestion = (query) => {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchWithTimeout = (url, options, timeout = 3000) => {
|
||||||
|
const timeoutPromise = new Promise((_, reject) => {
|
||||||
|
const id = setTimeout(() => {
|
||||||
|
clearTimeout(id);
|
||||||
|
reject(new Error('Request timed out'));
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.race([
|
||||||
|
fetch(url, options),
|
||||||
|
timeoutPromise
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
let messages = [];
|
let messages = [];
|
||||||
|
|
||||||
|
console.log("Please choose a model: 1 for Mistral, 2 for Llama2");
|
||||||
|
const modelChoice = prompt('Enter your choice (1 or 2): ');
|
||||||
|
const model = modelChoice === '2' ? 'llama2' : 'mistral';
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const user_input = await askQuestion("User: ");
|
const user_input = await askQuestion("User: ");
|
||||||
if (user_input.toLowerCase() === 'exit') {
|
if (user_input.toLowerCase() === 'exit') {
|
||||||
|
@ -63,7 +81,7 @@ async function main() {
|
||||||
messages.push({ 'role': 'user', 'content': user_input + '\n' });
|
messages.push({ 'role': 'user', 'content': user_input + '\n' });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${BASE_URL}/chat/completions`, {
|
const response = await fetchWithTimeout(`${BASE_URL}/chat/completions`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${API_KEY}`,
|
'Authorization': `Bearer ${API_KEY}`,
|
||||||
|
@ -71,17 +89,16 @@ async function main() {
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
messages: messages,
|
messages: messages,
|
||||||
model: 'mistral',
|
model: model
|
||||||
stream: false
|
|
||||||
})
|
})
|
||||||
});
|
}, 25000);;
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
console.log(`\nKuzco (Mistral):\n\n${data.choices[0].message.content.trim()}\n`);
|
console.log(`\nKuzco (${model}):\n\n${data.choices[0].message.content.trim()}\n`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`An error occurred: ${error.message}`);
|
console.error(`An error occurred: ${error.message}`);
|
||||||
}
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "kyzco-cli",
|
"name": "kuzco-cli",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "kyzco-cli",
|
"name": "kuzco-cli",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"got": "^14.2.1",
|
"got": "^14.2.1",
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
"prompt-sync": "^4.2.0"
|
"prompt-sync": "^4.2.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"kyzco-cli": "kuzco-cli.js"
|
"kuzco-cli": "kuzco-cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sindresorhus/is": {
|
"node_modules/@sindresorhus/is": {
|
|
@ -0,0 +1,222 @@
|
||||||
|
body {
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #ecf0f1;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #34495e #2c3e50;
|
||||||
|
}
|
||||||
|
body, html {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#app {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#chatHistory {
|
||||||
|
height: 300px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 1px solid #34495e;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
#sendButton {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: white;
|
||||||
|
background-color: #007BFF;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
#sendButton:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sendButton:disabled {
|
||||||
|
background-color: #6c757d;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
#submitApiKey {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: white;
|
||||||
|
background-color: #007BFF;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
#stopButton {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: white;
|
||||||
|
background-color: #c0392b;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stopButton:hover {
|
||||||
|
background-color: #a93226;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stopButton:disabled {
|
||||||
|
background-color: #6c757d;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 10px 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #34495e;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
.userMessage {
|
||||||
|
align-self: flex-end;
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
.assistantMessage {
|
||||||
|
align-self: flex-start;
|
||||||
|
background-color: #16a085;
|
||||||
|
}
|
||||||
|
#chatForm {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
#promptInput {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #34495e;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: inherit;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
}
|
||||||
|
#apiKeyInput {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #34495e;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: inherit;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
#promptInput:focus {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px #007BFF;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
header h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.5em;
|
||||||
|
background: #2980b9;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
#sendPrompt {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #2980b9;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
#modelSelectionContainer {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
#chatHistory, #promptInput, #modelSelect {
|
||||||
|
border: 2px solid #34495e;
|
||||||
|
}
|
||||||
|
#modelSelect {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #34495e;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #ecf0f1;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modelSelect:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: #ecf0f1;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
#modelSelectionContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #333;
|
||||||
|
color: #f8f8f2;
|
||||||
|
border: 1px solid #2980b9;
|
||||||
|
border-left: 3px solid #2980b9;
|
||||||
|
padding: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
margin: 10px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #2c3e50;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #34495e;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ellipsis {
|
||||||
|
0%, 20% {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
content: '.';
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
content: '..';
|
||||||
|
}
|
||||||
|
80%, 100% {
|
||||||
|
content: '...';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ellipsis::after {
|
||||||
|
content: '';
|
||||||
|
animation: ellipsis 2s infinite;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Kuzco Chat</title>
|
||||||
|
<link rel="stylesheet" href="../css/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<header>
|
||||||
|
<h1>Welcome to Kuzco Chat</h1>
|
||||||
|
</header>
|
||||||
|
<div id="modelSelectionContainer">
|
||||||
|
<label for="modelSelect">Choose AI Model:</label>
|
||||||
|
<select id="modelSelect">
|
||||||
|
<option value="mistral">Mistral</option>
|
||||||
|
<option value="llama2">Llama2</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<main id="chatHistory" class="chat-history">
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<form id="chatForm" class="chat-form">
|
||||||
|
<input id="promptInput" type="text" placeholder="Enter your prompt" autofocus>
|
||||||
|
<button id="sendButton">Send</button>
|
||||||
|
<button id="stopButton" disabled>Stop</button>
|
||||||
|
</form>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<script src="../js/renderer.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Enter API Key</title>
|
||||||
|
<link rel="stylesheet" href="../css/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Enter API Key</h1>
|
||||||
|
<input type="text" id="apiKeyInput" placeholder="API Key">
|
||||||
|
<button id="submitApiKey">Submit</button>
|
||||||
|
<script>
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
document.getElementById('submitApiKey').addEventListener('click', () => {
|
||||||
|
const apiKey = document.getElementById('apiKeyInput').value;
|
||||||
|
ipcRenderer.send('submit-api-key', apiKey);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,92 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
|
async function fetchData(url, options = {}) {
|
||||||
|
const { default: fetch } = await import('node-fetch');
|
||||||
|
const response = await fetch(url, options);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
class KuzcoCore {
|
||||||
|
constructor() {
|
||||||
|
this.configPath = path.join(os.homedir(), '.kuzco-cli', 'config.json');
|
||||||
|
this.API_KEY = this.loadApiKey();
|
||||||
|
this.controller = new AbortController();
|
||||||
|
this.isAborted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadApiKey() {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(this.configPath)) {
|
||||||
|
const configFile = fs.readFileSync(this.configPath);
|
||||||
|
const config = JSON.parse(configFile);
|
||||||
|
return config.API_KEY;
|
||||||
|
} else {
|
||||||
|
console.log('API Key config file does not exist. Please set up your API Key.');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`An error occurred while reading the API key: ${error.message}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apiKeyExists() {
|
||||||
|
return fs.existsSync(this.configPath) && this.API_KEY !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
abortFetch() {
|
||||||
|
this.isAborted = true;
|
||||||
|
this.controller.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendPrompt(prompt, model) {
|
||||||
|
console.log("Model received in sendPrompt:", model)
|
||||||
|
this.controller = new AbortController();
|
||||||
|
const signal = this.controller.signal;
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => this.controller.abort(), 25000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://relay.kuzco.xyz/v1/chat/completions', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${this.API_KEY}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
messages: [{ role: 'user', 'content': prompt + '\n' }],
|
||||||
|
model: model,
|
||||||
|
stream: false,
|
||||||
|
}),
|
||||||
|
signal: signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
const errorMessage = this.isAborted
|
||||||
|
? 'Request aborted by the user. Please try again.'
|
||||||
|
: 'Request timed out. Please try again.';
|
||||||
|
|
||||||
|
this.isAborted = false;
|
||||||
|
this.controller = new AbortController();
|
||||||
|
|
||||||
|
return error.name === 'AbortError'
|
||||||
|
? { error: errorMessage }
|
||||||
|
: { error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = KuzcoCore;
|
|
@ -0,0 +1,7 @@
|
||||||
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
sendPrompt: (prompt, model) => ipcRenderer.invoke('send-prompt', { prompt, model }),
|
||||||
|
onApiKeySaved: (callback) => ipcRenderer.on('api-key-saved', callback),
|
||||||
|
abortPrompt: () => ipcRenderer.send('abort-prompt')
|
||||||
|
});
|
|
@ -0,0 +1,99 @@
|
||||||
|
function displayMessage(message, sender) {
|
||||||
|
const chatHistory = document.getElementById('chatHistory');
|
||||||
|
const messageDiv = document.createElement('div');
|
||||||
|
messageDiv.classList.add('message');
|
||||||
|
|
||||||
|
const parts = message.split('```');
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
const textPart = document.createElement('span');
|
||||||
|
textPart.textContent = parts[i];
|
||||||
|
messageDiv.appendChild(textPart);
|
||||||
|
} else {
|
||||||
|
const codeBlock = document.createElement('pre');
|
||||||
|
const code = document.createElement('code');
|
||||||
|
code.textContent = parts[i];
|
||||||
|
codeBlock.appendChild(code);
|
||||||
|
messageDiv.appendChild(codeBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender === 'user') {
|
||||||
|
messageDiv.classList.add('userMessage');
|
||||||
|
} else {
|
||||||
|
messageDiv.classList.add('assistantMessage');
|
||||||
|
}
|
||||||
|
|
||||||
|
chatHistory.appendChild(messageDiv);
|
||||||
|
chatHistory.scrollTop = chatHistory.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const chatForm = document.getElementById('chatForm');
|
||||||
|
const promptInput = document.getElementById('promptInput');
|
||||||
|
const modelSelect = document.getElementById('modelSelect');
|
||||||
|
const sendButton = document.getElementById('sendButton');
|
||||||
|
const stopButton = document.getElementById('stopButton');
|
||||||
|
const modelSelectionContainer = document.getElementById('modelSelectionContainer');
|
||||||
|
|
||||||
|
if (chatForm && promptInput && modelSelect) {
|
||||||
|
chatForm.addEventListener('submit', async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const userInput = promptInput.value;
|
||||||
|
const selectedModel = modelSelect.value;
|
||||||
|
promptInput.value = '';
|
||||||
|
modelSelectionContainer.style.display = 'none';
|
||||||
|
|
||||||
|
sendButton.disabled = true;
|
||||||
|
promptInput.disabled = true;
|
||||||
|
stopButton.disabled = false;
|
||||||
|
|
||||||
|
displayMessage(userInput, 'user');
|
||||||
|
|
||||||
|
const typingIndicator = displayTypingIndicator();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await window.electronAPI.sendPrompt(userInput, selectedModel);
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
const assistantMessage = response.choices[0].message.content.trim();
|
||||||
|
displayMessage(assistantMessage, 'assistant');
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error sending prompt: ${error.message}`);
|
||||||
|
displayMessage(`Error: ${error.message}`, 'assistant');
|
||||||
|
} finally {
|
||||||
|
typingIndicator.remove();
|
||||||
|
sendButton.disabled = false;
|
||||||
|
promptInput.disabled = false;
|
||||||
|
promptInput.focus();
|
||||||
|
stopButton.disabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('chatForm or promptInput elements not found!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('stopButton').addEventListener('click', () => {
|
||||||
|
window.electronAPI.abortPrompt();
|
||||||
|
document.getElementById('stopButton').disabled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
function displayTypingIndicator() {
|
||||||
|
const chatHistory = document.getElementById('chatHistory');
|
||||||
|
const typingIndicator = document.createElement('div');
|
||||||
|
typingIndicator.classList.add('message', 'assistantMessage', 'ellipsis');
|
||||||
|
typingIndicator.textContent = 'Processing';
|
||||||
|
chatHistory.appendChild(typingIndicator);
|
||||||
|
chatHistory.scrollTop = chatHistory.scrollHeight;
|
||||||
|
return typingIndicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'F11') {
|
||||||
|
e.preventDefault();
|
||||||
|
ipcRenderer.send('toggle-fullscreen');
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,82 @@
|
||||||
|
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
const KuzcoCore = require('./js/kuzcoCore');
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'js/preload.js'),
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
enableRemoteModule: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.loadFile('html/index.html');
|
||||||
|
|
||||||
|
if (!kuzcoCore.apiKeyExists()) {
|
||||||
|
promptForApiKey(mainWindow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcMain.on('submit-api-key', (event, apiKey) => {
|
||||||
|
const configDir = path.join(os.homedir(), '.kuzco-cli');
|
||||||
|
const configPath = path.join(configDir, 'config.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(configDir)){
|
||||||
|
fs.mkdirSync(configDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify({ API_KEY: apiKey }, null, 2), 'utf8');
|
||||||
|
event.reply('api-key-saved');
|
||||||
|
|
||||||
|
app.relaunch();
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let inputWindow;
|
||||||
|
|
||||||
|
function promptForApiKey() {
|
||||||
|
inputWindow = new BrowserWindow({
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
|
enableRemoteModule: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
inputWindow.loadFile('html/prompt.html');
|
||||||
|
inputWindow.on('closed', () => {
|
||||||
|
inputWindow = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow();
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') app.quit();
|
||||||
|
});
|
||||||
|
|
||||||
|
const kuzcoCore = new KuzcoCore();
|
||||||
|
|
||||||
|
ipcMain.handle('send-prompt', async (event, { prompt, model }) => {
|
||||||
|
console.log("Received model in main process:", model);
|
||||||
|
return await kuzcoCore.sendPrompt(prompt, model);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('abort-prompt', () => {
|
||||||
|
kuzcoCore.abortFetch();
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"name": "kuzco-gui",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Simple gui for kuzco api",
|
||||||
|
"author": "Wizzard <Wizzard@deadzone.lol>",
|
||||||
|
"main": "kuzco-gui.js",
|
||||||
|
"homepage": "https://git.deadzone.lol/Wizzard/kuzco-cli",
|
||||||
|
"scripts": {
|
||||||
|
"start": "electron .",
|
||||||
|
"pack": "electron-builder --dir",
|
||||||
|
"dist-linux": "electron-builder --linux",
|
||||||
|
"dist-mac": "electron-builder --mac",
|
||||||
|
"dist-win": "electron-builder --win",
|
||||||
|
"dist-all": "electron-builder -mwl"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "com.codjointops.kuzco",
|
||||||
|
"productName": "KuzcoChat",
|
||||||
|
"directories": {
|
||||||
|
"output": "dist"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"**/*",
|
||||||
|
"css/**/*",
|
||||||
|
"js/**/*",
|
||||||
|
"html/**/*",
|
||||||
|
"!**/*.ts",
|
||||||
|
"!*.code-workspace",
|
||||||
|
"!**/*.js.map",
|
||||||
|
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
|
||||||
|
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
|
||||||
|
"!**/node_modules/*.d.ts",
|
||||||
|
"!**/node_modules/.bin",
|
||||||
|
"!**/*.{o,hprof,orig,pyc,pyo,rbc}",
|
||||||
|
"!**/._*",
|
||||||
|
"!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,__pycache__,thumbs.db,.db,desktop.ini}"
|
||||||
|
],
|
||||||
|
"win": {
|
||||||
|
"target": [
|
||||||
|
"nsis",
|
||||||
|
"portable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"target": "dmg",
|
||||||
|
"category": "public.app-category.utilities"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"target": [
|
||||||
|
"AppImage",
|
||||||
|
"deb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"electron": "latest",
|
||||||
|
"electron-builder": "^22.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.7.0"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue