antri_cli 1.26.0 → 1.27.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -60
  3. package/assets/banner.png +0 -0
  4. package/dist/cli/dialogs/providerPicker.d.ts.map +1 -1
  5. package/dist/cli/dialogs/providerPicker.js +42 -14
  6. package/dist/cli/dialogs/providerPicker.js.map +1 -1
  7. package/dist/cli/promptToolkit.d.ts.map +1 -1
  8. package/dist/cli/promptToolkit.js +1 -0
  9. package/dist/cli/promptToolkit.js.map +1 -1
  10. package/dist/cli/shortcuts.d.ts.map +1 -1
  11. package/dist/cli/shortcuts.js +7 -0
  12. package/dist/cli/shortcuts.js.map +1 -1
  13. package/dist/core/config.d.ts.map +1 -1
  14. package/dist/core/config.js +39 -3
  15. package/dist/core/config.js.map +1 -1
  16. package/dist/core/updater.d.ts +1 -1
  17. package/dist/core/updater.js +1 -1
  18. package/dist/desktop/public/app.js +459 -0
  19. package/dist/desktop/public/index.html +318 -0
  20. package/dist/desktop/public/style.css +812 -0
  21. package/dist/desktop/server.d.ts +11 -0
  22. package/dist/desktop/server.d.ts.map +1 -0
  23. package/dist/desktop/server.js +315 -0
  24. package/dist/desktop/server.js.map +1 -0
  25. package/dist/index.js +16 -2
  26. package/dist/index.js.map +1 -1
  27. package/dist/memory/episodic.d.ts +1 -0
  28. package/dist/memory/episodic.d.ts.map +1 -1
  29. package/dist/memory/episodic.js +3 -0
  30. package/dist/memory/episodic.js.map +1 -1
  31. package/dist/memory/manager.d.ts +6 -0
  32. package/dist/memory/manager.d.ts.map +1 -1
  33. package/dist/memory/manager.js +8 -0
  34. package/dist/memory/manager.js.map +1 -1
  35. package/dist/providers/index.d.ts.map +1 -1
  36. package/dist/providers/index.js +35 -7
  37. package/dist/providers/index.js.map +1 -1
  38. package/dist/providers/models.d.ts.map +1 -1
  39. package/dist/providers/models.js +198 -2
  40. package/dist/providers/models.js.map +1 -1
  41. package/dist/types.d.ts +9 -1
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +10 -4
@@ -0,0 +1,459 @@
1
+ // ANTRI Desktop Control Plane Client Engine
2
+
3
+ let currentConfig = null;
4
+ let activeTab = 'chat';
5
+
6
+ // Initialize on Load
7
+ document.addEventListener('DOMContentLoaded', async () => {
8
+ await loadStatus();
9
+ await loadProfiles();
10
+ await loadSkills();
11
+ await loadMemory();
12
+ });
13
+
14
+ // Load System Status & Config
15
+ async function loadStatus() {
16
+ try {
17
+ const res = await fetch('/api/status');
18
+ const data = await res.json();
19
+ currentConfig = data.config;
20
+
21
+ // Update Mode buttons
22
+ switchMode(currentConfig.mode || 'vibe', false);
23
+
24
+ // Update Perms badge
25
+ updatePermsBadge(currentConfig.alwaysAllow);
26
+
27
+ // Update Provider selector
28
+ const provSelect = document.getElementById('select-provider');
29
+ if (provSelect) provSelect.value = currentConfig.provider;
30
+
31
+ await loadModels();
32
+ } catch (err) {
33
+ console.error('Failed to load status:', err);
34
+ }
35
+ }
36
+
37
+ // Load Models for Provider
38
+ async function loadModels() {
39
+ try {
40
+ const res = await fetch('/api/models');
41
+ const data = await res.json();
42
+ const modelSelect = document.getElementById('select-model');
43
+ modelSelect.innerHTML = '';
44
+
45
+ data.models.forEach((m) => {
46
+ const opt = document.createElement('option');
47
+ opt.value = m.id;
48
+ opt.textContent = `${m.name} (${m.category})`;
49
+ if (m.id === currentConfig.model) opt.selected = true;
50
+ modelSelect.appendChild(opt);
51
+ });
52
+ } catch (err) {
53
+ console.error('Failed to load models:', err);
54
+ }
55
+ }
56
+
57
+ // Switch Mode (Plan / Vibe)
58
+ async function switchMode(mode, triggerSave = true) {
59
+ const btnVibe = document.getElementById('btn-mode-vibe');
60
+ const btnPlan = document.getElementById('btn-mode-plan');
61
+
62
+ if (mode === 'plan') {
63
+ btnPlan.classList.add('active');
64
+ btnVibe.classList.remove('active');
65
+ } else {
66
+ btnVibe.classList.add('active');
67
+ btnPlan.classList.remove('active');
68
+ }
69
+
70
+ if (triggerSave && currentConfig) {
71
+ currentConfig.mode = mode;
72
+ await fetch('/api/config', {
73
+ method: 'POST',
74
+ headers: { 'Content-Type': 'application/json' },
75
+ body: JSON.stringify({ mode }),
76
+ });
77
+ }
78
+ }
79
+
80
+ // Toggle Always-Allow Permissions
81
+ async function toggleAlwaysAllow() {
82
+ if (!currentConfig) return;
83
+ const next = !currentConfig.alwaysAllow;
84
+ currentConfig.alwaysAllow = next;
85
+ updatePermsBadge(next);
86
+
87
+ await fetch('/api/config', {
88
+ method: 'POST',
89
+ headers: { 'Content-Type': 'application/json' },
90
+ body: JSON.stringify({ alwaysAllow: next }),
91
+ });
92
+ }
93
+
94
+ function updatePermsBadge(alwaysAllow) {
95
+ const badge = document.getElementById('perms-text');
96
+ if (alwaysAllow) {
97
+ badge.textContent = 'Always-Allow';
98
+ } else {
99
+ badge.textContent = 'Ask-First';
100
+ }
101
+ }
102
+
103
+ // Provider & Model Handlers
104
+ async function onProviderChange(provider) {
105
+ await fetch('/api/config', {
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify({ provider }),
109
+ });
110
+ await loadStatus();
111
+ }
112
+
113
+ async function onModelChange(model) {
114
+ await fetch('/api/config', {
115
+ method: 'POST',
116
+ headers: { 'Content-Type': 'application/json' },
117
+ body: JSON.stringify({ model }),
118
+ });
119
+ }
120
+
121
+ // Tab Switching
122
+ function showTab(tabName) {
123
+ activeTab = tabName;
124
+ document.querySelectorAll('.tab-panel').forEach((el) => el.classList.remove('active'));
125
+ document.querySelectorAll('.nav-item').forEach((el) => el.classList.remove('active'));
126
+
127
+ const targetPanel = document.getElementById(`tab-${tabName}`);
128
+ if (targetPanel) targetPanel.classList.add('active');
129
+
130
+ const navIndex = ['chat', 'dialectic', 'goal', 'profiles', 'skills', 'memory'].indexOf(tabName);
131
+ const navButtons = document.querySelectorAll('.nav-item');
132
+ if (navButtons[navIndex]) navButtons[navIndex].classList.add('active');
133
+ }
134
+
135
+ // Chat Prompt Submission with SSE Streaming
136
+ async function submitPrompt() {
137
+ const input = document.getElementById('prompt-input');
138
+ const prompt = input.value.trim();
139
+ if (!prompt) return;
140
+
141
+ input.value = '';
142
+
143
+ // Intercept /debate or /goal inside chat
144
+ if (prompt.startsWith('/debate')) {
145
+ showTab('dialectic');
146
+ document.getElementById('debate-query-input').value = prompt.replace('/debate', '').trim();
147
+ startDebate();
148
+ return;
149
+ }
150
+ if (prompt.startsWith('/goal') || prompt.startsWith('/loop')) {
151
+ showTab('goal');
152
+ document.getElementById('goal-objective-input').value = prompt.replace(/^\/(goal|loop)/, '').trim();
153
+ startGoalLoop();
154
+ return;
155
+ }
156
+
157
+ appendMessage('user', prompt);
158
+
159
+ const assistantMsgEl = appendMessage('assistant', '');
160
+ const contentEl = assistantMsgEl.querySelector('.msg-content');
161
+
162
+ const sendBtn = document.getElementById('send-btn');
163
+ sendBtn.disabled = true;
164
+ sendBtn.textContent = 'Thinking...';
165
+
166
+ try {
167
+ const response = await fetch('/api/chat', {
168
+ method: 'POST',
169
+ headers: { 'Content-Type': 'application/json' },
170
+ body: JSON.stringify({ prompt }),
171
+ });
172
+
173
+ const reader = response.body.getReader();
174
+ const decoder = new TextDecoder();
175
+ let accumulated = '';
176
+
177
+ while (true) {
178
+ const { done, value } = await reader.read();
179
+ if (done) break;
180
+
181
+ const chunk = decoder.decode(value);
182
+ const lines = chunk.split('\n');
183
+
184
+ for (const line of lines) {
185
+ if (line.startsWith('data: ')) {
186
+ try {
187
+ const data = JSON.parse(line.slice(6));
188
+ if (data.token) {
189
+ accumulated += data.token;
190
+ contentEl.textContent = accumulated;
191
+ scrollToBottom();
192
+ } else if (data.name && data.arguments) {
193
+ // Tool call badge
194
+ const toolBadge = document.createElement('div');
195
+ toolBadge.className = 'tool-badge-pill';
196
+ toolBadge.textContent = `Tool: ${data.name}`;
197
+ assistantMsgEl.insertBefore(toolBadge, contentEl);
198
+ }
199
+ } catch (e) {}
200
+ }
201
+ }
202
+ }
203
+ } catch (err) {
204
+ contentEl.textContent = `Error: ${err.message}`;
205
+ } finally {
206
+ sendBtn.disabled = false;
207
+ sendBtn.innerHTML = '<span>Send</span>';
208
+ }
209
+ }
210
+
211
+ function appendMessage(role, text) {
212
+ const container = document.getElementById('chat-messages');
213
+ const row = document.createElement('div');
214
+ row.className = `msg-row ${role}`;
215
+ const content = document.createElement('div');
216
+ content.className = 'msg-content';
217
+ content.textContent = text;
218
+ row.appendChild(content);
219
+ container.appendChild(row);
220
+ scrollToBottom();
221
+ return row;
222
+ }
223
+
224
+ function scrollToBottom() {
225
+ const container = document.getElementById('chat-messages');
226
+ container.scrollTop = container.scrollHeight;
227
+ }
228
+
229
+ function setPrompt(text) {
230
+ const input = document.getElementById('prompt-input');
231
+ input.value = text;
232
+ input.focus();
233
+ }
234
+
235
+ function handleInputKey(event) {
236
+ if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
237
+ event.preventDefault();
238
+ submitPrompt();
239
+ }
240
+ }
241
+
242
+ // Dialectic Debate Runner
243
+ async function startDebate() {
244
+ const input = document.getElementById('debate-query-input');
245
+ const query = input.value.trim();
246
+ if (!query) return;
247
+
248
+ const depth = document.getElementById('debate-depth-select').value;
249
+
250
+ document.getElementById('dialectic-thesis').textContent = 'Generating initial thesis & hypothesis...';
251
+ document.getElementById('dialectic-antithesis').textContent = 'Awaiting thesis to challenge assumptions...';
252
+ document.getElementById('dialectic-verification').textContent = 'Researcher standby for fact-checking...';
253
+ document.getElementById('dialectic-synthesis').textContent = 'Synthesizer awaiting debate completion...';
254
+
255
+ try {
256
+ const res = await fetch('/api/debate', {
257
+ method: 'POST',
258
+ headers: { 'Content-Type': 'application/json' },
259
+ body: JSON.stringify({ query, depth }),
260
+ });
261
+
262
+ const reader = res.body.getReader();
263
+ const decoder = new TextDecoder();
264
+
265
+ while (true) {
266
+ const { done, value } = await reader.read();
267
+ if (done) break;
268
+
269
+ const chunk = decoder.decode(value);
270
+ const lines = chunk.split('\n');
271
+
272
+ for (const line of lines) {
273
+ if (line.startsWith('data: ')) {
274
+ try {
275
+ const data = JSON.parse(line.slice(6));
276
+ if (data.thesis) document.getElementById('dialectic-thesis').textContent = data.thesis;
277
+ if (data.antithesis) document.getElementById('dialectic-antithesis').textContent = data.antithesis;
278
+ if (data.verification) document.getElementById('dialectic-verification').textContent = data.verification;
279
+ if (data.synthesis) document.getElementById('dialectic-synthesis').textContent = data.synthesis;
280
+ } catch (e) {}
281
+ }
282
+ }
283
+ }
284
+ } catch (err) {
285
+ document.getElementById('dialectic-synthesis').textContent = `Debate error: ${err.message}`;
286
+ }
287
+ }
288
+
289
+ // Goal Loop Runner
290
+ async function startGoalLoop() {
291
+ const input = document.getElementById('goal-objective-input');
292
+ const objective = input.value.trim();
293
+ if (!objective) return;
294
+
295
+ document.getElementById('goal-stage-1-content').textContent = 'Stage 1 executing: Drafting initial plan and solution...';
296
+ document.getElementById('goal-stage-2-content').textContent = 'Stage 2 standby: Awaiting draft for adversarial critique...';
297
+ document.getElementById('goal-stage-3-content').textContent = 'Stage 3 standby: Awaiting synthesis for hardened output...';
298
+
299
+ try {
300
+ const res = await fetch('/api/goal', {
301
+ method: 'POST',
302
+ headers: { 'Content-Type': 'application/json' },
303
+ body: JSON.stringify({ objective }),
304
+ });
305
+
306
+ const reader = res.body.getReader();
307
+ const decoder = new TextDecoder();
308
+
309
+ while (true) {
310
+ const { done, value } = await reader.read();
311
+ if (done) break;
312
+
313
+ const chunk = decoder.decode(value);
314
+ const lines = chunk.split('\n');
315
+
316
+ for (const line of lines) {
317
+ if (line.startsWith('data: ')) {
318
+ try {
319
+ const data = JSON.parse(line.slice(6));
320
+ if (data.draft) document.getElementById('goal-stage-1-content').textContent = data.draft;
321
+ if (data.critique) document.getElementById('goal-stage-2-content').textContent = data.critique;
322
+ if (data.finalOutput) document.getElementById('goal-stage-3-content').textContent = data.finalOutput;
323
+ } catch (e) {}
324
+ }
325
+ }
326
+ }
327
+ } catch (err) {
328
+ document.getElementById('goal-stage-3-content').textContent = `Goal execution error: ${err.message}`;
329
+ }
330
+ }
331
+
332
+ // Profile Management
333
+ async function loadProfiles() {
334
+ try {
335
+ const res = await fetch('/api/profiles');
336
+ const data = await res.json();
337
+
338
+ const select = document.getElementById('select-profile');
339
+ const list = document.getElementById('profile-items-list');
340
+ select.innerHTML = '';
341
+ list.innerHTML = '';
342
+
343
+ data.profiles.forEach((p) => {
344
+ // Dropdown option
345
+ const opt = document.createElement('option');
346
+ opt.value = p.name;
347
+ opt.textContent = p.name;
348
+ if (p.name === data.activeName) opt.selected = true;
349
+ select.appendChild(opt);
350
+
351
+ // List button
352
+ const btn = document.createElement('button');
353
+ btn.className = `profile-item-btn ${p.name === data.activeName ? 'active' : ''}`;
354
+ btn.textContent = `${p.name}.md`;
355
+ btn.onclick = () => selectProfile(p.name);
356
+ list.appendChild(btn);
357
+ });
358
+
359
+ document.getElementById('active-profile-title').textContent = `${data.activeName}.md`;
360
+ document.getElementById('profile-editor').value = data.activeContent || '';
361
+ } catch (err) {
362
+ console.error('Failed to load profiles:', err);
363
+ }
364
+ }
365
+
366
+ async function selectProfile(name) {
367
+ await fetch('/api/profile/select', {
368
+ method: 'POST',
369
+ headers: { 'Content-Type': 'application/json' },
370
+ body: JSON.stringify({ name }),
371
+ });
372
+ await loadProfiles();
373
+ }
374
+
375
+ async function createProfile() {
376
+ const input = document.getElementById('new-profile-name');
377
+ const name = input.value.trim();
378
+ if (!name) return;
379
+
380
+ await fetch('/api/profile/create', {
381
+ method: 'POST',
382
+ headers: { 'Content-Type': 'application/json' },
383
+ body: JSON.stringify({ name }),
384
+ });
385
+
386
+ input.value = '';
387
+ await loadProfiles();
388
+ }
389
+
390
+ async function saveActiveProfile() {
391
+ const content = document.getElementById('profile-editor').value;
392
+ await fetch('/api/profile/save', {
393
+ method: 'POST',
394
+ headers: { 'Content-Type': 'application/json' },
395
+ body: JSON.stringify({ content }),
396
+ });
397
+ alert('Profile saved.');
398
+ }
399
+
400
+ async function onProfileChange(name) {
401
+ await selectProfile(name);
402
+ }
403
+
404
+ // Skills & Memory Loaders
405
+ async function loadSkills() {
406
+ try {
407
+ const res = await fetch('/api/skills');
408
+ const data = await res.json();
409
+ const grid = document.getElementById('skills-catalog-grid');
410
+ grid.innerHTML = '';
411
+
412
+ data.allTools.forEach((t) => {
413
+ const card = document.createElement('div');
414
+ card.className = 'skill-card';
415
+ card.innerHTML = `
416
+ <h4>${t.name}</h4>
417
+ <p>${t.description}</p>
418
+ <button class="skill-btn" onclick="testSkill('${t.name}')">Dry-Run Skill</button>
419
+ `;
420
+ grid.appendChild(card);
421
+ });
422
+ } catch (err) {
423
+ console.error('Failed to load skills:', err);
424
+ }
425
+ }
426
+
427
+ async function testSkill(skillName) {
428
+ try {
429
+ const res = await fetch('/api/skill/test', {
430
+ method: 'POST',
431
+ headers: { 'Content-Type': 'application/json' },
432
+ body: JSON.stringify({ skillName, args: {} }),
433
+ });
434
+ const data = await res.json();
435
+ alert(`Skill '${skillName}' Output:\n${data.output || 'Execution complete'}`);
436
+ } catch (err) {
437
+ alert(`Execution failed: ${err.message}`);
438
+ }
439
+ }
440
+
441
+ async function loadMemory() {
442
+ try {
443
+ const res = await fetch('/api/memory');
444
+ const data = await res.json();
445
+
446
+ const semanticList = document.getElementById('semantic-memory-list');
447
+ const episodicList = document.getElementById('episodic-memory-list');
448
+
449
+ semanticList.innerHTML = (data.semanticItems || [])
450
+ .map((item) => `<div style="margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-light); padding-bottom: 0.5rem;"><b>[${item.category}]</b> ${item.text}</div>`)
451
+ .join('') || '<div>No semantic vectors stored yet.</div>';
452
+
453
+ episodicList.innerHTML = (data.episodes || [])
454
+ .map((ep) => `<div style="margin-bottom: 0.75rem; border-bottom: 1px solid var(--border-light); padding-bottom: 0.5rem;"><b>Query:</b> ${ep.query}<br/><span style="color: var(--text-tertiary);">${ep.response.slice(0, 100)}...</span></div>`)
455
+ .join('') || '<div>No episodes recorded in current session.</div>';
456
+ } catch (err) {
457
+ console.error('Failed to load memory:', err);
458
+ }
459
+ }