@hina114514/chaite 1.9.4 → 1.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapters-DLb1sL9p.mjs → adapters-BtoEz02k.mjs} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-DGgOl4PZ.mjs → src-DpLgiiEQ.mjs} +3 -3
- package/dist/{types-BN6lBxWd.mjs → types-kzT5dZX7.mjs} +1 -1
- package/frontend/build/index.html +527 -12
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as init_adapters, f as createClient, g as AbstractClient, h as OpenAIClient, m as ClaudeClient, p as GeminiClient } from "./src-
|
|
1
|
+
import { d as init_adapters, f as createClient, g as AbstractClient, h as OpenAIClient, m as ClaudeClient, p as GeminiClient } from "./src-DpLgiiEQ.mjs";
|
|
2
2
|
import "./chunks/lop/index.mjs-BWbOMJZv.mjs";
|
|
3
3
|
import "./chunks/bluebird/index.mjs-D4zFjek8.mjs";
|
|
4
4
|
import "./chunks/mammoth/index.mjs-C9xwBIgf.mjs";
|
package/dist/index.d.ts
CHANGED
|
@@ -1567,7 +1567,7 @@ declare const PROCESSOR_TYPE_MAP: {
|
|
|
1567
1567
|
};
|
|
1568
1568
|
//#endregion
|
|
1569
1569
|
//#region src/version.d.ts
|
|
1570
|
-
declare const VERSION = "1.9.
|
|
1570
|
+
declare const VERSION = "1.9.5";
|
|
1571
1571
|
//#endregion
|
|
1572
1572
|
//#region src/controllers/index.d.ts
|
|
1573
1573
|
declare function createApp(configure?: (app: Application) => void): Application;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as ChaiteStorage, A as FrontEndAuthHandler, B as VectorizerImpl, C as Channel, D as ExecutableShareableManager, E as ToolManager, F as PureTextFileParser, G as KVHistoryManager, H as RAGManager, I as DocxBufferParser, J as getKey, K as asyncLocalStorage, L as DocxFileParser, M as DEFAULT_PORT, N as GlobalConfig, O as NonExecutableShareableManager, P as PureTextBufferParser, Q as ChaiteResponse, R as PdfBufferParser, S as ChatPreset, T as DefaultCloudService, U as TextProcessor, V as AbstractVectorDatabase, W as InMemoryHistoryManager, X as useEvent, Y as saveAndLoadModule, Z as AbstractUserModeSelector, _ as TriggerManager, _t as AbstractShareable, a as EventTrigger, at as AbstractHistoryManager, b as ProcessorsManager, c as createApp, ct as DocumentFileParser, dt as ToolDTO, et as PostProcessor, f as createClient, ft as ToolsGroupDTO, g as AbstractClient, gt as MultipleKeyStrategyChoice, h as OpenAIClient, ht as DefaultLogger, i as CronTrigger, it as PROCESSOR_TYPE_MAP, j as DEFAULT_HOST, k as getLogger, l as runServer, lt as DocumentPathParser, m as ClaudeClient, mt as ChaiteContext, nt as ProcessorDTO, o as TriggerDTO, ot as SendMessageOption, p as GeminiClient, pt as BaseClientOptions, q as extractClassName, r as BaseTrigger, rt as CHANNEL_STATUS_MAP, s as Chaite, st as ChannelStatistics, t as init_src, tt as PreProcessor, u as VERSION, ut as CustomTool, v as ToolsGroupManager, w as DefaultChannelLoadBalancer, x as ChannelsManager, y as ChatPresetManager, z as PdfFileParser } from "./src-
|
|
1
|
+
import { $ as ChaiteStorage, A as FrontEndAuthHandler, B as VectorizerImpl, C as Channel, D as ExecutableShareableManager, E as ToolManager, F as PureTextFileParser, G as KVHistoryManager, H as RAGManager, I as DocxBufferParser, J as getKey, K as asyncLocalStorage, L as DocxFileParser, M as DEFAULT_PORT, N as GlobalConfig, O as NonExecutableShareableManager, P as PureTextBufferParser, Q as ChaiteResponse, R as PdfBufferParser, S as ChatPreset, T as DefaultCloudService, U as TextProcessor, V as AbstractVectorDatabase, W as InMemoryHistoryManager, X as useEvent, Y as saveAndLoadModule, Z as AbstractUserModeSelector, _ as TriggerManager, _t as AbstractShareable, a as EventTrigger, at as AbstractHistoryManager, b as ProcessorsManager, c as createApp, ct as DocumentFileParser, dt as ToolDTO, et as PostProcessor, f as createClient, ft as ToolsGroupDTO, g as AbstractClient, gt as MultipleKeyStrategyChoice, h as OpenAIClient, ht as DefaultLogger, i as CronTrigger, it as PROCESSOR_TYPE_MAP, j as DEFAULT_HOST, k as getLogger, l as runServer, lt as DocumentPathParser, m as ClaudeClient, mt as ChaiteContext, nt as ProcessorDTO, o as TriggerDTO, ot as SendMessageOption, p as GeminiClient, pt as BaseClientOptions, q as extractClassName, r as BaseTrigger, rt as CHANNEL_STATUS_MAP, s as Chaite, st as ChannelStatistics, t as init_src, tt as PreProcessor, u as VERSION, ut as CustomTool, v as ToolsGroupManager, w as DefaultChannelLoadBalancer, x as ChannelsManager, y as ChatPresetManager, z as PdfFileParser } from "./src-DpLgiiEQ.mjs";
|
|
2
2
|
import "./chunks/lop/index.mjs-BWbOMJZv.mjs";
|
|
3
3
|
import "./chunks/bluebird/index.mjs-D4zFjek8.mjs";
|
|
4
4
|
import "./chunks/mammoth/index.mjs-C9xwBIgf.mjs";
|
|
@@ -4441,7 +4441,7 @@ var init_trigger$1 = __esm({ "src/controllers/trigger.ts": (() => {
|
|
|
4441
4441
|
//#region src/version.ts
|
|
4442
4442
|
var VERSION;
|
|
4443
4443
|
var init_version = __esm({ "src/version.ts": (() => {
|
|
4444
|
-
VERSION = "1.9.
|
|
4444
|
+
VERSION = "1.9.5";
|
|
4445
4445
|
}) });
|
|
4446
4446
|
|
|
4447
4447
|
//#endregion
|
|
@@ -4567,8 +4567,8 @@ var init_basic = __esm({ "src/controllers/basic.ts": (() => {
|
|
|
4567
4567
|
res.status(404).json(ChaiteResponse.fail(null, "Channel not found"));
|
|
4568
4568
|
return;
|
|
4569
4569
|
}
|
|
4570
|
-
const { createClient: createClient$1 } = await import("./adapters-
|
|
4571
|
-
const { ChaiteContext: ChaiteContext$1 } = await import("./types-
|
|
4570
|
+
const { createClient: createClient$1 } = await import("./adapters-BtoEz02k.mjs");
|
|
4571
|
+
const { ChaiteContext: ChaiteContext$1 } = await import("./types-kzT5dZX7.mjs");
|
|
4572
4572
|
const ctx = new ChaiteContext$1(chaite.getLogger());
|
|
4573
4573
|
ctx.setChaite(chaite);
|
|
4574
4574
|
const client = createClient$1(channel.adapterType, channel.options, ctx);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as ChaiteStorage, Q as ChaiteResponse, Z as AbstractUserModeSelector, _t as AbstractShareable, a as EventTrigger, at as AbstractHistoryManager, ct as DocumentFileParser, dt as ToolDTO, et as PostProcessor, ft as ToolsGroupDTO, gt as MultipleKeyStrategyChoice, ht as DefaultLogger, i as CronTrigger, lt as DocumentPathParser, mt as ChaiteContext, n as init_types, nt as ProcessorDTO, o as TriggerDTO, ot as SendMessageOption, pt as BaseClientOptions, r as BaseTrigger, st as ChannelStatistics, tt as PreProcessor, ut as CustomTool } from "./src-
|
|
1
|
+
import { $ as ChaiteStorage, Q as ChaiteResponse, Z as AbstractUserModeSelector, _t as AbstractShareable, a as EventTrigger, at as AbstractHistoryManager, ct as DocumentFileParser, dt as ToolDTO, et as PostProcessor, ft as ToolsGroupDTO, gt as MultipleKeyStrategyChoice, ht as DefaultLogger, i as CronTrigger, lt as DocumentPathParser, mt as ChaiteContext, n as init_types, nt as ProcessorDTO, o as TriggerDTO, ot as SendMessageOption, pt as BaseClientOptions, r as BaseTrigger, st as ChannelStatistics, tt as PreProcessor, ut as CustomTool } from "./src-DpLgiiEQ.mjs";
|
|
2
2
|
import "./chunks/lop/index.mjs-BWbOMJZv.mjs";
|
|
3
3
|
import "./chunks/bluebird/index.mjs-D4zFjek8.mjs";
|
|
4
4
|
import "./chunks/mammoth/index.mjs-C9xwBIgf.mjs";
|
|
@@ -1,18 +1,533 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="
|
|
3
|
-
|
|
2
|
+
<html lang="zh-CN">
|
|
4
3
|
<head>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
<meta charset="UTF-8"/>
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
6
|
+
<title>Chaite Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
9
|
+
:root{
|
|
10
|
+
--bg:#0c0e14;--surface:#141720;--surface2:#1a1e2a;--surface3:#222738;
|
|
11
|
+
--border:#2a2f42;--text:#e0e4ef;--text2:#8b92a8;--accent:#6c8cff;--accent2:#4a6aef;
|
|
12
|
+
--green:#4ade80;--red:#f87171;--yellow:#facc15;--purple:#a78bfa;
|
|
13
|
+
--radius:10px;--shadow:0 2px 12px rgba(0,0,0,.4);
|
|
14
|
+
}
|
|
15
|
+
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;min-height:100vh}
|
|
16
|
+
a{color:var(--accent);text-decoration:none}
|
|
17
|
+
a:hover{text-decoration:underline}
|
|
18
|
+
.app{max-width:1280px;margin:0 auto;padding:20px}
|
|
19
|
+
|
|
20
|
+
/* Login */
|
|
21
|
+
.login-wrap{display:flex;align-items:center;justify-content:center;min-height:100vh}
|
|
22
|
+
.login-box{background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:48px 40px;width:380px;text-align:center;box-shadow:var(--shadow)}
|
|
23
|
+
.login-box h1{font-size:24px;margin-bottom:8px}
|
|
24
|
+
.login-box p{color:var(--text2);font-size:14px;margin-bottom:32px}
|
|
25
|
+
.login-box .spinner{display:inline-block;width:18px;height:18px;border:2px solid var(--text2);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}
|
|
26
|
+
.login-box .err{color:var(--red);font-size:13px;margin-bottom:12px;min-height:20px}
|
|
27
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
28
|
+
|
|
29
|
+
/* Header */
|
|
30
|
+
.header{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;margin-bottom:24px}
|
|
31
|
+
.header h1{font-size:20px;display:flex;align-items:center;gap:8px}
|
|
32
|
+
.header h1 span{color:var(--accent)}
|
|
33
|
+
.header .meta{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
|
|
34
|
+
.badge{background:var(--surface2);border:1px solid var(--border);border-radius:20px;padding:3px 12px;font-size:12px;color:var(--text2)}
|
|
35
|
+
.badge.ok{border-color:var(--green);color:var(--green)}
|
|
36
|
+
.badge.err{border-color:var(--red);color:var(--red)}
|
|
37
|
+
|
|
38
|
+
/* Buttons */
|
|
39
|
+
.btn{background:var(--surface2);border:1px solid var(--border);border-radius:8px;color:var(--text);padding:6px 14px;font-size:13px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all .15s}
|
|
40
|
+
.btn:hover{background:var(--surface3);border-color:var(--accent)}
|
|
41
|
+
.btn:disabled{opacity:.5;cursor:not-allowed}
|
|
42
|
+
.btn.primary{background:var(--accent2);border-color:var(--accent);color:#fff}
|
|
43
|
+
.btn.primary:hover{background:var(--accent)}
|
|
44
|
+
.btn.danger{border-color:var(--red);color:var(--red)}
|
|
45
|
+
.btn.danger:hover{background:rgba(248,113,113,.15)}
|
|
46
|
+
.btn.sm{padding:3px 10px;font-size:12px}
|
|
47
|
+
|
|
48
|
+
/* Cards */
|
|
49
|
+
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;margin-bottom:16px;box-shadow:var(--shadow)}
|
|
50
|
+
.card h3{font-size:15px;margin-bottom:14px;display:flex;align-items:center;gap:8px}
|
|
51
|
+
.card h3 .dot{width:8px;height:8px;border-radius:50%}
|
|
12
52
|
|
|
53
|
+
/* Stat cards */
|
|
54
|
+
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:20px}
|
|
55
|
+
.stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:16px;text-align:center}
|
|
56
|
+
.stat .val{font-size:28px;font-weight:700;color:var(--accent)}
|
|
57
|
+
.stat .label{font-size:12px;color:var(--text2);margin-top:4px}
|
|
58
|
+
|
|
59
|
+
/* Table */
|
|
60
|
+
.tbl{width:100%;border-collapse:collapse;font-size:13px}
|
|
61
|
+
.tbl th{text-align:left;padding:10px 12px;color:var(--text2);font-weight:600;border-bottom:1px solid var(--border);font-size:12px;text-transform:uppercase;letter-spacing:.5px}
|
|
62
|
+
.tbl td{padding:10px 12px;border-bottom:1px solid var(--border)}
|
|
63
|
+
.tbl tr:hover td{background:var(--surface2)}
|
|
64
|
+
.pill{display:inline-block;padding:2px 10px;border-radius:12px;font-size:11px;font-weight:600}
|
|
65
|
+
.pill.on{background:rgba(74,222,128,.15);color:var(--green)}
|
|
66
|
+
.pill.off{background:rgba(248,113,113,.12);color:var(--red)}
|
|
67
|
+
.pill.model{background:rgba(108,140,255,.12);color:var(--accent);margin:2px 3px 2px 0}
|
|
68
|
+
|
|
69
|
+
/* Tabs */
|
|
70
|
+
.tabs{display:flex;gap:0;margin-bottom:20px;border-bottom:1px solid var(--border)}
|
|
71
|
+
.tab{padding:10px 20px;font-size:13px;cursor:pointer;color:var(--text2);border-bottom:2px solid transparent;transition:all .15s}
|
|
72
|
+
.tab:hover{color:var(--text)}
|
|
73
|
+
.tab.active{color:var(--accent);border-bottom-color:var(--accent)}
|
|
74
|
+
|
|
75
|
+
/* Modal */
|
|
76
|
+
.modal-mask{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:100}
|
|
77
|
+
.modal{background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:28px;width:520px;max-height:85vh;overflow-y:auto;box-shadow:0 8px 32px rgba(0,0,0,.5)}
|
|
78
|
+
.modal h3{margin-bottom:20px;font-size:17px}
|
|
79
|
+
.form-row{margin-bottom:14px}
|
|
80
|
+
.form-row label{display:block;font-size:12px;color:var(--text2);margin-bottom:5px}
|
|
81
|
+
.form-row input,.form-row select,.form-row textarea{width:100%;padding:8px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;font-family:inherit}
|
|
82
|
+
.form-row textarea{min-height:80px;resize:vertical}
|
|
83
|
+
.form-row input:focus,.form-row select:focus,.form-row textarea:focus{outline:none;border-color:var(--accent)}
|
|
84
|
+
.modal-btns{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}
|
|
85
|
+
|
|
86
|
+
/* Toast */
|
|
87
|
+
.toast{position:fixed;top:20px;right:20px;padding:10px 20px;border-radius:8px;font-size:13px;z-index:200;animation:slideIn .3s}
|
|
88
|
+
.toast.ok{background:var(--green);color:#000}
|
|
89
|
+
.toast.err{background:var(--red);color:#fff}
|
|
90
|
+
@keyframes slideIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
|
|
91
|
+
|
|
92
|
+
/* Responsive */
|
|
93
|
+
@media(max-width:768px){
|
|
94
|
+
.stats{grid-template-columns:repeat(2,1fr)}
|
|
95
|
+
.modal{width:95%;margin:10px}
|
|
96
|
+
.header{flex-direction:column;align-items:flex-start}
|
|
97
|
+
}
|
|
98
|
+
</style>
|
|
99
|
+
</head>
|
|
13
100
|
<body>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
101
|
+
<div id="app"></div>
|
|
102
|
+
<script>
|
|
103
|
+
const{createApp,ref,reactive,computed,onMounted,onUnmounted,watch}=Vue
|
|
104
|
+
|
|
105
|
+
const API=(path,opt={})=>{
|
|
106
|
+
const t=localStorage.getItem('chaite_jwt')
|
|
107
|
+
return fetch(path,{...opt,headers:{'Content-Type':'application/json','Authorization':t?'Bearer '+t:'',...(opt.headers||{})}}).then(async r=>{
|
|
108
|
+
if(r.status===401){localStorage.removeItem('chaite_jwt');location.reload()}
|
|
109
|
+
return r.json()
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
createApp({
|
|
114
|
+
setup(){
|
|
115
|
+
// Auth
|
|
116
|
+
const jwt=ref(localStorage.getItem('chaite_jwt')||'')
|
|
117
|
+
const loggedIn=ref(!!jwt.value)
|
|
118
|
+
const loginLoading=ref(false)
|
|
119
|
+
const loginError=ref('')
|
|
120
|
+
|
|
121
|
+
// State
|
|
122
|
+
const tab=ref('overview')
|
|
123
|
+
const health=ref(null)
|
|
124
|
+
const stats=ref(null)
|
|
125
|
+
const channels=ref([])
|
|
126
|
+
const tools=ref([])
|
|
127
|
+
const presets=ref([])
|
|
128
|
+
const processors=ref([])
|
|
129
|
+
const triggers=ref([])
|
|
130
|
+
const toolGroups=ref([])
|
|
131
|
+
const loading=ref(true)
|
|
132
|
+
const refreshTime=ref('')
|
|
133
|
+
let timer=null
|
|
134
|
+
|
|
135
|
+
// Modal
|
|
136
|
+
const modal=ref(null) // 'channel'|'preset'|'tool'|'processor'|'trigger'|'toolGroup'|null
|
|
137
|
+
const editing=ref(null) // object being edited
|
|
138
|
+
const form=ref({})
|
|
139
|
+
|
|
140
|
+
// Toast
|
|
141
|
+
const toastMsg=ref('')
|
|
142
|
+
const toastType=ref('ok')
|
|
143
|
+
let toastTimer=null
|
|
144
|
+
function toast(msg,type='ok'){toastMsg.value=msg;toastType.value=type;clearTimeout(toastTimer);toastTimer=setTimeout(()=>toastMsg.value='',3000)}
|
|
145
|
+
|
|
146
|
+
// Login
|
|
147
|
+
async function doLogin(){
|
|
148
|
+
loginError.value='';loginLoading.value=true
|
|
149
|
+
try{
|
|
150
|
+
const url=new URL(location.href)
|
|
151
|
+
const urlToken=url.searchParams.get('token')
|
|
152
|
+
const token=urlToken||prompt('Enter access token:')
|
|
153
|
+
if(!token){loginLoading.value=false;return}
|
|
154
|
+
const d=await fetch('/api/auth/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token})}).then(r=>r.json())
|
|
155
|
+
if(d.data?.token){
|
|
156
|
+
jwt.value=d.data.token;localStorage.setItem('chaite_jwt',d.data.token);loggedIn.value=true
|
|
157
|
+
url.searchParams.delete('token');history.replaceState(null,'',url.pathname+url.search)
|
|
158
|
+
refreshAll()
|
|
159
|
+
}else{loginError.value=d.message||'Login failed'}
|
|
160
|
+
}catch(e){loginError.value='Network error'}
|
|
161
|
+
finally{loginLoading.value=false}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function logout(){jwt.value='';localStorage.removeItem('chaite_jwt');loggedIn.value=false;clearInterval(timer)}
|
|
165
|
+
|
|
166
|
+
// Data fetching
|
|
167
|
+
async function refreshAll(){
|
|
168
|
+
loading.value=true
|
|
169
|
+
try{
|
|
170
|
+
const[h,s,c,t,p,pr,tr,tg]=await Promise.all([
|
|
171
|
+
API('/api/system/health').catch(()=>({})),
|
|
172
|
+
API('/api/system/stats').catch(()=>({})),
|
|
173
|
+
API('/api/channels/list').catch(()=>({})),
|
|
174
|
+
API('/api/tools/list').catch(()=>({})),
|
|
175
|
+
API('/api/preset/list').catch(()=>({})),
|
|
176
|
+
API('/api/processors/list').catch(()=>({})),
|
|
177
|
+
API('/api/triggers/list').catch(()=>({})),
|
|
178
|
+
API('/api/toolGroups/list').catch(()=>({})),
|
|
179
|
+
])
|
|
180
|
+
health.value=h.data||null
|
|
181
|
+
stats.value=s.data||null
|
|
182
|
+
channels.value=c.data||[]
|
|
183
|
+
tools.value=t.data||[]
|
|
184
|
+
presets.value=p.data||[]
|
|
185
|
+
processors.value=pr.data||[]
|
|
186
|
+
triggers.value=tr.data||[]
|
|
187
|
+
toolGroups.value=tg.data||[]
|
|
188
|
+
refreshTime.value=new Date().toLocaleTimeString()
|
|
189
|
+
}catch(e){console.error(e)}
|
|
190
|
+
finally{loading.value=false}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// CRUD helpers
|
|
194
|
+
async function testChannel(id){
|
|
195
|
+
const d=await API('/api/system/test-channel',{method:'POST',body:JSON.stringify({channelId:id})})
|
|
196
|
+
if(d.data?.status==='ok')toast(`✅ ${d.data.channelName} — ${d.data.latency}ms`)
|
|
197
|
+
else toast(`❌ ${d.data?.error||d.message}`,'err')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function openModal(type,item=null){
|
|
201
|
+
modal.value=type
|
|
202
|
+
editing.value=item
|
|
203
|
+
if(item)form.value=JSON.parse(JSON.stringify(item))
|
|
204
|
+
else{
|
|
205
|
+
const defaults={
|
|
206
|
+
channel:{name:'',adapterType:'openai',type:'openai',models:[''],options:{baseUrl:'',apiKey:''},status:'enabled',weight:1,priority:0},
|
|
207
|
+
preset:{name:'',prefix:'',sendMessageOption:{model:'',temperature:0.8,maxToken:4096,systemOverride:''}},
|
|
208
|
+
tool:{name:'',description:'',code:''},
|
|
209
|
+
processor:{name:'',type:'pre',description:'',code:''},
|
|
210
|
+
trigger:{name:'',description:'',code:''},
|
|
211
|
+
toolGroup:{name:'',description:'',toolIds:[],status:'enabled',isDefault:false},
|
|
212
|
+
}
|
|
213
|
+
form.value=defaults[type]||{}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function closeModal(){modal.value=null;editing.value=null;form.value={}}
|
|
217
|
+
|
|
218
|
+
async function saveItem(){
|
|
219
|
+
const type=modal.value
|
|
220
|
+
const apiMap={channel:'/api/channels',preset:'/api/preset',tool:'/api/tools',processor:'/api/processors',trigger:'/api/triggers',toolGroup:'/api/toolGroups'}
|
|
221
|
+
try{
|
|
222
|
+
const id=editing.value?.id
|
|
223
|
+
const url=apiMap[type]+(id?'/'+id:'')
|
|
224
|
+
const method=id?'PUT':'POST'
|
|
225
|
+
const d=await API(url,{method,body:JSON.stringify(form.value)})
|
|
226
|
+
if(d.data!==undefined){toast('Saved');closeModal();refreshAll()}
|
|
227
|
+
else toast(d.message||'Error','err')
|
|
228
|
+
}catch(e){toast(e.message,'err')}
|
|
229
|
+
}
|
|
17
230
|
|
|
231
|
+
async function deleteItem(type,id){
|
|
232
|
+
if(!confirm('Delete this item?'))return
|
|
233
|
+
const apiMap={channel:'/api/channels',preset:'/api/preset',tool:'/api/tools',processor:'/api/processors',trigger:'/api/triggers',toolGroup:'/api/toolGroups'}
|
|
234
|
+
try{
|
|
235
|
+
await API(apiMap[type]+'/'+id,{method:'DELETE'})
|
|
236
|
+
toast('Deleted');refreshAll()
|
|
237
|
+
}catch(e){toast(e.message,'err')}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function uptimeStr(s){
|
|
241
|
+
if(!s)return '-'
|
|
242
|
+
const h=Math.floor(s/3600),m=Math.floor(s%3600/60),sec=s%60
|
|
243
|
+
return h>0?`${h}h ${m}m ${sec}s`:`${m}m ${sec}s`
|
|
244
|
+
}
|
|
245
|
+
function fmtNum(n){if(n>=1e6)return(n/1e6).toFixed(1)+'M';if(n>=1e3)return(n/1e3).toFixed(1)+'K';return String(n||0)}
|
|
246
|
+
|
|
247
|
+
onMounted(()=>{
|
|
248
|
+
if(loggedIn.value)refreshAll()
|
|
249
|
+
timer=setInterval(()=>{if(logged.value)refreshAll()},30000)
|
|
250
|
+
})
|
|
251
|
+
onUnmounted(()=>clearInterval(timer))
|
|
252
|
+
|
|
253
|
+
const loggedIn=computed(()=>!!jwt.value)
|
|
254
|
+
|
|
255
|
+
return{
|
|
256
|
+
jwt,loggedIn,loginLoading,loginError,doLogin,logout,
|
|
257
|
+
tab,health,stats,channels,tools,presets,processors,triggers,toolGroups,loading,refreshTime,
|
|
258
|
+
modal,editing,form,openModal,closeModal,saveItem,deleteItem,testChannel,
|
|
259
|
+
toastMsg,toastType,uptimeStr,fmtNum,refreshAll,
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
template:`
|
|
263
|
+
<!-- Toast -->
|
|
264
|
+
<div v-if="toastMsg" :class="['toast',toastType]">{{toastMsg}}</div>
|
|
265
|
+
|
|
266
|
+
<!-- Login -->
|
|
267
|
+
<div v-if="!loggedIn" class="login-wrap">
|
|
268
|
+
<div class="login-box">
|
|
269
|
+
<h1>⚡ <span style="color:var(--accent)">Chaite</span></h1>
|
|
270
|
+
<p>Management Dashboard</p>
|
|
271
|
+
<div v-if="!loginLoading">
|
|
272
|
+
<div style="margin-bottom:16px">
|
|
273
|
+
<input v-model="loginInput" type="password" placeholder="Paste access token here..."
|
|
274
|
+
@keyup.enter="doLogin" autofocus
|
|
275
|
+
style="width:100%;padding:10px 14px;border-radius:8px;border:1px solid var(--border);background:var(--surface2);color:var(--text);font-size:14px;outline:none"/>
|
|
276
|
+
</div>
|
|
277
|
+
<div class="err">{{loginError}}</div>
|
|
278
|
+
<button class="btn primary" @click="doLogin" style="width:100%;padding:10px">Login</button>
|
|
279
|
+
<p style="margin-top:16px;font-size:12px;color:var(--text2)">Or append <code>?token=xxx</code> to URL</p>
|
|
280
|
+
</div>
|
|
281
|
+
<div v-else><div class="spinner"></div></div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<!-- Dashboard -->
|
|
286
|
+
<div v-else class="app">
|
|
287
|
+
<!-- Header -->
|
|
288
|
+
<div class="header">
|
|
289
|
+
<h1>⚡ <span>Chaite</span> Dashboard</h1>
|
|
290
|
+
<div class="meta">
|
|
291
|
+
<span class="badge" v-if="refreshTime">↻ {{refreshTime}}</span>
|
|
292
|
+
<span class="badge ok" v-if="health?.status==='ok'">Healthy</span>
|
|
293
|
+
<span class="badge err" v-else-if="health">Error</span>
|
|
294
|
+
<button class="btn" @click="refreshAll" :disabled="loading">
|
|
295
|
+
<span v-if="loading" style="display:inline-block;width:14px;height:14px;border:2px solid var(--text2);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite"></span>
|
|
296
|
+
<span v-else>↻ Refresh</span>
|
|
297
|
+
</button>
|
|
298
|
+
<button class="btn" @click="logout">Logout</button>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<!-- Stats -->
|
|
303
|
+
<div class="stats" v-if="health">
|
|
304
|
+
<div class="stat"><div class="val">{{uptimeStr(health.uptime)}}</div><div class="label">Uptime</div></div>
|
|
305
|
+
<div class="stat"><div class="val">{{health.channels?.total||0}}</div><div class="label">Channels</div></div>
|
|
306
|
+
<div class="stat"><div class="val">{{health.models?.count||0}}</div><div class="label">Models</div></div>
|
|
307
|
+
<div class="stat"><div class="val">{{health.tools?.count||0}}</div><div class="label">Tools</div></div>
|
|
308
|
+
<div class="stat"><div class="val">{{health.system?.processMemory||0}}MB</div><div class="label">Memory</div></div>
|
|
309
|
+
<div class="stat"><div class="val">{{fmtNum(stats?.summary?.totalCalls)}}</div><div class="label">Total Calls</div></div>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<!-- Tabs -->
|
|
313
|
+
<div class="tabs">
|
|
314
|
+
<div :class="['tab',tab==='overview'&&'active']" @click="tab='overview'">Overview</div>
|
|
315
|
+
<div :class="['tab',tab==='channels'&&'active']" @click="tab='channels'">Channels</div>
|
|
316
|
+
<div :class="['tab',tab==='tools'&&'active']" @click="tab='tools'">Tools</div>
|
|
317
|
+
<div :class="['tab',tab==='presets'&&'active']" @click="tab='presets'">Presets</div>
|
|
318
|
+
<div :class="['tab',tab==='processors'&&'active']" @click="tab='processors'">Processors</div>
|
|
319
|
+
<div :class="['tab',tab==='triggers'&&'active']" @click="tab='triggers'">Triggers</div>
|
|
320
|
+
<div :class="['tab',tab==='groups'&&'active']" @click="tab='groups'">Groups</div>
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<!-- Overview -->
|
|
324
|
+
<div v-if="tab==='overview'">
|
|
325
|
+
<div class="card" v-if="health">
|
|
326
|
+
<h3><span class="dot" style="background:var(--green)"></span> System</h3>
|
|
327
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;font-size:13px">
|
|
328
|
+
<div><span style="color:var(--text2)">Version:</span> {{health.version}}</div>
|
|
329
|
+
<div><span style="color:var(--text2)">Node:</span> {{health.system?.nodeVersion}}</div>
|
|
330
|
+
<div><span style="color:var(--text2)">Platform:</span> {{health.system?.platform}} {{health.system?.arch}}</div>
|
|
331
|
+
<div><span style="color:var(--text2)">CPUs:</span> {{health.system?.cpus}}</div>
|
|
332
|
+
<div><span style="color:var(--text2)">Heap:</span> {{health.system?.heapUsed}}MB / {{health.system?.processMemory}}MB</div>
|
|
333
|
+
<div><span style="color:var(--text2)">System Mem:</span> {{health.system?.freeMemory}}MB free / {{health.system?.totalMemory}}MB</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<div class="card" v-if="health?.models?.list?.length">
|
|
338
|
+
<h3>Available Models</h3>
|
|
339
|
+
<div><span v-for="m in health.models.list" class="pill model">{{m}}</span></div>
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
<div class="card" v-if="stats">
|
|
343
|
+
<h3>Usage by Model</h3>
|
|
344
|
+
<table class="tbl">
|
|
345
|
+
<tr><th>Model</th><th>Calls</th><th>Tokens</th></tr>
|
|
346
|
+
<tr v-for="(v,k) in stats.perModel" :key="k">
|
|
347
|
+
<td><span class="pill model">{{k}}</span></td>
|
|
348
|
+
<td>{{fmtNum(v.calls)}}</td>
|
|
349
|
+
<td>{{fmtNum(v.tokens)}}</td>
|
|
350
|
+
</tr>
|
|
351
|
+
<tr v-if="!Object.keys(stats.perModel||{}).length"><td colspan="3" style="color:var(--text2)">No usage data</td></tr>
|
|
352
|
+
</table>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<!-- Channels -->
|
|
357
|
+
<div v-if="tab==='channels'">
|
|
358
|
+
<div class="card">
|
|
359
|
+
<h3 style="justify-content:space-between">Channels <button class="btn primary sm" @click="openModal('channel')">+ New</button></h3>
|
|
360
|
+
<table class="tbl">
|
|
361
|
+
<tr><th>Status</th><th>Name</th><th>Adapter</th><th>Models</th><th>Priority</th><th>Calls</th><th>Actions</th></tr>
|
|
362
|
+
<tr v-for="ch in channels" :key="ch.id">
|
|
363
|
+
<td><span :class="['pill',ch.status==='enabled'?'on':'off']">{{ch.status}}</span></td>
|
|
364
|
+
<td>{{ch.name}}</td>
|
|
365
|
+
<td>{{ch.adapterType}}</td>
|
|
366
|
+
<td><span v-for="m in (ch.models||[])" class="pill model">{{m}}</span></td>
|
|
367
|
+
<td>{{ch.priority}}</td>
|
|
368
|
+
<td>{{fmtNum(ch.statistics?.callTimes)}}</td>
|
|
369
|
+
<td>
|
|
370
|
+
<button class="btn sm" @click="testChannel(ch.id)">Test</button>
|
|
371
|
+
<button class="btn sm" @click="openModal('channel',ch)">Edit</button>
|
|
372
|
+
<button class="btn sm danger" @click="deleteItem('channel',ch.id)">Del</button>
|
|
373
|
+
</td>
|
|
374
|
+
</tr>
|
|
375
|
+
<tr v-if="!channels.length"><td colspan="7" style="color:var(--text2)">No channels</td></tr>
|
|
376
|
+
</table>
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
|
|
380
|
+
<!-- Tools -->
|
|
381
|
+
<div v-if="tab==='tools'">
|
|
382
|
+
<div class="card">
|
|
383
|
+
<h3 style="justify-content:space-between">Tools <button class="btn primary sm" @click="openModal('tool')">+ New</button></h3>
|
|
384
|
+
<table class="tbl">
|
|
385
|
+
<tr><th>Name</th><th>Description</th><th>Actions</th></tr>
|
|
386
|
+
<tr v-for="t in tools" :key="t.id">
|
|
387
|
+
<td>{{t.name||t.id}}</td>
|
|
388
|
+
<td style="max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{t.description}}</td>
|
|
389
|
+
<td>
|
|
390
|
+
<button class="btn sm" @click="openModal('tool',t)">Edit</button>
|
|
391
|
+
<button class="btn sm danger" @click="deleteItem('tool',t.id)">Del</button>
|
|
392
|
+
</td>
|
|
393
|
+
</tr>
|
|
394
|
+
<tr v-if="!tools.length"><td colspan="3" style="color:var(--text2)">No tools</td></tr>
|
|
395
|
+
</table>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
|
|
399
|
+
<!-- Presets -->
|
|
400
|
+
<div v-if="tab==='presets'">
|
|
401
|
+
<div class="card">
|
|
402
|
+
<h3 style="justify-content:space-between">Presets <button class="btn primary sm" @click="openModal('preset')">+ New</button></h3>
|
|
403
|
+
<table class="tbl">
|
|
404
|
+
<tr><th>Name</th><th>Prefix</th><th>Model</th><th>Temp</th><th>Actions</th></tr>
|
|
405
|
+
<tr v-for="p in presets" :key="p.id">
|
|
406
|
+
<td>{{p.name}}</td>
|
|
407
|
+
<td><code>{{p.prefix}}</code></td>
|
|
408
|
+
<td><span class="pill model">{{p.sendMessageOption?.model||'-'}}</span></td>
|
|
409
|
+
<td>{{p.sendMessageOption?.temperature}}</td>
|
|
410
|
+
<td>
|
|
411
|
+
<button class="btn sm" @click="openModal('preset',p)">Edit</button>
|
|
412
|
+
<button class="btn sm danger" @click="deleteItem('preset',p.id)">Del</button>
|
|
413
|
+
</td>
|
|
414
|
+
</tr>
|
|
415
|
+
<tr v-if="!presets.length"><td colspan="5" style="color:var(--text2)">No presets</td></tr>
|
|
416
|
+
</table>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
|
|
420
|
+
<!-- Processors -->
|
|
421
|
+
<div v-if="tab==='processors'">
|
|
422
|
+
<div class="card">
|
|
423
|
+
<h3 style="justify-content:space-between">Processors <button class="btn primary sm" @click="openModal('processor')">+ New</button></h3>
|
|
424
|
+
<table class="tbl">
|
|
425
|
+
<tr><th>Name</th><th>Type</th><th>Description</th><th>Actions</th></tr>
|
|
426
|
+
<tr v-for="p in processors" :key="p.id">
|
|
427
|
+
<td>{{p.name}}</td>
|
|
428
|
+
<td><span class="pill" :style="{background:p.type==='pre'?'rgba(167,139,250,.15)':'rgba(250,204,21,.15)',color:p.type==='pre'?'var(--purple)':'var(--yellow)'}">{{p.type}}</span></td>
|
|
429
|
+
<td>{{p.description}}</td>
|
|
430
|
+
<td>
|
|
431
|
+
<button class="btn sm" @click="openModal('processor',p)">Edit</button>
|
|
432
|
+
<button class="btn sm danger" @click="deleteItem('processor',p.id)">Del</button>
|
|
433
|
+
</td>
|
|
434
|
+
</tr>
|
|
435
|
+
<tr v-if="!processors.length"><td colspan="4" style="color:var(--text2)">No processors</td></tr>
|
|
436
|
+
</table>
|
|
437
|
+
</div>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
<!-- Triggers -->
|
|
441
|
+
<div v-if="tab==='triggers'">
|
|
442
|
+
<div class="card">
|
|
443
|
+
<h3 style="justify-content:space-between">Triggers <button class="btn primary sm" @click="openModal('trigger')">+ New</button></h3>
|
|
444
|
+
<table class="tbl">
|
|
445
|
+
<tr><th>Name</th><th>Description</th><th>Actions</th></tr>
|
|
446
|
+
<tr v-for="t in triggers" :key="t.id">
|
|
447
|
+
<td>{{t.name||t.id}}</td>
|
|
448
|
+
<td>{{t.description}}</td>
|
|
449
|
+
<td>
|
|
450
|
+
<button class="btn sm" @click="openModal('trigger',t)">Edit</button>
|
|
451
|
+
<button class="btn sm danger" @click="deleteItem('trigger',t.id)">Del</button>
|
|
452
|
+
</td>
|
|
453
|
+
</tr>
|
|
454
|
+
<tr v-if="!triggers.length"><td colspan="3" style="color:var(--text2)">No triggers</td></tr>
|
|
455
|
+
</table>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
|
|
459
|
+
<!-- Tool Groups -->
|
|
460
|
+
<div v-if="tab==='groups'">
|
|
461
|
+
<div class="card">
|
|
462
|
+
<h3 style="justify-content:space-between">Tool Groups <button class="btn primary sm" @click="openModal('toolGroup')">+ New</button></h3>
|
|
463
|
+
<table class="tbl">
|
|
464
|
+
<tr><th>Name</th><th>Description</th><th>Default</th><th>Actions</th></tr>
|
|
465
|
+
<tr v-for="g in toolGroups" :key="g.id">
|
|
466
|
+
<td>{{g.name}}</td>
|
|
467
|
+
<td>{{g.description}}</td>
|
|
468
|
+
<td><span v-if="g.isDefault" class="pill on">Default</span></td>
|
|
469
|
+
<td>
|
|
470
|
+
<button class="btn sm" @click="openModal('toolGroup',g)">Edit</button>
|
|
471
|
+
<button class="btn sm danger" @click="deleteItem('toolGroup',g.id)">Del</button>
|
|
472
|
+
</td>
|
|
473
|
+
</tr>
|
|
474
|
+
<tr v-if="!toolGroups.length"><td colspan="4" style="color:var(--text2)">No groups</td></tr>
|
|
475
|
+
</table>
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
|
|
479
|
+
<!-- Modal -->
|
|
480
|
+
<div v-if="modal" class="modal-mask" @click.self="closeModal">
|
|
481
|
+
<div class="modal">
|
|
482
|
+
<h3>{{editing?'Edit':'New'}} {{modal}}</h3>
|
|
483
|
+
|
|
484
|
+
<!-- Channel form -->
|
|
485
|
+
<template v-if="modal==='channel'">
|
|
486
|
+
<div class="form-row"><label>Name</label><input v-model="form.name"/></div>
|
|
487
|
+
<div class="form-row"><label>Adapter Type</label><select v-model="form.adapterType"><option>openai</option><option>gemini</option><option>claude</option></select></div>
|
|
488
|
+
<div class="form-row"><label>Models (comma separated)</label><input v-model="form._models" :placeholder="(form.models||[]).join(', ')"/></div>
|
|
489
|
+
<div class="form-row"><label>Base URL</label><input v-model="form.options.baseUrl"/></div>
|
|
490
|
+
<div class="form-row"><label>API Key</label><input v-model="form.options.apiKey" type="password"/></div>
|
|
491
|
+
<div class="form-row"><label>Priority</label><input v-model.number="form.priority" type="number"/></div>
|
|
492
|
+
<div class="form-row"><label>Weight</label><input v-model.number="form.weight" type="number"/></div>
|
|
493
|
+
<div class="form-row"><label>Status</label><select v-model="form.status"><option>enabled</option><option>disabled</option></select></div>
|
|
494
|
+
</template>
|
|
495
|
+
|
|
496
|
+
<!-- Preset form -->
|
|
497
|
+
<template v-if="modal==='preset'">
|
|
498
|
+
<div class="form-row"><label>Name</label><input v-model="form.name"/></div>
|
|
499
|
+
<div class="form-row"><label>Prefix</label><input v-model="form.prefix"/></div>
|
|
500
|
+
<div class="form-row"><label>Model</label><input v-model="form.sendMessageOption.model"/></div>
|
|
501
|
+
<div class="form-row"><label>Temperature</label><input v-model.number="form.sendMessageOption.temperature" type="number" step="0.1"/></div>
|
|
502
|
+
<div class="form-row"><label>Max Token</label><input v-model.number="form.sendMessageOption.maxToken" type="number"/></div>
|
|
503
|
+
<div class="form-row"><label>System Prompt</label><textarea v-model="form.sendMessageOption.systemOverride"></textarea></div>
|
|
504
|
+
</template>
|
|
505
|
+
|
|
506
|
+
<!-- Tool / Processor / Trigger form -->
|
|
507
|
+
<template v-if="modal==='tool'||modal==='processor'||modal==='trigger'">
|
|
508
|
+
<div class="form-row"><label>Name</label><input v-model="form.name"/></div>
|
|
509
|
+
<div class="form-row" v-if="modal==='processor'"><label>Type</label><select v-model="form.type"><option>pre</option><option>post</option></select></div>
|
|
510
|
+
<div class="form-row"><label>Description</label><input v-model="form.description"/></div>
|
|
511
|
+
<div class="form-row"><label>Code</label><textarea v-model="form.code" style="min-height:200px;font-family:monospace;font-size:12px"></textarea></div>
|
|
512
|
+
</template>
|
|
513
|
+
|
|
514
|
+
<!-- Tool Group form -->
|
|
515
|
+
<template v-if="modal==='toolGroup'">
|
|
516
|
+
<div class="form-row"><label>Name</label><input v-model="form.name"/></div>
|
|
517
|
+
<div class="form-row"><label>Description</label><input v-model="form.description"/></div>
|
|
518
|
+
<div class="form-row"><label>Tool IDs (comma separated)</label><input v-model="form._toolIds" :placeholder="(form.toolIds||[]).join(', ')"/></div>
|
|
519
|
+
<div class="form-row"><label>Status</label><select v-model="form.status"><option>enabled</option><option>disabled</option></select></div>
|
|
520
|
+
</template>
|
|
521
|
+
|
|
522
|
+
<div class="modal-btns">
|
|
523
|
+
<button class="btn" @click="closeModal">Cancel</button>
|
|
524
|
+
<button class="btn primary" @click="saveItem">Save</button>
|
|
525
|
+
</div>
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
</div>
|
|
529
|
+
`
|
|
530
|
+
}).mount('#app')
|
|
531
|
+
</script>
|
|
532
|
+
</body>
|
|
18
533
|
</html>
|