@vitronai/alethia 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -41
- package/demo/admin-panel.html +326 -36
- package/demo/agent-oversight.html +354 -49
- package/demo/claude-code-app.html +688 -94
- package/demo/crypto-readiness.html +611 -177
- package/demo/ea1-stress-test.html +262 -34
- package/demo/ecommerce.html +1116 -0
- package/demo/financial-dashboard.html +298 -34
- package/demo/incident-response.html +636 -196
- package/demo/intentional-failures.html +157 -0
- package/demo/nist-compliance.html +458 -102
- package/demo/threat-intel.html +310 -51
- package/demo/wcag-audit.html +495 -84
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +145 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/alethia/SKILL.md +15 -5
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ The cockpit is an **oversight surface**, not an authoring IDE. Humans do not wri
|
|
|
26
26
|
| Who writes the test | a human, in a `.spec` file | an AI agent, in plain English |
|
|
27
27
|
| Per-step policy gate | none | VITRON-EA1 fail-closed, write-high blocked by default |
|
|
28
28
|
| Destructive-action proof | manual review | `alethia_assert_safety` — automated, machine-readable |
|
|
29
|
-
| Speed | ~
|
|
29
|
+
| Speed (per call) | ~200 ms via Playwright MCP, ~2 s via Playwright CLI | ~40 ms — 2-5× faster than Playwright MCP; up to 50× vs Playwright CLI on simple flows — [reproduce the numbers yourself](https://github.com/vitron-ai/alethia-starter#verify-the-faster-than-cdp-based-tools-claim-yourself) |
|
|
30
30
|
| Evidence | screenshots, videos | signed evidence pack with per-step integrity hashes |
|
|
31
31
|
| Network | Telemetry on by default; optional cloud dashboards | **Air-gap deployable** — no cloud product, no telemetry path, bound to 127.0.0.1 |
|
|
32
32
|
|
|
@@ -162,64 +162,80 @@ To pin a specific runtime version (reproducible CI, bisection, deliberate stay-b
|
|
|
162
162
|
|
|
163
163
|
## Your 5-minute demo
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
Five literal prompts. Paste each into Claude / Cursor / Cline in order. The agent runs Alethia for you.
|
|
166
166
|
|
|
167
167
|
### 1. Start the bundled demo server
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
Paste:
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
> *"Use `alethia_serve_demo` to start the demo server and tell me the base URL."*
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
Returns something like `http://127.0.0.1:57307`. Every demo page lives under `<base>/<page>.html` — keep the URL handy for the next steps.
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
### 2. Smoke test the financial dashboard
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
Paste:
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
navigate to
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
```
|
|
179
|
+
> *"Call `alethia_tell` with these instructions as a single block:*
|
|
180
|
+
>
|
|
181
|
+
> *navigate to `<base>/financial-dashboard.html`*
|
|
182
|
+
> *assert Risk Monitor is visible*
|
|
183
|
+
> *assert Open Positions is visible*
|
|
184
|
+
> *assert Compliance Checks is visible"*
|
|
186
185
|
|
|
187
|
-
|
|
186
|
+
Expected: 4 steps pass (1 navigate + 3 asserts). Response carries per-step timings, DOM diffs (what changed after the navigate), a semantic page snapshot (~200 tokens), policy audit records, and a SHA-256 integrity hash.
|
|
188
187
|
|
|
189
|
-
### 3.
|
|
188
|
+
### 3. Prove the EA1 safety gate works
|
|
190
189
|
|
|
191
|
-
|
|
190
|
+
Paste:
|
|
192
191
|
|
|
193
|
-
|
|
192
|
+
> *"Call `alethia_tell` with these instructions as one block:*
|
|
193
|
+
>
|
|
194
|
+
> *navigate to `<base>/financial-dashboard.html`*
|
|
195
|
+
> *expect block: click Liquidate All*
|
|
196
|
+
> *expect block: click Purge Audit Log*
|
|
197
|
+
> *expect block: click Wire Funds"*
|
|
194
198
|
|
|
195
|
-
|
|
199
|
+
**`expect block:` is unique to Alethia.** The step passes only when the **EA1 policy gate** — a framework-level safety layer no other E2E tool ships — refuses the action with reason code `WRITE_HIGH`. Other frameworks can assert *"nothing destructive happened"* by inspecting the app's state after a click; only Alethia's assertion is about the runtime itself refusing to let the click through in the first place. Meaningfully different guarantee, and the thing compliance reviewers actually want in the evidence pack. This run should report all three clicks blocked.
|
|
196
200
|
|
|
197
|
-
|
|
201
|
+
Shortcut if you want Alethia to auto-discover destructive controls instead of naming them:
|
|
198
202
|
|
|
199
|
-
|
|
203
|
+
> *"Use `alethia_assert_safety` against `<base>/financial-dashboard.html`."*
|
|
204
|
+
|
|
205
|
+
Returns a per-action block/allow report with `totalDestructive`, `blocked`, and per-action detail.
|
|
206
|
+
|
|
207
|
+
### 4. Full compliance audit (WCAG + NIST + signed evidence)
|
|
208
|
+
|
|
209
|
+
Paste:
|
|
210
|
+
|
|
211
|
+
> *"Call `alethia_tell` to navigate to `<base>/wcag-audit.html`, then call `alethia_audit_wcag`, then `alethia_audit_nist`, then `alethia_export_session`. Summarize findings by severity and tell me the SHA-256 integrity hash of the evidence."*
|
|
212
|
+
|
|
213
|
+
Expected: a list of WCAG 2.1 AA criteria + NIST SP 800-53 controls with findings, plus a signed evidence pack you can hand to an auditor.
|
|
214
|
+
|
|
215
|
+
### 5. What a "block" is — and why we run one at a time
|
|
216
|
+
|
|
217
|
+
`alethia_propose_tests` returns **named test blocks**, each a cohesive multi-step flow:
|
|
200
218
|
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
"blocked": 3,
|
|
206
|
-
"results": [
|
|
207
|
-
{ "action": "Liquidate All", "blocked": true, "detail": "..." },
|
|
208
|
-
{ "action": "Wire Funds", "blocked": true, "detail": "..." },
|
|
209
|
-
{ "action": "Purge Audit Log", "blocked": true, "detail": "..." }
|
|
210
|
-
]
|
|
211
|
-
}
|
|
219
|
+
```
|
|
220
|
+
Block 1 — Page Structure Verification (4 steps)
|
|
221
|
+
Block 2 — Safe Button Interactions (2 steps)
|
|
222
|
+
Block 3 — EA1 Safety Gate Verification (3 steps, all expect-block)
|
|
212
223
|
```
|
|
213
224
|
|
|
214
|
-
|
|
225
|
+
Calling `alethia_tell` once per block (rather than merging all blocks into one giant NLP string) is deliberate:
|
|
215
226
|
|
|
216
|
-
|
|
227
|
+
- **Each block becomes its own signed `PlanRun`** with its own integrity hash and its own history entry. Merged, you lose the audit boundary.
|
|
228
|
+
- **Named blocks stay named.** "EA1 Safety Gate Verification" shows up labeled in history, logs, and the evidence pack.
|
|
229
|
+
- **One block's failure doesn't sink the others.** Partial success + targeted rerun is the default.
|
|
230
|
+
- **The cockpit UI paints each block as it runs** — partner watching a live demo sees discrete, legible runs rather than one opaque mega-script.
|
|
217
231
|
|
|
218
|
-
|
|
232
|
+
If you don't care about any of those (quick iteration, scratch testing), you can paste multiple blocks' NLP into a single `alethia_tell` — it works, you just give up the boundaries.
|
|
219
233
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
**More paste-ready demos:** see the [agent cookbook](./docs/agent-cookbook.md) — compliance audits, parallel multi-page checks, live partner walkthroughs, and more. Every scenario is a literal prompt you drop into Claude / Cursor / Cline.
|
|
237
|
+
|
|
238
|
+
**Designing a UI to be driven by agents?** See [UI for agents](./docs/ui-for-agents.md) — how Alethia's resolver sees your DOM, when to add `data-alethia` hooks, and patterns that trip the ranker.
|
|
223
239
|
|
|
224
240
|
---
|
|
225
241
|
|
|
@@ -240,6 +256,7 @@ Returns a signed JSON pack with every tool call, input, output, policy decision,
|
|
|
240
256
|
| `alethia_export_session` | Signed evidence pack of the whole session. |
|
|
241
257
|
| `alethia_activate_kill_switch` / `alethia_reset_kill_switch` | Emergency halt and resume. |
|
|
242
258
|
| `alethia_serve_demo` | Start the bundled localhost demo server. |
|
|
259
|
+
| `alethia_show_cockpit` / `alethia_hide_cockpit` | Toggle the live oversight window mid-session. |
|
|
243
260
|
|
|
244
261
|
Destructive actions (delete, purchase, transfer, liquidate, revoke, terminate, ...) are blocked by default under the hardened local-only profile. Sensitive-input fields (passwords, tokens, credit cards) are blocked unless `allowSensitiveInput: true` is passed. Profile overrides from the agent are stripped by the bridge — profile changes require human configuration.
|
|
245
262
|
|
|
@@ -324,9 +341,9 @@ alethia-mcp --debug Run with debug logging on stderr
|
|
|
324
341
|
2. Confirm the runtime process is listening on `127.0.0.1:47432`.
|
|
325
342
|
3. If auto-install failed, check network reachability to the releases host and retry.
|
|
326
343
|
|
|
327
|
-
### "
|
|
344
|
+
### "WRITE_HIGH" / "EA1 POLICY BLOCK" in the audit log
|
|
328
345
|
|
|
329
|
-
A destructive action was blocked by the default `
|
|
346
|
+
A destructive action was blocked by the default `local-only` profile. This is correct behavior. Profile overrides from the agent are stripped by the bridge; human configuration is required to widen the gate.
|
|
330
347
|
|
|
331
348
|
### "SENSITIVE_INPUT_DENIED"
|
|
332
349
|
|
|
@@ -363,6 +380,18 @@ Then fully restart your MCP client (Cmd-Q on macOS, not just close the window).
|
|
|
363
380
|
|
|
364
381
|
Every 0.6.1+ bridge is symlink-spawn-safe — if you're on a current version and still see this, open an issue at https://github.com/vitron-ai/alethia-mcp/issues.
|
|
365
382
|
|
|
383
|
+
### "I see a new release on GitHub but my runtime hasn't upgraded"
|
|
384
|
+
|
|
385
|
+
The bridge caches the "what is the current runtime version?" lookup for 1 hour so we don't hammer the GitHub API on every spawn. If you want a new release to take effect immediately rather than waiting for cache expiry, bust the cache manually:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
rm ~/.alethia/.latest-release ~/.alethia/.bridge-registry-cache 2>/dev/null
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Then fully restart your MCP client (Cmd-Q → reopen). On the next spawn the bridge re-queries GitHub + npm, picks up the new versions, downloads + verifies + installs them.
|
|
392
|
+
|
|
393
|
+
The 1h TTL is a deliberate tradeoff. You can shorten it for CI or dev loops via `ALETHIA_SKIP_AUTO_UPDATE=1` + `ALETHIA_RUNTIME_VERSION=x.y.z` (pins an exact runtime, skips the check entirely).
|
|
394
|
+
|
|
366
395
|
---
|
|
367
396
|
|
|
368
397
|
## Go deeper
|
|
@@ -371,6 +400,7 @@ Every 0.6.1+ bridge is symlink-spawn-safe — if you're on a current version and
|
|
|
371
400
|
- [VITRON-EA1 safety standard](https://vitron.ai/safety)
|
|
372
401
|
- [FAQ](https://vitron.ai/faq)
|
|
373
402
|
- [Releases](https://github.com/vitron-ai/alethia/releases)
|
|
403
|
+
- [Starter + benchmarks](https://github.com/vitron-ai/alethia-starter) — working starter repo with CI, Playwright comparison kit, and reproducible numbers
|
|
374
404
|
|
|
375
405
|
---
|
|
376
406
|
|
|
@@ -378,7 +408,9 @@ Every 0.6.1+ bridge is symlink-spawn-safe — if you're on a current version and
|
|
|
378
408
|
|
|
379
409
|
The Alethia runtime (which this bridge connects to) is local-only **by architecture**, not by default setting. Its signed binary refuses to navigate to any origin outside `file://`, `localhost`, `127.0.0.1`, `.local`, and RFC1918 private ranges. The allowlist is a compile-time constant — **not a CLI flag, env var, MCP argument, profile, or UI toggle**. For partner-specific production-origin access we issue custom-signed builds; we do not ship configurability.
|
|
380
410
|
|
|
381
|
-
|
|
411
|
+
**Full security posture** — threat model, cryptographic chain of custody, supply-chain posture, update cadence, disclosure process — is at [`SECURITY.md`](./SECURITY.md).
|
|
412
|
+
|
|
413
|
+
Abuse reports + vulnerability disclosure: **`gatekeeper@vitron.ai`**.
|
|
382
414
|
|
|
383
415
|
---
|
|
384
416
|
|
package/demo/admin-panel.html
CHANGED
|
@@ -5,42 +5,332 @@
|
|
|
5
5
|
<title>Admin Panel — Classified</title>
|
|
6
6
|
<style>
|
|
7
7
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #08070d;
|
|
10
|
+
--bg-1: #110e1c;
|
|
11
|
+
--bg-2: #1a1428;
|
|
12
|
+
--edge: rgba(255,255,255,.06);
|
|
13
|
+
--edge-strong: rgba(255,255,255,.12);
|
|
14
|
+
--ink: #f5f0ff;
|
|
15
|
+
--ink-2: #d8d3e8;
|
|
16
|
+
--ink-muted: #a8a0c5;
|
|
17
|
+
--ink-faint: #6b6488;
|
|
18
|
+
--purple: #a78bfa;
|
|
19
|
+
--purple-bright: #c4b5fd;
|
|
20
|
+
--purple-deep: #5b21b6;
|
|
21
|
+
--pink: #f0abfc;
|
|
22
|
+
--indigo: #818cf8;
|
|
23
|
+
--emerald: #10b981;
|
|
24
|
+
--emerald-bright: #34d399;
|
|
25
|
+
--red: #ef4444;
|
|
26
|
+
--red-bright: #f87171;
|
|
27
|
+
--amber: #f59e0b;
|
|
28
|
+
--amber-bright: #fbbf24;
|
|
29
|
+
--teal: #14b8a6;
|
|
30
|
+
}
|
|
31
|
+
html { color-scheme: dark; }
|
|
32
|
+
body {
|
|
33
|
+
font-family: "SF Pro Display", -apple-system, system-ui, sans-serif;
|
|
34
|
+
background:
|
|
35
|
+
radial-gradient(ellipse 70% 40% at 10% -10%, rgba(167,139,250,.10), transparent 70%),
|
|
36
|
+
radial-gradient(ellipse 60% 40% at 90% 110%, rgba(240,171,252,.06), transparent 70%),
|
|
37
|
+
var(--bg);
|
|
38
|
+
background-attachment: fixed;
|
|
39
|
+
color: var(--ink-2);
|
|
40
|
+
min-height: 100vh;
|
|
41
|
+
padding: 1.5rem 1.75rem 2rem;
|
|
42
|
+
font-size: 13.5px;
|
|
43
|
+
line-height: 1.5;
|
|
44
|
+
-webkit-font-smoothing: antialiased;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Header */
|
|
48
|
+
.header {
|
|
49
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
50
|
+
padding-bottom: 16px;
|
|
51
|
+
margin-bottom: 22px;
|
|
52
|
+
border-bottom: 1px solid var(--edge);
|
|
53
|
+
}
|
|
54
|
+
.header > div { display: flex; flex-direction: column; gap: 4px; }
|
|
55
|
+
h1 {
|
|
56
|
+
font-size: 19px; font-weight: 700;
|
|
57
|
+
color: var(--ink); letter-spacing: -.015em;
|
|
58
|
+
display: inline-flex; align-items: center; gap: 10px;
|
|
59
|
+
}
|
|
60
|
+
h1::before {
|
|
61
|
+
content: ""; width: 8px; height: 8px; border-radius: 999px;
|
|
62
|
+
background: var(--purple);
|
|
63
|
+
box-shadow: 0 0 12px rgba(167,139,250,.7);
|
|
64
|
+
}
|
|
65
|
+
.user-info {
|
|
66
|
+
color: var(--ink-faint);
|
|
67
|
+
font-size: 12px;
|
|
68
|
+
font-family: ui-monospace, "SF Mono", monospace;
|
|
69
|
+
}
|
|
70
|
+
.classification {
|
|
71
|
+
font-family: ui-monospace, "SF Mono", monospace;
|
|
72
|
+
background: linear-gradient(135deg, rgba(127,29,29,.85), rgba(127,29,29,.55));
|
|
73
|
+
color: #fecaca;
|
|
74
|
+
padding: 5px 10px;
|
|
75
|
+
border-radius: 4px;
|
|
76
|
+
font-size: 10.5px;
|
|
77
|
+
font-weight: 800;
|
|
78
|
+
letter-spacing: .12em;
|
|
79
|
+
text-transform: uppercase;
|
|
80
|
+
border: 1px solid rgba(252,165,165,.3);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* Layout */
|
|
84
|
+
.grid {
|
|
85
|
+
display: grid;
|
|
86
|
+
grid-template-columns: 240px 1fr;
|
|
87
|
+
gap: 18px;
|
|
88
|
+
align-items: start;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Sidebar */
|
|
92
|
+
.sidebar {
|
|
93
|
+
display: flex; flex-direction: column;
|
|
94
|
+
gap: 4px;
|
|
95
|
+
padding: 8px;
|
|
96
|
+
border-radius: 12px;
|
|
97
|
+
background: linear-gradient(180deg, var(--bg-1), rgba(17,14,28,.5));
|
|
98
|
+
border: 1px solid var(--edge);
|
|
99
|
+
box-shadow: inset 0 1px 0 rgba(255,255,255,.03);
|
|
100
|
+
}
|
|
101
|
+
.nav-item {
|
|
102
|
+
position: relative;
|
|
103
|
+
padding: 10px 14px 10px 18px;
|
|
104
|
+
border-radius: 8px;
|
|
105
|
+
background: transparent;
|
|
106
|
+
border: 1px solid transparent;
|
|
107
|
+
color: var(--ink-muted);
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
text-align: left;
|
|
110
|
+
font: inherit;
|
|
111
|
+
font-size: 13px;
|
|
112
|
+
font-weight: 500;
|
|
113
|
+
transition: background .14s, color .14s, border-color .14s;
|
|
114
|
+
}
|
|
115
|
+
.nav-item::before {
|
|
116
|
+
content: "";
|
|
117
|
+
position: absolute;
|
|
118
|
+
left: 6px; top: 14px; bottom: 14px;
|
|
119
|
+
width: 2px;
|
|
120
|
+
border-radius: 2px;
|
|
121
|
+
background: transparent;
|
|
122
|
+
transition: background .14s;
|
|
123
|
+
}
|
|
124
|
+
.nav-item:hover {
|
|
125
|
+
background: rgba(255,255,255,.04);
|
|
126
|
+
color: var(--ink);
|
|
127
|
+
}
|
|
128
|
+
.nav-item.active {
|
|
129
|
+
background: rgba(167,139,250,.10);
|
|
130
|
+
color: var(--purple-bright);
|
|
131
|
+
font-weight: 600;
|
|
132
|
+
}
|
|
133
|
+
.nav-item.active::before {
|
|
134
|
+
background: var(--purple);
|
|
135
|
+
box-shadow: 0 0 8px rgba(167,139,250,.7);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Content panel */
|
|
139
|
+
.content {
|
|
140
|
+
border: 1px solid var(--edge);
|
|
141
|
+
border-radius: 14px;
|
|
142
|
+
background: linear-gradient(180deg, var(--bg-1), rgba(17,14,28,.4));
|
|
143
|
+
padding: 22px 24px;
|
|
144
|
+
box-shadow: inset 0 1px 0 rgba(255,255,255,.035);
|
|
145
|
+
}
|
|
146
|
+
.content h2 {
|
|
147
|
+
font-size: 15px; font-weight: 700;
|
|
148
|
+
color: var(--ink);
|
|
149
|
+
margin-bottom: 16px;
|
|
150
|
+
letter-spacing: -.01em;
|
|
151
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
152
|
+
}
|
|
153
|
+
.content h2::before {
|
|
154
|
+
content: ""; width: 4px; height: 4px; border-radius: 999px;
|
|
155
|
+
background: var(--purple);
|
|
156
|
+
box-shadow: 0 0 6px var(--purple);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Table */
|
|
160
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
161
|
+
th {
|
|
162
|
+
text-align: left;
|
|
163
|
+
padding: 10px 12px;
|
|
164
|
+
color: var(--ink-faint);
|
|
165
|
+
border-bottom: 1px solid var(--edge);
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
font-size: 10.5px;
|
|
168
|
+
text-transform: uppercase;
|
|
169
|
+
letter-spacing: .08em;
|
|
170
|
+
}
|
|
171
|
+
td {
|
|
172
|
+
padding: 11px 12px;
|
|
173
|
+
border-bottom: 1px solid rgba(255,255,255,.03);
|
|
174
|
+
color: var(--ink-2);
|
|
175
|
+
vertical-align: middle;
|
|
176
|
+
}
|
|
177
|
+
tbody tr { transition: background .12s; }
|
|
178
|
+
tbody tr:hover { background: rgba(167,139,250,.04); }
|
|
179
|
+
tbody tr:last-child td { border-bottom: none; }
|
|
180
|
+
|
|
181
|
+
/* Status pills */
|
|
182
|
+
.status-active, .status-suspended, .status-pending {
|
|
183
|
+
display: inline-block;
|
|
184
|
+
padding: 3px 10px;
|
|
185
|
+
border-radius: 999px;
|
|
186
|
+
font-size: 11px; font-weight: 600;
|
|
187
|
+
font-family: ui-monospace, "SF Mono", monospace;
|
|
188
|
+
letter-spacing: .02em;
|
|
189
|
+
}
|
|
190
|
+
.status-active {
|
|
191
|
+
color: var(--emerald-bright);
|
|
192
|
+
background: rgba(16,185,129,.10);
|
|
193
|
+
border: 1px solid rgba(16,185,129,.3);
|
|
194
|
+
}
|
|
195
|
+
.status-suspended {
|
|
196
|
+
color: var(--red-bright);
|
|
197
|
+
background: rgba(239,68,68,.10);
|
|
198
|
+
border: 1px solid rgba(239,68,68,.3);
|
|
199
|
+
}
|
|
200
|
+
.status-pending {
|
|
201
|
+
color: var(--amber-bright);
|
|
202
|
+
background: rgba(245,158,11,.10);
|
|
203
|
+
border: 1px solid rgba(245,158,11,.3);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* Action buttons */
|
|
207
|
+
.actions { display: flex; gap: 6px; }
|
|
208
|
+
.actions button {
|
|
209
|
+
padding: 5px 11px;
|
|
210
|
+
border-radius: 6px;
|
|
211
|
+
border: 1px solid var(--edge);
|
|
212
|
+
background: rgba(255,255,255,.02);
|
|
213
|
+
color: var(--ink-2);
|
|
214
|
+
cursor: pointer;
|
|
215
|
+
font: inherit;
|
|
216
|
+
font-size: 11.5px;
|
|
217
|
+
font-weight: 600;
|
|
218
|
+
transition: background .12s, border-color .12s, color .12s, transform .08s;
|
|
219
|
+
}
|
|
220
|
+
.actions button:hover { background: rgba(255,255,255,.05); border-color: var(--edge-strong); }
|
|
221
|
+
.actions button:active { transform: translateY(1px); }
|
|
222
|
+
.btn-view {
|
|
223
|
+
color: var(--indigo);
|
|
224
|
+
border-color: rgba(129,140,248,.35);
|
|
225
|
+
background: rgba(129,140,248,.06);
|
|
226
|
+
}
|
|
227
|
+
.btn-view:hover { background: rgba(129,140,248,.14); border-color: var(--indigo); }
|
|
228
|
+
.btn-suspend {
|
|
229
|
+
color: var(--amber-bright);
|
|
230
|
+
border-color: rgba(245,158,11,.4);
|
|
231
|
+
background: rgba(245,158,11,.06);
|
|
232
|
+
}
|
|
233
|
+
.btn-suspend:hover { background: rgba(245,158,11,.14); border-color: var(--amber); }
|
|
234
|
+
.btn-delete {
|
|
235
|
+
color: var(--red-bright);
|
|
236
|
+
border-color: rgba(239,68,68,.4);
|
|
237
|
+
background: rgba(239,68,68,.06);
|
|
238
|
+
}
|
|
239
|
+
.btn-delete:hover { background: rgba(239,68,68,.14); border-color: var(--red); }
|
|
240
|
+
.btn-approve {
|
|
241
|
+
color: var(--emerald-bright);
|
|
242
|
+
border-color: rgba(16,185,129,.4);
|
|
243
|
+
background: rgba(16,185,129,.06);
|
|
244
|
+
}
|
|
245
|
+
.btn-approve:hover { background: rgba(16,185,129,.14); border-color: var(--emerald); }
|
|
246
|
+
|
|
247
|
+
/* Audit log */
|
|
248
|
+
.audit-log {
|
|
249
|
+
margin-top: 18px;
|
|
250
|
+
font-family: ui-monospace, "SF Mono", monospace;
|
|
251
|
+
font-size: 11.5px;
|
|
252
|
+
background: rgba(255,255,255,.02);
|
|
253
|
+
border: 1px solid var(--edge);
|
|
254
|
+
border-radius: 10px;
|
|
255
|
+
padding: 12px 14px;
|
|
256
|
+
max-height: 220px;
|
|
257
|
+
overflow-y: auto;
|
|
258
|
+
}
|
|
259
|
+
.audit-entry {
|
|
260
|
+
padding: 4px 0;
|
|
261
|
+
color: var(--ink-muted);
|
|
262
|
+
display: flex; gap: 10px;
|
|
263
|
+
border-bottom: 1px dashed rgba(255,255,255,.03);
|
|
264
|
+
}
|
|
265
|
+
.audit-entry:last-child { border-bottom: none; }
|
|
266
|
+
.audit-entry .timestamp { color: var(--ink-faint); white-space: nowrap; }
|
|
267
|
+
.audit-entry .action {
|
|
268
|
+
color: var(--purple-bright);
|
|
269
|
+
font-weight: 700;
|
|
270
|
+
letter-spacing: .04em;
|
|
271
|
+
min-width: 96px;
|
|
272
|
+
flex-shrink: 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Modal */
|
|
276
|
+
.modal {
|
|
277
|
+
display: none;
|
|
278
|
+
position: fixed; inset: 0;
|
|
279
|
+
background: rgba(8,7,13,.85);
|
|
280
|
+
backdrop-filter: blur(8px);
|
|
281
|
+
z-index: 100;
|
|
282
|
+
align-items: center; justify-content: center;
|
|
283
|
+
}
|
|
284
|
+
.modal.visible { display: flex; animation: modalFadeIn .2s ease-out; }
|
|
285
|
+
@keyframes modalFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
286
|
+
.modal-content {
|
|
287
|
+
background: linear-gradient(180deg, var(--bg-2), var(--bg-1));
|
|
288
|
+
border: 1px solid rgba(239,68,68,.45);
|
|
289
|
+
border-radius: 14px;
|
|
290
|
+
padding: 24px 26px;
|
|
291
|
+
max-width: 440px;
|
|
292
|
+
text-align: center;
|
|
293
|
+
box-shadow: 0 20px 60px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.04);
|
|
294
|
+
}
|
|
295
|
+
.modal-content h3 {
|
|
296
|
+
color: var(--red-bright);
|
|
297
|
+
margin-bottom: 10px;
|
|
298
|
+
font-size: 16px;
|
|
299
|
+
font-weight: 700;
|
|
300
|
+
}
|
|
301
|
+
.modal-content p {
|
|
302
|
+
color: var(--ink-2);
|
|
303
|
+
margin-bottom: 18px;
|
|
304
|
+
font-size: 13px;
|
|
305
|
+
line-height: 1.55;
|
|
306
|
+
}
|
|
307
|
+
.modal-actions { display: flex; gap: 10px; justify-content: center; }
|
|
308
|
+
.modal-actions button {
|
|
309
|
+
padding: 9px 18px;
|
|
310
|
+
border-radius: 8px;
|
|
311
|
+
border: 1px solid var(--edge);
|
|
312
|
+
cursor: pointer;
|
|
313
|
+
font: inherit;
|
|
314
|
+
font-size: 12.5px;
|
|
315
|
+
font-weight: 600;
|
|
316
|
+
transition: background .12s, border-color .12s, color .12s, transform .08s;
|
|
317
|
+
}
|
|
318
|
+
.modal-actions button:active { transform: translateY(1px); }
|
|
319
|
+
.btn-cancel {
|
|
320
|
+
background: rgba(255,255,255,.04);
|
|
321
|
+
color: var(--ink-2);
|
|
322
|
+
border-color: var(--edge-strong);
|
|
323
|
+
}
|
|
324
|
+
.btn-cancel:hover { background: rgba(255,255,255,.08); color: var(--ink); }
|
|
325
|
+
.btn-confirm-delete {
|
|
326
|
+
background: linear-gradient(180deg, var(--red-bright), var(--red));
|
|
327
|
+
color: #fff;
|
|
328
|
+
border-color: var(--red);
|
|
329
|
+
font-weight: 700;
|
|
330
|
+
}
|
|
331
|
+
.btn-confirm-delete:hover { filter: brightness(1.08); }
|
|
332
|
+
|
|
333
|
+
::selection { background: rgba(167,139,250,.3); }
|
|
44
334
|
</style>
|
|
45
335
|
</head>
|
|
46
336
|
<body>
|