maqcli 0.6.6 → 0.8.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/dist/core/exec.d.ts +40 -0
- package/dist/core/exec.js +47 -2
- package/dist/core/launcher.js +1 -1
- package/dist/core/update.d.ts +27 -76
- package/dist/core/update.js +45 -116
- package/dist/index.js +1 -1
- package/dist/server/daemon.js +1 -1
- package/dist/server/webui.d.ts +18 -11
- package/dist/server/webui.js +537 -262
- package/package.json +1 -1
package/dist/server/webui.js
CHANGED
|
@@ -1,188 +1,388 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* webui.ts — the self-contained
|
|
3
|
-
*
|
|
2
|
+
* webui.ts — the self-contained control surface the daemon serves at `/app`
|
|
3
|
+
* (and `/`). Zero build step, zero dependencies: one HTML document with
|
|
4
4
|
* inline CSS + vanilla JS. It renders the SAME normalized event stream the
|
|
5
5
|
* mobile app consumes, so there is no second source of truth.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
7
|
+
* Design system: dark OLED canvas, two elevation surfaces, one signal accent
|
|
8
|
+
* (teal — "alive/running", not the generic AI purple/blue gradient). Type:
|
|
9
|
+
* Space Grotesk for the brand mark + section labels (used sparingly), Inter
|
|
10
|
+
* for UI/body, JetBrains Mono for the console/data surfaces. Signature
|
|
11
|
+
* element: a live depth-of-work TIMELINE — phases render as connected nodes
|
|
12
|
+
* that fill in as work progresses, with orchestration rounds (parallel/loop/
|
|
13
|
+
* safe) nesting as a sub-rail beneath the active phase — replacing static
|
|
14
|
+
* chips with something that actually shows depth of work over time.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
+
* Layout: a named-grid-area app shell. Sidebar collapses to a drawer under
|
|
17
|
+
* 900px; the preview panel becomes a full-screen sheet under 1200px; the
|
|
18
|
+
* composer sticks to the bottom on narrow viewports. Motion is 150/220ms
|
|
19
|
+
* ease-out and fully disabled under prefers-reduced-motion.
|
|
20
|
+
*
|
|
21
|
+
* Auth: the daemon requires a Bearer token for /v1/*. The page reads it from
|
|
16
22
|
* ?token=… or a prompt, keeps it in memory only, and streams SSE via fetch +
|
|
17
|
-
* ReadableStream (so the token rides in the Authorization header, never the
|
|
23
|
+
* ReadableStream (so the token rides in the Authorization header, never the
|
|
24
|
+
* URL).
|
|
18
25
|
*/
|
|
19
26
|
export function webuiHtml(version) {
|
|
20
27
|
// NB: the client script uses only single/double quotes and string
|
|
21
28
|
// concatenation — no backticks / ${} — so it lives safely inside this
|
|
22
|
-
// template literal.
|
|
29
|
+
// template literal (a backtick here previously broke the TS build twice).
|
|
23
30
|
return `<!doctype html>
|
|
24
31
|
<html lang="en"><head>
|
|
25
32
|
<meta charset="utf-8">
|
|
26
33
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
27
|
-
<title>MAQ ·
|
|
34
|
+
<title>MAQ · control</title>
|
|
28
35
|
<style>
|
|
36
|
+
@media (prefers-reduced-motion: no-preference){
|
|
37
|
+
:root{ --dur-1:150ms; --dur-2:220ms; --ease:cubic-bezier(.2,.7,.2,1); }
|
|
38
|
+
}
|
|
39
|
+
@media (prefers-reduced-motion: reduce){
|
|
40
|
+
:root{ --dur-1:0ms; --dur-2:0ms; --ease:linear; }
|
|
41
|
+
*{ animation-duration:0ms !important; transition-duration:0ms !important; }
|
|
42
|
+
}
|
|
29
43
|
:root{
|
|
30
|
-
|
|
31
|
-
--
|
|
32
|
-
--
|
|
44
|
+
/* --- color tokens --- */
|
|
45
|
+
--bg:#05060a; --surface:#0d0f16; --surface-2:#12151f; --border:#1c2130; --border-soft:#161a26;
|
|
46
|
+
--ink:#e8ebf3; --ink-dim:#8890a3; --ink-faint:#565d70;
|
|
47
|
+
--accent:#4fd1c5; --accent-ink:#04211d; --accent-soft:rgba(79,209,197,.14); --accent-line:rgba(79,209,197,.4);
|
|
48
|
+
--warn:#f2b84b; --warn-soft:rgba(242,184,75,.14);
|
|
49
|
+
--danger:#f2555a; --danger-soft:rgba(242,85,90,.14);
|
|
50
|
+
--ok:#34d399; --ok-soft:rgba(52,211,153,.14);
|
|
51
|
+
/* --- type scale --- */
|
|
52
|
+
--fs-11:.6875rem; --fs-12:.75rem; --fs-13:.8125rem; --fs-15:.9375rem; --fs-19:1.1875rem; --fs-24:1.5rem;
|
|
53
|
+
--font-display:'Space Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
54
|
+
--font-body:'Inter',ui-sans-serif,-apple-system,'Segoe UI',Roboto,sans-serif;
|
|
55
|
+
--font-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
|
|
56
|
+
/* --- spacing --- */
|
|
57
|
+
--sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-8:32px;
|
|
58
|
+
--radius:10px; --radius-sm:7px; --radius-lg:14px;
|
|
33
59
|
}
|
|
34
|
-
*{box-sizing:border-box}
|
|
35
|
-
body{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.
|
|
60
|
+
*{box-sizing:border-box}
|
|
61
|
+
html,body{height:100%}
|
|
62
|
+
body{
|
|
63
|
+
margin:0; background:var(--bg); color:var(--ink); font-family:var(--font-body);
|
|
64
|
+
font-size:var(--fs-13); line-height:1.55; -webkit-font-smoothing:antialiased;
|
|
65
|
+
}
|
|
66
|
+
h1,h2,h3,h4{font-family:var(--font-display);font-weight:600;margin:0;letter-spacing:-.01em}
|
|
67
|
+
.mono{font-family:var(--font-mono)}
|
|
68
|
+
a{color:var(--accent)}
|
|
69
|
+
button,input,select,textarea{font-family:inherit;color:inherit}
|
|
70
|
+
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
|
|
71
|
+
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
|
|
72
|
+
.eyebrow{font-size:var(--fs-11);text-transform:uppercase;letter-spacing:.09em;color:var(--ink-faint);font-weight:600}
|
|
73
|
+
|
|
74
|
+
/* ---------------- app shell ---------------- */
|
|
75
|
+
#app{
|
|
76
|
+
display:grid;height:100vh;
|
|
77
|
+
grid-template-columns:248px 1fr 0px;
|
|
78
|
+
grid-template-rows:56px 1fr;
|
|
79
|
+
grid-template-areas:"topbar topbar topbar" "sidebar main preview";
|
|
80
|
+
transition:grid-template-columns var(--dur-2) var(--ease);
|
|
81
|
+
}
|
|
82
|
+
#app.preview-open{grid-template-columns:248px 1fr 380px}
|
|
83
|
+
@media (max-width:1200px){
|
|
84
|
+
#app.preview-open{grid-template-columns:248px 1fr}
|
|
85
|
+
#app.preview-open #preview{position:fixed;inset:56px 0 0 0;z-index:50;width:100%}
|
|
86
|
+
}
|
|
87
|
+
@media (max-width:900px){
|
|
88
|
+
#app{grid-template-columns:1fr}
|
|
89
|
+
#app{grid-template-areas:"topbar" "main"}
|
|
90
|
+
#sidebar{position:fixed;inset:56px 0 0 0;z-index:60;width:280px;transform:translateX(-100%);
|
|
91
|
+
transition:transform var(--dur-2) var(--ease)}
|
|
92
|
+
#sidebar.open{transform:translateX(0)}
|
|
93
|
+
#scrim{position:fixed;inset:56px 0 0 0;background:#000a;z-index:55;display:none}
|
|
94
|
+
#scrim.show{display:block}
|
|
95
|
+
#app.preview-open #preview{inset:56px 0 0 0}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ---------------- topbar ---------------- */
|
|
99
|
+
#topbar{
|
|
100
|
+
grid-area:topbar;display:flex;align-items:center;gap:var(--sp-3);padding:0 var(--sp-4);
|
|
101
|
+
border-bottom:1px solid var(--border);background:var(--surface);
|
|
102
|
+
}
|
|
103
|
+
.menu-btn{display:none}
|
|
104
|
+
@media (max-width:900px){ .menu-btn{display:inline-flex} }
|
|
105
|
+
.brand{display:flex;align-items:center;gap:var(--sp-2)}
|
|
106
|
+
.brand-mark{width:22px;height:22px;border-radius:6px;background:var(--accent-soft);border:1px solid var(--accent-line);
|
|
107
|
+
display:flex;align-items:center;justify-content:center;color:var(--accent);font-family:var(--font-display);
|
|
108
|
+
font-weight:700;font-size:12px}
|
|
109
|
+
.brand h1{font-size:var(--fs-15)}
|
|
110
|
+
.status-dot{width:7px;height:7px;border-radius:50%;background:var(--ink-faint);flex:none}
|
|
111
|
+
.status-dot.on{background:var(--ok);box-shadow:0 0 0 3px var(--ok-soft)}
|
|
46
112
|
.grow{flex:1}
|
|
47
|
-
.
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
.
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
.
|
|
62
|
-
.
|
|
63
|
-
.badge{
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
.
|
|
67
|
-
.
|
|
68
|
-
|
|
69
|
-
.
|
|
70
|
-
.
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
.
|
|
79
|
-
.
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
.
|
|
89
|
-
.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
.
|
|
113
|
+
.topbar-nav{display:flex;gap:var(--sp-1);flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none}
|
|
114
|
+
.topbar-nav::-webkit-scrollbar{display:none}
|
|
115
|
+
.tbtn{
|
|
116
|
+
background:transparent;color:var(--ink-dim);border:1px solid transparent;border-radius:var(--radius-sm);
|
|
117
|
+
padding:6px 11px;cursor:pointer;font-size:var(--fs-12);font-weight:500;white-space:nowrap;
|
|
118
|
+
display:inline-flex;align-items:center;gap:6px;transition:background var(--dur-1) var(--ease),color var(--dur-1) var(--ease);
|
|
119
|
+
}
|
|
120
|
+
.tbtn:hover{background:var(--surface-2);color:var(--ink)}
|
|
121
|
+
.tbtn.accent{color:var(--accent)}
|
|
122
|
+
.tbtn svg{width:14px;height:14px;flex:none}
|
|
123
|
+
|
|
124
|
+
/* ---------------- sidebar ---------------- */
|
|
125
|
+
#sidebar{grid-area:sidebar;background:var(--surface);border-right:1px solid var(--border);overflow-y:auto;
|
|
126
|
+
display:flex;flex-direction:column}
|
|
127
|
+
.side-sec{padding:var(--sp-3) var(--sp-4) var(--sp-2)}
|
|
128
|
+
.side-sec-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--sp-1)}
|
|
129
|
+
.count-badge{background:var(--surface-2);border:1px solid var(--border);border-radius:999px;color:var(--ink-dim);
|
|
130
|
+
font-size:10px;padding:1px 7px;font-weight:600}
|
|
131
|
+
.count-badge.alert{background:var(--warn-soft);border-color:var(--warn);color:var(--warn)}
|
|
132
|
+
.list{display:flex;flex-direction:column}
|
|
133
|
+
.row{padding:9px var(--sp-4);border-bottom:1px solid var(--border-soft);cursor:pointer;
|
|
134
|
+
display:flex;flex-direction:column;gap:3px;transition:background var(--dur-1) var(--ease)}
|
|
135
|
+
.row:hover{background:var(--surface-2)}
|
|
136
|
+
.row.active{background:var(--surface-2);box-shadow:inset 3px 0 0 var(--accent)}
|
|
137
|
+
.row-title{font-size:var(--fs-13);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
138
|
+
.row-meta{font-size:var(--fs-11);color:var(--ink-faint);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
|
139
|
+
.tag{border:1px solid var(--border);border-radius:999px;padding:0 6px;font-size:10px;font-weight:600}
|
|
140
|
+
.tag.running{color:var(--accent);border-color:var(--accent-line)}
|
|
141
|
+
.tag.done{color:var(--ok);border-color:var(--ok)}
|
|
142
|
+
.tag.error,.tag.cancelled{color:var(--danger);border-color:var(--danger)}
|
|
143
|
+
.tag.paused{color:var(--warn);border-color:var(--warn)}
|
|
144
|
+
.empty-state{padding:var(--sp-6) var(--sp-4);text-align:center;color:var(--ink-faint);font-size:var(--fs-12)}
|
|
145
|
+
.empty-state svg{width:28px;height:28px;color:var(--ink-faint);margin-bottom:var(--sp-2);opacity:.6}
|
|
146
|
+
.skeleton{background:linear-gradient(90deg,var(--surface-2) 25%,var(--border) 37%,var(--surface-2) 63%);
|
|
147
|
+
background-size:400% 100%;animation:shimmer 1.4s ease infinite;border-radius:6px;height:13px;margin:4px var(--sp-4)}
|
|
148
|
+
@keyframes shimmer{0%{background-position:100% 0}100%{background-position:0 0}}
|
|
149
|
+
@media (prefers-reduced-motion: reduce){ .skeleton{animation:none;opacity:.5} }
|
|
150
|
+
|
|
151
|
+
/* ---------------- main / composer ---------------- */
|
|
152
|
+
#main{grid-area:main;display:flex;flex-direction:column;min-width:0;background:var(--bg)}
|
|
153
|
+
#composer{padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--border)}
|
|
154
|
+
.mode-rail{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-2);margin-bottom:var(--sp-3)}
|
|
155
|
+
@media (max-width:640px){ .mode-rail{grid-template-columns:repeat(2,1fr)} }
|
|
156
|
+
.mode-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
|
|
157
|
+
padding:var(--sp-2) var(--sp-3);cursor:pointer;text-align:left;transition:border-color var(--dur-1) var(--ease),
|
|
158
|
+
background var(--dur-1) var(--ease)}
|
|
159
|
+
.mode-card:hover{border-color:var(--ink-faint)}
|
|
160
|
+
.mode-card.sel{border-color:var(--accent);background:var(--accent-soft)}
|
|
161
|
+
.mode-card h4{font-size:var(--fs-12);color:var(--ink);font-family:var(--font-body);font-weight:600}
|
|
162
|
+
.mode-card p{margin:2px 0 0;font-size:var(--fs-11);color:var(--ink-faint);line-height:1.4}
|
|
163
|
+
.mode-card.sel h4{color:var(--accent)}
|
|
164
|
+
.composer-row{display:flex;gap:var(--sp-2);align-items:flex-end}
|
|
165
|
+
#goal{
|
|
166
|
+
flex:1;resize:vertical;min-height:46px;max-height:180px;background:var(--surface);color:var(--ink);
|
|
167
|
+
border:1px solid var(--border);border-radius:var(--radius);padding:11px 13px;font:var(--fs-13)/1.5 var(--font-body);
|
|
168
|
+
}
|
|
169
|
+
#goal::placeholder{color:var(--ink-faint)}
|
|
170
|
+
#goal:focus{outline:none;border-color:var(--accent)}
|
|
171
|
+
.send-btn{
|
|
172
|
+
background:var(--accent);color:var(--accent-ink);font-weight:700;border:none;border-radius:var(--radius);
|
|
173
|
+
padding:12px 18px;cursor:pointer;font-size:var(--fs-13);display:inline-flex;align-items:center;gap:7px;
|
|
174
|
+
transition:filter var(--dur-1) var(--ease)
|
|
175
|
+
}
|
|
176
|
+
.send-btn:hover{filter:brightness(1.08)}
|
|
177
|
+
.send-btn:disabled{opacity:.45;cursor:not-allowed;filter:none}
|
|
178
|
+
.send-btn svg{width:14px;height:14px}
|
|
179
|
+
.opts-row{display:flex;gap:var(--sp-4);align-items:center;margin-top:var(--sp-3);flex-wrap:wrap}
|
|
180
|
+
.opt{display:flex;align-items:center;gap:6px;color:var(--ink-dim);font-size:var(--fs-12)}
|
|
181
|
+
.opt select,.opt input[type=number]{background:var(--surface);color:var(--ink);border:1px solid var(--border);
|
|
182
|
+
border-radius:6px;padding:3px 7px;font-size:var(--fs-12)}
|
|
183
|
+
.opt input[type=checkbox]{accent-color:var(--accent);width:14px;height:14px}
|
|
184
|
+
.kbd-hint{color:var(--ink-faint);font-size:var(--fs-11);margin-left:auto}
|
|
185
|
+
.kbd-hint kbd{background:var(--surface-2);border:1px solid var(--border);border-radius:4px;padding:1px 5px;
|
|
186
|
+
font-family:var(--font-mono);font-size:10px}
|
|
187
|
+
|
|
188
|
+
/* ---------------- timeline (signature element) ---------------- */
|
|
189
|
+
#timeline{padding:var(--sp-3) var(--sp-5);border-bottom:1px solid var(--border);overflow-x:auto}
|
|
190
|
+
.track{display:flex;align-items:center;min-height:34px}
|
|
191
|
+
.node{display:flex;align-items:center;flex:none}
|
|
192
|
+
.node-dot{width:20px;height:20px;border-radius:50%;border:2px solid var(--border);background:var(--surface);
|
|
193
|
+
display:flex;align-items:center;justify-content:center;flex:none;transition:border-color var(--dur-2) var(--ease),
|
|
194
|
+
background var(--dur-2) var(--ease)}
|
|
195
|
+
.node-dot svg{width:11px;height:11px;opacity:0}
|
|
196
|
+
.node.done .node-dot{border-color:var(--ok);background:var(--ok-soft)}
|
|
197
|
+
.node.done .node-dot svg{opacity:1;color:var(--ok)}
|
|
198
|
+
.node.active .node-dot{border-color:var(--accent);background:var(--accent-soft);
|
|
199
|
+
box-shadow:0 0 0 4px var(--accent-soft)}
|
|
200
|
+
.node.active .node-dot{animation:pulse 1.6s ease-in-out infinite}
|
|
201
|
+
@keyframes pulse{0%,100%{box-shadow:0 0 0 3px var(--accent-soft)}50%{box-shadow:0 0 0 7px transparent}}
|
|
202
|
+
.node-label{font-size:var(--fs-12);color:var(--ink-faint);margin-left:7px;white-space:nowrap}
|
|
203
|
+
.node.done .node-label{color:var(--ok)}
|
|
204
|
+
.node.active .node-label{color:var(--accent);font-weight:600}
|
|
205
|
+
.node-line{width:28px;height:1px;background:var(--border);flex:none;margin:0 4px}
|
|
206
|
+
.node.done + .node-line{background:var(--ok)}
|
|
207
|
+
.sub-rail{display:flex;align-items:center;gap:6px;margin-top:6px;margin-left:26px;flex-wrap:wrap}
|
|
208
|
+
.sub-chip{font-size:10px;color:var(--accent);background:var(--accent-soft);border:1px solid var(--accent-line);
|
|
209
|
+
border-radius:999px;padding:2px 8px;font-weight:600}
|
|
210
|
+
|
|
211
|
+
/* ---------------- console ---------------- */
|
|
212
|
+
#console{flex:1;overflow-y:auto;padding:var(--sp-3) var(--sp-5);display:flex;flex-direction:column;gap:6px}
|
|
213
|
+
.ev-card{display:flex;gap:var(--sp-2);padding:7px 9px;border-radius:8px;border:1px solid transparent;
|
|
214
|
+
font-size:var(--fs-12);animation:fade-in var(--dur-2) var(--ease)}
|
|
215
|
+
@keyframes fade-in{from{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}
|
|
216
|
+
@media (prefers-reduced-motion: reduce){ .ev-card{animation:none} }
|
|
217
|
+
.ev-ic{width:16px;height:16px;flex:none;margin-top:1px}
|
|
218
|
+
.ev-ic svg{width:100%;height:100%}
|
|
219
|
+
.ev-body{flex:1;min-width:0}
|
|
220
|
+
.ev-text{white-space:pre-wrap;word-break:break-word;font-family:var(--font-mono);font-size:11.5px;line-height:1.5}
|
|
221
|
+
.ev-time{color:var(--ink-faint);font-size:10px;margin-left:7px}
|
|
222
|
+
.c-accent{color:var(--accent)} .c-dim{color:var(--ink-faint)} .c-ok{color:var(--ok)}
|
|
223
|
+
.c-danger{color:var(--danger)} .c-warn{color:var(--warn)}
|
|
224
|
+
.ev-card.bg-danger{background:var(--danger-soft)} .ev-card.bg-warn{background:var(--warn-soft)}
|
|
225
|
+
|
|
226
|
+
/* ---------------- preview panel ---------------- */
|
|
227
|
+
#preview{grid-area:preview;background:var(--surface);border-left:1px solid var(--border);overflow-y:auto;min-width:0}
|
|
228
|
+
.panel-head{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-3) var(--sp-4);
|
|
229
|
+
border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--surface);z-index:2}
|
|
230
|
+
.panel-head h3{font-size:var(--fs-13)}
|
|
231
|
+
.icon-btn{background:transparent;border:1px solid var(--border);border-radius:7px;color:var(--ink-dim);
|
|
232
|
+
cursor:pointer;padding:5px 9px;font-size:var(--fs-12);display:inline-flex;align-items:center;gap:5px}
|
|
233
|
+
.icon-btn:hover{color:var(--ink);border-color:var(--ink-faint)}
|
|
234
|
+
|
|
235
|
+
pre.data{white-space:pre-wrap;word-break:break-word;font-family:var(--font-mono);font-size:11.5px;color:var(--ink);
|
|
236
|
+
padding:var(--sp-4);margin:0;line-height:1.6}
|
|
237
|
+
|
|
238
|
+
/* ---------------- full-screen overlays ---------------- */
|
|
239
|
+
.overlay{position:fixed;inset:0;background:var(--bg);z-index:100;display:none;flex-direction:column}
|
|
94
240
|
.overlay.show{display:flex}
|
|
95
|
-
.
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
.
|
|
101
|
-
.
|
|
102
|
-
.
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
.
|
|
106
|
-
.
|
|
107
|
-
.
|
|
108
|
-
|
|
109
|
-
.
|
|
110
|
-
.
|
|
111
|
-
.
|
|
112
|
-
.
|
|
113
|
-
.
|
|
114
|
-
.
|
|
241
|
+
.overlay-head{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3) var(--sp-5);
|
|
242
|
+
border-bottom:1px solid var(--border);background:var(--surface)}
|
|
243
|
+
.back-btn{background:var(--surface-2);border:1px solid var(--border);color:var(--ink);border-radius:var(--radius-sm);
|
|
244
|
+
padding:7px 12px;cursor:pointer;font-size:var(--fs-12);display:inline-flex;align-items:center;gap:6px;font-weight:500}
|
|
245
|
+
.back-btn:hover{border-color:var(--accent);color:var(--accent)}
|
|
246
|
+
.overlay-body{flex:1;overflow-y:auto;padding:var(--sp-5)}
|
|
247
|
+
.fgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--sp-3)}
|
|
248
|
+
.fcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:var(--sp-4);
|
|
249
|
+
cursor:pointer;transition:border-color var(--dur-1) var(--ease),transform var(--dur-1) var(--ease);text-align:left}
|
|
250
|
+
.fcard:hover{border-color:var(--accent-line);transform:translateY(-1px)}
|
|
251
|
+
.fcard .eyebrow{color:var(--accent)}
|
|
252
|
+
.fcard h4{font-size:var(--fs-13);margin:var(--sp-1) 0 4px;font-family:var(--font-body)}
|
|
253
|
+
.fcard p{margin:0;font-size:var(--fs-12);color:var(--ink-dim);line-height:1.5}
|
|
254
|
+
|
|
255
|
+
.req-card{padding:10px var(--sp-4);border-bottom:1px solid var(--border-soft);border-left:3px solid transparent}
|
|
256
|
+
.req-card.destructive{border-left-color:var(--danger);background:var(--danger-soft)}
|
|
257
|
+
.req-card.major{border-left-color:var(--warn);background:var(--warn-soft)}
|
|
258
|
+
.req-action{font-size:var(--fs-12);color:var(--ink)}
|
|
259
|
+
.req-reason{font-size:var(--fs-11);color:var(--ink-faint);margin:3px 0 8px}
|
|
260
|
+
.req-btns{display:flex;gap:6px}
|
|
261
|
+
.btn-sm{border:none;border-radius:6px;padding:5px 12px;cursor:pointer;font-size:11px;font-weight:600}
|
|
262
|
+
.btn-approve{background:var(--ok);color:#04231a}
|
|
263
|
+
.btn-deny{background:var(--danger);color:#2e0507}
|
|
264
|
+
|
|
265
|
+
.sec-block{margin-bottom:var(--sp-6)}
|
|
266
|
+
.sec-head{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-2)}
|
|
267
|
+
.sec-head h4{font-size:var(--fs-13);font-family:var(--font-body)}
|
|
268
|
+
.sec-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:6px;margin-bottom:var(--sp-2)}
|
|
269
|
+
.sec-item{background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:7px 10px;
|
|
270
|
+
font-family:var(--font-mono);font-size:11.5px;color:var(--ink);word-break:break-all}
|
|
271
|
+
.sec-note{color:var(--ink-faint);font-size:var(--fs-11);line-height:1.5}
|
|
272
|
+
.posture-row{display:flex;gap:var(--sp-2);margin-bottom:var(--sp-5);flex-wrap:wrap}
|
|
273
|
+
.posture-pill{display:inline-flex;align-items:center;gap:6px;background:var(--ok-soft);border:1px solid var(--ok);
|
|
274
|
+
color:var(--ok);border-radius:999px;padding:5px 12px;font-size:var(--fs-12);font-weight:600}
|
|
275
|
+
.posture-pill svg{width:12px;height:12px}
|
|
115
276
|
</style></head>
|
|
116
277
|
<body>
|
|
117
|
-
<
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
278
|
+
<div id="app">
|
|
279
|
+
<header id="topbar">
|
|
280
|
+
<button class="menu-btn icon-btn" id="menuBtn" aria-label="Open sidebar" onclick="toggleSidebar()">
|
|
281
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
|
|
282
|
+
</button>
|
|
283
|
+
<div class="brand">
|
|
284
|
+
<span class="brand-mark" aria-hidden="true">M</span>
|
|
285
|
+
<h1>MAQ</h1>
|
|
286
|
+
<span class="status-dot" id="statusDot" role="status" aria-label="Disconnected"></span>
|
|
287
|
+
</div>
|
|
288
|
+
<span class="grow"></span>
|
|
289
|
+
<nav class="topbar-nav" aria-label="Tools">
|
|
290
|
+
<button class="tbtn accent" onclick="openFeatures()">
|
|
291
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
|
|
292
|
+
Features
|
|
293
|
+
</button>
|
|
294
|
+
<button class="tbtn" onclick="feat('agents')">Agents</button>
|
|
295
|
+
<button class="tbtn" onclick="feat('connectivity')">Connectivity</button>
|
|
296
|
+
<button class="tbtn" onclick="feat('doctor')">Doctor</button>
|
|
297
|
+
<button class="tbtn" onclick="feat('models')">Models</button>
|
|
298
|
+
<button class="tbtn" onclick="openSecurity()">
|
|
299
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 4 5v6c0 5 3.5 8.5 8 11 4.5-2.5 8-6 8-11V5l-8-3z"/></svg>
|
|
300
|
+
Security
|
|
301
|
+
</button>
|
|
302
|
+
<button class="tbtn" id="previewToggle" onclick="togglePreview()">Preview</button>
|
|
303
|
+
</nav>
|
|
304
|
+
</header>
|
|
305
|
+
|
|
306
|
+
<div id="scrim" onclick="toggleSidebar()"></div>
|
|
307
|
+
|
|
308
|
+
<aside id="sidebar" aria-label="Sessions and agents">
|
|
309
|
+
<div class="side-sec">
|
|
310
|
+
<div class="side-sec-head">
|
|
311
|
+
<span class="eyebrow">Request box</span>
|
|
312
|
+
<span class="count-badge" id="reqCount">0</span>
|
|
313
|
+
</div>
|
|
314
|
+
<div class="list" id="requests" role="region" aria-label="Pending permission requests"></div>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="side-sec">
|
|
317
|
+
<div class="side-sec-head"><span class="eyebrow">Sessions</span></div>
|
|
318
|
+
<div class="list" id="sessions" role="region" aria-label="Session history"></div>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="side-sec">
|
|
321
|
+
<div class="side-sec-head"><span class="eyebrow">Agents</span></div>
|
|
322
|
+
<div class="list" id="agents" role="region" aria-label="Detected worker agents"></div>
|
|
323
|
+
</div>
|
|
324
|
+
</aside>
|
|
325
|
+
|
|
326
|
+
<main id="main">
|
|
327
|
+
<div id="composer">
|
|
328
|
+
<div class="mode-rail" id="modes" role="radiogroup" aria-label="Execution mode"></div>
|
|
329
|
+
<div class="composer-row">
|
|
330
|
+
<label class="sr-only" for="goal">Goal</label>
|
|
145
331
|
<textarea id="goal" placeholder="Describe a goal — e.g. add pagination to the users endpoint and add tests"></textarea>
|
|
146
|
-
<button class="send" id="send" onclick="startTask()">
|
|
332
|
+
<button class="send-btn" id="send" onclick="startTask()">
|
|
333
|
+
Send
|
|
334
|
+
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 12l18-9-6 9 6 9-18-9z"/></svg>
|
|
335
|
+
</button>
|
|
147
336
|
</div>
|
|
148
|
-
<div class="opts">
|
|
149
|
-
<label>target
|
|
337
|
+
<div class="opts-row">
|
|
338
|
+
<label class="opt">target
|
|
150
339
|
<select id="target">
|
|
151
340
|
<option value="none">none (raw)</option><option value="auto">auto</option>
|
|
152
341
|
<option value="claude-code">claude-code</option><option value="codex">codex</option>
|
|
153
342
|
<option value="gemini">gemini</option>
|
|
154
343
|
</select>
|
|
155
344
|
</label>
|
|
156
|
-
<label><input type="checkbox" id="dry"> dry-run</label>
|
|
157
|
-
<label>concurrency <input type="number" id="conc" value="4" min="1" max="16" style="width:52px"></label>
|
|
345
|
+
<label class="opt"><input type="checkbox" id="dry"> dry-run</label>
|
|
346
|
+
<label class="opt">concurrency <input type="number" id="conc" value="4" min="1" max="16" style="width:52px"></label>
|
|
347
|
+
<span class="kbd-hint"><kbd>Ctrl</kbd>+<kbd>Enter</kbd> to send</span>
|
|
158
348
|
</div>
|
|
159
349
|
</div>
|
|
160
|
-
<div class="
|
|
161
|
-
<div
|
|
162
|
-
</
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
<div
|
|
166
|
-
|
|
167
|
-
</
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
350
|
+
<div id="timeline" aria-label="Progress timeline"><div class="track" id="track"></div><div id="subrail"></div></div>
|
|
351
|
+
<div id="console" role="log" aria-live="polite" aria-label="Live activity"></div>
|
|
352
|
+
</main>
|
|
353
|
+
|
|
354
|
+
<aside id="preview" aria-label="Preview panel">
|
|
355
|
+
<div class="panel-head"><h3>Preview</h3><button class="icon-btn" onclick="togglePreview()">Close</button></div>
|
|
356
|
+
<div id="previewBody"></div>
|
|
357
|
+
</aside>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<div class="overlay" id="features" role="dialog" aria-label="All features">
|
|
361
|
+
<div class="overlay-head">
|
|
362
|
+
<button class="back-btn" onclick="closeFeatures()">
|
|
363
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
|
364
|
+
Back to home
|
|
365
|
+
</button>
|
|
366
|
+
<h3 style="margin-left:4px">All features</h3>
|
|
172
367
|
<span class="grow"></span>
|
|
173
|
-
<span class="
|
|
368
|
+
<span class="eyebrow" style="text-transform:none;letter-spacing:0">everything MAQ can do — click to run</span>
|
|
174
369
|
</div>
|
|
175
|
-
<div class="
|
|
370
|
+
<div class="overlay-body"><div class="fgrid" id="featureGrid"></div></div>
|
|
176
371
|
</div>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
<
|
|
372
|
+
|
|
373
|
+
<div class="overlay" id="security" role="dialog" aria-label="Security rules">
|
|
374
|
+
<div class="overlay-head">
|
|
375
|
+
<button class="back-btn" onclick="closeSecurity()">
|
|
376
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
|
377
|
+
Back to home
|
|
378
|
+
</button>
|
|
379
|
+
<h3 style="margin-left:4px">Security rules</h3>
|
|
181
380
|
<span class="grow"></span>
|
|
182
|
-
<span class="
|
|
381
|
+
<span class="eyebrow" style="text-transform:none;letter-spacing:0">enforced at the code level — same rules the terminal ('maq security') shows and enforces</span>
|
|
183
382
|
</div>
|
|
184
|
-
<div
|
|
383
|
+
<div class="overlay-body" id="securityBody"></div>
|
|
185
384
|
</div>
|
|
385
|
+
|
|
186
386
|
<script>
|
|
187
387
|
"use strict";
|
|
188
388
|
var base = location.origin;
|
|
@@ -190,6 +390,20 @@ var token = new URLSearchParams(location.search).get('token') || sessionStorage.
|
|
|
190
390
|
if(!token){ token = prompt('Daemon auth token (printed by: maq serve — or the launcher):') || ''; }
|
|
191
391
|
sessionStorage.setItem('maqToken', token);
|
|
192
392
|
|
|
393
|
+
var ICONS = {
|
|
394
|
+
play:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>',
|
|
395
|
+
chevron:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>',
|
|
396
|
+
dot:'<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="4"/></svg>',
|
|
397
|
+
check:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 13l4 4L19 7"/></svg>',
|
|
398
|
+
warn:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 9v4M12 17h.01M10.3 3.9L2.7 17a2 2 0 0 0 1.7 3h15.2a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z"/></svg>',
|
|
399
|
+
x:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>',
|
|
400
|
+
tool:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>',
|
|
401
|
+
flag:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 22V4M4 4h11l-1.5 4L15 12H4"/></svg>',
|
|
402
|
+
verified:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3 1.5 3.3-.3 1 3.1 2.7 2-1.3 3 1.3 3-2.7 2-1 3.1-3.3-.3L12 22l-3-1.5-3.3.3-1-3.1-2.7-2 1.3-3-1.3-3 2.7-2 1-3.1 3.3.3z"/><path d="M9 12l2 2 4-4"/></svg>',
|
|
403
|
+
circle:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/></svg>',
|
|
404
|
+
empty:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="7" width="18" height="13" rx="2"/><path d="M3 7l3-4h12l3 4"/></svg>'
|
|
405
|
+
};
|
|
406
|
+
|
|
193
407
|
var MODES = [
|
|
194
408
|
{id:'', h:'Single', p:'one Scout→Plan→Execute→Verify pass'},
|
|
195
409
|
{id:'parallel',h:'Parallel', p:'split, run at once, join & repeat'},
|
|
@@ -198,72 +412,117 @@ var MODES = [
|
|
|
198
412
|
];
|
|
199
413
|
var mode = 'parallel';
|
|
200
414
|
var activeId = null;
|
|
415
|
+
var connected = false;
|
|
201
416
|
|
|
202
|
-
function h(tag,cls,
|
|
417
|
+
function h(tag,cls,html){ var e=document.createElement(tag); if(cls)e.className=cls; if(html!=null)e.innerHTML=html; return e; }
|
|
203
418
|
function headers(){ return {authorization:'Bearer '+token,'content-type':'application/json'}; }
|
|
204
419
|
function api(path,opts){ opts=opts||{}; opts.headers=headers(); return fetch(base+path,opts); }
|
|
420
|
+
function setConnected(v){
|
|
421
|
+
connected=v;
|
|
422
|
+
var dot=document.getElementById('statusDot');
|
|
423
|
+
dot.className='status-dot'+(v?' on':'');
|
|
424
|
+
dot.setAttribute('aria-label', v?'Connected':'Disconnected');
|
|
425
|
+
}
|
|
426
|
+
function emptyState(icon,title,hint){
|
|
427
|
+
return '<div class="empty-state">'+(ICONS[icon]||ICONS.circle)+'<div>'+title+'</div>'+
|
|
428
|
+
(hint?'<div style="margin-top:2px;opacity:.8">'+hint+'</div>':'')+'</div>';
|
|
429
|
+
}
|
|
430
|
+
function skeletons(n){
|
|
431
|
+
var s=''; for(var i=0;i<n;i++)s+='<div class="skeleton" style="width:'+(60+((i*17)%35))+'%"></div>';
|
|
432
|
+
return s;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function toggleSidebar(){
|
|
436
|
+
document.getElementById('sidebar').classList.toggle('open');
|
|
437
|
+
document.getElementById('scrim').classList.toggle('show');
|
|
438
|
+
}
|
|
205
439
|
|
|
206
440
|
function renderModes(){
|
|
207
441
|
var c=document.getElementById('modes'); c.innerHTML='';
|
|
208
442
|
MODES.forEach(function(m){
|
|
209
|
-
var d=h('
|
|
210
|
-
d.
|
|
443
|
+
var d=h('button','mode-card'+(m.id===mode?' sel':''),'<h4>'+m.h+'</h4><p>'+m.p+'</p>');
|
|
444
|
+
d.setAttribute('role','radio'); d.setAttribute('aria-checked', m.id===mode?'true':'false');
|
|
211
445
|
d.onclick=function(){ mode=m.id; renderModes(); };
|
|
212
446
|
c.appendChild(d);
|
|
213
447
|
});
|
|
214
448
|
}
|
|
215
449
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
450
|
+
/* ---------------- signature element: depth-of-work timeline ---------------- */
|
|
451
|
+
var PHASES=[
|
|
452
|
+
{id:'scout',label:'Scout'},{id:'plan',label:'Plan'},{id:'execute',label:'Execute'},{id:'verify',label:'Verify'}
|
|
453
|
+
];
|
|
454
|
+
function renderTimeline(events){
|
|
455
|
+
var done={}, active=null, orchRounds=[];
|
|
219
456
|
events.forEach(function(e){
|
|
220
457
|
var p=e.data&&e.data.phase;
|
|
221
458
|
if(!p)return;
|
|
222
|
-
if(/^(parallel-round|loop-iteration|safe-)/.test(p)){
|
|
459
|
+
if(/^(parallel-round|loop-iteration|safe-)/.test(p)){
|
|
460
|
+
if(e.type==='phase.started' && orchRounds.indexOf(p)===-1) orchRounds.push(p);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
223
463
|
if(e.type==='phase.started')active=p;
|
|
224
|
-
if(e.type==='phase.done'){done[p]=true; if(active===p)active=null;}
|
|
464
|
+
if(e.type==='phase.done'){ done[p]=true; if(active===p)active=null; }
|
|
225
465
|
});
|
|
226
|
-
var
|
|
227
|
-
PHASES.forEach(function(
|
|
228
|
-
var
|
|
229
|
-
var
|
|
466
|
+
var track=document.getElementById('track'); track.innerHTML='';
|
|
467
|
+
PHASES.forEach(function(ph,i){
|
|
468
|
+
var state = done[ph.id] ? 'done' : (active===ph.id ? 'active' : '');
|
|
469
|
+
var node=h('div','node'+(state?' '+state:''));
|
|
470
|
+
node.innerHTML='<span class="node-dot">'+ICONS.check+'</span><span class="node-label">'+ph.label+'</span>';
|
|
471
|
+
track.appendChild(node);
|
|
472
|
+
if(i<PHASES.length-1) track.appendChild(h('div','node-line'));
|
|
230
473
|
});
|
|
231
|
-
var
|
|
232
|
-
if(
|
|
474
|
+
var sub=document.getElementById('subrail');
|
|
475
|
+
if(orchRounds.length){
|
|
476
|
+
sub.innerHTML='<div class="sub-rail">'+orchRounds.map(function(r){return '<span class="sub-chip">'+r+'</span>';}).join('')+'</div>';
|
|
477
|
+
} else { sub.innerHTML=''; }
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function fmtTime(ts){
|
|
481
|
+
try{ var d=new Date(ts); return d.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit',second:'2-digit'}); }catch(e){ return ''; }
|
|
233
482
|
}
|
|
234
483
|
|
|
235
|
-
function
|
|
484
|
+
function renderEvent(e){
|
|
236
485
|
switch(e.type){
|
|
237
|
-
case 'task.started': return [
|
|
238
|
-
case 'phase.started': return [
|
|
239
|
-
case 'phase.done': return [
|
|
240
|
-
case 'agent.event': return [
|
|
241
|
-
case 'agent.stdout': return [
|
|
242
|
-
case 'agent.stderr': return [
|
|
243
|
-
case 'tool.call': return [
|
|
244
|
-
case 'task.done': return [e.data.verified?
|
|
245
|
-
|
|
246
|
-
case 'task.
|
|
247
|
-
|
|
486
|
+
case 'task.started': return [ICONS.play,'c-accent',(e.data.goal||e.data.task||'')+(e.data.mode?' ['+e.data.mode+']':''),false];
|
|
487
|
+
case 'phase.started': return [ICONS.chevron,'c-accent',(e.data.phase||'')+' …',false];
|
|
488
|
+
case 'phase.done': return [ICONS.dot,'c-dim',(e.data.phase||'')+(e.data.reason?' '+e.data.reason:''),false];
|
|
489
|
+
case 'agent.event': return [ICONS.dot,'c-dim',e.data.note||JSON.stringify(e.data),false];
|
|
490
|
+
case 'agent.stdout': return [ICONS.dot,'c-dim',e.data.text||'',false];
|
|
491
|
+
case 'agent.stderr': return [ICONS.warn,'c-warn',e.data.text||'',true];
|
|
492
|
+
case 'tool.call': return [ICONS.tool,'c-accent','tool: '+(e.data.tool||JSON.stringify(e.data.command||e.data.raw||{})),false];
|
|
493
|
+
case 'task.done': return [e.data.verified?ICONS.verified:ICONS.flag, e.data.verified?'c-ok':'c-warn',
|
|
494
|
+
'done · verified='+e.data.verified+(e.data.mode?' · '+e.data.mode:'')+(e.data.summary?' · '+e.data.summary:''), !e.data.verified];
|
|
495
|
+
case 'task.error': return [ICONS.x,'c-danger','error: '+(e.data.message||''),true];
|
|
496
|
+
case 'task.cancelled': return [ICONS.x,'c-danger','cancelled',true];
|
|
497
|
+
default: return [ICONS.circle,'c-dim',e.type,false];
|
|
248
498
|
}
|
|
249
499
|
}
|
|
250
500
|
function pushEvent(e){
|
|
251
501
|
var c=document.getElementById('console');
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
var
|
|
255
|
-
var
|
|
256
|
-
|
|
257
|
-
|
|
502
|
+
var first=c.firstElementChild;
|
|
503
|
+
if(first && first.classList && first.classList.contains('empty-state-wrap')) c.innerHTML='';
|
|
504
|
+
var parts=renderEvent(e);
|
|
505
|
+
var card=h('div','ev-card'+(parts[3]?(parts[1]==='c-danger'?' bg-danger':' bg-warn'):''));
|
|
506
|
+
card.innerHTML='<span class="ev-ic '+parts[1]+'">'+parts[0]+'</span>'+
|
|
507
|
+
'<span class="ev-body"><span class="ev-text '+parts[1]+'"></span><span class="ev-time">'+fmtTime(e.ts)+'</span></span>';
|
|
508
|
+
card.querySelector('.ev-text').textContent = parts[2];
|
|
509
|
+
c.appendChild(card);
|
|
258
510
|
c.scrollTop=c.scrollHeight;
|
|
259
511
|
}
|
|
512
|
+
function clearConsole(){
|
|
513
|
+
document.getElementById('console').innerHTML =
|
|
514
|
+
'<div class="empty-state-wrap">'+emptyState('empty','Pick a mode, describe a goal, and press Send.',
|
|
515
|
+
'The live Scout→Plan→Execute→Verify stream renders here.')+'</div>';
|
|
516
|
+
}
|
|
260
517
|
|
|
261
518
|
var evList=[];
|
|
262
519
|
async function streamEvents(id){
|
|
263
520
|
evList=[];
|
|
521
|
+
clearConsole();
|
|
264
522
|
document.getElementById('console').innerHTML='';
|
|
523
|
+
renderTimeline([]);
|
|
265
524
|
var res=await fetch(base+'/v1/sessions/'+id+'/events',{headers:{authorization:'Bearer '+token}});
|
|
266
|
-
if(!res.ok||!res.body){ pushEvent({type:'task.error',data:{message:'stream failed '+res.status}}); return; }
|
|
525
|
+
if(!res.ok||!res.body){ pushEvent({type:'task.error',ts:new Date().toISOString(),data:{message:'stream failed '+res.status}}); return; }
|
|
267
526
|
var reader=res.body.getReader(), dec=new TextDecoder(), buf='';
|
|
268
527
|
while(true){
|
|
269
528
|
var r=await reader.read(); if(r.done)break;
|
|
@@ -275,7 +534,7 @@ async function streamEvents(id){
|
|
|
275
534
|
var ln=lines[j];
|
|
276
535
|
if(ln.indexOf('data:')===0){
|
|
277
536
|
var payload=ln.slice(5).trim(); if(!payload)continue;
|
|
278
|
-
try{ var ev=JSON.parse(payload); if(ev.type==='stream.end'){continue;} evList.push(ev); pushEvent(ev);
|
|
537
|
+
try{ var ev=JSON.parse(payload); if(ev.type==='stream.end'){continue;} evList.push(ev); pushEvent(ev); renderTimeline(evList); }catch(err){}
|
|
279
538
|
}
|
|
280
539
|
}
|
|
281
540
|
}
|
|
@@ -285,7 +544,8 @@ async function streamEvents(id){
|
|
|
285
544
|
}
|
|
286
545
|
|
|
287
546
|
async function startTask(){
|
|
288
|
-
var
|
|
547
|
+
var goalEl=document.getElementById('goal');
|
|
548
|
+
var goal=goalEl.value.trim(); if(!goal)return;
|
|
289
549
|
var btn=document.getElementById('send'); btn.disabled=true;
|
|
290
550
|
var body={task:goal, target:document.getElementById('target').value, dryRun:document.getElementById('dry').checked,
|
|
291
551
|
maxConcurrency:Number(document.getElementById('conc').value)||4};
|
|
@@ -293,131 +553,143 @@ async function startTask(){
|
|
|
293
553
|
try{
|
|
294
554
|
var res=await api('/v1/sessions',{method:'POST',body:JSON.stringify(body)});
|
|
295
555
|
var s=await res.json();
|
|
296
|
-
if(!res.ok){ pushEvent({type:'task.error',data:{message:s.error||('HTTP '+res.status)}}); return; }
|
|
297
|
-
activeId=s.id;
|
|
556
|
+
if(!res.ok){ pushEvent({type:'task.error',ts:new Date().toISOString(),data:{message:s.error||('HTTP '+res.status)}}); return; }
|
|
557
|
+
activeId=s.id; goalEl.value='';
|
|
298
558
|
await refreshSessions();
|
|
299
559
|
streamEvents(s.id);
|
|
300
|
-
}catch(e){ pushEvent({type:'task.error',data:{message:String(e)}}); }
|
|
560
|
+
}catch(e){ pushEvent({type:'task.error',ts:new Date().toISOString(),data:{message:String(e)}}); }
|
|
301
561
|
finally{ btn.disabled=false; }
|
|
302
562
|
}
|
|
303
563
|
|
|
564
|
+
function sessionRow(s){
|
|
565
|
+
var it=h('div','row'+(s.id===activeId?' active':''));
|
|
566
|
+
it.setAttribute('tabindex','0'); it.setAttribute('role','button');
|
|
567
|
+
it.setAttribute('aria-label','Session: '+s.task);
|
|
568
|
+
var verifiedMark = s.verified===true?' · '+ICONS.check.replace('<svg','<svg style=\\'width:10px;height:10px;vertical-align:-1px\\''):(s.verified===false?' · unverified':'');
|
|
569
|
+
it.innerHTML =
|
|
570
|
+
'<div class="row-title">'+escText(s.task)+'</div>'+
|
|
571
|
+
'<div class="row-meta"><span class="tag '+s.status+'">'+s.status+'</span>'+
|
|
572
|
+
(s.mode?'<span>'+s.mode+'</span>':'')+'<span>'+(s.target||'auto')+'</span><span>'+s.eventCount+' ev</span>'+verifiedMark+'</div>';
|
|
573
|
+
it.onclick=function(){ activeId=s.id; refreshSessions(); openPreview(s.id); streamEvents(s.id); if(window.innerWidth<=900)toggleSidebar(); };
|
|
574
|
+
it.onkeydown=function(ev){ if(ev.key==='Enter')it.click(); };
|
|
575
|
+
return it;
|
|
576
|
+
}
|
|
577
|
+
function escText(s){ var d=document.createElement('div'); d.textContent=String(s==null?'':s); return d.innerHTML; }
|
|
578
|
+
|
|
579
|
+
var sessionsLoaded=false;
|
|
304
580
|
async function refreshSessions(){
|
|
581
|
+
var box=document.getElementById('sessions');
|
|
582
|
+
if(!sessionsLoaded) box.innerHTML = skeletons(3);
|
|
305
583
|
try{
|
|
306
584
|
var res=await api('/v1/sessions'); var d=await res.json();
|
|
307
|
-
|
|
585
|
+
setConnected(true);
|
|
308
586
|
var list=(d.sessions||[]);
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
list.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
var meta=h('div','s');
|
|
315
|
-
meta.innerHTML='<span class="badge b-'+s.status+'">'+s.status+'</span> '+
|
|
316
|
-
(s.mode?('· '+s.mode+' '):'')+'· '+(s.target||'auto')+' · '+s.eventCount+' ev'+
|
|
317
|
-
(s.verified===true?' · ✓':(s.verified===false?' · ✗':''));
|
|
318
|
-
it.appendChild(t); it.appendChild(meta);
|
|
319
|
-
it.onclick=function(){ activeId=s.id; refreshSessions(); openPreview(s.id); streamEvents(s.id); };
|
|
320
|
-
box.appendChild(it);
|
|
321
|
-
});
|
|
322
|
-
}catch(e){ document.getElementById('dot').className='dot'; }
|
|
587
|
+
sessionsLoaded=true;
|
|
588
|
+
box.innerHTML='';
|
|
589
|
+
if(!list.length){ box.innerHTML=emptyState('empty','No sessions yet','Send a goal to start one.'); return; }
|
|
590
|
+
list.slice().reverse().forEach(function(s){ box.appendChild(sessionRow(s)); });
|
|
591
|
+
}catch(e){ setConnected(false); if(!sessionsLoaded) box.innerHTML=emptyState('warn','Can\\'t reach the daemon',''); }
|
|
323
592
|
}
|
|
324
593
|
|
|
325
594
|
async function openPreview(id){
|
|
326
|
-
document.getElementById('
|
|
327
|
-
var box=document.getElementById('
|
|
595
|
+
document.getElementById('app').classList.add('preview-open');
|
|
596
|
+
var box=document.getElementById('previewBody'); box.innerHTML=skeletons(4);
|
|
328
597
|
try{
|
|
329
598
|
var res=await api('/v1/sessions/'+id); var d=await res.json();
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
box.appendChild(pre);
|
|
333
|
-
}catch(e){ box.
|
|
599
|
+
var pre=h('pre','data');
|
|
600
|
+
pre.textContent=JSON.stringify({id:d.id,task:d.task,status:d.status,mode:d.mode,verified:d.verified,error:d.error},null,2);
|
|
601
|
+
box.innerHTML=''; box.appendChild(pre);
|
|
602
|
+
}catch(e){ box.innerHTML=emptyState('warn','Could not load session',String(e)); }
|
|
334
603
|
}
|
|
335
|
-
function togglePreview(){ document.getElementById('
|
|
604
|
+
function togglePreview(){ document.getElementById('app').classList.toggle('preview-open'); }
|
|
336
605
|
|
|
337
606
|
async function feat(kind){
|
|
338
|
-
document.getElementById('
|
|
339
|
-
var box=document.getElementById('
|
|
607
|
+
document.getElementById('app').classList.add('preview-open');
|
|
608
|
+
var box=document.getElementById('previewBody'); box.innerHTML=skeletons(5);
|
|
340
609
|
try{
|
|
341
610
|
var out;
|
|
342
611
|
if(kind==='agents'){ out=await (await api('/v1/agents')).json(); renderAgents(out.agents||[]); }
|
|
343
612
|
else if(kind==='connectivity'){ out=await (await api('/v1/connectivity')).json(); }
|
|
344
613
|
else { out=await (await api('/v1/exec',{method:'POST',body:JSON.stringify({argv:[kind]})})).json(); }
|
|
345
|
-
var pre=h('pre'
|
|
614
|
+
var pre=h('pre','data');
|
|
615
|
+
pre.textContent=(out.stdout!=null?out.stdout:'')+(out.stderr?('\\n'+out.stderr):'')||JSON.stringify(out,null,2);
|
|
346
616
|
box.innerHTML=''; box.appendChild(pre);
|
|
347
|
-
}catch(e){ box.
|
|
617
|
+
}catch(e){ box.innerHTML=emptyState('warn','Could not run '+kind,String(e)); }
|
|
348
618
|
}
|
|
349
619
|
function renderAgents(agents){
|
|
350
|
-
var box=document.getElementById('agents');
|
|
351
|
-
if(!agents.length){ box.innerHTML='
|
|
620
|
+
var box=document.getElementById('agents');
|
|
621
|
+
if(!agents.length){ box.innerHTML=emptyState('empty','No agents detected','Install/log in to an AI CLI.'); return; }
|
|
622
|
+
box.innerHTML='';
|
|
352
623
|
agents.forEach(function(a){
|
|
353
|
-
var it=h('div','
|
|
354
|
-
var
|
|
355
|
-
|
|
624
|
+
var it=h('div','row');
|
|
625
|
+
var statusText = a.installed?(a.authenticated?'authenticated':'installed, logged out'):'not found';
|
|
626
|
+
var tagCls = a.installed?(a.authenticated?'done':'paused'):'cancelled';
|
|
627
|
+
it.innerHTML='<div class="row-title">'+escText(a.name)+'</div><div class="row-meta"><span class="tag '+tagCls+'">'+statusText+'</span></div>';
|
|
628
|
+
box.appendChild(it);
|
|
356
629
|
});
|
|
357
630
|
}
|
|
358
631
|
|
|
359
632
|
async function openFeatures(){
|
|
360
633
|
document.getElementById('features').classList.add('show');
|
|
361
|
-
var grid=document.getElementById('featureGrid'); grid.innerHTML=
|
|
634
|
+
var grid=document.getElementById('featureGrid'); grid.innerHTML=skeletons(6);
|
|
362
635
|
try{
|
|
363
636
|
var cat=await (await api('/v1/commands')).json();
|
|
364
637
|
var cmds=(cat.maq||[]); grid.innerHTML='';
|
|
638
|
+
if(!cmds.length){ grid.innerHTML=emptyState('empty','No commands available',''); return; }
|
|
365
639
|
cmds.forEach(function(c){
|
|
366
|
-
var card=h('
|
|
367
|
-
card.innerHTML='<div class="
|
|
640
|
+
var card=h('button','fcard');
|
|
641
|
+
card.innerHTML='<div class="eyebrow">'+escText(c.category)+'</div><h4>'+escText(c.name)+'</h4><p>'+escText(c.summary)+'</p>';
|
|
368
642
|
card.onclick=function(){ runFeature(c); };
|
|
369
643
|
grid.appendChild(card);
|
|
370
644
|
});
|
|
371
|
-
}catch(e){ grid.innerHTML='
|
|
645
|
+
}catch(e){ grid.innerHTML=emptyState('warn','Could not load features',String(e)); }
|
|
372
646
|
}
|
|
373
647
|
function closeFeatures(){ document.getElementById('features').classList.remove('show'); }
|
|
374
648
|
|
|
375
649
|
async function openSecurity(){
|
|
376
650
|
document.getElementById('security').classList.add('show');
|
|
377
|
-
var box=document.getElementById('securityBody'); box.innerHTML=
|
|
651
|
+
var box=document.getElementById('securityBody'); box.innerHTML=skeletons(8);
|
|
378
652
|
try{
|
|
379
653
|
var d=await (await api('/v1/security')).json();
|
|
380
654
|
var r=d.rules, evs=(d.events||[]);
|
|
381
655
|
var html='';
|
|
382
|
-
html+='<div
|
|
383
|
-
html+='<span class="
|
|
384
|
-
html+='<span class="
|
|
385
|
-
html+='<span class="
|
|
656
|
+
html+='<div class="posture-row">';
|
|
657
|
+
html+='<span class="posture-pill">'+ICONS.check+' permission posture: '+r.permissionMode+'</span>';
|
|
658
|
+
html+='<span class="posture-pill">'+ICONS.check+' secret env scrubbing: on</span>';
|
|
659
|
+
html+='<span class="posture-pill">'+ICONS.check+' prompt-injection scanning: on</span>';
|
|
386
660
|
html+='</div>';
|
|
387
661
|
|
|
388
|
-
html+=
|
|
389
|
-
|
|
390
|
-
html+='
|
|
391
|
-
|
|
392
|
-
html+=sec('Protected file patterns (matched by name, anywhere in the project)', r.protectedNamePatterns.length);
|
|
393
|
-
html+='<div class="seclist">'+r.protectedNamePatterns.map(function(p){return '<div class="secitem">'+esc(p)+'</div>';}).join('')+'</div>';
|
|
394
|
-
|
|
662
|
+
html+=secBlock('Protected paths', 'never writable — cannot be overridden by config', r.protectedPaths,
|
|
663
|
+
'Enforced in sandbox.ts checkWrite(), unconditionally, before any tier logic — a compromised agent cannot argue past this with a clever prompt.');
|
|
664
|
+
html+=secBlock('Protected file patterns', 'matched by name, anywhere in the project', r.protectedNamePatterns, '');
|
|
395
665
|
if(r.extraProtectedPaths && r.extraProtectedPaths.length){
|
|
396
|
-
html+=
|
|
397
|
-
html+='<div class="seclist">'+r.extraProtectedPaths.map(function(p){return '<div class="secitem">'+esc(p)+'</div>';}).join('')+'</div>';
|
|
666
|
+
html+=secBlock('Project-added protected paths', '', r.extraProtectedPaths, '');
|
|
398
667
|
}
|
|
668
|
+
html+=secBlock('Network egress allowlist', 'default-deny everything else', r.netAllowlist,
|
|
669
|
+
'Enforced in tools.ts http_get and available to any outbound call site via security.ts checkEgress(). Not just a suggestion in a prompt.');
|
|
670
|
+
html+=secBlock('Prompt-injection scanning', '', r.promptInjectionScanning,
|
|
671
|
+
'README, manifest, git commits, AGENTS.md and skill files are scanned for override/exfiltration/jailbreak patterns before they reach a model. Findings are surfaced loudly, never silently dropped.');
|
|
399
672
|
|
|
400
|
-
html+=
|
|
401
|
-
html+='<div class="
|
|
402
|
-
html+='<div class="secnote">Enforced in tools.ts http_get and available to any outbound call site via security.ts checkEgress(). Not just a suggestion in a prompt.</div>';
|
|
403
|
-
|
|
404
|
-
html+=sec('Prompt-injection scanning', r.promptInjectionScanning.length);
|
|
405
|
-
html+='<div class="seclist">'+r.promptInjectionScanning.map(function(p){return '<div class="secitem">'+esc(p)+'</div>';}).join('')+'</div>';
|
|
406
|
-
html+='<div class="secnote">README, manifest, git commits, AGENTS.md and skill files are scanned for override/exfiltration/jailbreak patterns before they reach a model. Findings are surfaced loudly, never silently dropped.</div>';
|
|
407
|
-
|
|
408
|
-
html+=sec('Recent findings (this session)', evs.length);
|
|
409
|
-
if(!evs.length){ html+='<div class="secnote">None recorded yet — findings appear here as tasks run.</div>'; }
|
|
673
|
+
html+='<div class="sec-block"><div class="sec-head"><h4>Recent findings</h4><span class="count-badge'+(evs.length?' alert':'')+'">'+evs.length+'</span></div>';
|
|
674
|
+
if(!evs.length){ html+='<div class="sec-note">None recorded yet — findings appear here as tasks run.</div>'; }
|
|
410
675
|
else {
|
|
411
676
|
html+='<div>'+evs.slice().reverse().map(function(e){
|
|
412
|
-
var cls=e.severity==='high'?'
|
|
413
|
-
return '<div class="ev"><span class="ic '+cls+'"
|
|
677
|
+
var cls=e.severity==='high'?'c-danger':'c-warn';
|
|
678
|
+
return '<div class="ev-card"><span class="ev-ic '+cls+'">'+ICONS.warn+'</span><span class="ev-text '+cls+'">'+escText(e.kind)+': '+escText(e.detail)+'</span></div>';
|
|
414
679
|
}).join('')+'</div>';
|
|
415
680
|
}
|
|
416
|
-
|
|
417
|
-
|
|
681
|
+
html+='</div>';
|
|
682
|
+
box.innerHTML = html;
|
|
683
|
+
}catch(e){ box.innerHTML=emptyState('warn','Could not load security rules',String(e)); }
|
|
684
|
+
}
|
|
685
|
+
function secBlock(title,note,items,footer){
|
|
686
|
+
var html='<div class="sec-block"><div class="sec-head"><h4>'+title+'</h4><span class="count-badge">'+(items?items.length:0)+'</span>'+
|
|
687
|
+
(note?'<span class="sec-note">'+note+'</span>':'')+'</div>';
|
|
688
|
+
html+='<div class="sec-list">'+(items||[]).map(function(p){return '<div class="sec-item">'+escText(p)+'</div>';}).join('')+'</div>';
|
|
689
|
+
if(footer) html+='<div class="sec-note">'+footer+'</div>';
|
|
690
|
+
html+='</div>';
|
|
691
|
+
return html;
|
|
418
692
|
}
|
|
419
|
-
function sec(title,count){ return '<div class="sechead"><b>'+title+'</b><span class="n">'+count+'</span></div>'; }
|
|
420
|
-
function esc(s){ var d=document.createElement('div'); d.textContent=String(s); return d.innerHTML; }
|
|
421
693
|
function closeSecurity(){ document.getElementById('security').classList.remove('show'); }
|
|
422
694
|
|
|
423
695
|
function runFeature(c){
|
|
@@ -435,18 +707,19 @@ async function loadRequests(){
|
|
|
435
707
|
try{
|
|
436
708
|
var d=await (await api('/v1/requests')).json();
|
|
437
709
|
var box=document.getElementById('requests'); var pend=(d.pending||[]);
|
|
438
|
-
document.getElementById('reqCount')
|
|
439
|
-
|
|
710
|
+
var badge=document.getElementById('reqCount');
|
|
711
|
+
badge.textContent=String(pend.length);
|
|
712
|
+
badge.className='count-badge'+(pend.length?' alert':'');
|
|
713
|
+
if(!pend.length){ box.innerHTML=emptyState('empty','No pending requests',''); return; }
|
|
440
714
|
box.innerHTML='';
|
|
441
715
|
pend.forEach(function(r){
|
|
442
|
-
var it=h('div','req '+r.risk);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
it.appendChild(a); it.appendChild(reason); it.appendChild(btns); box.appendChild(it);
|
|
716
|
+
var it=h('div','req-card '+r.risk);
|
|
717
|
+
it.innerHTML='<div class="req-action">'+escText(r.action)+' — '+escText(r.detail||'')+'</div>'+
|
|
718
|
+
'<div class="req-reason">'+escText(r.risk)+' · '+escText(r.reason||'')+'</div>'+
|
|
719
|
+
'<div class="req-btns"><button class="btn-sm btn-approve">Approve</button><button class="btn-sm btn-deny">Deny</button></div>';
|
|
720
|
+
it.querySelector('.btn-approve').onclick=function(){ decideReq(r.id,'approve'); };
|
|
721
|
+
it.querySelector('.btn-deny').onclick=function(){ decideReq(r.id,'deny'); };
|
|
722
|
+
box.appendChild(it);
|
|
450
723
|
});
|
|
451
724
|
}catch(e){}
|
|
452
725
|
}
|
|
@@ -454,9 +727,11 @@ async function decideReq(id,action){
|
|
|
454
727
|
try{ await api('/v1/requests/'+id,{method:'POST',body:JSON.stringify({action:action})}); loadRequests(); }catch(e){}
|
|
455
728
|
}
|
|
456
729
|
|
|
457
|
-
|
|
730
|
+
clearConsole();
|
|
731
|
+
renderModes(); renderTimeline([]); refreshSessions(); feat('agents'); loadRequests();
|
|
458
732
|
setInterval(refreshSessions, 5000); setInterval(loadRequests, 3000);
|
|
459
733
|
document.getElementById('goal').addEventListener('keydown', function(e){ if((e.metaKey||e.ctrlKey)&&e.key==='Enter')startTask(); });
|
|
734
|
+
document.addEventListener('keydown', function(e){ if(e.key==='Escape'){ closeFeatures(); closeSecurity(); } });
|
|
460
735
|
</script>
|
|
461
736
|
</body></html>`;
|
|
462
737
|
}
|