agentgui 1.0.919 → 1.0.920
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/CHANGELOG.md +5 -0
- package/package.json +1 -1
- package/site/app/index.html +14 -35
- package/site/app/js/app.js +132 -93
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## [Unreleased] - site/app live client migrated to 247420 SDK components
|
|
2
|
+
|
|
3
|
+
- site/app/index.html: removed 35 lines of bespoke CSS overriding design tokens; now uses SDK panel/font tokens with only structural overrides (chat flex layout, history pane grid)
|
|
4
|
+
- site/app/js/app.js: replaced raw h() + applyDiff loop with AppShell, Topbar, Crumb, Side, Chat, ChatComposer, Row, Status from anentrypoint-design; uses mount() so motion system fires; font resolves to Nunito via SDK
|
|
5
|
+
|
|
1
6
|
## [1.0.908] - manual audit fixes: server retry idempotency, cli-version detection, cleanup FK ordering, refresh-all path
|
|
2
7
|
|
|
3
8
|
- server.js: `onServerListenStart` one-shot guard prevents `onServerReady` + `loadPluginExtensions` from re-firing when EADDRINUSE retry succeeds — previously every retry re-ran autoProvision/installGMAgentConfigs/setIntervals causing 100+ duplicate provision passes and stacked timers
|
package/package.json
CHANGED
package/site/app/index.html
CHANGED
|
@@ -12,41 +12,20 @@
|
|
|
12
12
|
} }
|
|
13
13
|
</script>
|
|
14
14
|
<style>
|
|
15
|
-
html,body { margin:0; height:100%; }
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.
|
|
19
|
-
.
|
|
20
|
-
.
|
|
21
|
-
.
|
|
22
|
-
.
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
.
|
|
28
|
-
.
|
|
29
|
-
.pill { display:inline-block; padding:1px 7px; border-radius:999px; background: rgba(255,255,255,.1); font-size:11px; margin-left:6px; }
|
|
30
|
-
.pill.ok { background: rgba(80,200,120,.18); color:#7fd; }
|
|
31
|
-
.pill.bad { background: rgba(255,80,80,.18); color:#f88; }
|
|
32
|
-
.row { display:block; padding:8px 10px; border-radius:6px; cursor:pointer; }
|
|
33
|
-
.row:hover { background: rgba(255,255,255,.04); }
|
|
34
|
-
.row.active { background: rgba(255,255,255,.08); }
|
|
35
|
-
.row .t { font-size:13px; }
|
|
36
|
-
.row .s { font-size:11px; opacity:.6; margin-top:2px; font-family: var(--ff-mono, ui-monospace, monospace); }
|
|
37
|
-
.ev { padding:6px 0; border-bottom: 1px dashed rgba(255,255,255,.06); font-family: var(--ff-mono, ui-monospace, monospace); font-size:12px; }
|
|
38
|
-
.ev .h { opacity:.55; margin-bottom:2px; }
|
|
39
|
-
.ev pre { white-space: pre-wrap; margin:0; }
|
|
40
|
-
.chat-host { height:100%; display:flex; flex-direction:column; }
|
|
41
|
-
.chat-msgs { flex:1; overflow:auto; padding:10px 4px; }
|
|
42
|
-
.msg { padding:8px 10px; border-radius:8px; margin:6px 0; max-width:80ch; }
|
|
43
|
-
.msg.user { background: rgba(120,180,255,.08); }
|
|
44
|
-
.msg.assistant { background: rgba(255,255,255,.04); }
|
|
45
|
-
.msg .role { font-size:11px; opacity:.6; margin-bottom:4px; text-transform:uppercase; letter-spacing:.08em; }
|
|
46
|
-
.composer { display:flex; gap:8px; padding:8px; border-top:1px solid rgba(255,255,255,.08); }
|
|
47
|
-
.composer textarea { flex:1; min-height:48px; max-height:200px; resize:vertical; padding:8px 10px; border-radius:6px; border:1px solid rgba(255,255,255,.12); background:rgba(255,255,255,.04); color:inherit; font:inherit; }
|
|
48
|
-
.composer button { padding:8px 16px; border-radius:6px; border:1px solid rgba(255,255,255,.15); background: rgba(255,255,255,.06); color:inherit; cursor:pointer; }
|
|
49
|
-
.composer select { padding:6px 8px; border-radius:6px; border:1px solid rgba(255,255,255,.12); background:rgba(255,255,255,.04); color:inherit; font:inherit; }
|
|
15
|
+
html, body { margin: 0; height: 100%; }
|
|
16
|
+
#app { height: 100vh; }
|
|
17
|
+
.app { height: 100vh; }
|
|
18
|
+
.app-main { display: flex; flex-direction: column; min-height: 0; }
|
|
19
|
+
.chat { flex: 1; display: flex; flex-direction: column; min-height: 0; }
|
|
20
|
+
.chat-thread { flex: 1; overflow-y: auto; }
|
|
21
|
+
.agentgui-history-pane { display: grid; grid-template-columns: 300px 1fr; height: 100%; min-height: 0; overflow: hidden; }
|
|
22
|
+
.agentgui-history-list { overflow-y: auto; border-right: 1px solid var(--panel-3); padding: 8px; }
|
|
23
|
+
.agentgui-history-detail { overflow-y: auto; padding: 16px; }
|
|
24
|
+
.agentgui-ev { padding: 6px 0; border-bottom: 1px solid var(--panel-2); font-family: var(--ff-mono); font-size: 12px; }
|
|
25
|
+
.agentgui-ev .h { opacity: .55; margin-bottom: 2px; }
|
|
26
|
+
.agentgui-ev pre { white-space: pre-wrap; margin: 0; }
|
|
27
|
+
.agentgui-model-bar { display: flex; gap: 8px; padding: 8px 16px; border-bottom: 1px solid var(--panel-3); align-items: center; }
|
|
28
|
+
.agentgui-model-bar select { padding: 5px 8px; border-radius: 8px; border: 1px solid var(--panel-3); background: var(--panel-1); color: var(--ink); font-family: var(--ff-ui); font-size: 13px; }
|
|
50
29
|
</style>
|
|
51
30
|
</head>
|
|
52
31
|
<body>
|
package/site/app/js/app.js
CHANGED
|
@@ -1,89 +1,157 @@
|
|
|
1
|
-
import { h,
|
|
1
|
+
import { h, mount, installStyles, components as C } from 'anentrypoint-design';
|
|
2
2
|
import * as B from './backend.js';
|
|
3
|
+
|
|
3
4
|
installStyles().catch(() => {});
|
|
4
5
|
|
|
6
|
+
const { AppShell, Topbar, Crumb, Side, Status, Chip, Btn } = C;
|
|
7
|
+
const { Chat, ChatComposer, ChatMessage } = C;
|
|
8
|
+
const { Row } = C;
|
|
9
|
+
|
|
5
10
|
const state = {
|
|
6
11
|
backend: B.getBackend(),
|
|
7
12
|
health: { status: 'unknown' },
|
|
8
13
|
tab: 'chat',
|
|
9
14
|
models: [],
|
|
10
15
|
selectedModel: '',
|
|
11
|
-
// chat
|
|
12
16
|
chat: { messages: [], busy: false, abort: null, draft: '' },
|
|
13
|
-
// history
|
|
14
17
|
sessions: [],
|
|
15
18
|
selectedSid: null,
|
|
16
19
|
events: [],
|
|
17
20
|
searchQ: '',
|
|
18
21
|
searchHits: null,
|
|
19
|
-
liveTail: [],
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
let render;
|
|
23
25
|
|
|
24
|
-
function
|
|
26
|
+
function topbarItems() {
|
|
27
|
+
return [['chat', '#'], ['history', '#'], ['settings', '#']];
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
function view() {
|
|
27
|
-
|
|
31
|
+
const ok = state.health.status === 'ok';
|
|
32
|
+
const statusChip = ok
|
|
33
|
+
? h('span', { key: 'hc', style: 'color:var(--live);font-size:12px' }, '● connected')
|
|
34
|
+
: h('span', { key: 'hc', style: 'color:var(--flame);font-size:12px' }, '○ offline');
|
|
35
|
+
|
|
36
|
+
const topbar = Topbar({
|
|
37
|
+
brand: 'agentgui',
|
|
38
|
+
leaf: state.tab,
|
|
39
|
+
items: topbarItems(),
|
|
40
|
+
active: state.tab,
|
|
41
|
+
onNav: (label) => { state.tab = label; if (label === 'history') refreshHistory(); render(); },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const crumb = Crumb({
|
|
45
|
+
trail: ['agentgui'],
|
|
46
|
+
leaf: state.tab,
|
|
47
|
+
right: [statusChip],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const side = state.tab === 'history' ? historySide() : null;
|
|
51
|
+
const main = mainContent();
|
|
52
|
+
const status = Status({
|
|
53
|
+
left: [state.backend],
|
|
54
|
+
right: [state.selectedModel || 'no model'],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return AppShell({ topbar, crumb, side, main, status });
|
|
28
58
|
}
|
|
29
59
|
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
function mainContent() {
|
|
61
|
+
if (state.tab === 'chat') return chatMain();
|
|
62
|
+
if (state.tab === 'history') return historyDetail();
|
|
63
|
+
return settingsMain();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function chatMain() {
|
|
67
|
+
const msgs = state.chat.messages.map((m, i) => ({
|
|
68
|
+
key: i,
|
|
69
|
+
who: m.role === 'user' ? 'you' : 'them',
|
|
70
|
+
text: m.content || '',
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
const composer = ChatComposer({
|
|
74
|
+
value: state.chat.draft,
|
|
75
|
+
disabled: state.chat.busy,
|
|
76
|
+
placeholder: state.selectedModel ? 'message…' : 'choose a model first',
|
|
77
|
+
onInput: (v) => { state.chat.draft = v; render(); },
|
|
78
|
+
onSend: (v) => { state.chat.draft = v; sendChat(); },
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const modelBar = h('div', { class: 'agentgui-model-bar' },
|
|
82
|
+
h('select', { onchange: (e) => { state.selectedModel = e.target.value; render(); } },
|
|
83
|
+
h('option', { value: '' }, '— choose model —'),
|
|
84
|
+
...state.models.map(m => h('option', { value: m.id, selected: m.id === state.selectedModel }, m.id)),
|
|
41
85
|
),
|
|
86
|
+
state.chat.busy
|
|
87
|
+
? h('a', { style: 'cursor:pointer;font-size:12px;color:var(--flame)', onclick: cancelChat }, 'stop ✕')
|
|
88
|
+
: null,
|
|
42
89
|
);
|
|
43
|
-
}
|
|
44
90
|
|
|
45
|
-
|
|
46
|
-
|
|
91
|
+
return [
|
|
92
|
+
modelBar,
|
|
93
|
+
Chat({ title: 'chat', sub: state.selectedModel || undefined, messages: msgs, composer }),
|
|
94
|
+
];
|
|
47
95
|
}
|
|
48
96
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
97
|
+
function historySide() {
|
|
98
|
+
const rows = state.searchHits
|
|
99
|
+
? state.searchHits.results.slice(0, 30).map((r, i) =>
|
|
100
|
+
Row({
|
|
101
|
+
key: 'sr' + i,
|
|
102
|
+
title: r.snippet || '(no snippet)',
|
|
103
|
+
sub: r.project + ' · ' + r.role + (r.tool ? ' · ' + r.tool : ''),
|
|
104
|
+
active: false,
|
|
105
|
+
onClick: () => loadSession(r.sid),
|
|
106
|
+
})
|
|
107
|
+
)
|
|
108
|
+
: state.sessions.slice(0, 80).map((s, i) =>
|
|
109
|
+
Row({
|
|
110
|
+
key: 'sess' + i,
|
|
111
|
+
title: s.title || s.project || s.sid,
|
|
112
|
+
sub: s.events + ' ev · ' + s.tools + ' tools · ' + (s.errors ? s.errors + ' err' : 'ok'),
|
|
113
|
+
active: s.sid === state.selectedSid,
|
|
114
|
+
onClick: () => loadSession(s.sid),
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return h('div', { style: 'display:flex;flex-direction:column;height:100%;overflow:hidden' },
|
|
119
|
+
h('div', { style: 'padding:8px' },
|
|
120
|
+
h('input', {
|
|
121
|
+
style: 'width:100%;padding:6px 10px;border-radius:8px;border:1px solid var(--panel-3);background:var(--panel-1);color:var(--ink);font-family:var(--ff-ui);font-size:13px',
|
|
122
|
+
placeholder: 'search…',
|
|
123
|
+
value: state.searchQ,
|
|
124
|
+
onchange: (e) => { state.searchQ = e.target.value; runSearch(); },
|
|
125
|
+
}),
|
|
126
|
+
),
|
|
127
|
+
h('div', { style: 'flex:1;overflow-y:auto;padding:8px' }, ...rows,
|
|
128
|
+
rows.length === 0 ? h('div', { style: 'opacity:.6;padding:16px;font-size:13px' }, 'no sessions') : null,
|
|
129
|
+
),
|
|
130
|
+
);
|
|
53
131
|
}
|
|
54
132
|
|
|
55
|
-
function
|
|
56
|
-
return h('div', {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
133
|
+
function historyDetail() {
|
|
134
|
+
if (!state.selectedSid) return h('div', { style: 'padding:24px;opacity:.6' }, 'pick a session');
|
|
135
|
+
if (state.events.length === 0) return h('div', { style: 'padding:24px;opacity:.6' }, 'loading…');
|
|
136
|
+
return h('div', {},
|
|
137
|
+
...state.events.map((e, i) =>
|
|
138
|
+
h('div', { key: 'ev' + i, class: 'agentgui-ev' },
|
|
139
|
+
h('div', { class: 'h' },
|
|
140
|
+
new Date(e.ts).toLocaleString() + ' · ' + (e.role || '?') + ' · ' + (e.type || '?') + (e.tool ? ' · ' + e.tool : ''),
|
|
141
|
+
),
|
|
142
|
+
h('pre', {}, (e.text || '').slice(0, 4000)),
|
|
143
|
+
)
|
|
144
|
+
),
|
|
61
145
|
);
|
|
62
146
|
}
|
|
63
147
|
|
|
64
|
-
function
|
|
65
|
-
return h('div', {
|
|
66
|
-
h('
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
)),
|
|
72
|
-
state.chat.messages.length === 0 ? h('div', { class: 'empty' }, 'pick a model and start chatting') : null,
|
|
73
|
-
),
|
|
74
|
-
h('div', { class: 'composer' },
|
|
75
|
-
h('select', { onchange: e => { state.selectedModel = e.target.value; render(); } },
|
|
76
|
-
h('option', { value: '' }, '— choose model —'),
|
|
77
|
-
...state.models.map(m => h('option', { value: m.id, selected: m.id === state.selectedModel }, m.id)),
|
|
78
|
-
),
|
|
79
|
-
h('textarea', {
|
|
80
|
-
placeholder: 'message…',
|
|
81
|
-
value: state.chat.draft,
|
|
82
|
-
oninput: e => { state.chat.draft = e.target.value; },
|
|
83
|
-
onkeydown: e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendChat(); } },
|
|
84
|
-
}),
|
|
85
|
-
h('button', { onclick: () => state.chat.busy ? cancelChat() : sendChat() }, state.chat.busy ? 'stop' : 'send'),
|
|
86
|
-
),
|
|
148
|
+
function settingsMain() {
|
|
149
|
+
return h('div', { style: 'padding:24px;max-width:480px' },
|
|
150
|
+
h('h2', {}, 'settings'),
|
|
151
|
+
h('p', {}, 'backend: ', h('code', {}, state.backend)),
|
|
152
|
+
h('p', {}, 'health: ', h('code', {}, JSON.stringify(state.health))),
|
|
153
|
+
h('p', { style: 'opacity:.7;font-size:13px' },
|
|
154
|
+
'pass ', h('code', {}, '?backend=https://your-acptoapi-host'), ' or set localStorage[\'agentgui.backend\'] to override',
|
|
87
155
|
),
|
|
88
156
|
);
|
|
89
157
|
}
|
|
@@ -119,42 +187,9 @@ async function sendChat() {
|
|
|
119
187
|
|
|
120
188
|
function cancelChat() { state.chat.abort?.abort(); }
|
|
121
189
|
|
|
122
|
-
function historyView() {
|
|
123
|
-
return h('div', { class: 'body' },
|
|
124
|
-
h('div', { class: 'side' },
|
|
125
|
-
h('input', { class: 'search', placeholder: 'search…', value: state.searchQ, onchange: e => { state.searchQ = e.target.value; runSearch(); } }),
|
|
126
|
-
state.searchHits ? h('div', {},
|
|
127
|
-
h('div', { style: 'font-size:11px;opacity:.6;padding:6px 10px' }, state.searchHits.results.length + ' hits for "' + state.searchHits.query + '"'),
|
|
128
|
-
...state.searchHits.results.slice(0, 30).map(r => h('div', { class: 'row', onclick: () => loadSession(r.sid) },
|
|
129
|
-
h('div', { class: 't' }, r.snippet || '(no snippet)'),
|
|
130
|
-
h('div', { class: 's' }, r.project + ' · ' + r.role + (r.tool ? ' · ' + r.tool : '')),
|
|
131
|
-
)),
|
|
132
|
-
) : h('div', {},
|
|
133
|
-
...state.sessions.slice(0, 80).map(s => h('div', { class: 'row' + (s.sid === state.selectedSid ? ' active' : ''), onclick: () => loadSession(s.sid) },
|
|
134
|
-
h('div', { class: 't' }, s.title || s.project || s.sid),
|
|
135
|
-
h('div', { class: 's' }, s.events + ' ev · ' + s.tools + ' tools · ' + (s.errors ? s.errors + ' err' : 'ok')),
|
|
136
|
-
)),
|
|
137
|
-
state.sessions.length === 0 ? h('div', { class: 'empty' }, 'no sessions yet') : null,
|
|
138
|
-
),
|
|
139
|
-
),
|
|
140
|
-
h('div', { class: 'main' },
|
|
141
|
-
state.events.length === 0
|
|
142
|
-
? h('div', { class: 'empty' }, state.selectedSid ? 'loading…' : 'pick a session')
|
|
143
|
-
: h('div', {},
|
|
144
|
-
...state.events.map(e => h('div', { class: 'ev' },
|
|
145
|
-
h('div', { class: 'h' }, new Date(e.ts).toLocaleString() + ' · ' + (e.role || '?') + ' · ' + (e.type || '?') + (e.tool ? ' · ' + e.tool : '')),
|
|
146
|
-
h('pre', {}, (e.text || '').slice(0, 4000)),
|
|
147
|
-
)),
|
|
148
|
-
),
|
|
149
|
-
),
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
190
|
async function refreshHistory() {
|
|
154
|
-
try {
|
|
155
|
-
|
|
156
|
-
render();
|
|
157
|
-
} catch (e) { console.warn('history fetch failed:', e.message); }
|
|
191
|
+
try { state.sessions = await B.listSessions(state.backend); render(); }
|
|
192
|
+
catch (e) { console.warn('history fetch failed:', e.message); }
|
|
158
193
|
}
|
|
159
194
|
|
|
160
195
|
async function runSearch() {
|
|
@@ -170,12 +205,16 @@ async function loadSession(sid) {
|
|
|
170
205
|
}
|
|
171
206
|
|
|
172
207
|
async function init() {
|
|
173
|
-
state.health = await B.probeBackend(state.backend)
|
|
208
|
+
state.health = await B.probeBackend(state.backend)
|
|
209
|
+
.then(r => r.ok ? { status: 'ok', ...r.info } : { status: 'down', ...r });
|
|
174
210
|
render();
|
|
175
|
-
try {
|
|
176
|
-
|
|
177
|
-
|
|
211
|
+
try {
|
|
212
|
+
state.models = await B.listModels(state.backend);
|
|
213
|
+
if (!state.selectedModel && state.models[0]) state.selectedModel = state.models[0].id;
|
|
214
|
+
render();
|
|
215
|
+
} catch (e) { console.warn('models fetch failed:', e.message); }
|
|
178
216
|
}
|
|
179
217
|
|
|
218
|
+
render = mount(document.getElementById('app'), view);
|
|
180
219
|
window.__agentgui = { state, render };
|
|
181
220
|
init();
|