toolfunnel 0.7.0 → 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.
Files changed (73) hide show
  1. package/README.md +238 -235
  2. package/SECURITY.md +84 -84
  3. package/bin/toolfunnel.js +530 -451
  4. package/docs/KNOWN_BUGS.md +100 -80
  5. package/docs/MANUAL.pdf +0 -0
  6. package/docs/design.md +84 -84
  7. package/docs/packaging.md +116 -115
  8. package/hooks/hooks.manifest.json +27 -27
  9. package/hooks/scripts/example-audit.js +21 -21
  10. package/hooks/scripts/example-deny-dangerous.js +39 -39
  11. package/mcp/expose.example.json +30 -30
  12. package/mcp/servers/mock-modern/server.js +147 -147
  13. package/mcp/servers/mock-upstream/server.js +379 -380
  14. package/package.json +67 -67
  15. package/src/auth/config.js +172 -174
  16. package/src/auth/install.js +181 -181
  17. package/src/auth/resource-server.js +358 -353
  18. package/src/core/atomic-write.js +97 -0
  19. package/src/core/child-reaper.js +61 -61
  20. package/src/core/config-home.js +226 -134
  21. package/src/core/events.js +105 -105
  22. package/src/core/hook-engine.js +304 -329
  23. package/src/core/hook-loader.js +766 -824
  24. package/src/core/hook-runner.js +615 -566
  25. package/src/core/logger.js +295 -297
  26. package/src/core/matcher.js +100 -100
  27. package/src/core/metrics.js +90 -75
  28. package/src/core/output-cap.js +31 -0
  29. package/src/core/rename-migration.js +178 -163
  30. package/src/core/requires.js +138 -138
  31. package/src/core/server-config.js +98 -98
  32. package/src/extend/add-hook.md +155 -152
  33. package/src/extend/add-mcp.md +119 -119
  34. package/src/extend/configure.md +88 -88
  35. package/src/extend/create-tool.md +168 -168
  36. package/src/extend/howto.js +102 -102
  37. package/src/extend/package.md +190 -190
  38. package/src/extend/wrap.md +99 -99
  39. package/src/mcp/aggregator.js +2248 -1674
  40. package/src/mcp/expose-store.js +755 -577
  41. package/src/mcp/gated-run.js +210 -210
  42. package/src/mcp/http-transport.js +1657 -1563
  43. package/src/mcp/mcp-client.js +1174 -1070
  44. package/src/mcp/modern.js +409 -406
  45. package/src/mcp/protocol.js +473 -468
  46. package/src/mcp/server.js +3317 -3112
  47. package/src/mcp/tool-approvals.js +396 -0
  48. package/src/tools/registry.js +607 -661
  49. package/src/tools/tool-state.js +170 -170
  50. package/src/ui/public/app.js +1246 -1187
  51. package/src/ui/public/index.html +286 -286
  52. package/src/ui/public/styles.css +535 -535
  53. package/src/ui/server.js +1742 -1616
  54. package/tools/scripts/base64.js +139 -139
  55. package/tools/scripts/danger.js +61 -61
  56. package/tools/scripts/echo.js +39 -39
  57. package/tools/scripts/hash.js +101 -101
  58. package/tools/scripts/json.js +109 -109
  59. package/tools/scripts/text-stats.js +131 -131
  60. package/tools/scripts/tf-accept-change.js +67 -0
  61. package/tools/scripts/tf-env.js +43 -43
  62. package/tools/scripts/tf-hook-add.js +151 -151
  63. package/tools/scripts/tf-hook-set.js +120 -120
  64. package/tools/scripts/tf-list.js +192 -192
  65. package/tools/scripts/tf-log.js +86 -86
  66. package/tools/scripts/tf-mcp-add.js +130 -117
  67. package/tools/scripts/tf-mcp-set.js +121 -121
  68. package/tools/scripts/tf-pack.js +237 -237
  69. package/tools/scripts/tf-tool-add.js +134 -134
  70. package/tools/scripts/tf-tool-set.js +142 -142
  71. package/tools/scripts/tf-wrap.js +103 -103
  72. package/tools/scripts/uuid.js +102 -102
  73. package/tools/tools.register.json +16 -5
@@ -1,535 +1,535 @@
1
- /* ToolFunnel management console - dark, modern, technical. Vanilla CSS, zero external assets. */
2
-
3
- :root {
4
- --bg: #0c0e13;
5
- --bg-elev: #13161d;
6
- --bg-card: #161a22;
7
- --bg-card-hover: #1b202a;
8
- --bg-input: #0f1218;
9
- --border: #242a36;
10
- --border-strong: #313a48;
11
- --text: #e6e9ef;
12
- --text-dim: #9aa3b2;
13
- --text-faint: #6a7384;
14
- --accent: #5cc8ff;
15
- --accent-dim: #2b6f93;
16
- --accent-glow: rgba(92, 200, 255, 0.18);
17
- --good: #54d6a0;
18
- --warn: #f5b860;
19
- --danger: #ff6b78;
20
- --mono: ui-monospace, "SFMono-Regular", "JetBrains Mono", "Fira Code", Consolas, "Liberation Mono", monospace;
21
- --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
22
- --radius: 12px;
23
- --radius-sm: 8px;
24
- }
25
-
26
- * { box-sizing: border-box; }
27
-
28
- html, body {
29
- margin: 0;
30
- padding: 0;
31
- background: var(--bg);
32
- color: var(--text);
33
- font-family: var(--sans);
34
- font-size: 15px;
35
- line-height: 1.5;
36
- -webkit-font-smoothing: antialiased;
37
- }
38
-
39
- body {
40
- background:
41
- radial-gradient(900px 500px at 80% -10%, rgba(92, 200, 255, 0.06), transparent 60%),
42
- radial-gradient(700px 400px at 0% 0%, rgba(84, 214, 160, 0.04), transparent 55%),
43
- var(--bg);
44
- min-height: 100vh;
45
- }
46
-
47
- .app {
48
- max-width: 980px;
49
- margin: 0 auto;
50
- padding: 28px 24px 80px;
51
- }
52
-
53
- /* ── Top bar ─────────────────────────────────────────────────────────────── */
54
- .topbar {
55
- display: flex;
56
- align-items: center;
57
- justify-content: space-between;
58
- gap: 20px;
59
- flex-wrap: wrap;
60
- padding-bottom: 22px;
61
- border-bottom: 1px solid var(--border);
62
- }
63
-
64
- .brand { display: flex; align-items: center; gap: 14px; }
65
-
66
- .logo {
67
- display: grid;
68
- place-items: center;
69
- width: 42px;
70
- height: 42px;
71
- border-radius: var(--radius-sm);
72
- color: var(--accent);
73
- background: linear-gradient(160deg, rgba(92, 200, 255, 0.16), rgba(92, 200, 255, 0.03));
74
- border: 1px solid var(--border-strong);
75
- box-shadow: 0 0 24px var(--accent-glow);
76
- }
77
-
78
- .brand-text h1 { margin: 0; font-size: 22px; font-weight: 650; letter-spacing: -0.02em; }
79
- .tagline { margin: 1px 0 0; font-size: 12.5px; color: var(--text-faint); font-family: var(--mono); }
80
-
81
- .stats { display: flex; gap: 10px; }
82
- .stat {
83
- display: flex;
84
- flex-direction: column;
85
- align-items: center;
86
- min-width: 74px;
87
- padding: 9px 14px;
88
- background: var(--bg-elev);
89
- border: 1px solid var(--border);
90
- border-radius: var(--radius-sm);
91
- }
92
- .stat-num { font-family: var(--mono); font-size: 19px; font-weight: 600; color: var(--accent); line-height: 1.1; }
93
- .stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); }
94
-
95
- /* ── Tabs ────────────────────────────────────────────────────────────────── */
96
- .tabs { display: flex; gap: 4px; margin: 22px 0 18px; }
97
- .tab {
98
- appearance: none;
99
- border: none;
100
- background: transparent;
101
- color: var(--text-dim);
102
- font-family: var(--sans);
103
- font-size: 14px;
104
- font-weight: 550;
105
- padding: 8px 18px;
106
- border-radius: 999px;
107
- cursor: pointer;
108
- transition: background 0.15s, color 0.15s;
109
- }
110
- .tab:hover { color: var(--text); background: var(--bg-elev); }
111
- .tab.is-active { color: var(--bg); background: var(--accent); }
112
-
113
- .view { display: none; }
114
- .view.is-active { display: block; animation: fade 0.2s ease; }
115
- @keyframes fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
116
-
117
- /* ── Toolbar (search + add button) ─────────────────────────────────────────── */
118
- .toolbar { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
119
-
120
- .searchbar { position: relative; display: flex; align-items: center; flex: 1; }
121
- .search-icon { position: absolute; left: 14px; color: var(--text-faint); pointer-events: none; }
122
- .searchbar input {
123
- width: 100%;
124
- padding: 12px 16px 12px 42px;
125
- background: var(--bg-elev);
126
- border: 1px solid var(--border-strong);
127
- border-radius: var(--radius);
128
- color: var(--text);
129
- font-size: 15px;
130
- font-family: var(--sans);
131
- outline: none;
132
- transition: border-color 0.15s, box-shadow 0.15s;
133
- }
134
- .searchbar input::placeholder { color: var(--text-faint); }
135
- .searchbar input:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--accent-glow); }
136
- .result-count {
137
- position: absolute;
138
- right: 16px;
139
- font-family: var(--mono);
140
- font-size: 12px;
141
- color: var(--text-faint);
142
- pointer-events: none;
143
- }
144
-
145
- /* ── Buttons ─────────────────────────────────────────────────────────────── */
146
- .btn {
147
- appearance: none;
148
- font-family: var(--sans);
149
- font-size: 14px;
150
- font-weight: 550;
151
- padding: 11px 18px;
152
- border-radius: var(--radius-sm);
153
- border: 1px solid var(--border-strong);
154
- background: var(--bg-elev);
155
- color: var(--text);
156
- cursor: pointer;
157
- white-space: nowrap;
158
- transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.05s;
159
- }
160
- .btn:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); }
161
- .btn:active { transform: translateY(1px); }
162
- .btn:disabled { opacity: 0.5; cursor: progress; }
163
- .btn-add { color: var(--accent); }
164
- .btn-add.is-open { background: var(--accent); color: var(--bg); border-color: var(--accent); }
165
- .btn-primary { background: var(--accent); color: var(--bg); border-color: var(--accent); }
166
- .btn-primary:hover { background: #7ad3ff; border-color: #7ad3ff; }
167
- .btn-ghost { background: transparent; color: var(--text-dim); }
168
- .btn-ghost:hover { background: var(--bg-elev); color: var(--text); }
169
- .btn-sm { padding: 6px 12px; font-size: 12.5px; }
170
-
171
- .btn-remove {
172
- appearance: none;
173
- font-family: var(--sans);
174
- font-size: 12.5px;
175
- font-weight: 550;
176
- padding: 7px 12px;
177
- border-radius: var(--radius-sm);
178
- border: 1px solid var(--border-strong);
179
- background: transparent;
180
- color: var(--text-faint);
181
- cursor: pointer;
182
- transition: background 0.15s, border-color 0.15s, color 0.15s;
183
- }
184
- .btn-remove:hover { color: var(--danger); border-color: rgba(255, 107, 120, 0.4); background: rgba(255, 107, 120, 0.08); }
185
- .btn-remove.btn-sm { padding: 4px 9px; line-height: 1; }
186
-
187
- /* ── Add forms ───────────────────────────────────────────────────────────── */
188
- .addform {
189
- background: var(--bg-card);
190
- border: 1px solid var(--border-strong);
191
- border-radius: var(--radius);
192
- padding: 18px;
193
- margin-bottom: 18px;
194
- animation: fade 0.18s ease;
195
- }
196
- .field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
197
- .field { display: flex; flex-direction: column; gap: 5px; }
198
- .field.field-wide { grid-column: 1 / -1; }
199
- .field > span { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
200
- .field > span em { color: var(--accent); font-style: normal; }
201
- .field > span em.hint { color: var(--text-faint); text-transform: none; letter-spacing: 0; font-size: 11.5px; }
202
- .field input, .field select, .field textarea {
203
- width: 100%;
204
- padding: 10px 12px;
205
- background: var(--bg-input);
206
- border: 1px solid var(--border-strong);
207
- border-radius: var(--radius-sm);
208
- color: var(--text);
209
- font-size: 14px;
210
- font-family: var(--sans);
211
- outline: none;
212
- transition: border-color 0.15s, box-shadow 0.15s;
213
- }
214
- .field input[id$="path"], .field input[id$="command"], .field #t-id, .field #m-id, .field #h-id,
215
- .field #m-command, .field #h-command, .field #h-matcher, .field textarea { font-family: var(--mono); font-size: 13px; }
216
- .field textarea { resize: vertical; line-height: 1.5; }
217
- .field input:focus, .field select:focus, .field textarea:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--accent-glow); }
218
- .field select { cursor: pointer; }
219
-
220
- .subsection { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 14px; }
221
- .subsection-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
222
- .subsection-title { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
223
- .subsection-title em.hint { color: var(--text-faint); text-transform: none; letter-spacing: 0; font-size: 11.5px; }
224
- .expose-rows { display: flex; flex-direction: column; gap: 8px; }
225
- .expose-input-row { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 8px; align-items: center; }
226
- .expose-input-row input {
227
- padding: 8px 10px;
228
- background: var(--bg-input);
229
- border: 1px solid var(--border-strong);
230
- border-radius: var(--radius-sm);
231
- color: var(--text);
232
- font-family: var(--mono);
233
- font-size: 13px;
234
- outline: none;
235
- }
236
- .expose-input-row input:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--accent-glow); }
237
-
238
- .addform-actions { display: flex; gap: 10px; margin-top: 18px; }
239
-
240
- /* ── Rows (tools / mcps / hooks share the card layout) ─────────────────────── */
241
- .card-list { display: flex; flex-direction: column; gap: 10px; }
242
-
243
- .row {
244
- display: grid;
245
- grid-template-columns: 1fr auto;
246
- gap: 6px 18px;
247
- padding: 16px 18px;
248
- background: var(--bg-card);
249
- border: 1px solid var(--border);
250
- border-radius: var(--radius);
251
- transition: border-color 0.15s, background 0.15s;
252
- }
253
- .row:hover { background: var(--bg-card-hover); border-color: var(--border-strong); }
254
- .row.is-disabled { opacity: 0.55; }
255
-
256
- .row-head { grid-column: 1; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
257
- .row-name { font-size: 15.5px; font-weight: 600; letter-spacing: -0.01em; }
258
- .row-name.mono { font-family: var(--mono); font-size: 14.5px; }
259
- .row-id { font-family: var(--mono); font-size: 12px; color: var(--text-faint); }
260
- .row-summary { grid-column: 1; color: var(--text-dim); font-size: 13.5px; margin: 0; }
261
- .row-cmd { grid-column: 1; margin: 2px 0 0; font-family: var(--mono); font-size: 12px; color: var(--text-faint); word-break: break-all; }
262
-
263
- .chip {
264
- font-family: var(--mono);
265
- font-size: 11px;
266
- letter-spacing: 0.02em;
267
- color: var(--accent);
268
- background: rgba(92, 200, 255, 0.1);
269
- border: 1px solid rgba(92, 200, 255, 0.22);
270
- padding: 2px 9px;
271
- border-radius: 999px;
272
- }
273
-
274
- /* Controls cluster (right side, spans the card rows) */
275
- .row-controls {
276
- grid-column: 2;
277
- grid-row: 1 / span 3;
278
- display: flex;
279
- align-items: center;
280
- gap: 16px;
281
- flex-wrap: wrap;
282
- justify-content: flex-end;
283
- }
284
- .hook-toggles { display: flex; gap: 14px; }
285
- .toggle-group { display: flex; flex-direction: column; align-items: center; gap: 5px; }
286
- .toggle-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-faint); }
287
- .toggle-group.enable .toggle-label { color: var(--text-dim); }
288
- .toggle-group.mode .mode-label { color: var(--accent); font-family: var(--mono); text-transform: none; letter-spacing: 0; }
289
- .managed-note { font-size: 11.5px; color: var(--text-faint); font-style: italic; }
290
-
291
- /* Switch */
292
- .switch { position: relative; display: inline-block; width: 40px; height: 22px; }
293
- .switch input { opacity: 0; width: 0; height: 0; }
294
- .slider {
295
- position: absolute;
296
- inset: 0;
297
- cursor: pointer;
298
- background: #2a313d;
299
- border: 1px solid var(--border-strong);
300
- border-radius: 999px;
301
- transition: background 0.18s, border-color 0.18s;
302
- }
303
- .slider::before {
304
- content: "";
305
- position: absolute;
306
- height: 16px;
307
- width: 16px;
308
- left: 2px;
309
- top: 2px;
310
- background: #c2c9d6;
311
- border-radius: 50%;
312
- transition: transform 0.18s, background 0.18s;
313
- }
314
- .switch input:checked + .slider { background: var(--accent); border-color: var(--accent); }
315
- .switch input:checked + .slider::before { transform: translateX(18px); background: #06222e; }
316
- .switch.small { width: 34px; height: 19px; }
317
- .switch.small .slider::before { height: 13px; width: 13px; }
318
- .switch.small input:checked + .slider::before { transform: translateX(15px); }
319
- .switch input:focus-visible + .slider { box-shadow: 0 0 0 3px var(--accent-glow); }
320
- .switch input:disabled + .slider { opacity: 0.5; cursor: progress; }
321
-
322
- /* ── Badges ──────────────────────────────────────────────────────────────── */
323
- .badge {
324
- font-size: 11px;
325
- padding: 2px 8px;
326
- border-radius: 999px;
327
- border: 1px solid var(--border-strong);
328
- color: var(--text-dim);
329
- font-family: var(--mono);
330
- }
331
- .badge.on { color: var(--good); border-color: rgba(84, 214, 160, 0.35); background: rgba(84, 214, 160, 0.08); }
332
- .badge.off { color: var(--text-faint); }
333
- .badge.bad { color: var(--danger); border-color: rgba(255, 107, 120, 0.4); background: rgba(255, 107, 120, 0.08); }
334
-
335
- /* ── Exposed tools (MCP rows) ──────────────────────────────────────────────── */
336
- .exposed { grid-column: 1; margin-top: 10px; border-top: 1px solid var(--border); padding-top: 10px; }
337
- .exposed-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); margin: 0 0 8px; }
338
- .exposed-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; font-family: var(--mono); font-size: 12.5px; }
339
- .exposed-row .arrow { color: var(--text-faint); }
340
- .exposed-row .as { color: var(--accent); }
341
- .exposed-row .none { color: var(--text-faint); font-style: italic; font-family: var(--sans); }
342
-
343
- /* ── Logs ──────────────────────────────────────────────────────────────────── */
344
- .logs-toolbar { justify-content: space-between; }
345
- .log-config { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }
346
- .log-status { font-size: 13px; color: var(--text-dim); }
347
- .log-status.on { color: var(--good); }
348
- .log-path { font-size: 12px; color: var(--text-faint); word-break: break-all; }
349
- .log-count { position: static; }
350
-
351
- .log-list { max-height: 62vh; overflow-y: auto; padding-right: 4px; }
352
- .log-row { display: flex; flex-direction: column; gap: 6px; padding: 12px 16px; }
353
- .log-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
354
- .log-head .log-name { font-size: 14px; font-weight: 600; }
355
- .log-ts { margin-left: auto; font-family: var(--mono); font-size: 11.5px; color: var(--text-faint); }
356
- .log-detail { margin: 0; font-family: var(--mono); font-size: 12px; color: var(--text-dim); word-break: break-word; }
357
-
358
- .log-badge {
359
- font-family: var(--mono);
360
- font-size: 10.5px;
361
- text-transform: uppercase;
362
- letter-spacing: 0.06em;
363
- padding: 2px 8px;
364
- border-radius: 999px;
365
- border: 1px solid var(--border-strong);
366
- }
367
- .log-badge.gate { color: var(--warn); border-color: rgba(245, 184, 96, 0.35); background: rgba(245, 184, 96, 0.08); }
368
- .log-badge.tool { color: var(--accent); border-color: rgba(92, 200, 255, 0.3); background: rgba(92, 200, 255, 0.08); }
369
-
370
- /* ── Empty states ────────────────────────────────────────────────────────── */
371
- .empty {
372
- text-align: center;
373
- padding: 48px 20px;
374
- color: var(--text-dim);
375
- background: var(--bg-elev);
376
- border: 1px dashed var(--border-strong);
377
- border-radius: var(--radius);
378
- }
379
- .empty p { margin: 4px 0; }
380
- .empty .muted { color: var(--text-faint); font-size: 13px; }
381
- code { font-family: var(--mono); color: var(--accent); background: rgba(92, 200, 255, 0.08); padding: 1px 5px; border-radius: 4px; }
382
- .muted { color: var(--text-faint); }
383
-
384
- /* ── Toast ───────────────────────────────────────────────────────────────── */
385
- .toast {
386
- position: fixed;
387
- left: 50%;
388
- bottom: 26px;
389
- transform: translateX(-50%) translateY(20px);
390
- max-width: 560px;
391
- background: var(--bg-card);
392
- border: 1px solid var(--border-strong);
393
- border-left: 3px solid var(--accent);
394
- border-radius: var(--radius-sm);
395
- padding: 12px 16px;
396
- font-size: 13.5px;
397
- color: var(--text);
398
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
399
- opacity: 0;
400
- pointer-events: none;
401
- transition: opacity 0.2s, transform 0.2s;
402
- z-index: 50;
403
- }
404
- .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
405
- .toast.good { border-left-color: var(--good); }
406
- .toast.error { border-left-color: var(--danger); }
407
- .toast code { display: inline-block; margin-top: 2px; word-break: break-all; }
408
-
409
- /* ── Top-level surface panel (Tools tab - the visibility matrix's every-turn axis) ── */
410
- .surface-panel {
411
- background: var(--bg-elev);
412
- border: 1px solid var(--border);
413
- border-radius: var(--radius);
414
- padding: 14px 16px;
415
- margin-bottom: 18px;
416
- }
417
- .surface-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
418
- .surface-title { font-size: 12.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text); }
419
- .surface-sub { font-size: 12px; color: var(--text-faint); }
420
- .surface-count { margin-left: auto; font-family: var(--mono); font-size: 12px; color: var(--text-faint); }
421
- .surface-count.is-some { color: var(--accent); }
422
- .surface-count.is-bloat { color: var(--warn); }
423
- .surface-meta { display: flex; flex-wrap: wrap; gap: 12px 20px; margin-top: 12px; }
424
- .surface-meta-item { display: flex; align-items: center; gap: 8px; }
425
- .surface-meta-item code { font-family: var(--mono); font-size: 12px; color: var(--accent); }
426
- .surface-meta-item.is-off code { color: var(--text-faint); text-decoration: line-through; }
427
- .surface-warnings { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
428
- .surface-warning {
429
- margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--warn);
430
- background: rgba(245, 184, 96, 0.08); border: 1px solid rgba(245, 184, 96, 0.28);
431
- border-radius: var(--radius-sm); padding: 8px 11px;
432
- }
433
-
434
- /* Hot row accent + the per-row Hot toggle */
435
- .row.is-hot { border-color: var(--accent-dim); box-shadow: inset 3px 0 0 var(--accent); }
436
- /* A DISABLED tool is never actually on the every-turn surface - drop the hot accent so a dimmed row
437
- * doesn't read as "still promoted" (the Hot switch stays on, reflecting the stored intent). */
438
- .row.is-disabled.is-hot { box-shadow: none; border-color: var(--border); }
439
- .toggle-group.hot .toggle-label { color: var(--accent); }
440
- .toggle-group.hidden .toggle-label { color: var(--text-faint); }
441
-
442
- /* "show hidden" filter in the Tools toolbar */
443
- .show-hidden { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-dim); white-space: nowrap; cursor: pointer; }
444
- .show-hidden input { accent-color: var(--accent); cursor: pointer; margin: 0; }
445
-
446
- /* ── Discovered upstream tools (MCPs tab - the live Discover button) ───────────── */
447
- .discovered { grid-column: 1 / -1; margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
448
- .discovered-row {
449
- display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
450
- padding: 7px 0; font-family: var(--mono); font-size: 12.5px;
451
- }
452
- .discovered-row .tool { color: var(--text); }
453
- .discovered-row .arrow { color: var(--text-faint); }
454
- .discovered-row .as { color: var(--accent); }
455
- .discovered-row .disc-desc { font-family: var(--sans); color: var(--text-dim); font-size: 12px; flex: 1 1 160px; min-width: 0; }
456
- .discovered-row .toggle-group { margin-left: 6px; }
457
- .discovered-row .toggle-group.lean .toggle-label { color: var(--text-dim); }
458
- .discovered .none { color: var(--text-faint); font-style: italic; }
459
-
460
- /* ── Responsive ──────────────────────────────────────────────────────────── */
461
- @media (max-width: 720px) {
462
- .field-grid { grid-template-columns: 1fr; }
463
- .row { grid-template-columns: 1fr; }
464
- .row-controls { grid-column: 1; grid-row: auto; justify-content: flex-start; margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--border); }
465
- .toolbar { flex-wrap: wrap; }
466
- .expose-input-row { grid-template-columns: 1fr 1fr; }
467
- .discovered-row { gap: 6px 10px; }
468
- }
469
-
470
- /* ── Auth (OAuth 2.1) view ─────────────────────────────────────────────────── */
471
- .auth-intro {
472
- background: var(--bg-elev);
473
- border: 1px solid var(--border);
474
- border-radius: var(--radius);
475
- padding: 14px 18px;
476
- margin-bottom: 16px;
477
- }
478
- .auth-intro p { margin: 0; color: var(--text-dim); font-size: 14px; }
479
- .auth-intro code { font-family: var(--mono); color: var(--accent); font-size: 13px; }
480
- .auth-intro strong { color: var(--text); }
481
-
482
- .auth-card {
483
- background: var(--bg-card);
484
- border: 1px solid var(--border);
485
- border-radius: var(--radius);
486
- padding: 16px 18px;
487
- margin-bottom: 16px;
488
- }
489
- .auth-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
490
- .auth-dep-state { font-size: 14px; font-weight: 500; }
491
- .auth-dep-state.is-good { color: var(--good); }
492
- .auth-dep-state.is-off { color: var(--text-dim); }
493
- .auth-install-log {
494
- margin: 14px 0 0;
495
- padding: 12px 14px;
496
- background: var(--bg-input);
497
- border: 1px solid var(--border);
498
- border-radius: var(--radius-sm);
499
- color: var(--text-dim);
500
- font-family: var(--mono);
501
- font-size: 12px;
502
- white-space: pre-wrap;
503
- word-break: break-word;
504
- max-height: 240px;
505
- overflow: auto;
506
- }
507
-
508
- .auth-enable-row { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
509
- .auth-enable-label { font-weight: 500; }
510
- .auth-ready-badge { font-size: 13px; padding: 2px 10px; border-radius: 999px; }
511
- .auth-ready-badge.is-good { color: var(--good); background: rgba(84, 214, 160, 0.12); }
512
- .auth-ready-badge.is-bad { color: var(--warn); background: rgba(245, 184, 96, 0.12); }
513
- .auth-config-note { font-size: 13px; }
514
-
515
- /* ── Activity-log badges for the full set of entry types ───────────────────── */
516
- .log-badge.config { background: rgba(92, 200, 255, 0.16); color: var(--accent); }
517
- .log-badge.auth { background: rgba(245, 184, 96, 0.16); color: var(--warn); }
518
- .log-badge.client { background: rgba(84, 214, 160, 0.14); color: var(--good); }
519
- .log-badge.mcp { background: rgba(154, 163, 178, 0.16); color: var(--text-dim); }
520
- .log-badge.event { background: rgba(154, 163, 178, 0.16); color: var(--text-dim); }
521
-
522
- /* ── Tool detail / edit panel ──────────────────────────────────────────────── */
523
- .tool-detail {
524
- margin-top: 12px;
525
- padding: 14px 16px;
526
- background: var(--bg-input);
527
- border: 1px solid var(--border);
528
- border-radius: var(--radius-sm);
529
- }
530
- .tool-detail-id { margin-bottom: 10px; }
531
- .tool-details-btn.is-open { color: var(--accent); border-color: var(--accent-dim); }
532
-
533
- /* ── Brand logo image + blue "Tool" wordmark ───────────────────────────────── */
534
- .logo-img { width: 100%; height: 100%; object-fit: contain; border-radius: var(--radius-sm); display: block; }
535
- .brand-tool { color: var(--accent); }
1
+ /* ToolFunnel management console - dark, modern, technical. Vanilla CSS, zero external assets. */
2
+
3
+ :root {
4
+ --bg: #0c0e13;
5
+ --bg-elev: #13161d;
6
+ --bg-card: #161a22;
7
+ --bg-card-hover: #1b202a;
8
+ --bg-input: #0f1218;
9
+ --border: #242a36;
10
+ --border-strong: #313a48;
11
+ --text: #e6e9ef;
12
+ --text-dim: #9aa3b2;
13
+ --text-faint: #6a7384;
14
+ --accent: #5cc8ff;
15
+ --accent-dim: #2b6f93;
16
+ --accent-glow: rgba(92, 200, 255, 0.18);
17
+ --good: #54d6a0;
18
+ --warn: #f5b860;
19
+ --danger: #ff6b78;
20
+ --mono: ui-monospace, "SFMono-Regular", "JetBrains Mono", "Fira Code", Consolas, "Liberation Mono", monospace;
21
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
22
+ --radius: 12px;
23
+ --radius-sm: 8px;
24
+ }
25
+
26
+ * { box-sizing: border-box; }
27
+
28
+ html, body {
29
+ margin: 0;
30
+ padding: 0;
31
+ background: var(--bg);
32
+ color: var(--text);
33
+ font-family: var(--sans);
34
+ font-size: 15px;
35
+ line-height: 1.5;
36
+ -webkit-font-smoothing: antialiased;
37
+ }
38
+
39
+ body {
40
+ background:
41
+ radial-gradient(900px 500px at 80% -10%, rgba(92, 200, 255, 0.06), transparent 60%),
42
+ radial-gradient(700px 400px at 0% 0%, rgba(84, 214, 160, 0.04), transparent 55%),
43
+ var(--bg);
44
+ min-height: 100vh;
45
+ }
46
+
47
+ .app {
48
+ max-width: 980px;
49
+ margin: 0 auto;
50
+ padding: 28px 24px 80px;
51
+ }
52
+
53
+ /* ── Top bar ─────────────────────────────────────────────────────────────── */
54
+ .topbar {
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: space-between;
58
+ gap: 20px;
59
+ flex-wrap: wrap;
60
+ padding-bottom: 22px;
61
+ border-bottom: 1px solid var(--border);
62
+ }
63
+
64
+ .brand { display: flex; align-items: center; gap: 14px; }
65
+
66
+ .logo {
67
+ display: grid;
68
+ place-items: center;
69
+ width: 42px;
70
+ height: 42px;
71
+ border-radius: var(--radius-sm);
72
+ color: var(--accent);
73
+ background: linear-gradient(160deg, rgba(92, 200, 255, 0.16), rgba(92, 200, 255, 0.03));
74
+ border: 1px solid var(--border-strong);
75
+ box-shadow: 0 0 24px var(--accent-glow);
76
+ }
77
+
78
+ .brand-text h1 { margin: 0; font-size: 22px; font-weight: 650; letter-spacing: -0.02em; }
79
+ .tagline { margin: 1px 0 0; font-size: 12.5px; color: var(--text-faint); font-family: var(--mono); }
80
+
81
+ .stats { display: flex; gap: 10px; }
82
+ .stat {
83
+ display: flex;
84
+ flex-direction: column;
85
+ align-items: center;
86
+ min-width: 74px;
87
+ padding: 9px 14px;
88
+ background: var(--bg-elev);
89
+ border: 1px solid var(--border);
90
+ border-radius: var(--radius-sm);
91
+ }
92
+ .stat-num { font-family: var(--mono); font-size: 19px; font-weight: 600; color: var(--accent); line-height: 1.1; }
93
+ .stat-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); }
94
+
95
+ /* ── Tabs ────────────────────────────────────────────────────────────────── */
96
+ .tabs { display: flex; gap: 4px; margin: 22px 0 18px; }
97
+ .tab {
98
+ appearance: none;
99
+ border: none;
100
+ background: transparent;
101
+ color: var(--text-dim);
102
+ font-family: var(--sans);
103
+ font-size: 14px;
104
+ font-weight: 550;
105
+ padding: 8px 18px;
106
+ border-radius: 999px;
107
+ cursor: pointer;
108
+ transition: background 0.15s, color 0.15s;
109
+ }
110
+ .tab:hover { color: var(--text); background: var(--bg-elev); }
111
+ .tab.is-active { color: var(--bg); background: var(--accent); }
112
+
113
+ .view { display: none; }
114
+ .view.is-active { display: block; animation: fade 0.2s ease; }
115
+ @keyframes fade { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
116
+
117
+ /* ── Toolbar (search + add button) ─────────────────────────────────────────── */
118
+ .toolbar { display: flex; gap: 12px; align-items: center; margin-bottom: 16px; }
119
+
120
+ .searchbar { position: relative; display: flex; align-items: center; flex: 1; }
121
+ .search-icon { position: absolute; left: 14px; color: var(--text-faint); pointer-events: none; }
122
+ .searchbar input {
123
+ width: 100%;
124
+ padding: 12px 16px 12px 42px;
125
+ background: var(--bg-elev);
126
+ border: 1px solid var(--border-strong);
127
+ border-radius: var(--radius);
128
+ color: var(--text);
129
+ font-size: 15px;
130
+ font-family: var(--sans);
131
+ outline: none;
132
+ transition: border-color 0.15s, box-shadow 0.15s;
133
+ }
134
+ .searchbar input::placeholder { color: var(--text-faint); }
135
+ .searchbar input:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--accent-glow); }
136
+ .result-count {
137
+ position: absolute;
138
+ right: 16px;
139
+ font-family: var(--mono);
140
+ font-size: 12px;
141
+ color: var(--text-faint);
142
+ pointer-events: none;
143
+ }
144
+
145
+ /* ── Buttons ─────────────────────────────────────────────────────────────── */
146
+ .btn {
147
+ appearance: none;
148
+ font-family: var(--sans);
149
+ font-size: 14px;
150
+ font-weight: 550;
151
+ padding: 11px 18px;
152
+ border-radius: var(--radius-sm);
153
+ border: 1px solid var(--border-strong);
154
+ background: var(--bg-elev);
155
+ color: var(--text);
156
+ cursor: pointer;
157
+ white-space: nowrap;
158
+ transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.05s;
159
+ }
160
+ .btn:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); }
161
+ .btn:active { transform: translateY(1px); }
162
+ .btn:disabled { opacity: 0.5; cursor: progress; }
163
+ .btn-add { color: var(--accent); }
164
+ .btn-add.is-open { background: var(--accent); color: var(--bg); border-color: var(--accent); }
165
+ .btn-primary { background: var(--accent); color: var(--bg); border-color: var(--accent); }
166
+ .btn-primary:hover { background: #7ad3ff; border-color: #7ad3ff; }
167
+ .btn-ghost { background: transparent; color: var(--text-dim); }
168
+ .btn-ghost:hover { background: var(--bg-elev); color: var(--text); }
169
+ .btn-sm { padding: 6px 12px; font-size: 12.5px; }
170
+
171
+ .btn-remove {
172
+ appearance: none;
173
+ font-family: var(--sans);
174
+ font-size: 12.5px;
175
+ font-weight: 550;
176
+ padding: 7px 12px;
177
+ border-radius: var(--radius-sm);
178
+ border: 1px solid var(--border-strong);
179
+ background: transparent;
180
+ color: var(--text-faint);
181
+ cursor: pointer;
182
+ transition: background 0.15s, border-color 0.15s, color 0.15s;
183
+ }
184
+ .btn-remove:hover { color: var(--danger); border-color: rgba(255, 107, 120, 0.4); background: rgba(255, 107, 120, 0.08); }
185
+ .btn-remove.btn-sm { padding: 4px 9px; line-height: 1; }
186
+
187
+ /* ── Add forms ───────────────────────────────────────────────────────────── */
188
+ .addform {
189
+ background: var(--bg-card);
190
+ border: 1px solid var(--border-strong);
191
+ border-radius: var(--radius);
192
+ padding: 18px;
193
+ margin-bottom: 18px;
194
+ animation: fade 0.18s ease;
195
+ }
196
+ .field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
197
+ .field { display: flex; flex-direction: column; gap: 5px; }
198
+ .field.field-wide { grid-column: 1 / -1; }
199
+ .field > span { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
200
+ .field > span em { color: var(--accent); font-style: normal; }
201
+ .field > span em.hint { color: var(--text-faint); text-transform: none; letter-spacing: 0; font-size: 11.5px; }
202
+ .field input, .field select, .field textarea {
203
+ width: 100%;
204
+ padding: 10px 12px;
205
+ background: var(--bg-input);
206
+ border: 1px solid var(--border-strong);
207
+ border-radius: var(--radius-sm);
208
+ color: var(--text);
209
+ font-size: 14px;
210
+ font-family: var(--sans);
211
+ outline: none;
212
+ transition: border-color 0.15s, box-shadow 0.15s;
213
+ }
214
+ .field input[id$="path"], .field input[id$="command"], .field #t-id, .field #m-id, .field #h-id,
215
+ .field #m-command, .field #h-command, .field #h-matcher, .field textarea { font-family: var(--mono); font-size: 13px; }
216
+ .field textarea { resize: vertical; line-height: 1.5; }
217
+ .field input:focus, .field select:focus, .field textarea:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--accent-glow); }
218
+ .field select { cursor: pointer; }
219
+
220
+ .subsection { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 14px; }
221
+ .subsection-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
222
+ .subsection-title { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; }
223
+ .subsection-title em.hint { color: var(--text-faint); text-transform: none; letter-spacing: 0; font-size: 11.5px; }
224
+ .expose-rows { display: flex; flex-direction: column; gap: 8px; }
225
+ .expose-input-row { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 8px; align-items: center; }
226
+ .expose-input-row input {
227
+ padding: 8px 10px;
228
+ background: var(--bg-input);
229
+ border: 1px solid var(--border-strong);
230
+ border-radius: var(--radius-sm);
231
+ color: var(--text);
232
+ font-family: var(--mono);
233
+ font-size: 13px;
234
+ outline: none;
235
+ }
236
+ .expose-input-row input:focus { border-color: var(--accent-dim); box-shadow: 0 0 0 3px var(--accent-glow); }
237
+
238
+ .addform-actions { display: flex; gap: 10px; margin-top: 18px; }
239
+
240
+ /* ── Rows (tools / mcps / hooks share the card layout) ─────────────────────── */
241
+ .card-list { display: flex; flex-direction: column; gap: 10px; }
242
+
243
+ .row {
244
+ display: grid;
245
+ grid-template-columns: 1fr auto;
246
+ gap: 6px 18px;
247
+ padding: 16px 18px;
248
+ background: var(--bg-card);
249
+ border: 1px solid var(--border);
250
+ border-radius: var(--radius);
251
+ transition: border-color 0.15s, background 0.15s;
252
+ }
253
+ .row:hover { background: var(--bg-card-hover); border-color: var(--border-strong); }
254
+ .row.is-disabled { opacity: 0.55; }
255
+
256
+ .row-head { grid-column: 1; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
257
+ .row-name { font-size: 15.5px; font-weight: 600; letter-spacing: -0.01em; }
258
+ .row-name.mono { font-family: var(--mono); font-size: 14.5px; }
259
+ .row-id { font-family: var(--mono); font-size: 12px; color: var(--text-faint); }
260
+ .row-summary { grid-column: 1; color: var(--text-dim); font-size: 13.5px; margin: 0; }
261
+ .row-cmd { grid-column: 1; margin: 2px 0 0; font-family: var(--mono); font-size: 12px; color: var(--text-faint); word-break: break-all; }
262
+
263
+ .chip {
264
+ font-family: var(--mono);
265
+ font-size: 11px;
266
+ letter-spacing: 0.02em;
267
+ color: var(--accent);
268
+ background: rgba(92, 200, 255, 0.1);
269
+ border: 1px solid rgba(92, 200, 255, 0.22);
270
+ padding: 2px 9px;
271
+ border-radius: 999px;
272
+ }
273
+
274
+ /* Controls cluster (right side, spans the card rows) */
275
+ .row-controls {
276
+ grid-column: 2;
277
+ grid-row: 1 / span 3;
278
+ display: flex;
279
+ align-items: center;
280
+ gap: 16px;
281
+ flex-wrap: wrap;
282
+ justify-content: flex-end;
283
+ }
284
+ .hook-toggles { display: flex; gap: 14px; }
285
+ .toggle-group { display: flex; flex-direction: column; align-items: center; gap: 5px; }
286
+ .toggle-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-faint); }
287
+ .toggle-group.enable .toggle-label { color: var(--text-dim); }
288
+ .toggle-group.mode .mode-label { color: var(--accent); font-family: var(--mono); text-transform: none; letter-spacing: 0; }
289
+ .managed-note { font-size: 11.5px; color: var(--text-faint); font-style: italic; }
290
+
291
+ /* Switch */
292
+ .switch { position: relative; display: inline-block; width: 40px; height: 22px; }
293
+ .switch input { opacity: 0; width: 0; height: 0; }
294
+ .slider {
295
+ position: absolute;
296
+ inset: 0;
297
+ cursor: pointer;
298
+ background: #2a313d;
299
+ border: 1px solid var(--border-strong);
300
+ border-radius: 999px;
301
+ transition: background 0.18s, border-color 0.18s;
302
+ }
303
+ .slider::before {
304
+ content: "";
305
+ position: absolute;
306
+ height: 16px;
307
+ width: 16px;
308
+ left: 2px;
309
+ top: 2px;
310
+ background: #c2c9d6;
311
+ border-radius: 50%;
312
+ transition: transform 0.18s, background 0.18s;
313
+ }
314
+ .switch input:checked + .slider { background: var(--accent); border-color: var(--accent); }
315
+ .switch input:checked + .slider::before { transform: translateX(18px); background: #06222e; }
316
+ .switch.small { width: 34px; height: 19px; }
317
+ .switch.small .slider::before { height: 13px; width: 13px; }
318
+ .switch.small input:checked + .slider::before { transform: translateX(15px); }
319
+ .switch input:focus-visible + .slider { box-shadow: 0 0 0 3px var(--accent-glow); }
320
+ .switch input:disabled + .slider { opacity: 0.5; cursor: progress; }
321
+
322
+ /* ── Badges ──────────────────────────────────────────────────────────────── */
323
+ .badge {
324
+ font-size: 11px;
325
+ padding: 2px 8px;
326
+ border-radius: 999px;
327
+ border: 1px solid var(--border-strong);
328
+ color: var(--text-dim);
329
+ font-family: var(--mono);
330
+ }
331
+ .badge.on { color: var(--good); border-color: rgba(84, 214, 160, 0.35); background: rgba(84, 214, 160, 0.08); }
332
+ .badge.off { color: var(--text-faint); }
333
+ .badge.bad { color: var(--danger); border-color: rgba(255, 107, 120, 0.4); background: rgba(255, 107, 120, 0.08); }
334
+
335
+ /* ── Exposed tools (MCP rows) ──────────────────────────────────────────────── */
336
+ .exposed { grid-column: 1; margin-top: 10px; border-top: 1px solid var(--border); padding-top: 10px; }
337
+ .exposed-title { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); margin: 0 0 8px; }
338
+ .exposed-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; font-family: var(--mono); font-size: 12.5px; }
339
+ .exposed-row .arrow { color: var(--text-faint); }
340
+ .exposed-row .as { color: var(--accent); }
341
+ .exposed-row .none { color: var(--text-faint); font-style: italic; font-family: var(--sans); }
342
+
343
+ /* ── Logs ──────────────────────────────────────────────────────────────────── */
344
+ .logs-toolbar { justify-content: space-between; }
345
+ .log-config { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; }
346
+ .log-status { font-size: 13px; color: var(--text-dim); }
347
+ .log-status.on { color: var(--good); }
348
+ .log-path { font-size: 12px; color: var(--text-faint); word-break: break-all; }
349
+ .log-count { position: static; }
350
+
351
+ .log-list { max-height: 62vh; overflow-y: auto; padding-right: 4px; }
352
+ .log-row { display: flex; flex-direction: column; gap: 6px; padding: 12px 16px; }
353
+ .log-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
354
+ .log-head .log-name { font-size: 14px; font-weight: 600; }
355
+ .log-ts { margin-left: auto; font-family: var(--mono); font-size: 11.5px; color: var(--text-faint); }
356
+ .log-detail { margin: 0; font-family: var(--mono); font-size: 12px; color: var(--text-dim); word-break: break-word; }
357
+
358
+ .log-badge {
359
+ font-family: var(--mono);
360
+ font-size: 10.5px;
361
+ text-transform: uppercase;
362
+ letter-spacing: 0.06em;
363
+ padding: 2px 8px;
364
+ border-radius: 999px;
365
+ border: 1px solid var(--border-strong);
366
+ }
367
+ .log-badge.gate { color: var(--warn); border-color: rgba(245, 184, 96, 0.35); background: rgba(245, 184, 96, 0.08); }
368
+ .log-badge.tool { color: var(--accent); border-color: rgba(92, 200, 255, 0.3); background: rgba(92, 200, 255, 0.08); }
369
+
370
+ /* ── Empty states ────────────────────────────────────────────────────────── */
371
+ .empty {
372
+ text-align: center;
373
+ padding: 48px 20px;
374
+ color: var(--text-dim);
375
+ background: var(--bg-elev);
376
+ border: 1px dashed var(--border-strong);
377
+ border-radius: var(--radius);
378
+ }
379
+ .empty p { margin: 4px 0; }
380
+ .empty .muted { color: var(--text-faint); font-size: 13px; }
381
+ code { font-family: var(--mono); color: var(--accent); background: rgba(92, 200, 255, 0.08); padding: 1px 5px; border-radius: 4px; }
382
+ .muted { color: var(--text-faint); }
383
+
384
+ /* ── Toast ───────────────────────────────────────────────────────────────── */
385
+ .toast {
386
+ position: fixed;
387
+ left: 50%;
388
+ bottom: 26px;
389
+ transform: translateX(-50%) translateY(20px);
390
+ max-width: 560px;
391
+ background: var(--bg-card);
392
+ border: 1px solid var(--border-strong);
393
+ border-left: 3px solid var(--accent);
394
+ border-radius: var(--radius-sm);
395
+ padding: 12px 16px;
396
+ font-size: 13.5px;
397
+ color: var(--text);
398
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
399
+ opacity: 0;
400
+ pointer-events: none;
401
+ transition: opacity 0.2s, transform 0.2s;
402
+ z-index: 50;
403
+ }
404
+ .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
405
+ .toast.good { border-left-color: var(--good); }
406
+ .toast.error { border-left-color: var(--danger); }
407
+ .toast code { display: inline-block; margin-top: 2px; word-break: break-all; }
408
+
409
+ /* ── Top-level surface panel (Tools tab - the visibility matrix's every-turn axis) ── */
410
+ .surface-panel {
411
+ background: var(--bg-elev);
412
+ border: 1px solid var(--border);
413
+ border-radius: var(--radius);
414
+ padding: 14px 16px;
415
+ margin-bottom: 18px;
416
+ }
417
+ .surface-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
418
+ .surface-title { font-size: 12.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text); }
419
+ .surface-sub { font-size: 12px; color: var(--text-faint); }
420
+ .surface-count { margin-left: auto; font-family: var(--mono); font-size: 12px; color: var(--text-faint); }
421
+ .surface-count.is-some { color: var(--accent); }
422
+ .surface-count.is-bloat { color: var(--warn); }
423
+ .surface-meta { display: flex; flex-wrap: wrap; gap: 12px 20px; margin-top: 12px; }
424
+ .surface-meta-item { display: flex; align-items: center; gap: 8px; }
425
+ .surface-meta-item code { font-family: var(--mono); font-size: 12px; color: var(--accent); }
426
+ .surface-meta-item.is-off code { color: var(--text-faint); text-decoration: line-through; }
427
+ .surface-warnings { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
428
+ .surface-warning {
429
+ margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--warn);
430
+ background: rgba(245, 184, 96, 0.08); border: 1px solid rgba(245, 184, 96, 0.28);
431
+ border-radius: var(--radius-sm); padding: 8px 11px;
432
+ }
433
+
434
+ /* Hot row accent + the per-row Hot toggle */
435
+ .row.is-hot { border-color: var(--accent-dim); box-shadow: inset 3px 0 0 var(--accent); }
436
+ /* A DISABLED tool is never actually on the every-turn surface - drop the hot accent so a dimmed row
437
+ * doesn't read as "still promoted" (the Hot switch stays on, reflecting the stored intent). */
438
+ .row.is-disabled.is-hot { box-shadow: none; border-color: var(--border); }
439
+ .toggle-group.hot .toggle-label { color: var(--accent); }
440
+ .toggle-group.hidden .toggle-label { color: var(--text-faint); }
441
+
442
+ /* "show hidden" filter in the Tools toolbar */
443
+ .show-hidden { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-dim); white-space: nowrap; cursor: pointer; }
444
+ .show-hidden input { accent-color: var(--accent); cursor: pointer; margin: 0; }
445
+
446
+ /* ── Discovered upstream tools (MCPs tab - the live Discover button) ───────────── */
447
+ .discovered { grid-column: 1 / -1; margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
448
+ .discovered-row {
449
+ display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
450
+ padding: 7px 0; font-family: var(--mono); font-size: 12.5px;
451
+ }
452
+ .discovered-row .tool { color: var(--text); }
453
+ .discovered-row .arrow { color: var(--text-faint); }
454
+ .discovered-row .as { color: var(--accent); }
455
+ .discovered-row .disc-desc { font-family: var(--sans); color: var(--text-dim); font-size: 12px; flex: 1 1 160px; min-width: 0; }
456
+ .discovered-row .toggle-group { margin-left: 6px; }
457
+ .discovered-row .toggle-group.lean .toggle-label { color: var(--text-dim); }
458
+ .discovered .none { color: var(--text-faint); font-style: italic; }
459
+
460
+ /* ── Responsive ──────────────────────────────────────────────────────────── */
461
+ @media (max-width: 720px) {
462
+ .field-grid { grid-template-columns: 1fr; }
463
+ .row { grid-template-columns: 1fr; }
464
+ .row-controls { grid-column: 1; grid-row: auto; justify-content: flex-start; margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--border); }
465
+ .toolbar { flex-wrap: wrap; }
466
+ .expose-input-row { grid-template-columns: 1fr 1fr; }
467
+ .discovered-row { gap: 6px 10px; }
468
+ }
469
+
470
+ /* ── Auth (OAuth 2.1) view ─────────────────────────────────────────────────── */
471
+ .auth-intro {
472
+ background: var(--bg-elev);
473
+ border: 1px solid var(--border);
474
+ border-radius: var(--radius);
475
+ padding: 14px 18px;
476
+ margin-bottom: 16px;
477
+ }
478
+ .auth-intro p { margin: 0; color: var(--text-dim); font-size: 14px; }
479
+ .auth-intro code { font-family: var(--mono); color: var(--accent); font-size: 13px; }
480
+ .auth-intro strong { color: var(--text); }
481
+
482
+ .auth-card {
483
+ background: var(--bg-card);
484
+ border: 1px solid var(--border);
485
+ border-radius: var(--radius);
486
+ padding: 16px 18px;
487
+ margin-bottom: 16px;
488
+ }
489
+ .auth-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
490
+ .auth-dep-state { font-size: 14px; font-weight: 500; }
491
+ .auth-dep-state.is-good { color: var(--good); }
492
+ .auth-dep-state.is-off { color: var(--text-dim); }
493
+ .auth-install-log {
494
+ margin: 14px 0 0;
495
+ padding: 12px 14px;
496
+ background: var(--bg-input);
497
+ border: 1px solid var(--border);
498
+ border-radius: var(--radius-sm);
499
+ color: var(--text-dim);
500
+ font-family: var(--mono);
501
+ font-size: 12px;
502
+ white-space: pre-wrap;
503
+ word-break: break-word;
504
+ max-height: 240px;
505
+ overflow: auto;
506
+ }
507
+
508
+ .auth-enable-row { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
509
+ .auth-enable-label { font-weight: 500; }
510
+ .auth-ready-badge { font-size: 13px; padding: 2px 10px; border-radius: 999px; }
511
+ .auth-ready-badge.is-good { color: var(--good); background: rgba(84, 214, 160, 0.12); }
512
+ .auth-ready-badge.is-bad { color: var(--warn); background: rgba(245, 184, 96, 0.12); }
513
+ .auth-config-note { font-size: 13px; }
514
+
515
+ /* ── Activity-log badges for the full set of entry types ───────────────────── */
516
+ .log-badge.config { background: rgba(92, 200, 255, 0.16); color: var(--accent); }
517
+ .log-badge.auth { background: rgba(245, 184, 96, 0.16); color: var(--warn); }
518
+ .log-badge.client { background: rgba(84, 214, 160, 0.14); color: var(--good); }
519
+ .log-badge.mcp { background: rgba(154, 163, 178, 0.16); color: var(--text-dim); }
520
+ .log-badge.event { background: rgba(154, 163, 178, 0.16); color: var(--text-dim); }
521
+
522
+ /* ── Tool detail / edit panel ──────────────────────────────────────────────── */
523
+ .tool-detail {
524
+ margin-top: 12px;
525
+ padding: 14px 16px;
526
+ background: var(--bg-input);
527
+ border: 1px solid var(--border);
528
+ border-radius: var(--radius-sm);
529
+ }
530
+ .tool-detail-id { margin-bottom: 10px; }
531
+ .tool-details-btn.is-open { color: var(--accent); border-color: var(--accent-dim); }
532
+
533
+ /* ── Brand logo image + blue "Tool" wordmark ───────────────────────────────── */
534
+ .logo-img { width: 100%; height: 100%; object-fit: contain; border-radius: var(--radius-sm); display: block; }
535
+ .brand-tool { color: var(--accent); }