mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat: add prettier etc for ui-tui
This commit is contained in:
parent
2ea5345a7b
commit
2818dd8611
11 changed files with 5529 additions and 730 deletions
|
|
@ -159,7 +159,7 @@ def _(req_id, params: dict) -> dict:
|
||||||
status_callback=lambda text: _emit("status.update", sid, {"text": text}),
|
status_callback=lambda text: _emit("status.update", sid, {"text": text}),
|
||||||
clarify_callback=_make_clarify_cb(sid),
|
clarify_callback=_make_clarify_cb(sid),
|
||||||
)
|
)
|
||||||
_sessions[sid] = {"agent": agent, "session_key": session_key}
|
_sessions[sid] = {"agent": agent, "session_key": session_key, "history": []}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return _err(req_id, 5000, f"agent init failed: {e}")
|
return _err(req_id, 5000, f"agent init failed: {e}")
|
||||||
|
|
||||||
|
|
@ -180,16 +180,21 @@ def _(req_id, params: dict) -> dict:
|
||||||
return _err(req_id, 4001, "session not found")
|
return _err(req_id, 4001, "session not found")
|
||||||
|
|
||||||
agent = session["agent"]
|
agent = session["agent"]
|
||||||
|
history = session["history"]
|
||||||
_emit("message.start", sid)
|
_emit("message.start", sid)
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
try:
|
try:
|
||||||
result = agent.run_conversation(
|
result = agent.run_conversation(
|
||||||
text,
|
text,
|
||||||
|
conversation_history=list(history),
|
||||||
stream_callback=lambda delta: _emit("message.delta", sid, {"text": delta}),
|
stream_callback=lambda delta: _emit("message.delta", sid, {"text": delta}),
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(result, dict):
|
if isinstance(result, dict):
|
||||||
|
returned_msgs = result.get("messages")
|
||||||
|
if isinstance(returned_msgs, list):
|
||||||
|
session["history"] = returned_msgs
|
||||||
final = result.get("final_response", "")
|
final = result.get("final_response", "")
|
||||||
status = "interrupted" if result.get("interrupted") else "error" if result.get("error") else "complete"
|
status = "interrupted" if result.get("interrupted") else "error" if result.get("error") else "complete"
|
||||||
_emit("message.complete", sid, {
|
_emit("message.complete", sid, {
|
||||||
|
|
@ -248,8 +253,7 @@ def _(req_id, params: dict) -> dict:
|
||||||
session = _sessions.get(params.get("session_id", ""))
|
session = _sessions.get(params.get("session_id", ""))
|
||||||
if not session:
|
if not session:
|
||||||
return _err(req_id, 4001, "session not found")
|
return _err(req_id, 4001, "session not found")
|
||||||
history = getattr(session["agent"], "conversation_history", [])
|
return _ok(req_id, {"count": len(session.get("history", []))})
|
||||||
return _ok(req_id, {"count": len(history)})
|
|
||||||
|
|
||||||
|
|
||||||
@method("session.undo")
|
@method("session.undo")
|
||||||
|
|
@ -257,7 +261,7 @@ def _(req_id, params: dict) -> dict:
|
||||||
session = _sessions.get(params.get("session_id", ""))
|
session = _sessions.get(params.get("session_id", ""))
|
||||||
if not session:
|
if not session:
|
||||||
return _err(req_id, 4001, "session not found")
|
return _err(req_id, 4001, "session not found")
|
||||||
history = getattr(session["agent"], "conversation_history", [])
|
history = session.get("history", [])
|
||||||
removed = 0
|
removed = 0
|
||||||
while history and history[-1].get("role") in ("assistant", "tool"):
|
while history and history[-1].get("role") in ("assistant", "tool"):
|
||||||
history.pop(); removed += 1
|
history.pop(); removed += 1
|
||||||
|
|
|
||||||
11
ui-tui/.prettierrc
Normal file
11
ui-tui/.prettierrc
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
68
ui-tui/eslint.config.mjs
Normal file
68
ui-tui/eslint.config.mjs
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
||||||
|
import typescriptParser from '@typescript-eslint/parser'
|
||||||
|
import perfectionist from 'eslint-plugin-perfectionist'
|
||||||
|
import reactPlugin from 'eslint-plugin-react'
|
||||||
|
import hooksPlugin from 'eslint-plugin-react-hooks'
|
||||||
|
import unusedImports from 'eslint-plugin-unused-imports'
|
||||||
|
import globals from 'globals'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: { ...globals.node },
|
||||||
|
parser: typescriptParser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: { jsx: true },
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': typescriptEslint,
|
||||||
|
perfectionist,
|
||||||
|
react: reactPlugin,
|
||||||
|
'react-hooks': hooksPlugin,
|
||||||
|
'unused-imports': unusedImports
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
curly: ['error', 'all'],
|
||||||
|
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'no-undef': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'padding-line-between-statements': [
|
||||||
|
1,
|
||||||
|
{ blankLine: 'always', next: ['block-like', 'block', 'return', 'if', 'class', 'continue', 'debugger', 'break', 'multiline-const', 'multiline-let'], prev: '*' },
|
||||||
|
{ blankLine: 'always', next: '*', prev: ['case', 'default', 'multiline-const', 'multiline-let', 'multiline-block-like'] },
|
||||||
|
{ blankLine: 'never', next: ['block', 'block-like'], prev: ['case', 'default'] },
|
||||||
|
{ blankLine: 'always', next: ['block', 'block-like'], prev: ['block', 'block-like'] },
|
||||||
|
{ blankLine: 'always', next: ['empty'], prev: 'export' },
|
||||||
|
{ blankLine: 'never', next: 'iife', prev: ['block', 'block-like', 'empty'] }
|
||||||
|
],
|
||||||
|
'perfectionist/sort-exports': ['error', { order: 'asc', type: 'natural' }],
|
||||||
|
'perfectionist/sort-imports': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
groups: ['side-effect', 'builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
|
||||||
|
order: 'asc',
|
||||||
|
type: 'natural'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'perfectionist/sort-jsx-props': ['error', { order: 'asc', type: 'natural' }],
|
||||||
|
'perfectionist/sort-named-exports': ['error', { order: 'asc', type: 'natural' }],
|
||||||
|
'perfectionist/sort-named-imports': ['error', { order: 'asc', type: 'natural' }],
|
||||||
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'unused-imports/no-unused-imports': 'error'
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: { version: 'detect' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['node_modules/', 'dist/', '*.config.*']
|
||||||
|
}
|
||||||
|
]
|
||||||
4161
ui-tui/package-lock.json
generated
4161
ui-tui/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,10 @@
|
||||||
"dev": "tsx --watch src/main.tsx",
|
"dev": "tsx --watch src/main.tsx",
|
||||||
"start": "tsx src/main.tsx",
|
"start": "tsx src/main.tsx",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"test": "echo 'no tests yet'"
|
"lint": "eslint src/",
|
||||||
|
"lint:fix": "eslint src/ --fix",
|
||||||
|
"fmt": "prettier --write 'src/**/*.{ts,tsx}'",
|
||||||
|
"fix": "npm run lint:fix && npm run fmt"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ink": "^6.8.0",
|
"ink": "^6.8.0",
|
||||||
|
|
@ -15,8 +18,18 @@
|
||||||
"react": "^19.2.4"
|
"react": "^19.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9",
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.5.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8",
|
||||||
|
"@typescript-eslint/parser": "^8",
|
||||||
|
"eslint": "^9",
|
||||||
|
"eslint-plugin-perfectionist": "^5",
|
||||||
|
"eslint-plugin-react": "^7",
|
||||||
|
"eslint-plugin-react-hooks": "^7",
|
||||||
|
"eslint-plugin-unused-imports": "^4",
|
||||||
|
"globals": "^16",
|
||||||
|
"prettier": "^3",
|
||||||
"tsx": "^4.19.0",
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.7.0"
|
"typescript": "^5.7.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, type PropsWithChildren } from 'react'
|
|
||||||
import { Box, useStdout } from 'ink'
|
import { Box, useStdout } from 'ink'
|
||||||
|
import { type PropsWithChildren, useEffect } from 'react'
|
||||||
|
|
||||||
const ENTER = '\x1b[?1049h\x1b[2J\x1b[H'
|
const ENTER = '\x1b[?1049h\x1b[2J\x1b[H'
|
||||||
const LEAVE = '\x1b[?1049l'
|
const LEAVE = '\x1b[?1049l'
|
||||||
|
|
@ -22,7 +22,7 @@ export function AltScreen({ children }: PropsWithChildren) {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" height={rows} width={cols} overflow="hidden">
|
<Box flexDirection="column" height={rows} overflow="hidden" width={cols}>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ const LOGO_ART = [
|
||||||
'███████║█████╗ ██████╔╝██╔████╔██║█████╗ ███████╗█████╗███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ',
|
'███████║█████╗ ██████╔╝██╔████╔██║█████╗ ███████╗█████╗███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ',
|
||||||
'██╔══██║██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ╚════██║╚════╝██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ',
|
'██╔══██║██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ╚════██║╚════╝██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ',
|
||||||
'██║ ██║███████╗██║ ██║██║ ╚═╝ ██║███████╗███████║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ',
|
'██║ ██║███████╗██║ ██║██║ ╚═╝ ██║███████╗███████║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ',
|
||||||
'╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ',
|
'╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ '
|
||||||
]
|
]
|
||||||
|
|
||||||
const CADUCEUS_ART = [
|
const CADUCEUS_ART = [
|
||||||
|
|
@ -26,18 +26,19 @@ const CADUCEUS_ART = [
|
||||||
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠑⢶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠑⢶⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
||||||
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⢰⡆⠈⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠁⢰⡆⠈⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
||||||
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠈⣡⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠈⣡⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
||||||
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀',
|
'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'
|
||||||
]
|
]
|
||||||
|
|
||||||
const LOGO_GRADIENT = [0, 0, 1, 1, 2, 2] as const
|
const LOGO_GRADIENT = [0, 0, 1, 1, 2, 2] as const
|
||||||
const CADUC_GRADIENT = [2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3] as const
|
const CADUC_GRADIENT = [2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3] as const
|
||||||
|
|
||||||
function colorize(art: string[], gradient: readonly number[], c: ThemeColors): Line[] {
|
function colorize(art: string[], gradient: readonly number[], c: ThemeColors): Line[] {
|
||||||
const palette = [c.gold, c.amber, c.bronze, c.dim]
|
const palette = [c.gold, c.amber, c.bronze, c.dim]
|
||||||
|
|
||||||
return art.map((text, i) => [palette[gradient[i]] ?? c.dim, text])
|
return art.map((text, i) => [palette[gradient[i]] ?? c.dim, text])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LOGO_WIDTH = 98
|
export const LOGO_WIDTH = 98
|
||||||
|
|
||||||
export const logo = (c: ThemeColors) => colorize(LOGO_ART, LOGO_GRADIENT, c)
|
export const logo = (c: ThemeColors) => colorize(LOGO_ART, LOGO_GRADIENT, c)
|
||||||
export const caduceus = (c: ThemeColors) => colorize(CADUCEUS_ART, CADUC_GRADIENT, c)
|
export const caduceus = (c: ThemeColors) => colorize(CADUCEUS_ART, CADUC_GRADIENT, c)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { spawn, type ChildProcess } from 'node:child_process'
|
import { type ChildProcess, spawn } from 'node:child_process'
|
||||||
import { createInterface } from 'node:readline'
|
|
||||||
import { EventEmitter } from 'node:events'
|
import { EventEmitter } from 'node:events'
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'node:path'
|
||||||
|
import { createInterface } from 'node:readline'
|
||||||
|
|
||||||
export interface GatewayEvent {
|
export interface GatewayEvent {
|
||||||
type: string
|
type: string
|
||||||
|
|
@ -22,17 +22,20 @@ export class GatewayClient extends EventEmitter {
|
||||||
start() {
|
start() {
|
||||||
const root = resolve(import.meta.dirname, '../../')
|
const root = resolve(import.meta.dirname, '../../')
|
||||||
|
|
||||||
this.proc = spawn(
|
this.proc = spawn(process.env.HERMES_PYTHON ?? resolve(root, 'venv/bin/python'), ['-m', 'tui_gateway.entry'], {
|
||||||
process.env.HERMES_PYTHON ?? resolve(root, 'venv/bin/python'),
|
cwd: root,
|
||||||
['-m', 'tui_gateway.entry'],
|
stdio: ['pipe', 'pipe', 'inherit']
|
||||||
{ cwd: root, stdio: ['pipe', 'pipe', 'inherit'] },
|
|
||||||
)
|
|
||||||
|
|
||||||
createInterface({ input: this.proc.stdout! }).on('line', (raw) => {
|
|
||||||
try { this.dispatch(JSON.parse(raw)) } catch {}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.proc.on('exit', (code) => this.emit('exit', code))
|
createInterface({ input: this.proc.stdout! }).on('line', raw => {
|
||||||
|
try {
|
||||||
|
this.dispatch(JSON.parse(raw))
|
||||||
|
} catch {
|
||||||
|
/* malformed line */
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.proc.on('exit', code => this.emit('exit', code))
|
||||||
}
|
}
|
||||||
|
|
||||||
private dispatch(msg: Record<string, unknown>) {
|
private dispatch(msg: Record<string, unknown>) {
|
||||||
|
|
@ -41,32 +44,33 @@ export class GatewayClient extends EventEmitter {
|
||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
this.pending.delete(id!)
|
this.pending.delete(id!)
|
||||||
msg.error
|
msg.error ? p.reject(new Error((msg.error as any).message)) : p.resolve(msg.result)
|
||||||
? p.reject(new Error((msg.error as any).message))
|
|
||||||
: p.resolve(msg.result)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.method === 'event')
|
if (msg.method === 'event') {
|
||||||
this.emit('event', msg.params as GatewayEvent)
|
this.emit('event', msg.params as GatewayEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request(method: string, params: Record<string, unknown> = {}): Promise<unknown> {
|
request(method: string, params: Record<string, unknown> = {}): Promise<unknown> {
|
||||||
const id = `r${++this.reqId}`
|
const id = `r${++this.reqId}`
|
||||||
|
|
||||||
this.proc!.stdin!.write(
|
this.proc!.stdin!.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n')
|
||||||
JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n',
|
|
||||||
)
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.pending.set(id, { resolve, reject })
|
this.pending.set(id, { resolve, reject })
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.pending.delete(id))
|
if (this.pending.delete(id)) {
|
||||||
reject(new Error(`timeout: ${method}`))
|
reject(new Error(`timeout: ${method}`))
|
||||||
|
}
|
||||||
}, 30_000)
|
}, 30_000)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
kill() { this.proc?.kill() }
|
kill() {
|
||||||
|
this.proc?.kill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
"use strict";
|
|
||||||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
||||||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
||||||
if (ar || !(i in from)) {
|
|
||||||
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
||||||
ar[i] = from[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return to.concat(ar || Array.prototype.slice.call(from));
|
|
||||||
};
|
|
||||||
var _a;
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
var react_1 = require("react");
|
|
||||||
var ink_1 = require("ink");
|
|
||||||
var ink_text_input_1 = require("ink-text-input");
|
|
||||||
function App() {
|
|
||||||
var _a = (0, react_1.useState)(''), input = _a[0], setInput = _a[1];
|
|
||||||
var _b = (0, react_1.useState)([]), messages = _b[0], setMessages = _b[1];
|
|
||||||
var handleSubmit = function (value) {
|
|
||||||
if (!value.trim())
|
|
||||||
return;
|
|
||||||
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), ["> ".concat(value), "[echo] ".concat(value)], false); });
|
|
||||||
setInput('');
|
|
||||||
};
|
|
||||||
return (<ink_1.Box flexDirection="column" padding={1}>
|
|
||||||
<ink_1.Box marginBottom={1}>
|
|
||||||
<ink_1.Text bold color="yellow">hermes</ink_1.Text>
|
|
||||||
<ink_1.Text dimColor> (ink proof-of-concept)</ink_1.Text>
|
|
||||||
</ink_1.Box>
|
|
||||||
|
|
||||||
<ink_1.Box flexDirection="column" marginBottom={1}>
|
|
||||||
{messages.map(function (msg, i) { return (<ink_1.Text key={i}>{msg}</ink_1.Text>); })}
|
|
||||||
</ink_1.Box>
|
|
||||||
|
|
||||||
<ink_1.Box>
|
|
||||||
<ink_1.Text bold color="cyan">{'> '}</ink_1.Text>
|
|
||||||
<ink_text_input_1.default value={input} onChange={setInput} onSubmit={handleSubmit}/>
|
|
||||||
</ink_1.Box>
|
|
||||||
</ink_1.Box>);
|
|
||||||
}
|
|
||||||
var isTTY = (_a = process.stdin.isTTY) !== null && _a !== void 0 ? _a : false;
|
|
||||||
if (!isTTY) {
|
|
||||||
console.log('hermes-tui: ink loaded, no TTY attached (run in a real terminal)');
|
|
||||||
process.exit(0);
|
|
||||||
}
|
|
||||||
(0, ink_1.render)(<App />);
|
|
||||||
1764
ui-tui/src/main.tsx
1764
ui-tui/src/main.tsx
File diff suppressed because it is too large
Load diff
|
|
@ -1,30 +1,30 @@
|
||||||
export interface ThemeColors {
|
export interface ThemeColors {
|
||||||
gold: string
|
gold: string
|
||||||
amber: string
|
amber: string
|
||||||
bronze: string
|
bronze: string
|
||||||
cornsilk: string
|
cornsilk: string
|
||||||
dim: string
|
dim: string
|
||||||
|
|
||||||
label: string
|
label: string
|
||||||
ok: string
|
ok: string
|
||||||
error: string
|
error: string
|
||||||
warn: string
|
warn: string
|
||||||
|
|
||||||
statusBg: string
|
statusBg: string
|
||||||
statusFg: string
|
statusFg: string
|
||||||
statusGood: string
|
statusGood: string
|
||||||
statusWarn: string
|
statusWarn: string
|
||||||
statusBad: string
|
statusBad: string
|
||||||
statusCritical: string
|
statusCritical: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ThemeBrand {
|
export interface ThemeBrand {
|
||||||
name: string
|
name: string
|
||||||
icon: string
|
icon: string
|
||||||
prompt: string
|
prompt: string
|
||||||
welcome: string
|
welcome: string
|
||||||
goodbye: string
|
goodbye: string
|
||||||
tool: string
|
tool: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Theme {
|
export interface Theme {
|
||||||
|
|
@ -32,74 +32,69 @@ export interface Theme {
|
||||||
brand: ThemeBrand
|
brand: ThemeBrand
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const DEFAULT_THEME: Theme = {
|
export const DEFAULT_THEME: Theme = {
|
||||||
color: {
|
color: {
|
||||||
gold: '#FFD700',
|
gold: '#FFD700',
|
||||||
amber: '#FFBF00',
|
amber: '#FFBF00',
|
||||||
bronze: '#CD7F32',
|
bronze: '#CD7F32',
|
||||||
cornsilk: '#FFF8DC',
|
cornsilk: '#FFF8DC',
|
||||||
dim: '#B8860B',
|
dim: '#B8860B',
|
||||||
|
|
||||||
label: '#4dd0e1',
|
label: '#4dd0e1',
|
||||||
ok: '#4caf50',
|
ok: '#4caf50',
|
||||||
error: '#ef5350',
|
error: '#ef5350',
|
||||||
warn: '#ffa726',
|
warn: '#ffa726',
|
||||||
|
|
||||||
statusBg: '#1a1a2e',
|
statusBg: '#1a1a2e',
|
||||||
statusFg: '#C0C0C0',
|
statusFg: '#C0C0C0',
|
||||||
statusGood: '#8FBC8F',
|
statusGood: '#8FBC8F',
|
||||||
statusWarn: '#FFD700',
|
statusWarn: '#FFD700',
|
||||||
statusBad: '#FF8C00',
|
statusBad: '#FF8C00',
|
||||||
statusCritical: '#FF6B6B',
|
statusCritical: '#FF6B6B'
|
||||||
},
|
},
|
||||||
|
|
||||||
brand: {
|
brand: {
|
||||||
name: 'Hermes Agent',
|
name: 'Hermes Agent',
|
||||||
icon: '⚕',
|
icon: '⚕',
|
||||||
prompt: '❯',
|
prompt: '❯',
|
||||||
welcome: 'Type your message or /help for commands.',
|
welcome: 'Type your message or /help for commands.',
|
||||||
goodbye: 'Goodbye! ⚕',
|
goodbye: 'Goodbye! ⚕',
|
||||||
tool: '┊',
|
tool: '┊'
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function fromSkin(colors: Record<string, string>, branding: Record<string, string>): Theme {
|
||||||
export function fromSkin(
|
|
||||||
colors: Record<string, string>,
|
|
||||||
branding: Record<string, string>,
|
|
||||||
): Theme {
|
|
||||||
const d = DEFAULT_THEME
|
const d = DEFAULT_THEME
|
||||||
const c = (k: string) => colors[k]
|
const c = (k: string) => colors[k]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
color: {
|
color: {
|
||||||
gold: c('banner_title') ?? d.color.gold,
|
gold: c('banner_title') ?? d.color.gold,
|
||||||
amber: c('banner_accent') ?? d.color.amber,
|
amber: c('banner_accent') ?? d.color.amber,
|
||||||
bronze: c('banner_border') ?? d.color.bronze,
|
bronze: c('banner_border') ?? d.color.bronze,
|
||||||
cornsilk: c('banner_text') ?? d.color.cornsilk,
|
cornsilk: c('banner_text') ?? d.color.cornsilk,
|
||||||
dim: c('banner_dim') ?? d.color.dim,
|
dim: c('banner_dim') ?? d.color.dim,
|
||||||
|
|
||||||
label: c('ui_label') ?? d.color.label,
|
label: c('ui_label') ?? d.color.label,
|
||||||
ok: c('ui_ok') ?? d.color.ok,
|
ok: c('ui_ok') ?? d.color.ok,
|
||||||
error: c('ui_error') ?? d.color.error,
|
error: c('ui_error') ?? d.color.error,
|
||||||
warn: c('ui_warn') ?? d.color.warn,
|
warn: c('ui_warn') ?? d.color.warn,
|
||||||
|
|
||||||
statusBg: d.color.statusBg,
|
statusBg: d.color.statusBg,
|
||||||
statusFg: d.color.statusFg,
|
statusFg: d.color.statusFg,
|
||||||
statusGood: c('ui_ok') ?? d.color.statusGood,
|
statusGood: c('ui_ok') ?? d.color.statusGood,
|
||||||
statusWarn: c('ui_warn') ?? d.color.statusWarn,
|
statusWarn: c('ui_warn') ?? d.color.statusWarn,
|
||||||
statusBad: d.color.statusBad,
|
statusBad: d.color.statusBad,
|
||||||
statusCritical: d.color.statusCritical,
|
statusCritical: d.color.statusCritical
|
||||||
},
|
},
|
||||||
|
|
||||||
brand: {
|
brand: {
|
||||||
name: branding.agent_name ?? d.brand.name,
|
name: branding.agent_name ?? d.brand.name,
|
||||||
icon: d.brand.icon,
|
icon: d.brand.icon,
|
||||||
prompt: branding.prompt_symbol ?? d.brand.prompt,
|
prompt: branding.prompt_symbol ?? d.brand.prompt,
|
||||||
welcome: branding.welcome ?? d.brand.welcome,
|
welcome: branding.welcome ?? d.brand.welcome,
|
||||||
goodbye: branding.goodbye ?? d.brand.goodbye,
|
goodbye: branding.goodbye ?? d.brand.goodbye,
|
||||||
tool: d.brand.tool,
|
tool: d.brand.tool
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue