lazyclaw 6.3.0 → 6.4.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.
- package/README.ko.md +5 -1
- package/README.md +17 -13
- package/agents.mjs +54 -4
- package/channels-discord/index.mjs +5 -1
- package/channels-email/index.mjs +4 -3
- package/channels-whatsapp/index.mjs +3 -2
- package/chat_window.mjs +11 -2
- package/cli.mjs +103 -3
- package/commands/agents.mjs +6 -1
- package/commands/auth_nodes.mjs +19 -1
- package/commands/automation.mjs +20 -5
- package/commands/channels.mjs +46 -3
- package/commands/chat.mjs +95 -37
- package/commands/chat_hardening.mjs +23 -0
- package/commands/daemon.mjs +99 -1
- package/commands/gateway.mjs +123 -4
- package/commands/mcp.mjs +150 -0
- package/commands/misc.mjs +20 -0
- package/commands/sessions.mjs +68 -8
- package/commands/setup.mjs +44 -8
- package/commands/setup_channels.mjs +219 -47
- package/commands/skills.mjs +19 -1
- package/commands/workflow_named.mjs +137 -0
- package/config-validate.mjs +10 -1
- package/config_features.mjs +45 -0
- package/cron.mjs +19 -8
- package/daemon/lib/auth.mjs +25 -0
- package/daemon/lib/cost.mjs +36 -0
- package/daemon/lib/respond.mjs +20 -1
- package/daemon/lib/team_inbound.mjs +69 -0
- package/daemon/route_table.mjs +5 -1
- package/daemon/routes/_deps.mjs +2 -2
- package/daemon/routes/config.mjs +11 -6
- package/daemon/routes/conversation.mjs +103 -40
- package/daemon/routes/events.mjs +45 -0
- package/daemon/routes/meta.mjs +38 -7
- package/daemon/routes/providers.mjs +26 -6
- package/daemon/routes/workflows.mjs +30 -0
- package/daemon.mjs +26 -2
- package/dotenv_min.mjs +15 -3
- package/gateway/challenge_registry.mjs +90 -0
- package/gateway/device_auth.mjs +71 -97
- package/gateway/http_gateway.mjs +29 -3
- package/lib/args.mjs +69 -3
- package/lib/config.mjs +85 -3
- package/lib/render.mjs +43 -0
- package/lib/service_install.mjs +15 -1
- package/mas/agent_turn.mjs +61 -12
- package/mas/confidence.mjs +20 -1
- package/mas/embedder.mjs +100 -0
- package/mas/events.mjs +57 -0
- package/mas/index_db.mjs +103 -10
- package/mas/learning.mjs +96 -72
- package/mas/mention_router.mjs +48 -90
- package/mas/prompt_stack.mjs +60 -3
- package/mas/recall_blend.mjs +56 -0
- package/mas/redact.mjs +55 -0
- package/mas/router_posting.mjs +96 -0
- package/mas/router_termination.mjs +63 -0
- package/mas/scrub_env.mjs +21 -6
- package/mas/skill_synth.mjs +7 -33
- package/mas/tool_runner.mjs +3 -2
- package/mas/tools/bash.mjs +9 -2
- package/mas/tools/coding.mjs +28 -5
- package/mas/tools/delegation.mjs +34 -4
- package/mas/tools/git.mjs +19 -9
- package/mas/tools/ha.mjs +2 -0
- package/mas/tools/learning.mjs +55 -0
- package/mas/tools/media.mjs +21 -3
- package/mas/tools/os.mjs +70 -16
- package/mas/tools/recall.mjs +28 -2
- package/mcp/client.mjs +8 -1
- package/mcp/server_spawn.mjs +5 -1
- package/memory.mjs +24 -0
- package/package.json +3 -1
- package/providers/anthropic.mjs +101 -6
- package/providers/cache.mjs +9 -1
- package/providers/claude_cli.mjs +104 -22
- package/providers/claude_cli_session.mjs +166 -0
- package/providers/cli_error.mjs +38 -0
- package/providers/cli_login.mjs +179 -0
- package/providers/codex_cli.mjs +66 -11
- package/providers/gemini.mjs +101 -3
- package/providers/gemini_cli.mjs +62 -15
- package/providers/model_catalogue.mjs +33 -2
- package/providers/ollama.mjs +104 -3
- package/providers/openai.mjs +110 -8
- package/providers/openai_compat.mjs +97 -6
- package/providers/orchestrator.mjs +112 -12
- package/providers/registry.mjs +15 -9
- package/providers/retry.mjs +14 -2
- package/providers/tool_use/anthropic.mjs +17 -3
- package/providers/tool_use/claude_cli.mjs +72 -20
- package/providers/tool_use/gemini.mjs +18 -2
- package/providers/tool_use/openai.mjs +28 -5
- package/sandbox/confiners/seatbelt.mjs +37 -12
- package/sandbox/index.mjs +49 -0
- package/sandbox/local.mjs +7 -1
- package/sandbox/spawn.mjs +144 -0
- package/sandbox.mjs +5 -1
- package/scripts/loop-worker.mjs +25 -1
- package/sessions.mjs +0 -0
- package/skills/channel-style.md +20 -0
- package/skills/code-review.md +33 -0
- package/skills/commit-message.md +30 -0
- package/skills/concise.md +24 -0
- package/skills/debug-coach.md +25 -0
- package/skills/explain.md +24 -0
- package/skills/korean.md +25 -0
- package/skills/summarize.md +33 -0
- package/skills.mjs +24 -2
- package/skills_curator.mjs +6 -0
- package/skills_install.mjs +10 -2
- package/tasks.mjs +6 -1
- package/teams.mjs +78 -0
- package/tui/chat_mode_slash.mjs +59 -0
- package/tui/config_picker.mjs +1 -0
- package/tui/editor.mjs +178 -151
- package/tui/editor_keys.mjs +275 -0
- package/tui/hud.mjs +111 -0
- package/tui/login_flow.mjs +113 -0
- package/tui/modal_picker.mjs +10 -1
- package/tui/model_pick.mjs +287 -0
- package/tui/orchestrator_flow.mjs +164 -0
- package/tui/pickers.mjs +210 -95
- package/tui/repl.mjs +106 -44
- package/tui/repl_reset.mjs +37 -0
- package/tui/run_turn.mjs +228 -26
- package/tui/slash_args.mjs +159 -0
- package/tui/slash_commands.mjs +7 -5
- package/tui/slash_dispatcher.mjs +537 -344
- package/tui/slash_popup.mjs +5 -1
- package/tui/splash.mjs +15 -2
- package/tui/status_bar.mjs +26 -0
- package/tui/theme.mjs +28 -0
- package/web/avatars/01.png +0 -0
- package/web/avatars/02.png +0 -0
- package/web/avatars/03.png +0 -0
- package/web/avatars/04.png +0 -0
- package/web/avatars/05.png +0 -0
- package/web/avatars/06.png +0 -0
- package/web/avatars/07.png +0 -0
- package/web/avatars/08.png +0 -0
- package/web/avatars/09.png +0 -0
- package/web/avatars/10.png +0 -0
- package/web/avatars/11.png +0 -0
- package/web/avatars/12.png +0 -0
- package/web/avatars/13.png +0 -0
- package/web/avatars/14.png +0 -0
- package/web/avatars/15.png +0 -0
- package/web/avatars/16.png +0 -0
- package/web/avatars/17.png +0 -0
- package/web/avatars/18.png +0 -0
- package/web/avatars/19.png +0 -0
- package/web/avatars/20.png +0 -0
- package/web/dashboard.css +77 -0
- package/web/dashboard.html +29 -2
- package/web/dashboard.js +296 -33
- package/workflow/builtin_caps.mjs +94 -0
- package/workflow/declarative.mjs +101 -0
- package/workflow/named.mjs +71 -0
- package/workflow/named_cron.mjs +50 -0
- package/workflow/nodes.mjs +67 -0
- package/workflow/run_request.mjs +74 -0
- package/workflow/yaml_min.mjs +97 -0
package/tui/slash_popup.mjs
CHANGED
|
@@ -86,6 +86,10 @@ export function SlashPopup({
|
|
|
86
86
|
selectedIndex = 0,
|
|
87
87
|
maxRows = 8,
|
|
88
88
|
columns,
|
|
89
|
+
// When true, always render the selectable chooser (used by the arg popup,
|
|
90
|
+
// whose buffer has a space — which would otherwise collapse a single
|
|
91
|
+
// candidate into the non-selectable inline hint).
|
|
92
|
+
forceChooser = false,
|
|
89
93
|
}) {
|
|
90
94
|
if (!commands || commands.length === 0) return null;
|
|
91
95
|
const cols = columns
|
|
@@ -97,7 +101,7 @@ export function SlashPopup({
|
|
|
97
101
|
|
|
98
102
|
// Inline-hint mode: buffer already has args + a single match. Render
|
|
99
103
|
// the help text on one dimmed line. No border, no chooser.
|
|
100
|
-
const isInlineHint = buffer.includes(' ') && commands.length === 1;
|
|
104
|
+
const isInlineHint = !forceChooser && buffer.includes(' ') && commands.length === 1;
|
|
101
105
|
if (isInlineHint) {
|
|
102
106
|
const c = commands[0];
|
|
103
107
|
return React.createElement(
|
package/tui/splash.mjs
CHANGED
|
@@ -49,6 +49,19 @@ function shortCwd(cwd) {
|
|
|
49
49
|
return cwd;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Provider-aware splash tip. The $0 "learning loop" pitch only applies when
|
|
53
|
+
// the trainer runs on claude-cli (Claude Pro/Max subscription is keyless and
|
|
54
|
+
// free); for any other provider the loop costs API tokens, so showing the
|
|
55
|
+
// Claude pitch is misleading. The trainer provider wins because the pitch is
|
|
56
|
+
// about the learning loop, not the chat provider (see splash info line).
|
|
57
|
+
export function pickSplashTip({ provider, trainer = {} } = {}) {
|
|
58
|
+
const trainerProvider = trainer.provider || provider;
|
|
59
|
+
if (trainerProvider === 'claude-cli') {
|
|
60
|
+
return 'Tip: trainer learns from your Claude Pro subscription at $0.';
|
|
61
|
+
}
|
|
62
|
+
return 'Tip: /help lists every command, tool, and skill.';
|
|
63
|
+
}
|
|
64
|
+
|
|
52
65
|
function toolRow({ category, sensitive, verbs }) {
|
|
53
66
|
const label = sensitive ? `${category}*` : category;
|
|
54
67
|
const tail = verbs.slice(0, 6).join(' · ');
|
|
@@ -153,7 +166,7 @@ function renderWide(props, cols) {
|
|
|
153
166
|
if (sessionId) lines.push(`${LMARGIN}Session: ${sessionId}`);
|
|
154
167
|
lines.push('');
|
|
155
168
|
lines.push(`${LMARGIN}Welcome to lazyclaw. Type your message or /help for commands.`);
|
|
156
|
-
lines.push(`${LMARGIN}+
|
|
169
|
+
lines.push(`${LMARGIN}+ ${pickSplashTip({ provider, trainer })}`);
|
|
157
170
|
// v5.4.3 — the baked-in status row that used to live here duplicated
|
|
158
171
|
// ReplApp's real <StatusBar/> (tui/repl.mjs:476). Removing it cuts 4
|
|
159
172
|
// rows from the splash AND eliminates the visible overlap the user
|
|
@@ -237,7 +250,7 @@ function renderMedium(props, cols) {
|
|
|
237
250
|
if (sessionId) lines.push(`${LMARGIN}Session: ${sessionId}`);
|
|
238
251
|
lines.push('');
|
|
239
252
|
lines.push(`${LMARGIN}Welcome to lazyclaw. Type your message or /help for commands.`);
|
|
240
|
-
lines.push(`${LMARGIN}+
|
|
253
|
+
lines.push(`${LMARGIN}+ ${pickSplashTip({ provider, trainer })}`);
|
|
241
254
|
// v5.4.3 — baked-in status row removed; ReplApp renders the real one.
|
|
242
255
|
|
|
243
256
|
return lines;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// tui/status_bar.mjs — the sticky single/double status row above the chat
|
|
2
|
+
// input. Row 1: streaming indicator · provider · model · ctx gauge. Row 2
|
|
3
|
+
// (only when the HUD is enabled): real-time usage, cost, trainer, orchestrator.
|
|
4
|
+
// Extracted from repl.mjs so the HUD can grow without pushing repl.mjs over the
|
|
5
|
+
// file-size ratchet.
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { Box, Text } from 'ink';
|
|
9
|
+
import { theme } from './theme.mjs';
|
|
10
|
+
import { formatHudRow, formatGauge } from './hud.mjs';
|
|
11
|
+
|
|
12
|
+
export function StatusBar({ provider, model, streaming, ctxUsed, ctxTotal, hud }) {
|
|
13
|
+
// Numbers are computed upstream (chat-history budget, not provider self-report);
|
|
14
|
+
// formatGauge only changes the RENDERING — adds percent + bar + warn marker.
|
|
15
|
+
const ctx = (ctxUsed != null && ctxTotal != null) ? formatGauge(ctxUsed, ctxTotal) : '--';
|
|
16
|
+
const indicator = streaming ? theme.accent('● streaming') : theme.dim('○ idle');
|
|
17
|
+
const prov = provider || '?';
|
|
18
|
+
const mdl = model || '?';
|
|
19
|
+
const hudRow = hud ? formatHudRow(hud) : '';
|
|
20
|
+
return React.createElement(
|
|
21
|
+
Box,
|
|
22
|
+
{ flexShrink: 0, flexDirection: 'column', paddingX: 1 },
|
|
23
|
+
React.createElement(Text, null, `${indicator} ${prov} · ${mdl} ctx ${ctx}`),
|
|
24
|
+
hudRow ? React.createElement(Text, null, theme.dim(hudRow)) : null,
|
|
25
|
+
);
|
|
26
|
+
}
|
package/tui/theme.mjs
CHANGED
|
@@ -10,6 +10,34 @@ import chalk from 'chalk';
|
|
|
10
10
|
const AMBER_HEX = '#FFB347';
|
|
11
11
|
const BORDER_HEX = '#5A5A5A';
|
|
12
12
|
|
|
13
|
+
// Central color gate. Color is OFF when the NO_COLOR standard applies
|
|
14
|
+
// (https://no-color.org — any non-empty NO_COLOR), the terminal is `dumb`,
|
|
15
|
+
// or the target stream isn't a TTY (piped / screen-reader / CI). Evaluated
|
|
16
|
+
// per call so tests and runtime env changes are honored without re-importing.
|
|
17
|
+
// `stream` defaults to stdout; tests pass a fake `{ isTTY }` to probe the
|
|
18
|
+
// gate without mutating the real process streams. chalk self-detects the same
|
|
19
|
+
// conditions (level 0), so its tokens degrade automatically; this gate is for
|
|
20
|
+
// callers that emit raw ANSI.
|
|
21
|
+
export function colorEnabled(stream = process.stdout) {
|
|
22
|
+
if (process.env.NO_COLOR) return false;
|
|
23
|
+
if (process.env.TERM === 'dumb') return false;
|
|
24
|
+
if (!stream || !stream.isTTY) return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Wrap `str` in a raw SGR escape (`\x1b[<code>m … \x1b[0m`) when color is on,
|
|
29
|
+
// otherwise return it unchanged. Keeps inline-ANSI call sites accessible
|
|
30
|
+
// without each one re-implementing the NO_COLOR check.
|
|
31
|
+
export function paint(code, str) {
|
|
32
|
+
if (!colorEnabled()) return str;
|
|
33
|
+
return `\x1b[${code}m${str}\x1b[0m`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Align chalk's own level with our gate so theme tokens (amber/dim/accent)
|
|
37
|
+
// and raw-ANSI emitters agree under NO_COLOR / dumb / non-TTY. chalk reads
|
|
38
|
+
// this at import; our gate stays the runtime source of truth for raw ANSI.
|
|
39
|
+
if (!colorEnabled()) chalk.level = 0;
|
|
40
|
+
|
|
13
41
|
function amber(text) {
|
|
14
42
|
return chalk.hex(AMBER_HEX)(text);
|
|
15
43
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/web/dashboard.css
CHANGED
|
@@ -267,3 +267,80 @@
|
|
|
267
267
|
display: flex;
|
|
268
268
|
justify-content: space-between;
|
|
269
269
|
}
|
|
270
|
+
|
|
271
|
+
/* ── Team Live tab ─────────────────────────────────────────── */
|
|
272
|
+
.team-live { display: flex; gap: 16px; align-items: stretch; flex-wrap: wrap; }
|
|
273
|
+
.team-canvas {
|
|
274
|
+
flex: 2 1 420px; min-width: 0; min-height: 260px;
|
|
275
|
+
background: var(--card); border: 1px solid var(--border); border-radius: 12px;
|
|
276
|
+
padding: 20px; display: flex; flex-direction: column; align-items: center; gap: 18px;
|
|
277
|
+
}
|
|
278
|
+
.team-detail {
|
|
279
|
+
flex: 1 1 280px; min-width: 0;
|
|
280
|
+
background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 16px;
|
|
281
|
+
font-size: 13px;
|
|
282
|
+
}
|
|
283
|
+
/* tree: a lead row on top, a wrapping children row below, with connectors */
|
|
284
|
+
.team-row { display: flex; gap: 18px; flex-wrap: wrap; justify-content: center; }
|
|
285
|
+
.team-children { position: relative; padding-top: 18px; }
|
|
286
|
+
.team-children::before {
|
|
287
|
+
content: ''; position: absolute; top: 0; left: 50%; width: 1px; height: 18px;
|
|
288
|
+
background: var(--border); transform: translateX(-50%);
|
|
289
|
+
}
|
|
290
|
+
.tagent {
|
|
291
|
+
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
|
292
|
+
width: 104px; cursor: pointer; background: none; border: 0; color: inherit; font: inherit;
|
|
293
|
+
padding: 6px; border-radius: 10px;
|
|
294
|
+
}
|
|
295
|
+
.tagent:hover { background: rgba(217,179,90,0.06); }
|
|
296
|
+
.tagent:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
297
|
+
.tagent[aria-selected="true"] { background: rgba(217,179,90,0.12); }
|
|
298
|
+
.tagent-avatar {
|
|
299
|
+
width: 56px; height: 56px; border-radius: 50%;
|
|
300
|
+
position: relative; overflow: hidden;
|
|
301
|
+
display: flex; align-items: center; justify-content: center; font-size: 24px;
|
|
302
|
+
background: #1e2130; border: 3px solid #475569;
|
|
303
|
+
}
|
|
304
|
+
.tagent-avatar img {
|
|
305
|
+
position: absolute; inset: 0; width: 100%; height: 100%;
|
|
306
|
+
object-fit: cover; object-position: center top;
|
|
307
|
+
image-rendering: pixelated;
|
|
308
|
+
}
|
|
309
|
+
.detail-avatar {
|
|
310
|
+
width: 30px; height: 30px; border-radius: 50%; object-fit: cover;
|
|
311
|
+
object-position: center top; image-rendering: pixelated; vertical-align: middle;
|
|
312
|
+
margin-right: 8px; background: #1e2130; border: 2px solid #475569;
|
|
313
|
+
}
|
|
314
|
+
.tagent.working .tagent-avatar { border-color: #34d399; box-shadow: 0 0 0 4px rgba(52,211,153,0.16); }
|
|
315
|
+
.tagent.idle .tagent-avatar { border-color: #475569; }
|
|
316
|
+
.tagent.delegating .tagent-avatar { animation: tdelegate 900ms ease-out; }
|
|
317
|
+
@keyframes tdelegate {
|
|
318
|
+
0% { box-shadow: 0 0 0 0 rgba(217,179,90,0.55); }
|
|
319
|
+
100% { box-shadow: 0 0 0 14px rgba(217,179,90,0); }
|
|
320
|
+
}
|
|
321
|
+
.tagent-name { font-size: 12px; font-weight: 600; max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
322
|
+
.tagent-status { font-size: 10px; }
|
|
323
|
+
.tagent.working .tagent-status { color: #34d399; }
|
|
324
|
+
.tagent.idle .tagent-status { color: var(--dim); }
|
|
325
|
+
.harness-badge {
|
|
326
|
+
font-size: 9px; background: #312e81; color: #c7d2fe; border-radius: 5px; padding: 1px 6px;
|
|
327
|
+
max-width: 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
328
|
+
}
|
|
329
|
+
.team-detail h3 { margin: 0 0 6px; font-size: 15px; }
|
|
330
|
+
.team-detail .kv { margin: 8px 0; }
|
|
331
|
+
.team-detail .kv .label { color: var(--dim); font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
332
|
+
.team-detail .activity { margin: 3px 0; color: #cbd5e1; line-height: 1.6; }
|
|
333
|
+
.team-feed-wrap { margin-top: 16px; }
|
|
334
|
+
.team-feed { list-style: none; margin: 6px 0 0; padding: 0; max-height: 180px; overflow-y: auto; font-size: 12px; }
|
|
335
|
+
.team-feed li { padding: 4px 0; border-top: 1px solid var(--border); color: var(--dim); }
|
|
336
|
+
.team-feed li .who { color: var(--fg, #e8e8f0); font-weight: 600; }
|
|
337
|
+
.team-feed li .arrow { color: #34d399; }
|
|
338
|
+
|
|
339
|
+
@media (max-width: 760px) {
|
|
340
|
+
.team-live { flex-direction: column; }
|
|
341
|
+
.team-canvas, .team-detail { flex: 1 1 auto; }
|
|
342
|
+
}
|
|
343
|
+
@media (prefers-reduced-motion: reduce) {
|
|
344
|
+
.tagent.working .tagent-avatar { box-shadow: none; }
|
|
345
|
+
.tagent.delegating .tagent-avatar { animation: none; }
|
|
346
|
+
}
|
package/web/dashboard.html
CHANGED
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
<meta charset="utf-8">
|
|
15
15
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
16
16
|
<title>LazyClaw</title>
|
|
17
|
-
|
|
17
|
+
<!-- Absolute paths: the daemon serves this page at /dashboard AND /dashboard/
|
|
18
|
+
(and /), so a relative href would resolve to /dashboard/dashboard.css and
|
|
19
|
+
404 — leaving the page unstyled with every tab stuck on "Loading…". -->
|
|
20
|
+
<link rel="stylesheet" href="/dashboard.css">
|
|
21
|
+
<link rel="icon" href="data:,">
|
|
18
22
|
</head>
|
|
19
23
|
<body>
|
|
20
24
|
<header>
|
|
@@ -38,6 +42,7 @@
|
|
|
38
42
|
<button data-tab="agents">Agents</button>
|
|
39
43
|
<button data-tab="teams">Teams</button>
|
|
40
44
|
<button data-tab="tasks">Tasks</button>
|
|
45
|
+
<button data-tab="team">Team Live</button>
|
|
41
46
|
<button data-tab="trainer">Trainer</button>
|
|
42
47
|
<button data-tab="recall">Recall</button>
|
|
43
48
|
<button data-tab="sandbox">Sandbox</button>
|
|
@@ -182,6 +187,28 @@
|
|
|
182
187
|
<div id="tasks-list"><div class="empty">Loading…</div></div>
|
|
183
188
|
</section>
|
|
184
189
|
|
|
190
|
+
<section id="tab-team">
|
|
191
|
+
<h2>Team Live</h2>
|
|
192
|
+
<div class="toolbar">
|
|
193
|
+
<label for="team-select" class="dim">Team</label>
|
|
194
|
+
<select id="team-select" aria-label="Select a team to watch"></select>
|
|
195
|
+
<button class="btn btn-secondary" onclick="LOADERS.team()">Refresh</button>
|
|
196
|
+
<span class="dim" id="team-conn" role="status" aria-live="polite">○ connecting…</span>
|
|
197
|
+
</div>
|
|
198
|
+
<div id="team-live" class="team-live">
|
|
199
|
+
<div id="team-canvas" class="team-canvas" role="tree" aria-label="Agent team">
|
|
200
|
+
<div class="empty">Loading…</div>
|
|
201
|
+
</div>
|
|
202
|
+
<aside id="team-detail" class="team-detail" aria-live="polite">
|
|
203
|
+
<div class="empty">Click an agent to see its harness and what it's working on.</div>
|
|
204
|
+
</aside>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="team-feed-wrap">
|
|
207
|
+
<div class="label">Live activity</div>
|
|
208
|
+
<ul id="team-feed" class="team-feed" aria-live="polite"></ul>
|
|
209
|
+
</div>
|
|
210
|
+
</section>
|
|
211
|
+
|
|
185
212
|
<section id="tab-trainer">
|
|
186
213
|
<h2>Trainer</h2>
|
|
187
214
|
<div class="toolbar">
|
|
@@ -244,6 +271,6 @@
|
|
|
244
271
|
</div>
|
|
245
272
|
</div>
|
|
246
273
|
|
|
247
|
-
<script src="dashboard.js"></script>
|
|
274
|
+
<script src="/dashboard.js"></script>
|
|
248
275
|
</body>
|
|
249
276
|
</html>
|