exempclaw 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +306 -0
- package/dist/agent/agent.d.ts +91 -0
- package/dist/agent/agent.js +258 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/config.d.ts +49 -0
- package/dist/agent/config.js +58 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/persona.d.ts +39 -0
- package/dist/agent/persona.js +81 -0
- package/dist/agent/persona.js.map +1 -0
- package/dist/agents/registry.d.ts +21 -0
- package/dist/agents/registry.js +51 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/cli/approve.d.ts +17 -0
- package/dist/cli/approve.js +50 -0
- package/dist/cli/approve.js.map +1 -0
- package/dist/cli/chat.d.ts +16 -0
- package/dist/cli/chat.js +148 -0
- package/dist/cli/chat.js.map +1 -0
- package/dist/cli/demo.d.ts +7 -0
- package/dist/cli/demo.js +82 -0
- package/dist/cli/demo.js.map +1 -0
- package/dist/cli/init.d.ts +17 -0
- package/dist/cli/init.js +89 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/live.d.ts +10 -0
- package/dist/cli/live.js +109 -0
- package/dist/cli/live.js.map +1 -0
- package/dist/cli/offline.d.ts +23 -0
- package/dist/cli/offline.js +236 -0
- package/dist/cli/offline.js.map +1 -0
- package/dist/cli/probe.d.ts +23 -0
- package/dist/cli/probe.js +140 -0
- package/dist/cli/probe.js.map +1 -0
- package/dist/cli/render.d.ts +15 -0
- package/dist/cli/render.js +50 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/tui.d.ts +101 -0
- package/dist/cli/tui.js +334 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/config/index.d.ts +33 -0
- package/dist/config/index.js +48 -0
- package/dist/config/index.js.map +1 -0
- package/dist/connectors/connector.d.ts +58 -0
- package/dist/connectors/connector.js +30 -0
- package/dist/connectors/connector.js.map +1 -0
- package/dist/connectors/email/email-connector.d.ts +43 -0
- package/dist/connectors/email/email-connector.js +364 -0
- package/dist/connectors/email/email-connector.js.map +1 -0
- package/dist/connectors/github/github-connector.d.ts +52 -0
- package/dist/connectors/github/github-connector.js +271 -0
- package/dist/connectors/github/github-connector.js.map +1 -0
- package/dist/connectors/http.d.ts +34 -0
- package/dist/connectors/http.js +78 -0
- package/dist/connectors/http.js.map +1 -0
- package/dist/connectors/index.d.ts +34 -0
- package/dist/connectors/index.js +86 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/notion/notion-connector.d.ts +45 -0
- package/dist/connectors/notion/notion-connector.js +222 -0
- package/dist/connectors/notion/notion-connector.js.map +1 -0
- package/dist/connectors/slack/slack-connector.d.ts +43 -0
- package/dist/connectors/slack/slack-connector.js +291 -0
- package/dist/connectors/slack/slack-connector.js.map +1 -0
- package/dist/core/errors.d.ts +36 -0
- package/dist/core/errors.js +40 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/logger.d.ts +14 -0
- package/dist/core/logger.js +44 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/run-log.d.ts +37 -0
- package/dist/core/run-log.js +37 -0
- package/dist/core/run-log.js.map +1 -0
- package/dist/core/usage.d.ts +22 -0
- package/dist/core/usage.js +58 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/dashboard/data.d.ts +62 -0
- package/dist/dashboard/data.js +84 -0
- package/dist/dashboard/data.js.map +1 -0
- package/dist/dashboard/page.d.ts +9 -0
- package/dist/dashboard/page.js +421 -0
- package/dist/dashboard/page.js.map +1 -0
- package/dist/dashboard/server.d.ts +19 -0
- package/dist/dashboard/server.js +44 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/demo/bootstrap.d.ts +25 -0
- package/dist/demo/bootstrap.js +60 -0
- package/dist/demo/bootstrap.js.map +1 -0
- package/dist/demo/claude.d.ts +31 -0
- package/dist/demo/claude.js +230 -0
- package/dist/demo/claude.js.map +1 -0
- package/dist/demo/demo-connector.d.ts +19 -0
- package/dist/demo/demo-connector.js +168 -0
- package/dist/demo/demo-connector.js.map +1 -0
- package/dist/demo/world.d.ts +60 -0
- package/dist/demo/world.js +117 -0
- package/dist/demo/world.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +396 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/ingest.d.ts +63 -0
- package/dist/ingest/ingest.js +258 -0
- package/dist/ingest/ingest.js.map +1 -0
- package/dist/llm/claude.d.ts +97 -0
- package/dist/llm/claude.js +163 -0
- package/dist/llm/claude.js.map +1 -0
- package/dist/memory/compaction.d.ts +22 -0
- package/dist/memory/compaction.js +79 -0
- package/dist/memory/compaction.js.map +1 -0
- package/dist/memory/file-store.d.ts +28 -0
- package/dist/memory/file-store.js +110 -0
- package/dist/memory/file-store.js.map +1 -0
- package/dist/memory/store.d.ts +32 -0
- package/dist/memory/store.js +2 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +63 -0
- package/dist/orchestrator/orchestrator.js +181 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/scheduler.d.ts +33 -0
- package/dist/orchestrator/scheduler.js +67 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/orchestrator/seen-events.d.ts +21 -0
- package/dist/orchestrator/seen-events.js +71 -0
- package/dist/orchestrator/seen-events.js.map +1 -0
- package/dist/plugins/apply.d.ts +9 -0
- package/dist/plugins/apply.js +17 -0
- package/dist/plugins/apply.js.map +1 -0
- package/dist/plugins/define.d.ts +29 -0
- package/dist/plugins/define.js +30 -0
- package/dist/plugins/define.js.map +1 -0
- package/dist/plugins/loader.d.ts +31 -0
- package/dist/plugins/loader.js +61 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/scaffold.d.ts +5 -0
- package/dist/plugins/scaffold.js +72 -0
- package/dist/plugins/scaffold.js.map +1 -0
- package/dist/tools/builtin.d.ts +8 -0
- package/dist/tools/builtin.js +63 -0
- package/dist/tools/builtin.js.map +1 -0
- package/dist/tools/tool.d.ts +84 -0
- package/dist/tools/tool.js +70 -0
- package/dist/tools/tool.js.map +1 -0
- package/dist/ui/agent-view.test.d.ts +1 -0
- package/dist/ui/agent-view.test.js +54 -0
- package/dist/ui/agent-view.test.js.map +1 -0
- package/dist/ui/agents-data.d.ts +7 -0
- package/dist/ui/agents-data.js +25 -0
- package/dist/ui/agents-data.js.map +1 -0
- package/dist/ui/app.d.ts +24 -0
- package/dist/ui/app.js +59 -0
- package/dist/ui/app.js.map +1 -0
- package/dist/ui/app.test.d.ts +1 -0
- package/dist/ui/app.test.js +47 -0
- package/dist/ui/app.test.js.map +1 -0
- package/dist/ui/components/key-hints.d.ts +4 -0
- package/dist/ui/components/key-hints.js +6 -0
- package/dist/ui/components/key-hints.js.map +1 -0
- package/dist/ui/components/menu.d.ts +11 -0
- package/dist/ui/components/menu.js +20 -0
- package/dist/ui/components/menu.js.map +1 -0
- package/dist/ui/create-wizard.test.d.ts +1 -0
- package/dist/ui/create-wizard.test.js +58 -0
- package/dist/ui/create-wizard.test.js.map +1 -0
- package/dist/ui/doctor-data.d.ts +6 -0
- package/dist/ui/doctor-data.js +29 -0
- package/dist/ui/doctor-data.js.map +1 -0
- package/dist/ui/history-data.d.ts +2 -0
- package/dist/ui/history-data.js +18 -0
- package/dist/ui/history-data.js.map +1 -0
- package/dist/ui/screens/agent.d.ts +8 -0
- package/dist/ui/screens/agent.js +95 -0
- package/dist/ui/screens/agent.js.map +1 -0
- package/dist/ui/screens/agents.d.ts +7 -0
- package/dist/ui/screens/agents.js +47 -0
- package/dist/ui/screens/agents.js.map +1 -0
- package/dist/ui/screens/create.d.ts +7 -0
- package/dist/ui/screens/create.js +141 -0
- package/dist/ui/screens/create.js.map +1 -0
- package/dist/ui/screens/doctor.d.ts +5 -0
- package/dist/ui/screens/doctor.js +13 -0
- package/dist/ui/screens/doctor.js.map +1 -0
- package/dist/ui/screens/history.d.ts +7 -0
- package/dist/ui/screens/history.js +50 -0
- package/dist/ui/screens/history.js.map +1 -0
- package/dist/ui/screens/home.d.ts +8 -0
- package/dist/ui/screens/home.js +35 -0
- package/dist/ui/screens/home.js.map +1 -0
- package/dist/ui/screens/plugins.d.ts +7 -0
- package/dist/ui/screens/plugins.js +40 -0
- package/dist/ui/screens/plugins.js.map +1 -0
- package/dist/ui/services.d.ts +33 -0
- package/dist/ui/services.js +67 -0
- package/dist/ui/services.js.map +1 -0
- package/dist/ui/start.d.ts +1 -0
- package/dist/ui/start.js +16 -0
- package/dist/ui/start.js.map +1 -0
- package/dist/ui/theme.d.ts +6 -0
- package/dist/ui/theme.js +26 -0
- package/dist/ui/theme.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dashboard UI: a single self-contained HTML document, no build step.
|
|
3
|
+
*
|
|
4
|
+
* Design: "the succession ledger" — a personnel-dossier / ledger aesthetic
|
|
5
|
+
* for software whose whole job is stepping into someone's vacated role.
|
|
6
|
+
* Ink-black paper, amber phosphor, ruled lines, stamped disclosure modes,
|
|
7
|
+
* tabular monospace figures. Fraunces for display, IBM Plex Mono for data.
|
|
8
|
+
*/
|
|
9
|
+
export function dashboardPage() {
|
|
10
|
+
return `<!doctype html>
|
|
11
|
+
<html lang="en">
|
|
12
|
+
<head>
|
|
13
|
+
<meta charset="utf-8">
|
|
14
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
15
|
+
<title>Exempclaw — Succession Ledger</title>
|
|
16
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
17
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
18
|
+
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet">
|
|
19
|
+
<style>
|
|
20
|
+
:root {
|
|
21
|
+
--paper: #14120f;
|
|
22
|
+
--paper-raise: #1b1814;
|
|
23
|
+
--paper-card: #181511;
|
|
24
|
+
--ink: #eae3d2;
|
|
25
|
+
--ink-dim: #9a917d;
|
|
26
|
+
--ink-faint: #6b6353;
|
|
27
|
+
--rule: #2e2a22;
|
|
28
|
+
--rule-soft: #242019;
|
|
29
|
+
--amber: #f0a72e;
|
|
30
|
+
--amber-soft: rgba(240, 167, 46, .14);
|
|
31
|
+
--red: #d64a3a;
|
|
32
|
+
--red-soft: rgba(214, 74, 58, .12);
|
|
33
|
+
--green: #8aab6a;
|
|
34
|
+
--serif: "Fraunces", Georgia, "Times New Roman", serif;
|
|
35
|
+
--mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
36
|
+
}
|
|
37
|
+
* { box-sizing: border-box; margin: 0; }
|
|
38
|
+
html { color-scheme: dark; }
|
|
39
|
+
body {
|
|
40
|
+
background: var(--paper);
|
|
41
|
+
color: var(--ink);
|
|
42
|
+
font-family: var(--mono);
|
|
43
|
+
font-size: 13px;
|
|
44
|
+
line-height: 1.5;
|
|
45
|
+
min-height: 100vh;
|
|
46
|
+
}
|
|
47
|
+
/* paper grain + vignette */
|
|
48
|
+
body::before {
|
|
49
|
+
content: "";
|
|
50
|
+
position: fixed; inset: 0;
|
|
51
|
+
background:
|
|
52
|
+
radial-gradient(120% 90% at 50% 0%, transparent 55%, rgba(0,0,0,.5) 100%),
|
|
53
|
+
repeating-linear-gradient(0deg, rgba(255,255,255,.014) 0 1px, transparent 1px 3px);
|
|
54
|
+
pointer-events: none;
|
|
55
|
+
z-index: 2;
|
|
56
|
+
}
|
|
57
|
+
::selection { background: var(--amber); color: #181203; }
|
|
58
|
+
|
|
59
|
+
header {
|
|
60
|
+
display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
|
|
61
|
+
padding: 28px 32px 18px;
|
|
62
|
+
border-bottom: 1px solid var(--rule);
|
|
63
|
+
position: relative;
|
|
64
|
+
}
|
|
65
|
+
header::after {
|
|
66
|
+
content: "";
|
|
67
|
+
position: absolute; left: 32px; right: 32px; bottom: -4px;
|
|
68
|
+
border-bottom: 1px solid var(--rule-soft);
|
|
69
|
+
}
|
|
70
|
+
.masthead .kicker {
|
|
71
|
+
font-size: 10px; letter-spacing: .42em; text-transform: uppercase; color: var(--amber);
|
|
72
|
+
}
|
|
73
|
+
.masthead h1 {
|
|
74
|
+
font-family: var(--serif);
|
|
75
|
+
font-weight: 650;
|
|
76
|
+
font-size: 42px;
|
|
77
|
+
letter-spacing: .01em;
|
|
78
|
+
line-height: 1.04;
|
|
79
|
+
margin-top: 2px;
|
|
80
|
+
}
|
|
81
|
+
.masthead h1 em { font-style: italic; font-weight: 420; color: var(--ink-dim); }
|
|
82
|
+
.meta {
|
|
83
|
+
text-align: right; color: var(--ink-dim); font-size: 11px; line-height: 1.7;
|
|
84
|
+
}
|
|
85
|
+
.meta .live { color: var(--ink); }
|
|
86
|
+
.pulse {
|
|
87
|
+
display: inline-block; width: 7px; height: 7px; border-radius: 50%;
|
|
88
|
+
background: var(--amber); margin-right: 6px; vertical-align: 1px;
|
|
89
|
+
box-shadow: 0 0 0 0 rgba(240,167,46,.6);
|
|
90
|
+
}
|
|
91
|
+
.pulse.tick { animation: pulse 1s ease-out; }
|
|
92
|
+
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(240,167,46,.65); } 100% { box-shadow: 0 0 0 12px rgba(240,167,46,0); } }
|
|
93
|
+
|
|
94
|
+
main {
|
|
95
|
+
display: grid; grid-template-columns: 330px 1fr; gap: 0;
|
|
96
|
+
min-height: calc(100vh - 110px);
|
|
97
|
+
}
|
|
98
|
+
@media (max-width: 900px) { main { grid-template-columns: 1fr; } }
|
|
99
|
+
|
|
100
|
+
/* ── roster ─────────────────────────────── */
|
|
101
|
+
#roster {
|
|
102
|
+
border-right: 1px solid var(--rule);
|
|
103
|
+
padding: 22px 18px 40px 32px;
|
|
104
|
+
}
|
|
105
|
+
.roster-label, .section-label {
|
|
106
|
+
font-size: 10px; letter-spacing: .34em; text-transform: uppercase; color: var(--ink-faint);
|
|
107
|
+
margin-bottom: 14px;
|
|
108
|
+
}
|
|
109
|
+
.dossier {
|
|
110
|
+
position: relative;
|
|
111
|
+
background: var(--paper-card);
|
|
112
|
+
border: 1px solid var(--rule);
|
|
113
|
+
border-left: 3px solid var(--rule);
|
|
114
|
+
padding: 14px 14px 12px;
|
|
115
|
+
margin-bottom: 12px;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
opacity: 0;
|
|
118
|
+
transform: translateY(14px) rotate(-.4deg);
|
|
119
|
+
animation: rise .5s cubic-bezier(.2,.7,.25,1) forwards;
|
|
120
|
+
transition: border-color .2s, background .2s, transform .2s;
|
|
121
|
+
}
|
|
122
|
+
.dossier:hover { transform: translateY(-2px); border-left-color: var(--ink-dim); }
|
|
123
|
+
.dossier.selected { border-left-color: var(--amber); background: var(--paper-raise); }
|
|
124
|
+
@keyframes rise { to { opacity: 1; transform: translateY(0) rotate(0); } }
|
|
125
|
+
.dossier .file-no {
|
|
126
|
+
font-size: 10px; color: var(--ink-faint); letter-spacing: .18em; text-transform: uppercase;
|
|
127
|
+
display: flex; justify-content: space-between;
|
|
128
|
+
}
|
|
129
|
+
.dossier .name {
|
|
130
|
+
font-family: var(--serif); font-size: 22px; font-weight: 560; margin-top: 4px; line-height: 1.15;
|
|
131
|
+
}
|
|
132
|
+
.dossier .role { color: var(--ink-dim); font-size: 12px; }
|
|
133
|
+
.dossier .succession {
|
|
134
|
+
margin-top: 8px; font-size: 11px; color: var(--ink-dim);
|
|
135
|
+
display: flex; align-items: baseline; gap: 6px;
|
|
136
|
+
}
|
|
137
|
+
.dossier .succession .leader { flex: 1; border-bottom: 1px dotted var(--ink-faint); transform: translateY(-3px); }
|
|
138
|
+
.dossier .statline {
|
|
139
|
+
display: flex; gap: 14px; margin-top: 10px; padding-top: 9px;
|
|
140
|
+
border-top: 1px solid var(--rule-soft);
|
|
141
|
+
font-size: 11px; color: var(--ink-dim);
|
|
142
|
+
font-variant-numeric: tabular-nums;
|
|
143
|
+
}
|
|
144
|
+
.dossier .statline b { color: var(--ink); font-weight: 500; }
|
|
145
|
+
|
|
146
|
+
.stamp {
|
|
147
|
+
position: absolute; top: 12px; right: 12px;
|
|
148
|
+
font-size: 9px; letter-spacing: .22em; text-transform: uppercase;
|
|
149
|
+
border: 1.5px solid var(--amber); color: var(--amber);
|
|
150
|
+
border-radius: 2px;
|
|
151
|
+
padding: 2px 7px 1px;
|
|
152
|
+
transform: rotate(3.2deg);
|
|
153
|
+
opacity: .85;
|
|
154
|
+
}
|
|
155
|
+
.stamp.opaque { border-color: var(--red); color: var(--red); }
|
|
156
|
+
|
|
157
|
+
/* ── detail ─────────────────────────────── */
|
|
158
|
+
#detail { padding: 22px 32px 60px 28px; min-width: 0; }
|
|
159
|
+
.detail-head { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
|
|
160
|
+
.detail-head h2 { font-family: var(--serif); font-size: 30px; font-weight: 600; }
|
|
161
|
+
.detail-head h2 span { color: var(--ink-dim); font-weight: 400; font-style: italic; }
|
|
162
|
+
.detail-head .model { color: var(--ink-faint); font-size: 11px; letter-spacing: .08em; }
|
|
163
|
+
|
|
164
|
+
.stats {
|
|
165
|
+
display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
166
|
+
border: 1px solid var(--rule); margin: 18px 0 26px;
|
|
167
|
+
background: var(--paper-card);
|
|
168
|
+
}
|
|
169
|
+
.stat { padding: 12px 14px; border-right: 1px solid var(--rule-soft); }
|
|
170
|
+
.stat:last-child { border-right: 0; }
|
|
171
|
+
.stat .v {
|
|
172
|
+
font-size: 21px; font-weight: 600; font-variant-numeric: tabular-nums;
|
|
173
|
+
color: var(--ink); letter-spacing: -.01em;
|
|
174
|
+
}
|
|
175
|
+
.stat .v.amber { color: var(--amber); }
|
|
176
|
+
.stat .v.red { color: var(--red); }
|
|
177
|
+
.stat .k { font-size: 10px; letter-spacing: .22em; text-transform: uppercase; color: var(--ink-faint); margin-top: 2px; }
|
|
178
|
+
|
|
179
|
+
table.ledger { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
180
|
+
table.ledger th {
|
|
181
|
+
text-align: left; font-weight: 500; font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
|
|
182
|
+
color: var(--ink-faint); padding: 6px 10px 6px 0; border-bottom: 1px solid var(--rule);
|
|
183
|
+
}
|
|
184
|
+
table.ledger td {
|
|
185
|
+
padding: 8px 10px 8px 0; border-bottom: 1px solid var(--rule-soft);
|
|
186
|
+
vertical-align: top; font-variant-numeric: tabular-nums; color: var(--ink-dim);
|
|
187
|
+
}
|
|
188
|
+
table.ledger td.t { color: var(--ink); white-space: nowrap; }
|
|
189
|
+
table.ledger tr:hover td { background: rgba(240,167,46,.03); }
|
|
190
|
+
.chip {
|
|
191
|
+
display: inline-block; font-size: 10px; letter-spacing: .06em;
|
|
192
|
+
border: 1px solid var(--rule); border-radius: 2px; padding: 1px 6px; margin: 1px 4px 1px 0;
|
|
193
|
+
color: var(--ink-dim);
|
|
194
|
+
}
|
|
195
|
+
.chip.ok { border-color: rgba(138,171,106,.5); color: var(--green); }
|
|
196
|
+
.chip.deny { border-color: rgba(214,74,58,.55); color: var(--red); }
|
|
197
|
+
.err { color: var(--red); font-size: 11px; }
|
|
198
|
+
|
|
199
|
+
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; margin-top: 30px; }
|
|
200
|
+
@media (max-width: 1200px) { .columns { grid-template-columns: 1fr; } }
|
|
201
|
+
|
|
202
|
+
.memo {
|
|
203
|
+
border: 1px solid var(--rule); background: var(--paper-card);
|
|
204
|
+
padding: 10px 12px; margin-bottom: 10px; font-size: 12px;
|
|
205
|
+
}
|
|
206
|
+
.memo .src {
|
|
207
|
+
font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--amber);
|
|
208
|
+
display: flex; gap: 8px; align-items: baseline;
|
|
209
|
+
}
|
|
210
|
+
.memo .src .leader { flex: 1; border-bottom: 1px dotted var(--rule); transform: translateY(-3px); }
|
|
211
|
+
.memo .src time { color: var(--ink-faint); letter-spacing: 0; text-transform: none; }
|
|
212
|
+
.memo p { margin-top: 5px; color: var(--ink); }
|
|
213
|
+
.memo .tags { margin-top: 5px; }
|
|
214
|
+
|
|
215
|
+
.empty {
|
|
216
|
+
margin: 12vh auto 0; max-width: 460px; text-align: center;
|
|
217
|
+
border: 1.5px solid var(--rule); padding: 38px 30px;
|
|
218
|
+
transform: rotate(-.6deg);
|
|
219
|
+
}
|
|
220
|
+
.empty .stamp-big {
|
|
221
|
+
display: inline-block; border: 2px solid var(--amber); color: var(--amber);
|
|
222
|
+
letter-spacing: .3em; text-transform: uppercase; font-size: 11px;
|
|
223
|
+
padding: 5px 14px 4px; transform: rotate(-3deg); margin-bottom: 18px;
|
|
224
|
+
}
|
|
225
|
+
.empty p { color: var(--ink-dim); }
|
|
226
|
+
.empty code { color: var(--amber); }
|
|
227
|
+
|
|
228
|
+
a { color: var(--amber); }
|
|
229
|
+
.footer-note { margin-top: 40px; color: var(--ink-faint); font-size: 10px; letter-spacing: .12em; }
|
|
230
|
+
</style>
|
|
231
|
+
</head>
|
|
232
|
+
<body>
|
|
233
|
+
<header>
|
|
234
|
+
<div class="masthead">
|
|
235
|
+
<div class="kicker">Exempclaw · Fleet Console</div>
|
|
236
|
+
<h1>Succession <em>Ledger</em></h1>
|
|
237
|
+
</div>
|
|
238
|
+
<div class="meta">
|
|
239
|
+
<div class="live"><span class="pulse" id="pulse"></span><span id="updated">connecting…</span></div>
|
|
240
|
+
<div id="datadir"></div>
|
|
241
|
+
<div>read-only · 127.0.0.1</div>
|
|
242
|
+
</div>
|
|
243
|
+
</header>
|
|
244
|
+
<main>
|
|
245
|
+
<nav id="roster"></nav>
|
|
246
|
+
<section id="detail"></section>
|
|
247
|
+
</main>
|
|
248
|
+
<script>
|
|
249
|
+
(function () {
|
|
250
|
+
'use strict';
|
|
251
|
+
var state = { fleet: null, selected: null };
|
|
252
|
+
|
|
253
|
+
function esc(value) {
|
|
254
|
+
return String(value == null ? '' : value)
|
|
255
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
256
|
+
}
|
|
257
|
+
function money(value) {
|
|
258
|
+
if (value == null) return 'n/a';
|
|
259
|
+
return value > 0 && value < 0.01 ? '$' + value.toFixed(4) : '$' + value.toFixed(2);
|
|
260
|
+
}
|
|
261
|
+
function tokens(n) {
|
|
262
|
+
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
263
|
+
if (n >= 10000) return Math.round(n / 1000) + 'k';
|
|
264
|
+
return String(n);
|
|
265
|
+
}
|
|
266
|
+
function when(iso) {
|
|
267
|
+
if (!iso) return '—';
|
|
268
|
+
var d = new Date(iso);
|
|
269
|
+
var pad = function (x) { return String(x).padStart(2, '0'); };
|
|
270
|
+
return pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function rosterCard(agent, index) {
|
|
274
|
+
var persona = agent.persona || {};
|
|
275
|
+
var name = persona.name || agent.id;
|
|
276
|
+
var stampClass = persona.disclosure === 'opaque' ? 'stamp opaque' : 'stamp';
|
|
277
|
+
var stamp = persona.disclosure ? '<span class="' + stampClass + '">' + esc(persona.disclosure.replace('_', ' ')) + '</span>' : '';
|
|
278
|
+
var succession = persona.succeeds
|
|
279
|
+
? '<div class="succession"><span>succeeding</span><span class="leader"></span><span>' + esc(persona.succeeds) + '</span></div>'
|
|
280
|
+
: '';
|
|
281
|
+
return '<article class="dossier' + (state.selected === agent.id ? ' selected' : '') + '"' +
|
|
282
|
+
' style="animation-delay:' + (index * 70) + 'ms" data-id="' + esc(agent.id) + '">' +
|
|
283
|
+
stamp +
|
|
284
|
+
'<div class="file-no"><span>File № ' + esc(agent.id) + '</span></div>' +
|
|
285
|
+
'<div class="name">' + esc(name) + '</div>' +
|
|
286
|
+
'<div class="role">' + esc(persona.role || 'unregistered role') + '</div>' +
|
|
287
|
+
succession +
|
|
288
|
+
'<div class="statline">' +
|
|
289
|
+
'<span><b>' + agent.runs.total + '</b> runs</span>' +
|
|
290
|
+
'<span><b>' + agent.memoryCount + '</b> memories</span>' +
|
|
291
|
+
'<span><b>' + money(agent.costUsd) + '</b></span>' +
|
|
292
|
+
'</div>' +
|
|
293
|
+
'</article>';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function outwardChips(actions) {
|
|
297
|
+
if (!actions.length) return '';
|
|
298
|
+
return actions.map(function (a) {
|
|
299
|
+
var cls = a.approved ? 'chip ok' : 'chip deny';
|
|
300
|
+
var mark = a.approved ? '✓ ' : '✗ ';
|
|
301
|
+
return '<span class="' + cls + '" title="' + esc(a.summary) + '">' + mark + esc(a.tool) + '</span>';
|
|
302
|
+
}).join('');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function detailView(agent) {
|
|
306
|
+
var persona = agent.persona || {};
|
|
307
|
+
var name = persona.name || agent.id;
|
|
308
|
+
var roleBits = [persona.role, persona.succeeds ? 'succeeding ' + persona.succeeds : null]
|
|
309
|
+
.filter(Boolean).map(esc).join(' · ');
|
|
310
|
+
|
|
311
|
+
var rows = agent.runs.recent.map(function (r) {
|
|
312
|
+
return '<tr>' +
|
|
313
|
+
'<td class="t">' + when(r.startedAt) + '</td>' +
|
|
314
|
+
'<td>' + esc(r.trigger) + '</td>' +
|
|
315
|
+
'<td>' + r.iterations + '</td>' +
|
|
316
|
+
'<td>' + tokens(r.tokens) + '</td>' +
|
|
317
|
+
'<td>' + money(r.costUsd) + '</td>' +
|
|
318
|
+
'<td>' + (r.error ? '<span class="err">' + esc(r.error) + '</span>' : esc(r.stopReason || '—')) +
|
|
319
|
+
(r.outward.length ? '<br>' + outwardChips(r.outward) : '') + '</td>' +
|
|
320
|
+
'</tr>';
|
|
321
|
+
}).join('');
|
|
322
|
+
|
|
323
|
+
var memories = agent.recentMemories.map(function (m) {
|
|
324
|
+
var tags = m.tags.map(function (t) { return '<span class="chip">#' + esc(t) + '</span>'; }).join('');
|
|
325
|
+
return '<div class="memo">' +
|
|
326
|
+
'<div class="src"><span>' + esc(m.source) + '</span><span class="leader"></span><time>' + when(m.createdAt) + '</time></div>' +
|
|
327
|
+
'<p>' + esc(m.text) + '</p>' +
|
|
328
|
+
(tags ? '<div class="tags">' + tags + '</div>' : '') +
|
|
329
|
+
'</div>';
|
|
330
|
+
}).join('');
|
|
331
|
+
|
|
332
|
+
var deniedClass = agent.outward.denied > 0 ? 'v red' : 'v';
|
|
333
|
+
return '' +
|
|
334
|
+
'<div class="detail-head">' +
|
|
335
|
+
'<h2>' + esc(name) + (roleBits ? ' <span>— ' + roleBits + '</span>' : '') + '</h2>' +
|
|
336
|
+
'<div class="model">' + esc(agent.model || '') + '</div>' +
|
|
337
|
+
'</div>' +
|
|
338
|
+
'<div class="stats">' +
|
|
339
|
+
'<div class="stat"><div class="v">' + agent.runs.total + '</div><div class="k">runs</div></div>' +
|
|
340
|
+
'<div class="stat"><div class="v">' + tokens(agent.usage.inputTokens + agent.usage.cacheReadTokens + agent.usage.cacheWriteTokens) + '</div><div class="k">tokens in</div></div>' +
|
|
341
|
+
'<div class="stat"><div class="v">' + tokens(agent.usage.outputTokens) + '</div><div class="k">tokens out</div></div>' +
|
|
342
|
+
'<div class="stat"><div class="v">' + tokens(agent.usage.cacheReadTokens) + '</div><div class="k">cache reads</div></div>' +
|
|
343
|
+
'<div class="stat"><div class="v">' + agent.outward.total + '</div><div class="k">outward acts</div></div>' +
|
|
344
|
+
'<div class="stat"><div class="' + deniedClass + '">' + agent.outward.denied + '</div><div class="k">denied</div></div>' +
|
|
345
|
+
'<div class="stat"><div class="v amber">' + money(agent.costUsd) + '</div><div class="k">est. spend</div></div>' +
|
|
346
|
+
'</div>' +
|
|
347
|
+
'<div class="section-label">Ledger of runs</div>' +
|
|
348
|
+
(rows
|
|
349
|
+
? '<table class="ledger"><thead><tr><th>when</th><th>trigger</th><th>turns</th><th>tokens</th><th>cost</th><th>disposition</th></tr></thead><tbody>' + rows + '</tbody></table>'
|
|
350
|
+
: '<p style="color:var(--ink-dim)">No runs recorded yet.</p>') +
|
|
351
|
+
'<div class="columns"><div>' +
|
|
352
|
+
'<div class="section-label">Role memory — most recent</div>' +
|
|
353
|
+
(memories || '<p style="color:var(--ink-dim)">Nothing on file. Run an ingest pass to seed it.</p>') +
|
|
354
|
+
'</div><div>' +
|
|
355
|
+
'<div class="section-label">Connectors</div>' +
|
|
356
|
+
(agent.connectors.length
|
|
357
|
+
? agent.connectors.map(function (c) { return '<span class="chip">' + esc(c) + '</span>'; }).join(' ')
|
|
358
|
+
: '<p style="color:var(--ink-dim)">None wired.</p>') +
|
|
359
|
+
'</div></div>' +
|
|
360
|
+
'<div class="footer-note">Memories shown: ' + agent.recentMemories.length + ' of ' + agent.memoryCount +
|
|
361
|
+
' · runs shown: ' + agent.runs.recent.length + ' of ' + agent.runs.total + '</div>';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function emptyView() {
|
|
365
|
+
return '<div class="empty">' +
|
|
366
|
+
'<div class="stamp-big">No agents on file</div>' +
|
|
367
|
+
'<p>This ledger fills itself from the data directory.</p>' +
|
|
368
|
+
'<p style="margin-top:10px">Open the first dossier with<br><code>exempclaw run agents/your-agent.json "hello"</code></p>' +
|
|
369
|
+
'</div>';
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function render() {
|
|
373
|
+
var roster = document.getElementById('roster');
|
|
374
|
+
var detail = document.getElementById('detail');
|
|
375
|
+
var fleet = state.fleet;
|
|
376
|
+
if (!fleet || fleet.agents.length === 0) {
|
|
377
|
+
roster.innerHTML = '<div class="roster-label">Fleet roster</div>';
|
|
378
|
+
detail.innerHTML = emptyView();
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (!state.selected || !fleet.agents.some(function (a) { return a.id === state.selected; })) {
|
|
382
|
+
state.selected = fleet.agents[0].id;
|
|
383
|
+
}
|
|
384
|
+
roster.innerHTML = '<div class="roster-label">Fleet roster — ' + fleet.agents.length + ' on file</div>' +
|
|
385
|
+
fleet.agents.map(rosterCard).join('');
|
|
386
|
+
var selected = fleet.agents.find(function (a) { return a.id === state.selected; });
|
|
387
|
+
detail.innerHTML = detailView(selected);
|
|
388
|
+
|
|
389
|
+
Array.prototype.forEach.call(roster.querySelectorAll('.dossier'), function (el) {
|
|
390
|
+
el.addEventListener('click', function () {
|
|
391
|
+
state.selected = el.getAttribute('data-id');
|
|
392
|
+
render();
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
var firstLoad = true;
|
|
398
|
+
function refresh() {
|
|
399
|
+
fetch('/api/fleet').then(function (res) { return res.json(); }).then(function (fleet) {
|
|
400
|
+
var changed = JSON.stringify(fleet.agents) !== JSON.stringify(state.fleet && state.fleet.agents);
|
|
401
|
+
state.fleet = fleet;
|
|
402
|
+
document.getElementById('updated').textContent = 'updated ' + new Date().toLocaleTimeString();
|
|
403
|
+
document.getElementById('datadir').textContent = fleet.dataDir;
|
|
404
|
+
var pulse = document.getElementById('pulse');
|
|
405
|
+
pulse.classList.remove('tick');
|
|
406
|
+
void pulse.offsetWidth;
|
|
407
|
+
pulse.classList.add('tick');
|
|
408
|
+
if (changed || firstLoad) { render(); firstLoad = false; }
|
|
409
|
+
}).catch(function () {
|
|
410
|
+
document.getElementById('updated').textContent = 'connection lost — retrying';
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
refresh();
|
|
415
|
+
setInterval(refresh, 5000);
|
|
416
|
+
})();
|
|
417
|
+
</script>
|
|
418
|
+
</body>
|
|
419
|
+
</html>`;
|
|
420
|
+
}
|
|
421
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/dashboard/page.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAyZD,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import type { Logger } from "../core/logger.js";
|
|
3
|
+
import { type AgentMeta } from "./data.js";
|
|
4
|
+
/**
|
|
5
|
+
* Read-only local dashboard. Serves the single-page UI and a JSON snapshot of
|
|
6
|
+
* the fleet's state (runs, approvals, costs, memory). Binds to 127.0.0.1
|
|
7
|
+
* only — run records and memories are sensitive.
|
|
8
|
+
*/
|
|
9
|
+
export interface DashboardOptions {
|
|
10
|
+
dataDir: string;
|
|
11
|
+
port: number;
|
|
12
|
+
metas?: Map<string, AgentMeta>;
|
|
13
|
+
log: Logger;
|
|
14
|
+
}
|
|
15
|
+
export declare function createDashboardServer(opts: DashboardOptions): Server;
|
|
16
|
+
export declare function startDashboard(opts: DashboardOptions): Promise<{
|
|
17
|
+
server: Server;
|
|
18
|
+
url: string;
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { buildFleetSnapshot } from "./data.js";
|
|
3
|
+
import { dashboardPage } from "./page.js";
|
|
4
|
+
export function createDashboardServer(opts) {
|
|
5
|
+
const metas = opts.metas ?? new Map();
|
|
6
|
+
return createServer((req, res) => {
|
|
7
|
+
void (async () => {
|
|
8
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
9
|
+
if (req.method !== "GET") {
|
|
10
|
+
res.writeHead(405, { "content-type": "text/plain" }).end("method not allowed");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (url.pathname === "/") {
|
|
14
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end(dashboardPage());
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (url.pathname === "/api/fleet") {
|
|
18
|
+
const snapshot = await buildFleetSnapshot(opts.dataDir, metas);
|
|
19
|
+
res
|
|
20
|
+
.writeHead(200, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" })
|
|
21
|
+
.end(JSON.stringify(snapshot));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
res.writeHead(404, { "content-type": "text/plain" }).end("not found");
|
|
25
|
+
})().catch((err) => {
|
|
26
|
+
opts.log.error("dashboard request failed", { error: err.message });
|
|
27
|
+
if (!res.headersSent)
|
|
28
|
+
res.writeHead(500, { "content-type": "text/plain" });
|
|
29
|
+
res.end("internal error");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function startDashboard(opts) {
|
|
34
|
+
const server = createDashboardServer(opts);
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
server.once("error", reject);
|
|
37
|
+
server.listen(opts.port, "127.0.0.1", () => {
|
|
38
|
+
const address = server.address();
|
|
39
|
+
const port = typeof address === "object" && address ? address.port : opts.port;
|
|
40
|
+
resolve({ server, url: `http://127.0.0.1:${port}` });
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,WAAW,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAkB,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAc1C,MAAM,UAAU,qBAAqB,CAAC,IAAsB;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG,EAAqB,CAAC;IAEzD,OAAO,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC/B,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;YACxD,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBACzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;gBAC/E,OAAO;YACT,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;gBACzB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;gBACxF,OAAO;YACT,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;gBAClC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBAC/D,GAAG;qBACA,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,iCAAiC,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC;qBAClG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACjC,OAAO;YACT,CAAC;YACD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACxE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACjB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,0BAA0B,EAAE,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9E,IAAI,CAAC,GAAG,CAAC,WAAW;gBAAE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC;YAC3E,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAsB;IACnD,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE;YACzC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/E,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,oBAAoB,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ActionPolicy } from "../config/index.js";
|
|
2
|
+
import { Orchestrator } from "../orchestrator/orchestrator.js";
|
|
3
|
+
import type { ApprovalRequest } from "../tools/tool.js";
|
|
4
|
+
import { type DemoClaudeOptions } from "./claude.js";
|
|
5
|
+
/**
|
|
6
|
+
* Boots the offline demo: the real orchestrator/agent/tool/memory stack with
|
|
7
|
+
* a scripted brain (DemoClaude) and the fictional demo connector. No API key,
|
|
8
|
+
* no credentials, no network. State persists under the normal data dir with
|
|
9
|
+
* a clearly-prefixed agent id, so `memory`, `history`, `costs`, and the
|
|
10
|
+
* dashboard all work on it afterwards.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DEMO_AGENT_ID = "demo-jordan-support-lead";
|
|
13
|
+
export declare const DEMO_MODEL = "claude-demo";
|
|
14
|
+
export interface DemoBootstrapOptions extends DemoClaudeOptions {
|
|
15
|
+
policy?: ActionPolicy;
|
|
16
|
+
approve: (req: ApprovalRequest) => Promise<boolean>;
|
|
17
|
+
/** Override the data dir (tests). Defaults to the configured EXEMPCLAW_DATA_DIR. */
|
|
18
|
+
dataDir?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function bootstrapDemo(opts: DemoBootstrapOptions): Promise<{
|
|
21
|
+
orchestrator: Orchestrator;
|
|
22
|
+
agentId: string;
|
|
23
|
+
name: string;
|
|
24
|
+
model: string;
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { loadOfflineConfig } from "../config/index.js";
|
|
2
|
+
import { createLogger } from "../core/logger.js";
|
|
3
|
+
import { Orchestrator } from "../orchestrator/orchestrator.js";
|
|
4
|
+
import { AgentConfigSchema } from "../agent/config.js";
|
|
5
|
+
import { DemoClaude } from "./claude.js";
|
|
6
|
+
import { DEMO_SEED_MEMORIES } from "./world.js";
|
|
7
|
+
/**
|
|
8
|
+
* Boots the offline demo: the real orchestrator/agent/tool/memory stack with
|
|
9
|
+
* a scripted brain (DemoClaude) and the fictional demo connector. No API key,
|
|
10
|
+
* no credentials, no network. State persists under the normal data dir with
|
|
11
|
+
* a clearly-prefixed agent id, so `memory`, `history`, `costs`, and the
|
|
12
|
+
* dashboard all work on it afterwards.
|
|
13
|
+
*/
|
|
14
|
+
export const DEMO_AGENT_ID = "demo-jordan-support-lead";
|
|
15
|
+
export const DEMO_MODEL = "claude-demo";
|
|
16
|
+
const DEMO_AGENT_CONFIG = AgentConfigSchema.parse({
|
|
17
|
+
id: DEMO_AGENT_ID,
|
|
18
|
+
persona: {
|
|
19
|
+
name: "Jordan",
|
|
20
|
+
role: "Customer Support Lead",
|
|
21
|
+
succeeds: "Alex Rivera",
|
|
22
|
+
tone: "warm, concise, proactive",
|
|
23
|
+
guidance: "Prioritize unblocking customers quickly. Never commit to engineering timelines in writing.",
|
|
24
|
+
disclosure: "transparent",
|
|
25
|
+
},
|
|
26
|
+
model: DEMO_MODEL,
|
|
27
|
+
connectors: ["demo"],
|
|
28
|
+
maxIterations: 12,
|
|
29
|
+
});
|
|
30
|
+
export async function bootstrapDemo(opts) {
|
|
31
|
+
const offline = loadOfflineConfig();
|
|
32
|
+
const config = {
|
|
33
|
+
...offline,
|
|
34
|
+
anthropicApiKey: "demo-key-never-used",
|
|
35
|
+
defaultModel: DEMO_MODEL,
|
|
36
|
+
...(opts.dataDir ? { dataDir: opts.dataDir } : {}),
|
|
37
|
+
...(opts.policy ? { actionPolicy: opts.policy } : {}),
|
|
38
|
+
};
|
|
39
|
+
// Errors only: denials/tool failures already render in the TUI, and log
|
|
40
|
+
// lines would tear the animated live region mid-stream.
|
|
41
|
+
const log = createLogger("error");
|
|
42
|
+
const orchestrator = new Orchestrator(config, log, opts.approve, {
|
|
43
|
+
claude: new DemoClaude({ latencyMs: opts.latencyMs, streamDelayMs: opts.streamDelayMs }),
|
|
44
|
+
});
|
|
45
|
+
await orchestrator.addAgent(DEMO_AGENT_CONFIG);
|
|
46
|
+
// First boot: seed the role memory so recall/“/memory” have substance.
|
|
47
|
+
const { memory } = orchestrator.resources(DEMO_AGENT_ID);
|
|
48
|
+
if ((await memory.allMemories()).length === 0) {
|
|
49
|
+
for (const seed of DEMO_SEED_MEMORIES) {
|
|
50
|
+
await memory.addMemory({ text: seed.text, source: "onboarding", tags: seed.tags });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
orchestrator,
|
|
55
|
+
agentId: DEMO_AGENT_ID,
|
|
56
|
+
name: DEMO_AGENT_CONFIG.persona.name,
|
|
57
|
+
model: DEMO_MODEL,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/demo/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAyC,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,0BAA0B,CAAC;AACxD,MAAM,CAAC,MAAM,UAAU,GAAG,aAAa,CAAC;AAExC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,KAAK,CAAC;IAChD,EAAE,EAAE,aAAa;IACjB,OAAO,EAAE;QACP,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,uBAAuB;QAC7B,QAAQ,EAAE,aAAa;QACvB,IAAI,EAAE,0BAA0B;QAChC,QAAQ,EAAE,4FAA4F;QACtG,UAAU,EAAE,aAAa;KAC1B;IACD,KAAK,EAAE,UAAU;IACjB,UAAU,EAAE,CAAC,MAAM,CAAC;IACpB,aAAa,EAAE,EAAE;CAClB,CAAC,CAAC;AASH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAA0B;IAM5D,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,MAAM,GAAkB;QAC5B,GAAG,OAAO;QACV,eAAe,EAAE,qBAAqB;QACtC,YAAY,EAAE,UAAU;QACxB,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC;IACF,wEAAwE;IACxE,wDAAwD;IACxD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAClC,MAAM,YAAY,GAAG,IAAI,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE;QAC/D,MAAM,EAAE,IAAI,UAAU,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;KACzF,CAAC,CAAC;IACH,MAAM,YAAY,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC;IAE/C,uEAAuE;IACvE,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,kBAAkB,EAAE,CAAC;YACtC,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;IAED,OAAO;QACL,YAAY;QACZ,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,iBAAiB,CAAC,OAAO,CAAC,IAAI;QACpC,KAAK,EAAE,UAAU;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import type { ClaudeLike, ClaudeTurnParams } from "../llm/claude.js";
|
|
3
|
+
/**
|
|
4
|
+
* The demo brain: a scripted ClaudeLike that drives the real agent loop —
|
|
5
|
+
* tool calls, approval gates, memory writes, status animations — without an
|
|
6
|
+
* API key. It pattern-matches the operator's message to a scenario and plays
|
|
7
|
+
* it out turn by turn, reacting to denials. Token usage is fabricated and
|
|
8
|
+
* the "claude-demo" model prices at $0.00.
|
|
9
|
+
*/
|
|
10
|
+
export interface DemoClaudeOptions {
|
|
11
|
+
/** Pause before each turn, for a thinking feel. Default 700ms; tests use 0. */
|
|
12
|
+
latencyMs?: number;
|
|
13
|
+
/** Delay between streamed words. Default 16ms; tests use 0. */
|
|
14
|
+
streamDelayMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class DemoClaude implements ClaudeLike {
|
|
17
|
+
private readonly latencyMs;
|
|
18
|
+
private readonly streamDelayMs;
|
|
19
|
+
private toolCounter;
|
|
20
|
+
constructor(opts?: DemoClaudeOptions);
|
|
21
|
+
turn(params: ClaudeTurnParams): Promise<Anthropic.Message>;
|
|
22
|
+
summarize(): Promise<string>;
|
|
23
|
+
private toolUse;
|
|
24
|
+
private streamText;
|
|
25
|
+
}
|
|
26
|
+
/** Finds the operator's last real message, turns since it, and denial state. */
|
|
27
|
+
export declare function analyzeMessages(messages: Anthropic.MessageParam[]): {
|
|
28
|
+
text: string;
|
|
29
|
+
assistantTurnsSince: number;
|
|
30
|
+
denied: boolean;
|
|
31
|
+
};
|