@askrjs/cli 0.0.12 → 0.0.14
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 +19 -0
- package/dist/analyze.js +1 -1
- package/dist/cli.js +7 -1
- package/dist/{guardrails-GPM9PJqO.js → guardrails-D6KCKS-H.js} +1 -1
- package/dist/{runner-BRjjPhZY.js → runner-BSs9Ow3t.js} +504 -6
- package/dist/{runner-Ca43qqzG.js → runner-DetGSvGf.js} +1 -1
- package/dist/templates/spa/src/pages/app/admin-home.tsx +136 -133
- package/dist/templates/startkit/src/components/data-table.tsx +33 -33
- package/dist/verify-hydration.d.ts +35 -0
- package/dist/verify-hydration.js +325 -0
- package/package.json +11 -2
|
@@ -27,151 +27,154 @@ export default function AdminHomePage() {
|
|
|
27
27
|
const operations = resource(({ signal }) => loadOperations({ signal }), []);
|
|
28
28
|
const snapshot = operations.value;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
}
|
|
30
|
+
const errorState = (
|
|
31
|
+
<Section>
|
|
32
|
+
<EmptyState
|
|
33
|
+
icon={<AlertCircleIcon size={24} aria-hidden="true" />}
|
|
34
|
+
title="Operations could not load"
|
|
35
|
+
description="The dashboard keeps failures recoverable. Retry the owning resource instead of hiding the error in a toast."
|
|
36
|
+
actions={<Button onPress={() => operations.refresh()}>Retry</Button>}
|
|
37
|
+
/>
|
|
38
|
+
</Section>
|
|
39
|
+
);
|
|
42
40
|
|
|
43
41
|
return (
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
42
|
+
<Show when={!operations.error || snapshot} fallback={errorState}>
|
|
43
|
+
<Stack gap="5">
|
|
44
|
+
<section class="page-heading">
|
|
45
|
+
<Stack gap="2">
|
|
46
|
+
<Badge>projection v{snapshot?.version ?? '...'}</Badge>
|
|
47
|
+
<h1>Workspace home</h1>
|
|
48
|
+
<p class="lead">
|
|
49
|
+
A consistency-aware dashboard for agent runs, queue health, and
|
|
50
|
+
event-sourced read models.
|
|
51
|
+
</p>
|
|
52
|
+
</Stack>
|
|
53
|
+
<Inline gap="2" align="center">
|
|
54
|
+
{operations.pending && snapshot ? <Badge>refreshing</Badge> : null}
|
|
55
|
+
<Button variant="secondary" onPress={() => operations.refresh()}>
|
|
56
|
+
<RefreshCwIcon size={14} aria-hidden="true" /> Refresh
|
|
57
|
+
</Button>
|
|
58
|
+
</Inline>
|
|
59
|
+
</section>
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
{operations.pending && !snapshot ? (
|
|
62
|
+
<Block gap="md">
|
|
63
|
+
<Skeleton style="height: 8rem" />
|
|
64
|
+
<Skeleton style="height: 8rem" />
|
|
65
|
+
<Skeleton style="height: 8rem" />
|
|
66
|
+
</Block>
|
|
67
|
+
) : null}
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
<Show when={snapshot}>
|
|
70
|
+
{(currentSnapshot) => (
|
|
71
|
+
<>
|
|
72
|
+
<Block gap="md" class="metric-grid">
|
|
73
|
+
<For
|
|
74
|
+
each={currentSnapshot.metrics}
|
|
75
|
+
by={(metric) => metric.label}
|
|
76
|
+
>
|
|
77
|
+
{(metric) => (
|
|
78
|
+
<MetricCard
|
|
79
|
+
label={metric.label}
|
|
80
|
+
value={metric.value}
|
|
81
|
+
trend={metric.trend}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
</For>
|
|
85
|
+
</Block>
|
|
86
|
+
|
|
87
|
+
<Block gap="md" align="stretch" class="chart-grid">
|
|
88
|
+
<Card>
|
|
89
|
+
<CardHeader>
|
|
90
|
+
<CardTitle>Run throughput</CardTitle>
|
|
91
|
+
<CardDescription>
|
|
92
|
+
Accepted commands by work type.
|
|
93
|
+
</CardDescription>
|
|
94
|
+
</CardHeader>
|
|
95
|
+
<CardContent>
|
|
96
|
+
<OperationsPlot.Root
|
|
97
|
+
data={currentSnapshot.throughput}
|
|
98
|
+
rowKey="label"
|
|
99
|
+
label="Run throughput"
|
|
100
|
+
description="Accepted commands by work type."
|
|
101
|
+
>
|
|
102
|
+
<OperationsPlot.Bar x="label" y="value" />
|
|
103
|
+
</OperationsPlot.Root>
|
|
104
|
+
</CardContent>
|
|
105
|
+
</Card>
|
|
106
|
+
<Card>
|
|
107
|
+
<CardHeader>
|
|
108
|
+
<CardTitle>Projection lag</CardTitle>
|
|
109
|
+
<CardDescription>
|
|
110
|
+
Lower is better; stale states stay visible.
|
|
111
|
+
</CardDescription>
|
|
112
|
+
</CardHeader>
|
|
113
|
+
<CardContent>
|
|
114
|
+
<OperationsPlot.Root
|
|
115
|
+
data={currentSnapshot.lag}
|
|
116
|
+
rowKey="label"
|
|
117
|
+
label="Projection lag"
|
|
118
|
+
description="Projection lag over the last hour."
|
|
119
|
+
>
|
|
120
|
+
<OperationsPlot.Line x="label" y="value" />
|
|
121
|
+
<OperationsPlot.Point x="label" y="value" />
|
|
122
|
+
</OperationsPlot.Root>
|
|
123
|
+
</CardContent>
|
|
124
|
+
</Card>
|
|
125
|
+
</Block>
|
|
126
|
+
|
|
127
|
+
{currentSnapshot.consistency !== 'fresh' ? (
|
|
128
|
+
<Alert variant="warning">
|
|
129
|
+
Read models are {currentSnapshot.consistency}. Last processed
|
|
130
|
+
event is {currentSnapshot.lastEventId}.
|
|
131
|
+
</Alert>
|
|
132
|
+
) : null}
|
|
84
133
|
|
|
85
|
-
<Block gap="md" align="stretch" class="chart-grid">
|
|
86
|
-
<Card>
|
|
87
|
-
<CardHeader>
|
|
88
|
-
<CardTitle>Run throughput</CardTitle>
|
|
89
|
-
<CardDescription>
|
|
90
|
-
Accepted commands by work type.
|
|
91
|
-
</CardDescription>
|
|
92
|
-
</CardHeader>
|
|
93
|
-
<CardContent>
|
|
94
|
-
<OperationsPlot.Root
|
|
95
|
-
data={currentSnapshot.throughput}
|
|
96
|
-
rowKey="label"
|
|
97
|
-
label="Run throughput"
|
|
98
|
-
description="Accepted commands by work type."
|
|
99
|
-
>
|
|
100
|
-
<OperationsPlot.Bar x="label" y="value" />
|
|
101
|
-
</OperationsPlot.Root>
|
|
102
|
-
</CardContent>
|
|
103
|
-
</Card>
|
|
104
134
|
<Card>
|
|
105
135
|
<CardHeader>
|
|
106
|
-
<CardTitle>
|
|
136
|
+
<CardTitle>Recent agent runs</CardTitle>
|
|
107
137
|
<CardDescription>
|
|
108
|
-
|
|
138
|
+
Run state is modeled as product state, not a single loading
|
|
139
|
+
boolean.
|
|
109
140
|
</CardDescription>
|
|
110
141
|
</CardHeader>
|
|
111
142
|
<CardContent>
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
143
|
+
<div class="run-table-wrap">
|
|
144
|
+
<table class="run-table">
|
|
145
|
+
<thead>
|
|
146
|
+
<tr>
|
|
147
|
+
<th>Run</th>
|
|
148
|
+
<th>Status</th>
|
|
149
|
+
<th>Owner</th>
|
|
150
|
+
<th>Updated</th>
|
|
151
|
+
</tr>
|
|
152
|
+
</thead>
|
|
153
|
+
<tbody>
|
|
154
|
+
<For each={currentSnapshot.runs} by={(run) => run.id}>
|
|
155
|
+
{(run) => (
|
|
156
|
+
<tr>
|
|
157
|
+
<td>
|
|
158
|
+
<strong>{run.title}</strong>
|
|
159
|
+
<span>{run.id}</span>
|
|
160
|
+
</td>
|
|
161
|
+
<td>
|
|
162
|
+
<StatusBadge status={run.status} />
|
|
163
|
+
</td>
|
|
164
|
+
<td>{run.owner}</td>
|
|
165
|
+
<td>{formatRelativeTime(run.updatedAt)}</td>
|
|
166
|
+
</tr>
|
|
167
|
+
)}
|
|
168
|
+
</For>
|
|
169
|
+
</tbody>
|
|
170
|
+
</table>
|
|
171
|
+
</div>
|
|
121
172
|
</CardContent>
|
|
122
173
|
</Card>
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
event is {currentSnapshot.lastEventId}.
|
|
129
|
-
</Alert>
|
|
130
|
-
) : null}
|
|
131
|
-
|
|
132
|
-
<Card>
|
|
133
|
-
<CardHeader>
|
|
134
|
-
<CardTitle>Recent agent runs</CardTitle>
|
|
135
|
-
<CardDescription>
|
|
136
|
-
Run state is modeled as product state, not a single loading
|
|
137
|
-
boolean.
|
|
138
|
-
</CardDescription>
|
|
139
|
-
</CardHeader>
|
|
140
|
-
<CardContent>
|
|
141
|
-
<div class="run-table-wrap">
|
|
142
|
-
<table class="run-table">
|
|
143
|
-
<thead>
|
|
144
|
-
<tr>
|
|
145
|
-
<th>Run</th>
|
|
146
|
-
<th>Status</th>
|
|
147
|
-
<th>Owner</th>
|
|
148
|
-
<th>Updated</th>
|
|
149
|
-
</tr>
|
|
150
|
-
</thead>
|
|
151
|
-
<tbody>
|
|
152
|
-
<For each={currentSnapshot.runs} by={(run) => run.id}>
|
|
153
|
-
{(run) => (
|
|
154
|
-
<tr>
|
|
155
|
-
<td>
|
|
156
|
-
<strong>{run.title}</strong>
|
|
157
|
-
<span>{run.id}</span>
|
|
158
|
-
</td>
|
|
159
|
-
<td>
|
|
160
|
-
<StatusBadge status={run.status} />
|
|
161
|
-
</td>
|
|
162
|
-
<td>{run.owner}</td>
|
|
163
|
-
<td>{formatRelativeTime(run.updatedAt)}</td>
|
|
164
|
-
</tr>
|
|
165
|
-
)}
|
|
166
|
-
</For>
|
|
167
|
-
</tbody>
|
|
168
|
-
</table>
|
|
169
|
-
</div>
|
|
170
|
-
</CardContent>
|
|
171
|
-
</Card>
|
|
172
|
-
</>
|
|
173
|
-
)}
|
|
174
|
-
</Show>
|
|
175
|
-
</Stack>
|
|
174
|
+
</>
|
|
175
|
+
)}
|
|
176
|
+
</Show>
|
|
177
|
+
</Stack>
|
|
178
|
+
</Show>
|
|
176
179
|
);
|
|
177
180
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { For } from '@askrjs/askr/control';
|
|
1
|
+
import { Case, For, Match } from '@askrjs/askr/control';
|
|
2
2
|
import { Skeleton } from '@askrjs/themes/components';
|
|
3
3
|
import EmptyState from './empty-state';
|
|
4
4
|
import { joinClasses } from '../utils/join-classes';
|
|
@@ -22,38 +22,8 @@ export default function DataTable<Row>(props: {
|
|
|
22
22
|
emptyTitle?: string;
|
|
23
23
|
emptyDescription?: string;
|
|
24
24
|
}) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<EmptyState title="Could not load table" description={props.errorText} />
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (props.isLoading) {
|
|
32
|
-
return (
|
|
33
|
-
<div
|
|
34
|
-
class={joinClasses('panel stack-sm', props.class)}
|
|
35
|
-
aria-hidden="true"
|
|
36
|
-
>
|
|
37
|
-
<Skeleton class="skeleton-line" />
|
|
38
|
-
<Skeleton class="skeleton-line" />
|
|
39
|
-
<Skeleton class="skeleton-line" />
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (props.rows().length === 0) {
|
|
45
|
-
return (
|
|
46
|
-
<EmptyState
|
|
47
|
-
title={props.emptyTitle ?? 'No rows found'}
|
|
48
|
-
description={
|
|
49
|
-
props.emptyDescription ??
|
|
50
|
-
'Try changing filters or adding new records.'
|
|
51
|
-
}
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return (
|
|
25
|
+
const rows = props.rows();
|
|
26
|
+
const table = (
|
|
57
27
|
<div class={joinClasses('table-wrap', props.class)}>
|
|
58
28
|
<table class={props.tableClass}>
|
|
59
29
|
<thead>
|
|
@@ -79,4 +49,34 @@ export default function DataTable<Row>(props: {
|
|
|
79
49
|
</table>
|
|
80
50
|
</div>
|
|
81
51
|
);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Case fallback={table}>
|
|
55
|
+
<Match when={props.errorText}>
|
|
56
|
+
<EmptyState
|
|
57
|
+
title="Could not load table"
|
|
58
|
+
description={props.errorText ?? 'The table could not be loaded.'}
|
|
59
|
+
/>
|
|
60
|
+
</Match>
|
|
61
|
+
<Match when={props.isLoading}>
|
|
62
|
+
<div
|
|
63
|
+
class={joinClasses('panel stack-sm', props.class)}
|
|
64
|
+
aria-hidden="true"
|
|
65
|
+
>
|
|
66
|
+
<Skeleton class="skeleton-line" />
|
|
67
|
+
<Skeleton class="skeleton-line" />
|
|
68
|
+
<Skeleton class="skeleton-line" />
|
|
69
|
+
</div>
|
|
70
|
+
</Match>
|
|
71
|
+
<Match when={rows.length === 0}>
|
|
72
|
+
<EmptyState
|
|
73
|
+
title={props.emptyTitle ?? 'No rows found'}
|
|
74
|
+
description={
|
|
75
|
+
props.emptyDescription ??
|
|
76
|
+
'Try changing filters or adding new records.'
|
|
77
|
+
}
|
|
78
|
+
/>
|
|
79
|
+
</Match>
|
|
80
|
+
</Case>
|
|
81
|
+
);
|
|
82
82
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Browser } from "playwright-core";
|
|
2
|
+
//#region src/bin/verify-hydration.d.ts
|
|
3
|
+
type CliIo = Pick<Console, "error" | "log">;
|
|
4
|
+
interface ParsedVerifyHydrationArgs {
|
|
5
|
+
cwd: string;
|
|
6
|
+
outputDir: string;
|
|
7
|
+
routes: string[];
|
|
8
|
+
rootSelector: string;
|
|
9
|
+
buildScript: string;
|
|
10
|
+
build: boolean;
|
|
11
|
+
timeoutMs: number;
|
|
12
|
+
browserChannel?: string;
|
|
13
|
+
help: boolean;
|
|
14
|
+
errors: string[];
|
|
15
|
+
}
|
|
16
|
+
interface RouteMetadata {
|
|
17
|
+
path: string;
|
|
18
|
+
filePath: string;
|
|
19
|
+
status?: string;
|
|
20
|
+
}
|
|
21
|
+
interface StaticOutputServer {
|
|
22
|
+
origin: string;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
interface VerifyHydrationDeps {
|
|
26
|
+
runBuild?: (cwd: string, script: string) => Promise<void>;
|
|
27
|
+
launchBrowser?: (channel?: string) => Promise<Browser>;
|
|
28
|
+
startServer?: (outputDir: string, routes: readonly RouteMetadata[]) => Promise<StaticOutputServer>;
|
|
29
|
+
}
|
|
30
|
+
declare function parseVerifyHydrationArgs(args: string[], defaultCwd?: string): ParsedVerifyHydrationArgs;
|
|
31
|
+
declare function startStaticOutputServer(outputDir: string, routes: readonly RouteMetadata[]): Promise<StaticOutputServer>;
|
|
32
|
+
declare function verifyHydrationRoutes(browser: Browser, origin: string, routes: readonly RouteMetadata[], rootSelector: string, timeoutMs: number): Promise<string[]>;
|
|
33
|
+
declare function runVerifyHydrationCli(args?: string[], deps?: VerifyHydrationDeps, io?: CliIo): Promise<number>;
|
|
34
|
+
//#endregion
|
|
35
|
+
export { parseVerifyHydrationArgs, runVerifyHydrationCli, startStaticOutputServer, verifyHydrationRoutes };
|