@gr8ful/spf 0.1.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 +321 -0
- package/assets/defaults/spf.config.yaml +141 -0
- package/assets/prompts/builder/system.md +13 -0
- package/assets/prompts/builder/user.md +34 -0
- package/assets/prompts/documenter/system.md +17 -0
- package/assets/prompts/documenter/user.md +48 -0
- package/assets/prompts/planner/system.md +21 -0
- package/assets/prompts/planner/user.md +45 -0
- package/assets/prompts/reviewer/system.md +16 -0
- package/assets/prompts/reviewer/user.md +44 -0
- package/assets/prompts/scout/system.md +20 -0
- package/assets/prompts/scout/user.md +34 -0
- package/assets/skill/SKILL.md +80 -0
- package/assets/skill/cookbooks/authoring_chains.md +193 -0
- package/assets/skill/cookbooks/how_to_prompt_for_the_eng.md +109 -0
- package/assets/skill/cookbooks/roster.md +197 -0
- package/assets/skill/cookbooks/run_adw.md +92 -0
- package/assets/skill/cookbooks/spf_overview.md +111 -0
- package/assets/skill/references/config.md +188 -0
- package/assets/skill/references/handoff.md +162 -0
- package/assets/skill/references/observability.md +184 -0
- package/dist/chains/adw_build.d.ts +12 -0
- package/dist/chains/adw_build.js +27 -0
- package/dist/chains/adw_build_review.d.ts +21 -0
- package/dist/chains/adw_build_review.js +55 -0
- package/dist/chains/adw_build_test.d.ts +21 -0
- package/dist/chains/adw_build_test.js +67 -0
- package/dist/chains/adw_document.d.ts +23 -0
- package/dist/chains/adw_document.js +59 -0
- package/dist/chains/adw_plan.d.ts +12 -0
- package/dist/chains/adw_plan.js +27 -0
- package/dist/chains/adw_plan_build.d.ts +12 -0
- package/dist/chains/adw_plan_build.js +30 -0
- package/dist/chains/adw_plan_build_test.d.ts +16 -0
- package/dist/chains/adw_plan_build_test.js +65 -0
- package/dist/chains/adw_plan_build_test_quality.d.ts +18 -0
- package/dist/chains/adw_plan_build_test_quality.js +66 -0
- package/dist/chains/adw_prompt.d.ts +12 -0
- package/dist/chains/adw_prompt.js +25 -0
- package/dist/chains/adw_quality.d.ts +12 -0
- package/dist/chains/adw_quality.js +32 -0
- package/dist/chains/adw_scout.d.ts +12 -0
- package/dist/chains/adw_scout.js +27 -0
- package/dist/chains/adw_simple_sdlc.d.ts +43 -0
- package/dist/chains/adw_simple_sdlc.js +147 -0
- package/dist/chains/context.d.ts +19 -0
- package/dist/chains/context.js +1 -0
- package/dist/chains/index.d.ts +19 -0
- package/dist/chains/index.js +116 -0
- package/dist/cli/bin.d.ts +15 -0
- package/dist/cli/bin.js +24 -0
- package/dist/cli/commands/abort.d.ts +1 -0
- package/dist/cli/commands/abort.js +42 -0
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +165 -0
- package/dist/cli/commands/eject.d.ts +1 -0
- package/dist/cli/commands/eject.js +55 -0
- package/dist/cli/commands/events.d.ts +1 -0
- package/dist/cli/commands/events.js +49 -0
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +62 -0
- package/dist/cli/commands/install-skill.d.ts +1 -0
- package/dist/cli/commands/install-skill.js +122 -0
- package/dist/cli/commands/list.d.ts +1 -0
- package/dist/cli/commands/list.js +13 -0
- package/dist/cli/commands/migrate.d.ts +1 -0
- package/dist/cli/commands/migrate.js +167 -0
- package/dist/cli/commands/phases.d.ts +1 -0
- package/dist/cli/commands/phases.js +25 -0
- package/dist/cli/commands/run.d.ts +3 -0
- package/dist/cli/commands/run.js +27 -0
- package/dist/cli/commands/sessions.d.ts +1 -0
- package/dist/cli/commands/sessions.js +20 -0
- package/dist/cli/commands/trace.d.ts +8 -0
- package/dist/cli/commands/trace.js +10 -0
- package/dist/cli/commands/ui.d.ts +1 -0
- package/dist/cli/commands/ui.js +35 -0
- package/dist/cli/commands/version.d.ts +1 -0
- package/dist/cli/commands/version.js +8 -0
- package/dist/cli/commands/watch.d.ts +8 -0
- package/dist/cli/commands/watch.js +210 -0
- package/dist/cli/gitignore.d.ts +1 -0
- package/dist/cli/gitignore.js +14 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +156 -0
- package/dist/core/agent_cc.d.ts +75 -0
- package/dist/core/agent_cc.js +322 -0
- package/dist/core/agent_flue.d.ts +66 -0
- package/dist/core/agent_flue.js +321 -0
- package/dist/core/agents.d.ts +64 -0
- package/dist/core/agents.js +456 -0
- package/dist/core/changes.d.ts +35 -0
- package/dist/core/changes.js +98 -0
- package/dist/core/console.d.ts +36 -0
- package/dist/core/console.js +156 -0
- package/dist/core/data_types.d.ts +562 -0
- package/dist/core/data_types.js +382 -0
- package/dist/core/gates.d.ts +33 -0
- package/dist/core/gates.js +144 -0
- package/dist/core/git_helper.d.ts +59 -0
- package/dist/core/git_helper.js +115 -0
- package/dist/core/issues/github_provider.d.ts +52 -0
- package/dist/core/issues/github_provider.js +211 -0
- package/dist/core/issues/provider.d.ts +90 -0
- package/dist/core/issues/provider.js +14 -0
- package/dist/core/paths.d.ts +78 -0
- package/dist/core/paths.js +108 -0
- package/dist/core/permissions.d.ts +78 -0
- package/dist/core/permissions.js +187 -0
- package/dist/core/prompts.d.ts +4 -0
- package/dist/core/prompts.js +17 -0
- package/dist/core/quality.d.ts +65 -0
- package/dist/core/quality.js +194 -0
- package/dist/core/runner.d.ts +72 -0
- package/dist/core/runner.js +168 -0
- package/dist/core/session.d.ts +16 -0
- package/dist/core/session.js +60 -0
- package/dist/core/sqlite.d.ts +55 -0
- package/dist/core/sqlite.js +106 -0
- package/dist/core/tracer.d.ts +56 -0
- package/dist/core/tracer.js +246 -0
- package/dist/core/utils.d.ts +40 -0
- package/dist/core/utils.js +108 -0
- package/dist/core/watch.d.ts +44 -0
- package/dist/core/watch.js +194 -0
- package/dist/test/agent_cc.test.d.ts +1 -0
- package/dist/test/agent_cc.test.js +95 -0
- package/dist/test/agent_flue.test.d.ts +1 -0
- package/dist/test/agent_flue.test.js +83 -0
- package/dist/test/data_types.test.d.ts +10 -0
- package/dist/test/data_types.test.js +49 -0
- package/dist/test/ui_server.test.d.ts +1 -0
- package/dist/test/ui_server.test.js +119 -0
- package/dist/test/watch.test.d.ts +1 -0
- package/dist/test/watch.test.js +227 -0
- package/dist/ui/server/app.d.ts +3 -0
- package/dist/ui/server/app.js +98 -0
- package/dist/ui/server/db.d.ts +82 -0
- package/dist/ui/server/db.js +333 -0
- package/dist/ui/server/serve.d.ts +13 -0
- package/dist/ui/server/serve.js +80 -0
- package/dist/ui/server/static.d.ts +5 -0
- package/dist/ui/server/static.js +55 -0
- package/dist/ui/shared/types.d.ts +263 -0
- package/dist/ui/shared/types.js +8 -0
- package/package.json +52 -0
- package/web/assets/index-C7nF068F.css +1 -0
- package/web/assets/index-mzSArcnQ.js +11 -0
- package/web/assets/play-latin-400-normal-GKW-4YV7.woff2 +0 -0
- package/web/assets/play-latin-700-normal-DyPlLDbb.woff2 +0 -0
- package/web/index.html +14 -0
- package/web/logo.svg +6 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types shared by the read-only server and the Vue client.
|
|
3
|
+
*
|
|
4
|
+
* Every interface mirrors a table in spf.db one-for-one (see
|
|
5
|
+
* references/observability.md). Nothing here is derived state: phase durations,
|
|
6
|
+
* session progress and lane layout are computed in the UI, never stored.
|
|
7
|
+
*/
|
|
8
|
+
/** sessions.status — a run is running until it earns success. */
|
|
9
|
+
export type SessionStatus = "running" | "success" | "fail";
|
|
10
|
+
/** phases.status — queued only for manifest-declared phases not yet entered. */
|
|
11
|
+
export type PhaseStatus = "queued" | "running" | "success" | "fail";
|
|
12
|
+
/** phases.kind — decides which lane a block renders in. */
|
|
13
|
+
export type PhaseKind = "engineer" | "code" | "agent";
|
|
14
|
+
/** events.type — the ten types tracer.py emits. */
|
|
15
|
+
export type EventType = "phase_start" | "phase_end" | "agent_start" | "agent_end" | "tool_call" | "handoff" | "gate_pass" | "gate_fail" | "log" | "error";
|
|
16
|
+
export interface Session {
|
|
17
|
+
adw_id: string;
|
|
18
|
+
/** ADW script(s) that ran this session, e.g. "adw_plan + adw_build_test". */
|
|
19
|
+
adw_name: string | null;
|
|
20
|
+
request: string | null;
|
|
21
|
+
status: SessionStatus | null;
|
|
22
|
+
engineer: string | null;
|
|
23
|
+
started_at: string | null;
|
|
24
|
+
ended_at: string | null;
|
|
25
|
+
total_tokens: number | null;
|
|
26
|
+
total_cost: number | null;
|
|
27
|
+
/** 1 once archived out of the review list. Review state, not run state. */
|
|
28
|
+
archived: number | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A session row with its phases embedded, so the L1 table draws the
|
|
32
|
+
* mini-progress dots without a second request per row.
|
|
33
|
+
*/
|
|
34
|
+
export interface SessionSummary extends Session {
|
|
35
|
+
/** Full phase rows, ordered by seq — one dot each. */
|
|
36
|
+
phases: Phase[];
|
|
37
|
+
phase_count: number;
|
|
38
|
+
/**
|
|
39
|
+
* The session's agents, same shape and merge rules as SessionDetail.agents —
|
|
40
|
+
* so an L1 card can color its per-agent dots without a request per card.
|
|
41
|
+
*/
|
|
42
|
+
agents: AgentSession[];
|
|
43
|
+
}
|
|
44
|
+
export interface Phase {
|
|
45
|
+
phase_id: string;
|
|
46
|
+
adw_id: string;
|
|
47
|
+
seq: number | null;
|
|
48
|
+
name: string | null;
|
|
49
|
+
kind: PhaseKind | null;
|
|
50
|
+
owner: string | null;
|
|
51
|
+
description: string | null;
|
|
52
|
+
status: PhaseStatus | null;
|
|
53
|
+
attempt: number | null;
|
|
54
|
+
retries: number | null;
|
|
55
|
+
error: string | null;
|
|
56
|
+
started_at: string | null;
|
|
57
|
+
ended_at: string | null;
|
|
58
|
+
}
|
|
59
|
+
export interface Event {
|
|
60
|
+
/** SQLite rowid — the polling cursor. Monotonic, insertion-ordered. */
|
|
61
|
+
rowid: number;
|
|
62
|
+
event_id: string;
|
|
63
|
+
adw_id: string;
|
|
64
|
+
phase_id: string | null;
|
|
65
|
+
/** Span nesting: an agent phase expands into its tool-call children. */
|
|
66
|
+
parent_id: string | null;
|
|
67
|
+
type: EventType | null;
|
|
68
|
+
name: string | null;
|
|
69
|
+
/** Raw JSON string as written by the tracer; parse at the point of display. */
|
|
70
|
+
payload_json: string | null;
|
|
71
|
+
tokens: number | null;
|
|
72
|
+
started_at: string | null;
|
|
73
|
+
ended_at: string | null;
|
|
74
|
+
}
|
|
75
|
+
export interface Envelope {
|
|
76
|
+
envelope_id: string;
|
|
77
|
+
adw_id: string;
|
|
78
|
+
phase_id: string | null;
|
|
79
|
+
agent: string | null;
|
|
80
|
+
/** Name of the data_types model the response was parsed against. */
|
|
81
|
+
output_type: string | null;
|
|
82
|
+
payload_json: string | null;
|
|
83
|
+
/** SQLite integer boolean. */
|
|
84
|
+
valid: number | null;
|
|
85
|
+
attempt: number | null;
|
|
86
|
+
created_at: string | null;
|
|
87
|
+
}
|
|
88
|
+
export interface GateResult {
|
|
89
|
+
id: number;
|
|
90
|
+
adw_id: string;
|
|
91
|
+
phase_id: string | null;
|
|
92
|
+
attempt: number | null;
|
|
93
|
+
gate: string | null;
|
|
94
|
+
/** SQLite integer boolean. */
|
|
95
|
+
passed: number | null;
|
|
96
|
+
/** JSON array of violation strings; "[]" on a pass. */
|
|
97
|
+
violations_json: string | null;
|
|
98
|
+
/**
|
|
99
|
+
* JSON array of GateCheck — the per-item evidence behind the verdict, so a
|
|
100
|
+
* green gate can say WHAT it verified rather than only that it passed.
|
|
101
|
+
* Null on rows written before the tracer recorded checks; those are not
|
|
102
|
+
* backfilled, so fall back to the verdict alone.
|
|
103
|
+
*/
|
|
104
|
+
checks_json: string | null;
|
|
105
|
+
created_at: string | null;
|
|
106
|
+
}
|
|
107
|
+
/** One item a gate inspected — the parsed element of `checks_json`. */
|
|
108
|
+
export interface GateCheck {
|
|
109
|
+
item: string;
|
|
110
|
+
ok: boolean;
|
|
111
|
+
note: string;
|
|
112
|
+
}
|
|
113
|
+
/** agent_sessions — the queryable mirror of agent_map.json. Supplies lane labels (`name · model`). */
|
|
114
|
+
export interface AgentSession {
|
|
115
|
+
adw_id: string;
|
|
116
|
+
agent: string;
|
|
117
|
+
coding_agent: string | null;
|
|
118
|
+
model: string | null;
|
|
119
|
+
session_id: string | null;
|
|
120
|
+
/**
|
|
121
|
+
* The agent's lane color from spf.config.yaml, e.g. "#a78bfa". Null on dbs
|
|
122
|
+
* written by a tracer predating the column, and on agents with no configured
|
|
123
|
+
* color — fall back to the UI's own palette.
|
|
124
|
+
*/
|
|
125
|
+
color: string | null;
|
|
126
|
+
/**
|
|
127
|
+
* How full the agent's context window was after its last turn, and the
|
|
128
|
+
* model's ceiling. Null on dbs predating the columns and on an agent still
|
|
129
|
+
* running — the lane draws no bar rather than a misleading empty one.
|
|
130
|
+
*/
|
|
131
|
+
context_tokens: number | null;
|
|
132
|
+
context_window: number | null;
|
|
133
|
+
created_at: string | null;
|
|
134
|
+
last_used_at: string | null;
|
|
135
|
+
}
|
|
136
|
+
/** Parsed `agent_start` payload — the live source of a lane's label and color. */
|
|
137
|
+
export interface AgentStartPayload {
|
|
138
|
+
model?: string;
|
|
139
|
+
thinking?: string;
|
|
140
|
+
session_id?: string;
|
|
141
|
+
color?: string;
|
|
142
|
+
coding_agent?: string;
|
|
143
|
+
purpose?: string;
|
|
144
|
+
/** Tool allowlist; null means all tools. Absent on pre-config-payload rows. */
|
|
145
|
+
tools?: string[] | null;
|
|
146
|
+
harness_engineering?: string[];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Tokens and dollars per component for one agent phase, summed across every
|
|
150
|
+
* send it made (a retried phase paid more than once). Mirrors pi's `usage`:
|
|
151
|
+
* `input_tokens` EXCLUDES cache reads, which bill at their own rate.
|
|
152
|
+
*/
|
|
153
|
+
export interface UsageBreakdown {
|
|
154
|
+
input_tokens: number;
|
|
155
|
+
output_tokens: number;
|
|
156
|
+
cache_read_tokens: number;
|
|
157
|
+
cache_write_tokens: number;
|
|
158
|
+
/**
|
|
159
|
+
* Thinking tokens — the reasoning SHARE of `output_tokens`, not a fifth
|
|
160
|
+
* component. Billed at the output rate; adding it to the others would
|
|
161
|
+
* double-count. Absent (undefined) on runs predating the field.
|
|
162
|
+
*/
|
|
163
|
+
reasoning_tokens?: number;
|
|
164
|
+
total_tokens: number;
|
|
165
|
+
input_cost: number;
|
|
166
|
+
output_cost: number;
|
|
167
|
+
cache_read_cost: number;
|
|
168
|
+
cache_write_cost: number;
|
|
169
|
+
total_cost: number;
|
|
170
|
+
}
|
|
171
|
+
/** Parsed `agent_end` payload — closes out a call with its cost and context use. */
|
|
172
|
+
export interface AgentEndPayload {
|
|
173
|
+
cost?: number;
|
|
174
|
+
/** Absent on runs predating the breakdown; `cost` alone survives there. */
|
|
175
|
+
usage?: UsageBreakdown;
|
|
176
|
+
/** Window occupancy after the final turn, and the model's ceiling. */
|
|
177
|
+
context_tokens?: number;
|
|
178
|
+
context_window?: number;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Parsed `tool_call` payload — one event per real tool call, emitted when the
|
|
182
|
+
* tool returns. `result_snippet` and `duration_ms` are absent when the coding
|
|
183
|
+
* agent never reported a result.
|
|
184
|
+
*/
|
|
185
|
+
export interface ToolCallPayload {
|
|
186
|
+
tool?: string;
|
|
187
|
+
tool_call_id?: string;
|
|
188
|
+
args?: Record<string, unknown>;
|
|
189
|
+
result_snippet?: string;
|
|
190
|
+
ok?: boolean;
|
|
191
|
+
duration_ms?: number;
|
|
192
|
+
agent?: string;
|
|
193
|
+
}
|
|
194
|
+
/** GET /api/sessions */
|
|
195
|
+
export type SessionsResponse = SessionSummary[];
|
|
196
|
+
/** GET /api/sessions/:adw_id */
|
|
197
|
+
/**
|
|
198
|
+
* What actually moved through a session, summed across every agent.
|
|
199
|
+
*
|
|
200
|
+
* Deliberately NOT the billed total: `sessions.total_tokens` also counts every
|
|
201
|
+
* cached re-read, which is the same context charged again on each turn.
|
|
202
|
+
*/
|
|
203
|
+
export interface SessionUsage {
|
|
204
|
+
/** Raw prompt tokens read for the first time: new input + cache writes. */
|
|
205
|
+
read: number;
|
|
206
|
+
/** Tokens generated. Each produced exactly once, so this needs no adjusting. */
|
|
207
|
+
written: number;
|
|
208
|
+
}
|
|
209
|
+
export interface SessionDetail {
|
|
210
|
+
session: Session;
|
|
211
|
+
/** Derived from agent_end payloads, so historical runs have it too. */
|
|
212
|
+
usage: SessionUsage;
|
|
213
|
+
/** Ordered by seq. */
|
|
214
|
+
phases: Phase[];
|
|
215
|
+
/**
|
|
216
|
+
* One entry per agent that has run OR is running under this adw_id — lane
|
|
217
|
+
* labels come from here. Finished agents come from the agent_sessions table;
|
|
218
|
+
* an agent still in flight has no row there yet, so its entry is built from
|
|
219
|
+
* its agent_start event (coding_agent is null until it finishes).
|
|
220
|
+
*/
|
|
221
|
+
agents: AgentSession[];
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* GET /api/sessions/:adw_id/events?after=<rowid>&limit=500
|
|
225
|
+
*
|
|
226
|
+
* Poll with `after` = the cursor from the previous response. `cursor` is the
|
|
227
|
+
* highest rowid in this page (or the `after` you sent, when the page is empty),
|
|
228
|
+
* so it can be fed straight back in. `has_more` means the page hit the limit.
|
|
229
|
+
*/
|
|
230
|
+
export interface EventsPage {
|
|
231
|
+
events: Event[];
|
|
232
|
+
cursor: number;
|
|
233
|
+
has_more: boolean;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* GET /api/sessions/:adw_id/agents/:agent/prompts
|
|
237
|
+
*
|
|
238
|
+
* The exact compiled prompts sent to an agent, read from
|
|
239
|
+
* `{data_dir}/sessions/{adw_id}/{agent}/prompts/`. These live only as files —
|
|
240
|
+
* the db has no copy. Either field is null when that file isn't on disk, which
|
|
241
|
+
* is the normal state for an agent that never ran in this session, so a 200
|
|
242
|
+
* with two nulls is a valid answer rather than an error.
|
|
243
|
+
*/
|
|
244
|
+
export interface AgentPrompts {
|
|
245
|
+
system: string | null;
|
|
246
|
+
user: string | null;
|
|
247
|
+
}
|
|
248
|
+
/** Alias matching the naming of the other endpoint payloads. */
|
|
249
|
+
export type PromptsResponse = AgentPrompts;
|
|
250
|
+
/** GET /api/sessions/:adw_id/envelopes */
|
|
251
|
+
export type EnvelopesResponse = Envelope[];
|
|
252
|
+
/** GET /api/sessions/:adw_id/gates */
|
|
253
|
+
export type GatesResponse = GateResult[];
|
|
254
|
+
/** GET /api/health */
|
|
255
|
+
export interface HealthResponse {
|
|
256
|
+
ok: boolean;
|
|
257
|
+
db: string;
|
|
258
|
+
journal_mode: string;
|
|
259
|
+
sessions: number;
|
|
260
|
+
}
|
|
261
|
+
export interface ApiError {
|
|
262
|
+
error: string;
|
|
263
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types shared by the read-only server and the Vue client.
|
|
3
|
+
*
|
|
4
|
+
* Every interface mirrors a table in spf.db one-for-one (see
|
|
5
|
+
* references/observability.md). Nothing here is derived state: phase durations,
|
|
6
|
+
* session progress and lane layout are computed in the UI, never stored.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gr8ful/spf",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Super Portable Factory — a global CLI for repeatable agents-plus-code workflows (ADWs)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"spf": "dist/cli/bin.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"web",
|
|
13
|
+
"assets",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": "./dist/index.js",
|
|
19
|
+
"./core": "./dist/core/index.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.19.0"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"prepare": "lefthook install",
|
|
29
|
+
"build:cli": "tsc -p tsconfig.build.json && chmod +x dist/cli/bin.js",
|
|
30
|
+
"build:app": "npm --prefix app ci && npm --prefix app run build",
|
|
31
|
+
"build": "npm run clean && npm run build:cli && npm run build:app",
|
|
32
|
+
"clean": "rm -rf dist",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
34
|
+
"pretest": "npm run build:cli",
|
|
35
|
+
"test": "node --test \"dist/**/*.test.js\"",
|
|
36
|
+
"prepack": "node scripts/check-pack-ready.js",
|
|
37
|
+
"size-check": "node scripts/check-tarball-size.js"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@flue/runtime": "2.0.3",
|
|
41
|
+
"@hono/node-server": "^2.1.1",
|
|
42
|
+
"@valibot/to-json-schema": "^1.7.1",
|
|
43
|
+
"hono": "^4.13.3",
|
|
44
|
+
"valibot": "^1.4.2",
|
|
45
|
+
"yaml": "^2.5.1"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.10.0",
|
|
49
|
+
"lefthook": "^2.1.10",
|
|
50
|
+
"typescript": "^7.0.2"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:Play;font-style:normal;font-weight:400;font-display:swap;src:url(./play-latin-400-normal-GKW-4YV7.woff2) format("woff2")}@font-face{font-family:Play;font-style:normal;font-weight:700;font-display:swap;src:url(./play-latin-700-normal-DyPlLDbb.woff2) format("woff2")}.chip[data-v-2a944f45]{display:inline-flex;align-items:center;gap:7px;padding:3px 13px 3px 10px;border-radius:999px;border:1px solid var(--border);font-size:16px;color:var(--dim);white-space:nowrap}.chip-icon[data-v-2a944f45]{flex:none}.chip.success[data-v-2a944f45]{color:var(--green);border-color:#4ade8073;background:#4ade8017;box-shadow:0 0 12px #4ade801f}.chip.fail[data-v-2a944f45]{color:var(--red);border-color:#ff6f6773;background:#ff6f6717;box-shadow:0 0 12px #ff6f671f}.chip.running[data-v-2a944f45]{color:var(--blue);border-color:#6cb6ff73;background:#6cb6ff17;box-shadow:0 0 12px #6cb6ff2e}.chip.running .chip-icon[data-v-2a944f45]{animation:spin-2a944f45 1.1s linear infinite}@keyframes spin-2a944f45{to{transform:rotate(360deg)}}.chip.queued[data-v-2a944f45]{color:var(--dim);border-style:dashed}.stat[data-v-fa161459]{display:inline-flex;align-items:center;gap:7px;padding:3px 12px;border:1px solid var(--border-soft);border-radius:999px;background:#131a2699;font-size:16px;white-space:nowrap}.stat-icon[data-v-fa161459]{color:var(--faint);flex:none}.stat-value[data-v-fa161459]{color:var(--text);font-family:var(--mono);font-variant-numeric:tabular-nums}.stat.compact[data-v-fa161459]{padding:0;border:none;background:transparent}.stat.compact .stat-value[data-v-fa161459]{color:var(--dim)}.dots[data-v-14dd8bbd]{display:inline-flex;gap:5px;font-size:16px;letter-spacing:0}.d.success[data-v-14dd8bbd]{color:var(--green)}.d.fail[data-v-14dd8bbd]{color:var(--red)}.d.running[data-v-14dd8bbd]{color:var(--blue);animation:pulse 1.2s ease-in-out infinite}.d.queued[data-v-14dd8bbd]{color:var(--faint)}.card[data-v-4de2ceae]{height:420px;display:flex;flex-direction:column;gap:10px;padding:20px 22px;position:relative;border:1px solid var(--border-soft);border-radius:16px;background:var(--surface);color:var(--text);cursor:pointer;overflow:hidden;transition:border-color .18s ease,box-shadow .18s ease,transform .18s ease}.card-archive[data-v-4de2ceae]{position:absolute;top:10px;right:12px;width:26px;height:26px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--dim);font-family:inherit;font-size:20px;line-height:1;cursor:pointer;opacity:0;transition:opacity .15s ease,background .15s ease,color .15s ease}.card:hover .card-archive[data-v-4de2ceae],.card-archive[data-v-4de2ceae]:focus-visible{opacity:1}.card-archive[data-v-4de2ceae]:hover{background:#ff6f6729;color:#ff6f67}.card[data-v-4de2ceae]:hover{border-color:#94a3ff73;box-shadow:0 10px 34px #94a3ff1f;transform:translateY(-2px)}.card.running[data-v-4de2ceae]{border-color:#6cb6ff99;box-shadow:0 0 22px #6cb6ff29}.card.fail[data-v-4de2ceae]{border-color:#ff6f6799}.card-id[data-v-4de2ceae]{flex:none;font-family:var(--mono);font-size:18px;font-weight:700;color:var(--purple)}.card-adw[data-v-4de2ceae]{flex:none;font-family:var(--mono);font-size:16px;color:var(--cyan);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-req[data-v-4de2ceae]{flex:none;font-size:16px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tl[data-v-4de2ceae]{display:flex;flex-direction:column;margin-top:4px;height:194px;flex:none;overflow:hidden}.tl-more[data-v-4de2ceae]{display:flex;align-items:center;height:40px;padding-left:96px;font-size:16px}.tl-axis[data-v-4de2ceae]{display:flex;align-items:flex-end;height:28px;margin-bottom:6px}.tl-gutter[data-v-4de2ceae],.tl-agent[data-v-4de2ceae]{flex:none;width:96px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-right:8px}.tl-scale[data-v-4de2ceae]{position:relative;flex:1;height:100%;border-bottom:1px solid var(--border)}.tl-tick[data-v-4de2ceae]{position:absolute;bottom:4px;transform:translate(-50%);font-family:var(--mono);font-size:16px;color:var(--faint);white-space:nowrap}.tl-tick.edge[data-v-4de2ceae]{transform:none}.tl-row[data-v-4de2ceae]{display:flex;align-items:center;height:40px}.tl-agent[data-v-4de2ceae]{font-size:16px;color:var(--dim)}.tl-track[data-v-4de2ceae]{position:relative;flex:1;height:100%;border-bottom:1px solid var(--border-soft)}.tl-dot[data-v-4de2ceae]{position:absolute;top:50%;width:9px;height:9px;border-radius:50%;transform:translate(-50%,-50%)}.tl-dot.latest[data-v-4de2ceae]{width:13px;height:13px;box-shadow:0 0 10px currentColor;animation:pulse 1.4s ease-in-out infinite}.tl-empty[data-v-4de2ceae]{align-items:center;justify-content:center;font-size:16px}.card-foot[data-v-4de2ceae]{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:auto;font-size:16px}.foot-status[data-v-4de2ceae]{display:inline-flex;align-items:center;gap:14px}.card-stats[data-v-4de2ceae]{display:flex;align-items:center;gap:12px}.sessions[data-v-59eaca8a]{display:flex;flex-direction:column}.list-head[data-v-59eaca8a]{padding:16px 24px 0;font-size:16px}.cards[data-v-59eaca8a]{display:grid;grid-template-columns:repeat(auto-fill,minmax(460px,1fr));gap:18px;padding:16px 24px 28px}.dsec[data-v-ab91d33b]{margin-bottom:14px}.dsec-head[data-v-ab91d33b]{display:flex;align-items:center;gap:9px;width:100%;padding:6px 8px;background:none;border:none;border-bottom:1px solid var(--border-soft);border-radius:6px 6px 0 0;color:var(--dim);font-size:16px;font-weight:700;letter-spacing:.05em;text-transform:lowercase;cursor:pointer;text-align:left}.dsec-icon[data-v-ab91d33b]{flex:none;color:var(--faint)}.dsec-head[data-v-ab91d33b]:hover{background:var(--panel-2);color:var(--text)}.chev[data-v-ab91d33b]{color:var(--faint);flex:none}.dsec-count[data-v-ab91d33b]{font-weight:500}.dsec-body[data-v-ab91d33b]{padding-top:10px}.detail[data-v-567985aa]{margin:0 28px 28px;border:1px solid var(--border-soft);border-radius:16px;background:var(--surface)}.d-head[data-v-567985aa]{display:flex;align-items:center;gap:20px;flex-wrap:wrap;padding:14px 18px;border-bottom:1px solid var(--border);background:var(--panel-2);border-radius:10px 10px 0 0}.d-main[data-v-567985aa]{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.d-name[data-v-567985aa]{font-size:20px;font-weight:700}.d-tags[data-v-567985aa]{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-left:auto}.tag[data-v-567985aa]{display:inline-flex;align-items:baseline;gap:7px;padding:2px 12px;border:1px solid var(--border-soft);border-radius:999px;background:var(--panel-3);font-size:16px;white-space:nowrap}.tag-k[data-v-567985aa]{color:var(--faint)}.tag-v[data-v-567985aa]{color:var(--text)}.close[data-v-567985aa]{background:none;border:1px solid var(--border);border-radius:6px;color:var(--dim);font-family:var(--mono);font-size:16px;cursor:pointer;padding:3px 10px}.close[data-v-567985aa]:hover{color:var(--text);border-color:var(--dim)}.d-desc[data-v-567985aa]{margin:0;color:var(--dim)}.d-request[data-v-567985aa]{margin:0;color:var(--text);white-space:pre-wrap;overflow-wrap:anywhere}.cfg[data-v-567985aa]{display:flex;flex-direction:column;gap:7px}.cfg-row[data-v-567985aa]{display:flex;align-items:baseline;gap:12px}.cfg-k[data-v-567985aa]{flex:none;width:118px;color:var(--faint)}.cfg-v[data-v-567985aa]{color:var(--text);min-width:0;overflow-wrap:anywhere}.cfg-model-icon[data-v-567985aa]{width:17px;height:17px;flex:none;object-fit:contain}.cfg-chips[data-v-567985aa]{display:inline-flex;flex-wrap:wrap;gap:6px}.cfg-chip[data-v-567985aa]{display:inline-flex;align-items:center;gap:8px;padding:2px 12px;border:1px solid var(--border-soft);border-radius:999px;background:#131a2699;font-family:var(--mono);font-size:16px;overflow-wrap:anywhere}.cfg-icon[data-v-567985aa]{flex:none;color:var(--faint)}.d-error[data-v-567985aa]{margin:14px 18px 0}.d-grid[data-v-567985aa]{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,3fr);gap:28px;padding:16px 18px 20px}@media(max-width:1100px){.d-grid[data-v-567985aa]{grid-template-columns:1fr}}h3[data-v-567985aa]{display:flex;align-items:center;gap:9px;margin:18px 0 10px;padding-bottom:6px;border-bottom:1px solid var(--border-soft);font-size:16px;font-weight:700;color:var(--dim);text-transform:lowercase;letter-spacing:.05em}.h3-icon[data-v-567985aa]{flex:none;color:var(--faint)}h3[data-v-567985aa]:first-child{margin-top:0}.prompt-panel[data-v-567985aa]{margin-bottom:10px;border:1px solid var(--border-soft);border-radius:8px;background:var(--panel-3);overflow:hidden}.prompt-head[data-v-567985aa]{display:flex;align-items:baseline;gap:12px;width:100%;padding:9px 14px;background:none;border:none;color:var(--text);font-size:16px;cursor:pointer;text-align:left}.prompt-head[data-v-567985aa]:hover{background:var(--panel-2)}.chev[data-v-567985aa]{color:var(--faint);flex:none}.prompt-title[data-v-567985aa]{font-weight:700}.prompt-head .dim[data-v-567985aa]{margin-left:auto}.prompt-body[data-v-567985aa]{padding:12px 14px 14px;border-top:1px solid var(--border-soft);max-height:60vh;overflow:auto}.prompt-tools[data-v-567985aa]{display:flex;gap:8px;margin-bottom:12px}.prompt-tools button[data-v-567985aa]{padding:2px 12px;border:1px solid var(--border-soft);border-radius:6px;background:none;color:var(--dim);font-family:var(--mono);font-size:16px;cursor:pointer}.prompt-tools button.active[data-v-567985aa]{color:var(--text);border-color:var(--border);background:var(--panel-2)}.prompt-raw[data-v-567985aa]{border:none;padding:0;background:transparent;max-height:none}.gate[data-v-567985aa]{margin-bottom:10px;padding:10px 14px;border:1px solid var(--border-soft);border-left-width:3px;border-radius:8px;background:var(--panel-3)}.gate.pass[data-v-567985aa]{border-left-color:var(--green)}.gate.fail[data-v-567985aa]{border-left-color:var(--red)}.gate-line[data-v-567985aa]{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap}.gate-toggle[data-v-567985aa]{width:100%;padding:0;background:none;border:none;color:var(--text);font-size:16px;cursor:pointer;text-align:left}.gate-checks .check-item[data-v-567985aa]{font-family:var(--mono)}.gate-toggle .chev[data-v-567985aa]{color:var(--faint);flex:none}.gate-checks[data-v-567985aa]{margin-top:10px;padding-top:8px;border-top:1px solid var(--border-soft)}.gate-check[data-v-567985aa]{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;padding:3px 0}.gate-check.pass .check-mark[data-v-567985aa]{color:var(--green)}.gate-check.fail .check-mark[data-v-567985aa]{color:var(--red)}.check-item[data-v-567985aa]{overflow-wrap:anywhere;min-width:0}.check-note[data-v-567985aa]{white-space:pre-wrap;overflow-wrap:anywhere}.check-note-block[data-v-567985aa]{flex-basis:100%;margin:4px 0 6px 26px;max-height:30vh;overflow:auto}.tag-fail[data-v-567985aa]{border-color:#ff6f678c}.tag-fail .tag-v[data-v-567985aa]{color:var(--red)}.gate.pass .gate-mark[data-v-567985aa]{color:var(--green)}.gate.fail .gate-mark[data-v-567985aa]{color:var(--red)}.gate-name[data-v-567985aa]{color:var(--text);font-weight:700}.gate-time[data-v-567985aa]{margin-left:auto}.violations[data-v-567985aa]{margin:8px 0 2px;padding-left:24px;color:var(--red)}.usage[data-v-567985aa]{width:100%;max-width:420px;border-collapse:collapse;font-size:16px}.usage th[data-v-567985aa]{padding:0 0 6px;font-size:14px;font-weight:400;letter-spacing:.06em;text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--border-soft)}.usage td[data-v-567985aa]{padding:5px 0}.usage .u-k[data-v-567985aa]{text-align:left;color:var(--dim)}.usage .u-n[data-v-567985aa],.usage .u-c[data-v-567985aa]{text-align:right;font-family:var(--mono);color:var(--text)}.usage .u-c[data-v-567985aa]{color:var(--green)}.u-nested .u-k[data-v-567985aa]{padding-left:18px;color:var(--faint)}.u-nested .u-n[data-v-567985aa],.u-nested .u-c[data-v-567985aa]{color:var(--faint)}.u-total td[data-v-567985aa]{padding-top:8px;border-top:1px solid var(--border-soft);font-weight:700}.u-total .u-k[data-v-567985aa]{color:var(--text)}.u-note[data-v-567985aa]{margin:10px 0 0;font-size:15px}.output[data-v-567985aa]{margin-bottom:14px}.output-line[data-v-567985aa]{display:flex;gap:12px;align-items:baseline;flex-wrap:wrap;margin-bottom:8px}.output-type[data-v-567985aa]{color:var(--purple);font-weight:700}.output-valid.pass[data-v-567985aa]{color:var(--green)}.output-valid.fail[data-v-567985aa]{color:var(--red)}.output pre[data-v-567985aa]{max-height:40vh;overflow:auto}.event[data-v-567985aa]{border-bottom:1px solid var(--border-soft)}.event-row[data-v-567985aa]{display:flex;gap:14px;align-items:baseline;width:100%;padding:7px 6px;background:none;border:none;border-radius:6px;color:var(--text);font-family:var(--mono);font-size:16px;cursor:pointer;text-align:left}.event-row[data-v-567985aa]:hover,.event-row.open[data-v-567985aa]{background:var(--panel-2)}.e-time[data-v-567985aa]{flex:none;font-variant-numeric:tabular-nums}.e-type[data-v-567985aa]{flex:none;width:130px}.e-name[data-v-567985aa]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.e-extra[data-v-567985aa]{margin-left:auto;flex:none;display:inline-flex;gap:14px}.payload-panel[data-v-567985aa]{margin:6px 0 14px;padding:14px 16px;border:1px solid var(--border);border-radius:10px;background:var(--panel-3)}.p-meta[data-v-567985aa]{display:flex;gap:16px;align-items:baseline;margin-bottom:10px}.p-tool[data-v-567985aa]{color:var(--cyan);font-weight:700;font-size:17px}.payload-panel h4[data-v-567985aa]{margin:14px 0 6px;font-size:16px;font-weight:700;color:var(--dim);text-transform:lowercase;letter-spacing:.06em}.payload-panel h4[data-v-567985aa]:first-of-type{margin-top:0}.p-pre[data-v-567985aa]{border:1px solid var(--border-soft);border-radius:8px;padding:10px 12px;background:#06080f8c;max-height:42vh;overflow:auto}.t-red[data-v-567985aa]{color:var(--red)}.t-green[data-v-567985aa]{color:var(--green)}.t-cyan[data-v-567985aa]{color:var(--cyan)}.t-purple[data-v-567985aa]{color:var(--purple)}.t-violet[data-v-567985aa]{color:var(--violet)}.trace[data-v-aaa51626]{padding:0 0 40px}.run-strip[data-v-aaa51626]{display:flex;align-items:center;gap:18px;padding:14px 24px;border-bottom:1px solid var(--border-soft);flex-wrap:wrap}.run-strip .request[data-v-aaa51626]{font-size:17px;color:var(--text);max-width:52ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.run-stats[data-v-aaa51626]{display:inline-flex;gap:12px;flex-wrap:wrap}.waterfall[data-v-aaa51626]{margin:20px 28px;border:1px solid var(--border-soft);border-radius:16px;background:var(--surface);overflow:hidden}.row[data-v-aaa51626]{display:grid;grid-template-columns:280px 1fr}.axis-row[data-v-aaa51626]{border-bottom:1px solid var(--border);background:var(--panel-2)}.axis-row .track[data-v-aaa51626]{height:40px;overflow:hidden}.zone-head[data-v-aaa51626]{position:absolute;top:0;bottom:0;left:0;display:inline-flex;align-items:center;justify-content:center;font-size:16px;color:var(--amber);border-right:1px solid var(--border)}.axis-label[data-v-aaa51626]{position:absolute;bottom:7px;transform:translate(-50%);font-family:var(--mono);font-size:16px;color:var(--dim);white-space:nowrap}.label[data-v-aaa51626]{padding:12px 16px;display:flex;flex-direction:column;justify-content:center;gap:2px;border-right:1px solid var(--border);overflow:hidden;white-space:nowrap}.lane-name[data-v-aaa51626]{display:inline-flex;align-items:center;gap:8px;font-size:17px;font-weight:700;overflow:hidden;text-overflow:ellipsis}.lane-icon[data-v-aaa51626]{flex:none;opacity:.85}.lane-meta[data-v-aaa51626]{font-family:var(--mono);font-size:16px;color:var(--dim);overflow:hidden;text-overflow:ellipsis}.lane-model[data-v-aaa51626]{display:inline-flex;align-items:center;gap:7px}.model-icon[data-v-aaa51626]{width:17px;height:17px;flex:none;object-fit:contain}.lane-ctx[data-v-aaa51626]{display:flex;flex-direction:column;gap:4px;margin-top:2px;max-width:190px}.ctx-head[data-v-aaa51626]{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.ctx-label[data-v-aaa51626]{font-size:14px;letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}.ctx-pct[data-v-aaa51626]{font-family:var(--mono);font-size:14px;color:var(--dim)}.ctx-bar[data-v-aaa51626]{height:6px;border-radius:999px;background:#06080fbf;border:1px solid var(--border-soft);overflow:hidden}.ctx-fill[data-v-aaa51626]{display:block;height:100%;border-radius:999px;transition:width .3s ease}.lane[data-v-aaa51626]{border-bottom:1px solid var(--border-soft)}.lane[data-v-aaa51626]:last-child{border-bottom:none}.track[data-v-aaa51626]{position:relative;height:118px;overflow:hidden}.zone-divider[data-v-aaa51626]{position:absolute;top:0;bottom:0;border-left:1px solid var(--border)}.gridline[data-v-aaa51626]{position:absolute;top:0;bottom:0;border-left:1px dashed rgba(174,191,212,.14)}.block[data-v-aaa51626]{position:absolute;top:13px;height:92px;display:flex;flex-direction:column;justify-content:flex-start;gap:4px;padding:10px 12px 16px;border-radius:10px;border:1px solid;font-size:16px;color:var(--text);cursor:pointer;overflow:hidden;white-space:nowrap;text-align:left;transition:box-shadow .16s ease}.block[data-v-aaa51626]:hover{box-shadow:0 0 18px var(--lane-glow, rgba(108, 182, 255, .2))}.b-top[data-v-aaa51626]{display:flex;align-items:baseline;gap:10px;min-width:0}.b-status[data-v-aaa51626]{flex:none;font-size:16px}.b-status.success[data-v-aaa51626]{color:var(--green)}.b-status.fail[data-v-aaa51626]{color:var(--red)}.b-status.running[data-v-aaa51626]{color:var(--blue);animation:pulse 1.2s ease-in-out infinite}.b-status.queued[data-v-aaa51626]{color:var(--faint)}.block .b-name[data-v-aaa51626]{font-size:17px;font-weight:700;overflow:hidden;text-overflow:ellipsis}.block .b-dur[data-v-aaa51626]{margin-left:auto;flex:none}.block .b-desc[data-v-aaa51626]{color:var(--dim);font-size:16px;overflow:hidden;text-overflow:ellipsis;min-width:0}.block.running[data-v-aaa51626]{animation:pulse 1.6s ease-in-out infinite}.block.queued[data-v-aaa51626]{background:transparent;border-style:dashed;border-color:var(--faint);color:var(--dim)}.block.selected[data-v-aaa51626]{outline:2px solid var(--blue);outline-offset:2px;box-shadow:0 0 22px var(--lane-glow, rgba(108, 182, 255, .25))}.tool-tick[data-v-aaa51626]{position:absolute;bottom:4px;width:3px;height:9px;background:currentColor;opacity:.55;border-radius:1px}.tool-tick.err[data-v-aaa51626]{background:var(--red);opacity:1}.topbar[data-v-af109a43]{display:flex;align-items:center;justify-content:space-between;padding:15px 28px;background:#0b0f18b8;backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);position:sticky;top:0;z-index:10}.topbar[data-v-af109a43]:after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:linear-gradient(90deg,#c89bff73,#5ad2dd59 40%,#5ad2dd0f)}.crumbs[data-v-af109a43]{display:flex;align-items:center;gap:10px;font-size:17px;min-width:0}.logo[data-v-af109a43]{width:28px;height:28px;flex:none;filter:drop-shadow(0 0 8px rgba(200,155,255,.35))}.brand[data-v-af109a43]{background:linear-gradient(90deg,var(--purple),var(--cyan));-webkit-background-clip:text;background-clip:text;color:transparent;font-weight:700;letter-spacing:.05em;white-space:nowrap}.sep[data-v-af109a43]{color:var(--faint)}.crumbs a[data-v-af109a43]{color:var(--dim)}.crumbs a[data-v-af109a43]:hover,.crumbs .current[data-v-af109a43]{color:var(--text)}.live-hint[data-v-af109a43]{display:inline-flex;align-items:center;gap:8px;color:var(--dim);font-size:16px;white-space:nowrap}.live-dot[data-v-af109a43]{width:9px;height:9px;border-radius:50%;background:var(--green);box-shadow:0 0 10px #4ade80b3;animation:pulse 1.6s ease-in-out infinite}:root{--bg: #06080f;--panel: #0d1119;--panel-2: #131a26;--panel-3: #0a0e16;--border: #232c3d;--border-soft: #222b3d;--text: #f2f5fa;--dim: #aabdd5;--faint: #8b9cb6;--green: #4ade80;--red: #ff6f67;--blue: #6cb6ff;--amber: #e8b64a;--purple: #c89bff;--cyan: #5ad2dd;--violet: #94a3ff;--sans: "Play", "Helvetica Neue", system-ui, sans-serif;--mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Roboto Mono", monospace;--surface: linear-gradient(180deg, #10141f 0%, #0b0f18 100%)}*{box-sizing:border-box}html,body{margin:0;padding:0}body{background:radial-gradient(1100px 700px at 8% -10%,rgba(148,163,255,.09),transparent 62%),radial-gradient(1000px 720px at 102% 108%,rgba(90,210,221,.07),transparent 60%),linear-gradient(180deg,#06080f,#090d17);background-attachment:fixed;color:var(--text);font-family:var(--sans);font-size:16px;line-height:1.5}#app{min-height:100vh}a{color:var(--blue);text-decoration:none}pre{margin:0;padding:12px 14px;background:var(--panel-3);border:1px solid var(--border-soft);border-radius:8px;overflow-x:auto;font-family:var(--mono);font-size:16px;line-height:1.55;color:#cfdded;white-space:pre-wrap;word-break:break-word}.j-key{color:var(--purple)}.j-str{color:var(--green)}.j-num{color:var(--amber)}.j-bool{color:var(--cyan)}.j-null{color:var(--red)}.dim{color:var(--dim)}.faint{color:var(--faint)}.error-bar{margin:12px 24px;padding:10px 14px;border:1px solid rgba(255,111,103,.5);background:#ff6f671a;color:var(--red);border-radius:8px;font-size:16px}.empty-state{padding:56px 24px;text-align:center;color:var(--dim);font-size:16px}.md{font-size:16px;line-height:1.6;color:var(--text)}.md h1{font-size:20px;margin:14px 0 8px}.md h2{font-size:18px;margin:14px 0 8px}.md h3,.md h4{font-size:17px;margin:12px 0 6px}.md h1:first-child,.md h2:first-child,.md h3:first-child{margin-top:0}.md p{margin:8px 0;white-space:pre-wrap}.md code{background:var(--panel-2);border:1px solid var(--border-soft);border-radius:4px;padding:1px 7px;font-size:16px}.md pre.md-code{margin:10px 0;white-space:pre}.md pre.md-code code{background:transparent;border:none;padding:0}.md ul,.md ol{margin:8px 0;padding-left:28px}.md li{margin:3px 0}.md blockquote{margin:10px 0;padding:4px 0 4px 14px;border-left:3px solid var(--border);color:var(--dim);white-space:pre-wrap}.md hr{border:none;border-top:1px solid var(--border);margin:14px 0}@keyframes pulse{0%,to{opacity:1}50%{opacity:.35}}
|