@newsails/veil-cli 1.0.1
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/.veil/agents/analyst/AGENT.md +21 -0
- package/.veil/agents/analyst/agent.json +23 -0
- package/.veil/agents/assistant/AGENT.md +15 -0
- package/.veil/agents/assistant/agent.json +19 -0
- package/.veil/agents/coder/AGENT.md +18 -0
- package/.veil/agents/coder/agent.json +19 -0
- package/.veil/agents/hello/AGENT.md +5 -0
- package/.veil/agents/hello/agent.json +13 -0
- package/.veil/agents/writer/AGENT.md +12 -0
- package/.veil/agents/writer/agent.json +17 -0
- package/.veil/memory/MEMORY.md +343 -0
- package/.veil/memory/agents/analyst/MEMORY.md +55 -0
- package/.veil/memory/agents/hello/MEMORY.md +12 -0
- package/.veil/runtime.pid +1 -0
- package/.veil/settings.json +10 -0
- package/.veil-studio/studio.db +0 -0
- package/.veil-studio/studio.db-shm +0 -0
- package/.veil-studio/studio.db-wal +0 -0
- package/PLAN/01-vision.md +26 -0
- package/PLAN/02-tech-stack.md +94 -0
- package/PLAN/03-agents.md +232 -0
- package/PLAN/04-runtime.md +171 -0
- package/PLAN/05-tools.md +211 -0
- package/PLAN/06-communication.md +243 -0
- package/PLAN/07-storage.md +218 -0
- package/PLAN/08-api-cli.md +153 -0
- package/PLAN/09-permissions.md +108 -0
- package/PLAN/10-ably.md +105 -0
- package/PLAN/11-file-formats.md +442 -0
- package/PLAN/12-folder-structure.md +205 -0
- package/PLAN/13-operations.md +212 -0
- package/PLAN/README.md +23 -0
- package/README.md +128 -0
- package/REPORT.md +174 -0
- package/TODO.md +45 -0
- package/ai-tests/FRONTEND_PROMPT.md +220 -0
- package/ai-tests/Research & Planning.md +814 -0
- package/ai-tests/prompt-001-basic-api.md +230 -0
- package/ai-tests/prompt-002-basic-flows.md +230 -0
- package/ai-tests/prompt-003-agent-behaviors.md +220 -0
- package/api/middleware.js +60 -0
- package/api/routes/agents.js +193 -0
- package/api/routes/chat.js +93 -0
- package/api/routes/completions.js +122 -0
- package/api/routes/daemons.js +80 -0
- package/api/routes/memory.js +169 -0
- package/api/routes/models.js +40 -0
- package/api/routes/remote-methods.js +74 -0
- package/api/routes/sessions.js +208 -0
- package/api/routes/settings.js +108 -0
- package/api/routes/system.js +50 -0
- package/api/routes/tasks.js +270 -0
- package/api/server.js +120 -0
- package/cli/formatter.js +70 -0
- package/cli/index.js +443 -0
- package/cli/parser.js +113 -0
- package/config/config.json +10 -0
- package/config/models.json +6826 -0
- package/core/agent.js +329 -0
- package/core/cancel.js +38 -0
- package/core/compaction.js +176 -0
- package/core/events.js +13 -0
- package/core/loop.js +564 -0
- package/core/memory.js +51 -0
- package/core/prompt.js +185 -0
- package/core/queue.js +96 -0
- package/core/registry.js +291 -0
- package/core/remote-methods.js +124 -0
- package/core/router.js +386 -0
- package/core/running-sessions.js +18 -0
- package/docs/api/01-system.md +84 -0
- package/docs/api/02-agents.md +374 -0
- package/docs/api/03-chat.md +269 -0
- package/docs/api/04-tasks.md +470 -0
- package/docs/api/05-sessions.md +444 -0
- package/docs/api/06-daemons.md +142 -0
- package/docs/api/07-memory.md +186 -0
- package/docs/api/08-settings.md +133 -0
- package/docs/api/09-models.md +119 -0
- package/docs/api/09-websocket.md +350 -0
- package/docs/api/10-completions.md +134 -0
- package/docs/api/README.md +116 -0
- package/docs/guide/01-quickstart.md +220 -0
- package/docs/guide/02-folder-structure.md +185 -0
- package/docs/guide/03-configuration.md +252 -0
- package/docs/guide/04-agents.md +267 -0
- package/docs/guide/05-cli.md +290 -0
- package/docs/guide/06-tools.md +643 -0
- package/docs/guide/07-permissions.md +236 -0
- package/docs/guide/08-memory.md +139 -0
- package/docs/guide/09-multi-agent.md +271 -0
- package/docs/guide/10-daemons.md +226 -0
- package/docs/guide/README.md +53 -0
- package/docs/index.html +623 -0
- package/examples/README.md +151 -0
- package/examples/agents/assistant/AGENT.md +31 -0
- package/examples/agents/assistant/SOUL.md +9 -0
- package/examples/agents/assistant/agent.json +74 -0
- package/examples/agents/hello/AGENT.md +15 -0
- package/examples/agents/hello/agent.json +14 -0
- package/examples/agents/monitor/AGENT.md +51 -0
- package/examples/agents/monitor/agent.json +33 -0
- package/examples/agents/monitor/heartbeats/monitor.md +24 -0
- package/examples/agents/orchestrator/AGENT.md +70 -0
- package/examples/agents/orchestrator/agent.json +30 -0
- package/examples/agents/researcher/AGENT.md +52 -0
- package/examples/agents/researcher/agent.json +49 -0
- package/examples/agents/researcher/skills/web-research.md +28 -0
- package/examples/skills/code-review.md +72 -0
- package/examples/skills/summarise.md +59 -0
- package/examples/skills/web-research.md +42 -0
- package/examples/tools/word-count/index.js +27 -0
- package/examples/tools/word-count/tool.json +18 -0
- package/infrastructure/database.js +563 -0
- package/infrastructure/scheduler.js +122 -0
- package/llm/client.js +206 -0
- package/migrations/001-initial.sql +121 -0
- package/migrations/002-debuggability.sql +13 -0
- package/migrations/003-drop-orphaned-columns.sql +72 -0
- package/migrations/004-session-message-token-fields.sql +78 -0
- package/migrations/005-session-thinking.sql +5 -0
- package/package.json +30 -0
- package/schemas/agent.json +143 -0
- package/schemas/settings.json +111 -0
- package/scripts/fetch-models.js +93 -0
- package/session-debug-scenario.md +248 -0
- package/settings/fields.js +52 -0
- package/system-prompts/base-core.md +7 -0
- package/system-prompts/environment.md +13 -0
- package/system-prompts/reminders/anti-drift.md +6 -0
- package/system-prompts/reminders/stall-recovery.md +10 -0
- package/system-prompts/safety-rules.md +25 -0
- package/system-prompts/task-heuristics.md +27 -0
- package/test/client.js +71 -0
- package/test/integration/01-health.test.js +25 -0
- package/test/integration/02-agents.test.js +80 -0
- package/test/integration/03-chat-hello.test.js +48 -0
- package/test/integration/04-chat-multiturn.test.js +61 -0
- package/test/integration/05-chat-writer.test.js +48 -0
- package/test/integration/06-task-basic.test.js +68 -0
- package/test/integration/07-task-tools.test.js +74 -0
- package/test/integration/08-task-code-analysis.test.js +69 -0
- package/test/integration/09-memory-analyst.test.js +63 -0
- package/test/integration/10-task-advanced.test.js +85 -0
- package/test/integration/11-sessions-advanced.test.js +84 -0
- package/test/integration/12-assistant-chat-tools.test.js +75 -0
- package/test/integration/13-edge-cases.test.js +99 -0
- package/test/integration/14-cancel.test.js +62 -0
- package/test/integration/15-debug.test.js +106 -0
- package/test/integration/16-memory-api.test.js +83 -0
- package/test/integration/17-settings-api.test.js +41 -0
- package/test/integration/18-tool-search-activation.test.js +119 -0
- package/test/results/.gitkeep +0 -0
- package/test/runner.js +206 -0
- package/test/smoke.js +216 -0
- package/tools/agent_message.js +85 -0
- package/tools/agent_send.js +80 -0
- package/tools/agent_spawn.js +44 -0
- package/tools/bash.js +49 -0
- package/tools/edit_file.js +41 -0
- package/tools/glob.js +64 -0
- package/tools/grep.js +82 -0
- package/tools/list_dir.js +63 -0
- package/tools/log_write.js +31 -0
- package/tools/memory_read.js +38 -0
- package/tools/memory_search.js +65 -0
- package/tools/memory_write.js +42 -0
- package/tools/read_file.js +48 -0
- package/tools/sleep.js +22 -0
- package/tools/task_create.js +41 -0
- package/tools/task_respond.js +37 -0
- package/tools/task_spawn.js +64 -0
- package/tools/task_status.js +39 -0
- package/tools/task_subscribe.js +37 -0
- package/tools/todo_read.js +26 -0
- package/tools/todo_write.js +38 -0
- package/tools/tool_activate.js +24 -0
- package/tools/tool_search.js +24 -0
- package/tools/web_fetch.js +50 -0
- package/tools/web_search.js +52 -0
- package/tools/write_file.js +28 -0
- package/ui/api.js +190 -0
- package/ui/app.js +281 -0
- package/ui/index.html +382 -0
- package/ui/views/agents.js +377 -0
- package/ui/views/chat.js +610 -0
- package/ui/views/connection.js +96 -0
- package/ui/views/daemons.js +129 -0
- package/ui/views/feed.js +194 -0
- package/ui/views/memory.js +263 -0
- package/ui/views/models.js +146 -0
- package/ui/views/sessions.js +314 -0
- package/ui/views/settings.js +142 -0
- package/ui/views/tasks.js +415 -0
- package/utils/context.js +49 -0
- package/utils/id.js +16 -0
- package/utils/models.js +88 -0
- package/utils/paths.js +213 -0
- package/utils/settings.js +172 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
'use strict';
|
|
3
|
+
window.Veil = window.Veil || {};
|
|
4
|
+
window.Veil.views = window.Veil.views || {};
|
|
5
|
+
|
|
6
|
+
window.Veil.views.connection = {
|
|
7
|
+
render() {
|
|
8
|
+
const url = localStorage.getItem('veil_url') || 'http://localhost:5050';
|
|
9
|
+
const secret = localStorage.getItem('veil_secret') || '';
|
|
10
|
+
const U = window.Veil.utils;
|
|
11
|
+
return `
|
|
12
|
+
<div style="display:flex;align-items:center;justify-content:center;min-height:100vh;padding:24px;background:var(--bg);">
|
|
13
|
+
<div style="width:100%;max-width:400px;">
|
|
14
|
+
|
|
15
|
+
<!-- Logo -->
|
|
16
|
+
<div style="text-align:center;margin-bottom:40px;">
|
|
17
|
+
<div style="display:inline-flex;width:56px;height:56px;background:linear-gradient(135deg,var(--accent),#5B8AF0);border-radius:16px;align-items:center;justify-content:center;font-size:26px;box-shadow:0 4px 24px var(--accent-glow);margin-bottom:16px;">⚡</div>
|
|
18
|
+
<h1 style="font-size:22px;font-weight:700;letter-spacing:-0.03em;color:var(--text);margin-bottom:6px;">VeilCLI</h1>
|
|
19
|
+
<p style="font-size:13px;color:var(--text3);">Connect to your agent runtime</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<!-- Card -->
|
|
23
|
+
<div style="background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:28px;">
|
|
24
|
+
|
|
25
|
+
<!-- Error -->
|
|
26
|
+
<div id="conn-error" style="display:none;background:var(--red-dim);border:1px solid rgba(239,68,68,0.3);border-radius:8px;padding:10px 14px;margin-bottom:20px;font-size:13px;color:var(--red);"></div>
|
|
27
|
+
|
|
28
|
+
<div style="margin-bottom:16px;">
|
|
29
|
+
<label style="display:block;font-size:11.5px;font-weight:600;color:var(--text3);text-transform:uppercase;letter-spacing:0.06em;margin-bottom:7px;">Server URL</label>
|
|
30
|
+
<input id="conn-url" class="input" type="text" value="${U.esc(url)}"
|
|
31
|
+
placeholder="http://localhost:5050" autocomplete="off" spellcheck="false"
|
|
32
|
+
style="font-family:monospace;font-size:13px;" />
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div style="margin-bottom:24px;">
|
|
36
|
+
<label style="display:block;font-size:11.5px;font-weight:600;color:var(--text3);text-transform:uppercase;letter-spacing:0.06em;margin-bottom:7px;">
|
|
37
|
+
Secret Token <span style="color:var(--text3);font-weight:400;text-transform:none;letter-spacing:0;">(optional)</span>
|
|
38
|
+
</label>
|
|
39
|
+
<input id="conn-secret" class="input" type="password" value="${U.esc(secret)}"
|
|
40
|
+
placeholder="Leave empty if auth is disabled" autocomplete="off" />
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<button id="conn-btn" class="btn btn-primary" style="width:100%;justify-content:center;padding:10px 14px;font-size:14px;border-radius:9px;">
|
|
44
|
+
Connect
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<p style="font-size:12px;color:var(--text3);text-align:center;margin-top:20px;">
|
|
49
|
+
Credentials saved in <code style="color:var(--text2);">localStorage</code> · auto-reconnects on reload
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
</div>`;
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
mount() {
|
|
56
|
+
const btn = document.getElementById('conn-btn');
|
|
57
|
+
const urlEl = document.getElementById('conn-url');
|
|
58
|
+
const secEl = document.getElementById('conn-secret');
|
|
59
|
+
const errEl = document.getElementById('conn-error');
|
|
60
|
+
|
|
61
|
+
const connect = async () => {
|
|
62
|
+
const url = urlEl.value.trim();
|
|
63
|
+
const secret = secEl.value.trim();
|
|
64
|
+
if (!url) { showErr('Server URL is required.'); return; }
|
|
65
|
+
btn.disabled = true;
|
|
66
|
+
btn.innerHTML = `${window.Veil.utils.spinner()} Connecting…`;
|
|
67
|
+
errEl.style.display = 'none';
|
|
68
|
+
const api = new window.Veil.API(url, secret);
|
|
69
|
+
try {
|
|
70
|
+
const data = await api.health();
|
|
71
|
+
window.Veil.saveConnection(url, secret);
|
|
72
|
+
window.Veil.state.api = api;
|
|
73
|
+
window.Veil.state.connected = true;
|
|
74
|
+
window.Veil._updateIndicator();
|
|
75
|
+
document.getElementById('sidebar').style.display = 'flex';
|
|
76
|
+
window.Veil.utils.toast(`Connected to Veil v${data.version || '?'}`, 'success');
|
|
77
|
+
window.Veil.navigate('agents');
|
|
78
|
+
} catch (err) {
|
|
79
|
+
btn.disabled = false;
|
|
80
|
+
btn.textContent = 'Connect';
|
|
81
|
+
showErr(err.message || 'Connection failed');
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const showErr = (msg) => {
|
|
86
|
+
errEl.textContent = msg;
|
|
87
|
+
errEl.style.display = 'block';
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
btn.addEventListener('click', connect);
|
|
91
|
+
urlEl.addEventListener('keydown', e => { if (e.key === 'Enter') connect(); });
|
|
92
|
+
secEl.addEventListener('keydown', e => { if (e.key === 'Enter') connect(); });
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
unmount() {},
|
|
96
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
window.Veil = window.Veil || {};
|
|
3
|
+
window.Veil.views = window.Veil.views || {};
|
|
4
|
+
|
|
5
|
+
window.Veil.views.daemons = {
|
|
6
|
+
_refreshTimer: null,
|
|
7
|
+
|
|
8
|
+
render() {
|
|
9
|
+
return `
|
|
10
|
+
<div style="padding:20px;">
|
|
11
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;">
|
|
12
|
+
<h1 style="font-size:16px;font-weight:700;color:var(--text);">Daemon Schedulers</h1>
|
|
13
|
+
<button id="btn-refresh-daemons" class="btn btn-secondary btn-sm">↻ Refresh</button>
|
|
14
|
+
</div>
|
|
15
|
+
<div id="daemon-list">
|
|
16
|
+
<div style="text-align:center;padding:30px;color:var(--text3);">${window.Veil.utils.spinner()}</div>
|
|
17
|
+
</div>
|
|
18
|
+
</div>`;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async mount() {
|
|
22
|
+
await this._load();
|
|
23
|
+
document.getElementById('btn-refresh-daemons')?.addEventListener('click', () => this._load());
|
|
24
|
+
this._refreshTimer = setInterval(() => this._load(), 15000);
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
unmount() {
|
|
28
|
+
if (this._refreshTimer) { clearInterval(this._refreshTimer); this._refreshTimer = null; }
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async _load() {
|
|
32
|
+
const el = document.getElementById('daemon-list');
|
|
33
|
+
if (!el) return;
|
|
34
|
+
try {
|
|
35
|
+
const [daemonData, agentData] = await Promise.all([
|
|
36
|
+
window.Veil.state.api.listDaemons(),
|
|
37
|
+
window.Veil.state.api.listAgents(),
|
|
38
|
+
]);
|
|
39
|
+
const daemons = daemonData.daemons || [];
|
|
40
|
+
const allAgents = (agentData.agents || []).filter(a => a.modes && a.modes.includes('daemon'));
|
|
41
|
+
const activeNames = new Set(daemons.map(d => d.agentName));
|
|
42
|
+
const U = window.Veil.utils;
|
|
43
|
+
|
|
44
|
+
if (allAgents.length === 0 && daemons.length === 0) {
|
|
45
|
+
el.innerHTML = `<div class="card" style="padding:24px;text-align:center;color:var(--text3);font-size:13.5px;">No daemon-enabled agents found.<br><br>Add <code>modes.daemon.enabled: true</code> to an agent's config.</div>`;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const rows = allAgents.map(a => {
|
|
50
|
+
const d = daemons.find(x => x.agentName === a.name);
|
|
51
|
+
const isActive = !!d;
|
|
52
|
+
return `
|
|
53
|
+
<div class="card" style="padding:16px;margin-bottom:12px;" id="daemon-row-${U.esc(a.name)}">
|
|
54
|
+
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
55
|
+
<div style="flex:1;">
|
|
56
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px;">
|
|
57
|
+
<span style="font-weight:600;font-size:14px;color:var(--text);">${U.esc(a.name)}</span>
|
|
58
|
+
${isActive
|
|
59
|
+
? `<span class="badge badge-active">active</span>`
|
|
60
|
+
: `<span class="badge badge-closed">stopped</span>`}
|
|
61
|
+
</div>
|
|
62
|
+
<div style="font-size:12.5px;color:var(--text2);display:flex;gap:16px;flex-wrap:wrap;">
|
|
63
|
+
${d?.schedule ? `<span>⏱ ${U.esc(d.schedule)}</span>` : ''}
|
|
64
|
+
${d?.lastTick ? `<span>Last: ${U.relTime(d.lastTick)}</span>` : ''}
|
|
65
|
+
${d?.nextTick ? `<span>Next: ${U.relTime(d.nextTick)}</span>` : ''}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div style="display:flex;gap:8px;flex-shrink:0;">
|
|
69
|
+
${!isActive
|
|
70
|
+
? `<button class="btn btn-success btn-sm" data-daemon-start="${U.esc(a.name)}">▶ Start</button>`
|
|
71
|
+
: `<button class="btn btn-danger btn-sm" data-daemon-stop="${U.esc(a.name)}">■ Stop</button>`}
|
|
72
|
+
<button class="btn btn-secondary btn-sm" data-daemon-trigger="${U.esc(a.name)}">⚡ Trigger</button>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div id="daemon-result-${U.esc(a.name)}" style="margin-top:10px;display:none;font-size:13px;"></div>
|
|
76
|
+
</div>`;
|
|
77
|
+
});
|
|
78
|
+
el.innerHTML = rows.join('');
|
|
79
|
+
|
|
80
|
+
el.querySelectorAll('[data-daemon-start]').forEach(btn => {
|
|
81
|
+
btn.addEventListener('click', () => this._startDaemon(btn.getAttribute('data-daemon-start')));
|
|
82
|
+
});
|
|
83
|
+
el.querySelectorAll('[data-daemon-stop]').forEach(btn => {
|
|
84
|
+
btn.addEventListener('click', () => this._stopDaemon(btn.getAttribute('data-daemon-stop')));
|
|
85
|
+
});
|
|
86
|
+
el.querySelectorAll('[data-daemon-trigger]').forEach(btn => {
|
|
87
|
+
btn.addEventListener('click', () => this._triggerDaemon(btn.getAttribute('data-daemon-trigger')));
|
|
88
|
+
});
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (el) el.innerHTML = `<div class="card" style="padding:16px;color:var(--red);font-size:13px;">${window.Veil.utils.esc(err.message)}</div>`;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
async _startDaemon(name) {
|
|
95
|
+
try {
|
|
96
|
+
await window.Veil.state.api.startDaemon(name);
|
|
97
|
+
window.Veil.utils.toast(`Daemon "${name}" started`, 'success');
|
|
98
|
+
this._load();
|
|
99
|
+
} catch (err) { window.Veil.utils.toast('Start failed: ' + err.message, 'error'); }
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async _stopDaemon(name) {
|
|
103
|
+
try {
|
|
104
|
+
await window.Veil.state.api.stopDaemon(name);
|
|
105
|
+
window.Veil.utils.toast(`Daemon "${name}" stopped`, 'success');
|
|
106
|
+
this._load();
|
|
107
|
+
} catch (err) { window.Veil.utils.toast('Stop failed: ' + err.message, 'error'); }
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async _triggerDaemon(name) {
|
|
111
|
+
const resultEl = document.getElementById('daemon-result-' + name);
|
|
112
|
+
try {
|
|
113
|
+
const data = await window.Veil.state.api.triggerDaemon(name);
|
|
114
|
+
window.Veil.utils.toast(`Daemon "${name}" triggered`, 'success');
|
|
115
|
+
if (resultEl) {
|
|
116
|
+
resultEl.style.display = 'block';
|
|
117
|
+
resultEl.style.color = 'var(--green)';
|
|
118
|
+
resultEl.textContent = 'Triggered — status: ' + (data.status || 'triggered');
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
window.Veil.utils.toast('Trigger failed: ' + err.message, 'error');
|
|
122
|
+
if (resultEl) {
|
|
123
|
+
resultEl.style.display = 'block';
|
|
124
|
+
resultEl.style.color = 'var(--red)';
|
|
125
|
+
resultEl.textContent = 'Error: ' + err.message;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
};
|
package/ui/views/feed.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
window.Veil = window.Veil || {};
|
|
3
|
+
window.Veil.views = window.Veil.views || {};
|
|
4
|
+
|
|
5
|
+
window.Veil.views.feed = {
|
|
6
|
+
_ws: null,
|
|
7
|
+
_paused: false,
|
|
8
|
+
_autoScroll: true,
|
|
9
|
+
_events: [],
|
|
10
|
+
_filters: { task: true, chat: true, daemon: true, system: true },
|
|
11
|
+
|
|
12
|
+
render() {
|
|
13
|
+
return `
|
|
14
|
+
<div style="display:flex;flex-direction:column;height:100%;">
|
|
15
|
+
<!-- Toolbar -->
|
|
16
|
+
<div style="flex-shrink:0;padding:10px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
17
|
+
<div style="display:flex;align-items:center;gap:8px;">
|
|
18
|
+
<span id="feed-status-dot" style="display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--border2);"></span>
|
|
19
|
+
<span id="feed-status-text" style="font-size:12.5px;color:var(--text3);">Disconnected</span>
|
|
20
|
+
</div>
|
|
21
|
+
<button id="btn-feed-connect" class="btn btn-primary btn-sm">Connect</button>
|
|
22
|
+
<button id="btn-feed-pause" class="btn btn-secondary btn-sm">⏸ Pause</button>
|
|
23
|
+
<button id="btn-feed-clear" class="btn btn-secondary btn-sm">Clear</button>
|
|
24
|
+
<div style="width:1px;height:20px;background:var(--border);margin:0 4px;"></div>
|
|
25
|
+
<div style="display:flex;align-items:center;gap:10px;font-size:12.5px;">
|
|
26
|
+
<label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="filter-task" ${this._filters.task ?'checked':''}> task</label>
|
|
27
|
+
<label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="filter-chat" ${this._filters.chat ?'checked':''}> chat</label>
|
|
28
|
+
<label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="filter-daemon" ${this._filters.daemon ?'checked':''}> daemon</label>
|
|
29
|
+
<label style="display:flex;align-items:center;gap:4px;cursor:pointer;"><input type="checkbox" id="filter-system" ${this._filters.system ?'checked':''}> system</label>
|
|
30
|
+
</div>
|
|
31
|
+
<div style="margin-left:auto;display:flex;align-items:center;gap:8px;">
|
|
32
|
+
<label style="display:flex;align-items:center;gap:4px;font-size:12.5px;cursor:pointer;">
|
|
33
|
+
<input type="checkbox" id="feed-autoscroll" ${this._autoScroll ? 'checked' : ''}> Auto-scroll
|
|
34
|
+
</label>
|
|
35
|
+
<span id="feed-event-count" style="font-size:12px;color:var(--text3);">0 events</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<!-- Event log -->
|
|
39
|
+
<div id="feed-log" style="flex:1;overflow-y:auto;font-family:monospace;font-size:12.5px;padding:8px 0;background:var(--bg-alt);"></div>
|
|
40
|
+
</div>`;
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
mount() {
|
|
44
|
+
document.getElementById('btn-feed-connect').addEventListener('click', () => this._toggle());
|
|
45
|
+
document.getElementById('btn-feed-pause').addEventListener('click', () => this._togglePause());
|
|
46
|
+
document.getElementById('btn-feed-clear').addEventListener('click', () => this._clear());
|
|
47
|
+
document.getElementById('feed-autoscroll').addEventListener('change', (e) => {
|
|
48
|
+
this._autoScroll = e.target.checked;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
['task','chat','daemon','system'].forEach(type => {
|
|
52
|
+
document.getElementById('filter-' + type)?.addEventListener('change', (e) => {
|
|
53
|
+
this._filters[type] = e.target.checked;
|
|
54
|
+
this._reRender();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
document.getElementById('feed-log')?.addEventListener('scroll', (e) => {
|
|
59
|
+
const el = e.target;
|
|
60
|
+
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;
|
|
61
|
+
if (!atBottom && this._autoScroll) {
|
|
62
|
+
const cb = document.getElementById('feed-autoscroll');
|
|
63
|
+
if (cb) { cb.checked = false; this._autoScroll = false; }
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this._connect();
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
unmount() {
|
|
71
|
+
if (this._ws) {
|
|
72
|
+
try { this._ws.close(); } catch {}
|
|
73
|
+
this._ws = null;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
_connect() {
|
|
78
|
+
if (this._ws) {
|
|
79
|
+
try { this._ws.close(); } catch {}
|
|
80
|
+
this._ws = null;
|
|
81
|
+
}
|
|
82
|
+
this._setStatus('connecting', 'Connecting…', 'var(--yellow)');
|
|
83
|
+
this._ws = window.Veil.state.api.connectWS(
|
|
84
|
+
(msg) => this._onMessage(msg),
|
|
85
|
+
() => this._setStatus('connected', 'Connected', 'var(--green)'),
|
|
86
|
+
() => {
|
|
87
|
+
this._setStatus('disconnected', 'Disconnected', 'var(--red)');
|
|
88
|
+
const btn = document.getElementById('btn-feed-connect');
|
|
89
|
+
if (btn) btn.textContent = 'Reconnect';
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
const btn = document.getElementById('btn-feed-connect');
|
|
93
|
+
if (btn) btn.textContent = 'Disconnect';
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
_toggle() {
|
|
97
|
+
if (this._ws && this._ws.readyState <= 1) {
|
|
98
|
+
this._ws.close();
|
|
99
|
+
this._ws = null;
|
|
100
|
+
this._setStatus('disconnected', 'Disconnected', 'var(--border2)');
|
|
101
|
+
const btn = document.getElementById('btn-feed-connect');
|
|
102
|
+
if (btn) btn.textContent = 'Connect';
|
|
103
|
+
} else {
|
|
104
|
+
this._connect();
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
_togglePause() {
|
|
109
|
+
this._paused = !this._paused;
|
|
110
|
+
const btn = document.getElementById('btn-feed-pause');
|
|
111
|
+
if (btn) btn.textContent = this._paused ? '▶ Resume' : '⏸ Pause';
|
|
112
|
+
if (!this._paused) this._reRender();
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
_clear() {
|
|
116
|
+
this._events = [];
|
|
117
|
+
const el = document.getElementById('feed-log');
|
|
118
|
+
if (el) el.innerHTML = '';
|
|
119
|
+
this._updateCount();
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
_setStatus(state, text, color) {
|
|
123
|
+
const dot = document.getElementById('feed-status-dot');
|
|
124
|
+
const txt = document.getElementById('feed-status-text');
|
|
125
|
+
if (dot) dot.style.background = color;
|
|
126
|
+
if (txt) { txt.textContent = text; txt.style.color = color; }
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
_onMessage(msg) {
|
|
130
|
+
if (this._paused) { this._events.push(msg); return; }
|
|
131
|
+
this._events.push(msg);
|
|
132
|
+
const type = (msg.type || '').split('.')[0];
|
|
133
|
+
if (!this._filters[type] && !(type === 'connected' && this._filters.system)) return;
|
|
134
|
+
this._appendEvent(msg);
|
|
135
|
+
this._updateCount();
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
_reRender() {
|
|
139
|
+
const el = document.getElementById('feed-log');
|
|
140
|
+
if (!el) return;
|
|
141
|
+
el.innerHTML = '';
|
|
142
|
+
this._events.forEach(ev => {
|
|
143
|
+
const type = (ev.type || '').split('.')[0];
|
|
144
|
+
const show = this._filters[type] || (ev.type === 'connected' && this._filters.system);
|
|
145
|
+
if (show) this._appendEvent(ev);
|
|
146
|
+
});
|
|
147
|
+
this._updateCount();
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
_appendEvent(ev) {
|
|
151
|
+
const el = document.getElementById('feed-log');
|
|
152
|
+
if (!el) return;
|
|
153
|
+
const U = window.Veil.utils;
|
|
154
|
+
const row = document.createElement('div');
|
|
155
|
+
const now = new Date().toISOString().slice(11, 23);
|
|
156
|
+
|
|
157
|
+
const { typeColor, typeBg } = this._typeStyle(ev.type);
|
|
158
|
+
const agentPart = ev.agentName ? `<span style="color:var(--accent);">${U.esc(ev.agentName)}</span> ` : '';
|
|
159
|
+
const taskPart = ev.taskId ? `<span style="color:var(--text3);font-size:0.72rem;">${U.esc(U.shortId(ev.taskId))}</span> ` : '';
|
|
160
|
+
const sessionPart = ev.sessionId ? `<span style="color:var(--text3);font-size:0.72rem;">${U.esc(U.shortId(ev.sessionId))}</span> ` : '';
|
|
161
|
+
|
|
162
|
+
let dataPart = '';
|
|
163
|
+
if (ev.event && typeof ev.event === 'object') {
|
|
164
|
+
const e = ev.event;
|
|
165
|
+
if (e.status) dataPart = `<span style="color:var(--text);">status: ${U.esc(e.status)}</span>`;
|
|
166
|
+
else if (e.type === 'tool.start') dataPart = `<span style="color:var(--yellow);">⚙ ${U.esc(e.toolName)}</span>`;
|
|
167
|
+
else if (e.type === 'tool.end') dataPart = `<span style="color:var(--green);">✓ ${U.esc(e.toolName)} ${U.formatMs(e.durationMs)}</span>`;
|
|
168
|
+
else if (e.content) dataPart = `<span style="color:var(--text2);">${U.esc(U.truncate(e.content, 80))}</span>`;
|
|
169
|
+
else dataPart = `<span style="color:var(--text3);">${U.esc(JSON.stringify(e).slice(0, 100))}</span>`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
row.style.cssText = 'padding:4px 16px;border-bottom:1px solid var(--bg);display:flex;align-items:baseline;gap:8px;';
|
|
173
|
+
row.innerHTML = `
|
|
174
|
+
<span style="color:var(--text3);flex-shrink:0;">${now}</span>
|
|
175
|
+
<span style="background:${typeBg};color:${typeColor};padding:1px 6px;border-radius:3px;font-size:0.7rem;font-weight:600;flex-shrink:0;">${U.esc(ev.type || 'unknown')}</span>
|
|
176
|
+
<span style="flex:1;">${agentPart}${taskPart}${sessionPart}${dataPart}</span>`;
|
|
177
|
+
|
|
178
|
+
el.appendChild(row);
|
|
179
|
+
if (this._autoScroll) el.scrollTop = el.scrollHeight;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
_typeStyle(type) {
|
|
183
|
+
const t = (type || '').split('.')[0];
|
|
184
|
+
if (t === 'task') return { typeColor: 'var(--accent)', typeBg: 'var(--bg-task)' };
|
|
185
|
+
if (t === 'chat') return { typeColor: 'var(--green)', typeBg: 'var(--bg-chat)' };
|
|
186
|
+
if (t === 'daemon') return { typeColor: 'var(--yellow)', typeBg: 'var(--bg-daemon)' };
|
|
187
|
+
return { typeColor: 'var(--text2)', typeBg: 'var(--border)' };
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
_updateCount() {
|
|
191
|
+
const el = document.getElementById('feed-event-count');
|
|
192
|
+
if (el) el.textContent = this._events.length + ' events';
|
|
193
|
+
},
|
|
194
|
+
};
|