Compare commits

..

No commits in common. "master" and "0.01" have entirely different histories.
master ... 0.01

16 changed files with 16 additions and 4300 deletions

View File

@ -1,51 +0,0 @@
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

View File

@ -1,75 +0,0 @@
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

6
.gitignore vendored
View File

@ -1,4 +1,4 @@
node_modules/
gui/dist/
*.exe
main-linux
main-macos
*.exe

View File

@ -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:
```bash
git clone https://git.deadzone.lol/Wizzard/kuzco-cli.git
cd kuzco-cli/cli
cd kuzco-cli
```
2. Install the necessary Node.js dependencies:
@ -27,16 +27,6 @@ Welcome to Kuzco CLI, a sleek Node.js-based interface for interacting with Kuzco
npm install
```
3. Installing the GUI:
```bash
cd kuzco-cli/gui
```
4. Install the necessary Node.js dependencies:
```bash
npm install
```
### 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.
@ -45,26 +35,17 @@ On first run, you'll be prompted to enter your API key for accessing Kuzco's net
Run Kuzco CLI with:
```bash
cd kuzco-cli/cli
node kuzco-cli.js
```
Run Kuzco GUI with:
```bash
cd kuzco-cli/gui
electron19 kuzco-gui.js
node main.js
```
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
Your contributions are welcome!
## 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!

View File

@ -1,222 +0,0 @@
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;
}

View File

@ -1,32 +0,0 @@
<!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>

View File

@ -1,20 +0,0 @@
<!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>

View File

@ -1,92 +0,0 @@
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;

View File

@ -1,7 +0,0 @@
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')
});

View File

@ -1,99 +0,0 @@
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');
}
});

View File

@ -1,82 +0,0 @@
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();
});

3506
gui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,62 +0,0 @@
{
"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"
}
}

View File

@ -52,27 +52,9 @@ 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() {
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) {
const user_input = await askQuestion("User: ");
if (user_input.toLowerCase() === 'exit') {
@ -81,7 +63,7 @@ async function main() {
messages.push({ 'role': 'user', 'content': user_input + '\n' });
try {
const response = await fetchWithTimeout(`${BASE_URL}/chat/completions`, {
const response = await fetch(`${BASE_URL}/chat/completions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
@ -89,16 +71,17 @@ async function main() {
},
body: JSON.stringify({
messages: messages,
model: model
model: 'mistral',
stream: false
})
}, 25000);;
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(`\nKuzco (${model}):\n\n${data.choices[0].message.content.trim()}\n`);
console.log(`\nKuzco (Mistral):\n\n${data.choices[0].message.content.trim()}\n`);
} catch (error) {
console.error(`An error occurred: ${error.message}`);
}

View File

@ -1,11 +1,11 @@
{
"name": "kuzco-cli",
"name": "kyzco-cli",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "kuzco-cli",
"name": "kyzco-cli",
"version": "1.0.0",
"dependencies": {
"got": "^14.2.1",
@ -13,7 +13,7 @@
"prompt-sync": "^4.2.0"
},
"bin": {
"kuzco-cli": "kuzco-cli.js"
"kyzco-cli": "kuzco-cli.js"
}
},
"node_modules/@sindresorhus/is": {