generated from ztimson/template
Refactored code formatting to use consistent indentation and object destructuring across client and server files.
All checks were successful
Build and publish / Build Container (push) Successful in 1m44s
All checks were successful
Build and publish / Build Container (push) Successful in 1m44s
This commit is contained in:
@@ -13,7 +13,9 @@ function shadeColor(hex, amount) {
|
||||
}
|
||||
|
||||
function hex2Int(hex) {
|
||||
let r = 0, g = 0, b = 0;
|
||||
let r = 0,
|
||||
g = 0,
|
||||
b = 0;
|
||||
if(hex.length === 4) {
|
||||
r = parseInt(hex[1] + hex[1], 16);
|
||||
g = parseInt(hex[2] + hex[2], 16);
|
||||
@@ -23,7 +25,11 @@ function shadeColor(hex, amount) {
|
||||
g = parseInt(hex.slice(3, 5), 16);
|
||||
b = parseInt(hex.slice(5, 7), 16);
|
||||
}
|
||||
return { r, g, b };
|
||||
return {
|
||||
r,
|
||||
g,
|
||||
b
|
||||
};
|
||||
}
|
||||
|
||||
function hue2rgb(p, q, t) {
|
||||
@@ -39,10 +45,19 @@ function shadeColor(hex, amount) {
|
||||
return '#' + dec2Hex(r) + dec2Hex(g) + dec2Hex(b);
|
||||
}
|
||||
|
||||
let { r, g, b } = hex2Int(hex);
|
||||
r /= 255; g /= 255; b /= 255;
|
||||
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
let h, s, l = (max + min) / 2;
|
||||
let {
|
||||
r,
|
||||
g,
|
||||
b
|
||||
} = hex2Int(hex);
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
const max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b);
|
||||
let h,
|
||||
s,
|
||||
l = (max + min) / 2;
|
||||
|
||||
if(max === min) {
|
||||
h = s = 0;
|
||||
@@ -50,10 +65,18 @@ function shadeColor(hex, amount) {
|
||||
const d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch(max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
default: h = 0; break;
|
||||
case r:
|
||||
h = (g - b) / d + (g < b ? 6 : 0);
|
||||
break;
|
||||
case g:
|
||||
h = (b - r) / d + 2;
|
||||
break;
|
||||
case b:
|
||||
h = (r - g) / d + 4;
|
||||
break;
|
||||
default:
|
||||
h = 0;
|
||||
break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ class Game {
|
||||
this.world = data;
|
||||
applyTheme(this.world.theme);
|
||||
this.initializeRenderer();
|
||||
}
|
||||
};
|
||||
|
||||
this.worldActions.onPlayers = (players) => {
|
||||
players.forEach(player => {
|
||||
@@ -210,11 +210,11 @@ class Game {
|
||||
this.addOtherPlayer(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.worldActions.onJoined = (player) => {
|
||||
this.addOtherPlayer(player);
|
||||
}
|
||||
};
|
||||
|
||||
this.worldActions.onMoved = (data) => {
|
||||
const sprite = this.otherPlayers.get(data.socketId);
|
||||
@@ -226,7 +226,7 @@ class Game {
|
||||
this.worldActions.onLeft = (data) => {
|
||||
const sprite = this.otherPlayers.get(data.socketId);
|
||||
if(sprite) this.otherPlayers.delete(data.socketId);
|
||||
}
|
||||
};
|
||||
this.worldActions.onError = (error) => console.error('❌ World error:', error);
|
||||
|
||||
console.log('✨ Game initializing...');
|
||||
@@ -299,9 +299,7 @@ class Game {
|
||||
}
|
||||
|
||||
movePetTo(targetX, targetY) {
|
||||
if (this.isMoving ||
|
||||
targetX < 0 || targetX >= this.world.gridSize ||
|
||||
targetY < 0 || targetY >= this.world.gridSize) return;
|
||||
if(this.isMoving || targetX < 0 || targetX >= this.world.gridSize || targetY < 0 || targetY >= this.world.gridSize) return;
|
||||
|
||||
this.isMoving = true;
|
||||
const targetPos = isoToScreen(targetX, targetY);
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
<script type="module">
|
||||
import Navi from './navi.mjs';
|
||||
|
||||
window.navi = new Navi();
|
||||
</script>
|
||||
<script type="module" src="/components/jukebox.mjs"></script>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default class Navi {
|
||||
class Navi {
|
||||
api;
|
||||
connected = false;
|
||||
icon;
|
||||
@@ -99,7 +99,11 @@ export default class Navi {
|
||||
if(!response.ok) throw new Error('Message failed to send bestie');
|
||||
|
||||
const result = await response.json();
|
||||
this.emit('message:sent', { userId, message, result });
|
||||
this.emit('message:sent', {
|
||||
userId,
|
||||
message,
|
||||
result
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -112,7 +116,11 @@ export default class Navi {
|
||||
if(!response.ok) throw new Error('Link connection is bussin (negatively)');
|
||||
|
||||
const result = await response.json();
|
||||
this.emit('pet:linked', { petId, targetApiUrl, result });
|
||||
this.emit('pet:linked', {
|
||||
petId,
|
||||
targetApiUrl,
|
||||
result
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -142,19 +150,28 @@ export default class Navi {
|
||||
|
||||
const callbacks = {
|
||||
move: (x, y) => {
|
||||
this.#world.emit('move', {x, y});
|
||||
this.#world.emit('move', {
|
||||
x,
|
||||
y
|
||||
});
|
||||
},
|
||||
leave: () => {
|
||||
this.#world.disconnect();
|
||||
this.world = null;
|
||||
},
|
||||
onData: (data) => { },
|
||||
onPlayers: (players) => { },
|
||||
onJoined: (player) => { },
|
||||
onMoved: (player) => { },
|
||||
onLeft: (player) => { },
|
||||
onError: (error) => { }
|
||||
onData: (data) => {
|
||||
},
|
||||
onPlayers: (players) => {
|
||||
},
|
||||
onJoined: (player) => {
|
||||
},
|
||||
onMoved: (player) => {
|
||||
},
|
||||
onLeft: (player) => {
|
||||
},
|
||||
onError: (error) => {
|
||||
}
|
||||
};
|
||||
|
||||
this.#world.on('data', (data) => {
|
||||
this.world.data = data;
|
||||
@@ -166,7 +183,7 @@ export default class Navi {
|
||||
this.world.players.clear();
|
||||
players.forEach(p => this.world.players.set(p.socketId, p));
|
||||
callbacks.onPlayers(players);
|
||||
this.emit('world:players', players)
|
||||
this.emit('world:players', players);
|
||||
});
|
||||
|
||||
this.#world.on('joined', (player) => {
|
||||
@@ -198,7 +215,10 @@ export default class Navi {
|
||||
this.emit('world:error', error);
|
||||
});
|
||||
|
||||
this.#world.emit('join', {world, api});
|
||||
this.#world.emit('join', {
|
||||
world,
|
||||
api
|
||||
});
|
||||
|
||||
this.emit('world:join', world);
|
||||
return callbacks;
|
||||
@@ -214,7 +234,7 @@ export default class Navi {
|
||||
this.llmResolve = resolve;
|
||||
this.llmReject = reject;
|
||||
this.#socket.emit('llm-ask', {message});
|
||||
this.emit('llm:ask')
|
||||
this.emit('llm:ask');
|
||||
});
|
||||
|
||||
promise.abort = () => {
|
||||
@@ -223,7 +243,7 @@ export default class Navi {
|
||||
this.llmCallback = null;
|
||||
this.llmResolve = null;
|
||||
this.llmReject = null;
|
||||
this.emit('llm:abort')
|
||||
this.emit('llm:abort');
|
||||
};
|
||||
|
||||
return promise;
|
||||
@@ -252,3 +272,5 @@ export default class Navi {
|
||||
this.emit('disconnected');
|
||||
}
|
||||
}
|
||||
|
||||
export default Navi;
|
||||
|
||||
114
src/server.js
114
src/server.js
@@ -1,12 +1,34 @@
|
||||
import express from 'express';
|
||||
import { createServer } from 'http';
|
||||
import { Server } from 'socket.io';
|
||||
import cors from 'cors';
|
||||
import fs from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import {Ai, DateTimeTool, ExecTool, FetchTool, ReadWebpageTool, WebSearchTool} from '@ztimson/ai-utils';
|
||||
import {contrast, shadeColor} from '@ztimson/utils';
|
||||
import express
|
||||
from 'express';
|
||||
import {
|
||||
createServer
|
||||
} from 'http';
|
||||
import {
|
||||
Server
|
||||
} from 'socket.io';
|
||||
import cors
|
||||
from 'cors';
|
||||
import fs
|
||||
from 'fs';
|
||||
import {
|
||||
join,
|
||||
dirname
|
||||
} from 'path';
|
||||
import {
|
||||
fileURLToPath
|
||||
} from 'url';
|
||||
import {
|
||||
Ai,
|
||||
DateTimeTool,
|
||||
ExecTool,
|
||||
FetchTool,
|
||||
ReadWebpageTool,
|
||||
WebSearchTool
|
||||
} from '@ztimson/ai-utils';
|
||||
import {
|
||||
contrast,
|
||||
shadeColor
|
||||
} from '@ztimson/utils';
|
||||
|
||||
// ============================================
|
||||
// Settings
|
||||
@@ -38,10 +60,11 @@ function calcColors(theme) {
|
||||
mutedContrast: contrast(theme.muted),
|
||||
mutedDark: shadeColor(theme.muted, -.1),
|
||||
mutedLight: shadeColor(theme.muted, .1),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let memories = [], settings = {
|
||||
let memories = [],
|
||||
settings = {
|
||||
name: 'Navi',
|
||||
personality: 'You are inquisitive about your user trying to best adjust your personally to fit them',
|
||||
instructions: '',
|
||||
@@ -85,13 +108,22 @@ load();
|
||||
const ai = new Ai({
|
||||
llm: {
|
||||
models: {
|
||||
'Ministral-3': {proto: 'openai', host: 'http://10.69.0.55:11728', token: 'ignore'},
|
||||
'Ministral-3': {
|
||||
proto: 'openai',
|
||||
host: 'http://10.69.0.55:11728',
|
||||
token: 'ignore'
|
||||
},
|
||||
system: `You are a NetNavi, personal assistant & companion. Keep responses short and unstyled. You have your own debian environment you can access using the exec tool with the cli language. Use your remember tool liberally to store all facts. Adjust your personality with tools based on your interactions with the user.\n\nPersonality:\n${settings.personality || ''}\n\nUser Requests:\n${settings.instructions || ''}`,
|
||||
},
|
||||
system: `You are a NetNavi, personal assistant & companion. Keep responses short and unstyled. You have your own debian environment you can access using the exec tool with the cli language, use /tmp as your working directory. Use your remember tool liberally to store all facts. Adjust your personality with tools based on your interactions with the user.\n\nPersonality:\n${settings.personality || ''}\n\nUser Requests:\n${settings.instructions || ''}`,
|
||||
tools: [DateTimeTool, ExecTool, FetchTool, ReadWebpageTool, WebSearchTool, {
|
||||
name: 'adjust_personality',
|
||||
description: 'Replace your current personality instructions',
|
||||
args: {instructions: {type: 'string', description: 'Bullet point list of how to behave'}},
|
||||
args: {
|
||||
instructions: {
|
||||
type: 'string',
|
||||
description: 'Bullet point list of how to behave'
|
||||
}
|
||||
},
|
||||
fn: (args) => {
|
||||
settings.personality = args.instructions;
|
||||
updated = true;
|
||||
@@ -108,7 +140,10 @@ const ai = new Ai({
|
||||
const app = express();
|
||||
const httpServer = createServer(app);
|
||||
const io = new Server(httpServer, {
|
||||
cors: {origin: "*", methods: ["GET", "POST"]}
|
||||
cors: {
|
||||
origin: '*',
|
||||
methods: ['GET', 'POST']
|
||||
}
|
||||
});
|
||||
|
||||
app.use(cors());
|
||||
@@ -168,7 +203,8 @@ const worldPlayers = new Map();
|
||||
|
||||
// Load world data
|
||||
function loadWorld(name) {
|
||||
let w, t;
|
||||
let w,
|
||||
t;
|
||||
try {
|
||||
w = JSON.parse(fs.readFileSync(join(worlds, (name || 'home') + '.json'), 'utf-8'));
|
||||
} catch(error) {
|
||||
@@ -183,7 +219,10 @@ function loadWorld(name) {
|
||||
return null;
|
||||
}
|
||||
worldPlayers.set(name, new Map());
|
||||
return {...w, theme: t};
|
||||
return {
|
||||
...w,
|
||||
theme: t
|
||||
};
|
||||
}
|
||||
|
||||
io.of('/world').on('connection', (socket) => {
|
||||
@@ -194,7 +233,10 @@ io.of('/world').on('connection', (socket) => {
|
||||
|
||||
// Join a world
|
||||
socket.on('join', async (data) => {
|
||||
const { world, api } = data;
|
||||
const {
|
||||
world,
|
||||
api
|
||||
} = data;
|
||||
const info = await fetch(api.replace(/\/$/, '') + '/api/info').then(resp => {
|
||||
if(resp.ok) return resp.json();
|
||||
socket.emit('error', {message: `Invalid Navi API: ${api}`});
|
||||
@@ -236,10 +278,17 @@ io.of('/world').on('connection', (socket) => {
|
||||
// Player movement
|
||||
socket.on('move', (data) => {
|
||||
if(!currentWorld || !playerData) return;
|
||||
const { x, y } = data;
|
||||
const {
|
||||
x,
|
||||
y
|
||||
} = data;
|
||||
playerData.x = x;
|
||||
playerData.y = y;
|
||||
socket.to(`world:${currentWorld}`).emit('moved', {socketId: socket.id, x, y});
|
||||
socket.to(`world:${currentWorld}`).emit('moved', {
|
||||
socketId: socket.id,
|
||||
x,
|
||||
y
|
||||
});
|
||||
});
|
||||
|
||||
// Disconnect
|
||||
@@ -280,8 +329,14 @@ app.get('/api/sprite', (req, res) => {
|
||||
frameWidth: 32,
|
||||
frameHeight: 32,
|
||||
animations: {
|
||||
idle: { frames: [0, 1, 2, 3], speed: 200 },
|
||||
walk: { frames: [4, 5, 6, 7], speed: 100 }
|
||||
idle: {
|
||||
frames: [0, 1, 2, 3],
|
||||
speed: 200
|
||||
},
|
||||
walk: {
|
||||
frames: [4, 5, 6, 7],
|
||||
speed: 100
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -291,7 +346,10 @@ app.post('/api/message', (req, res) => {
|
||||
const {userId} = req.params;
|
||||
const {message} = req.body;
|
||||
// TODO: Implement notification system
|
||||
res.json({ success: true, message: 'Message sent' });
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Message sent'
|
||||
});
|
||||
});
|
||||
|
||||
// Send message to PET LLM
|
||||
@@ -299,7 +357,10 @@ app.post('/api/message', (req, res) => {
|
||||
const {petId} = req.params;
|
||||
const {message} = req.body;
|
||||
// TODO: Queue message for LLM processing
|
||||
res.json({ success: true, message: 'Message queued' });
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Message queued'
|
||||
});
|
||||
});
|
||||
|
||||
// Link another PET
|
||||
@@ -307,7 +368,10 @@ app.post('/api/link', (req, res) => {
|
||||
const {petId} = req.params;
|
||||
const {targetApiUrl} = req.body;
|
||||
// TODO: Store link in database
|
||||
res.json({success: true, message: 'PET linked'});
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'PET linked'
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================
|
||||
|
||||
Reference in New Issue
Block a user