@tetherto/mdk-skill 0.0.0 → 0.12.1
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 +202 -0
- package/README.md +133 -0
- package/dist/skills/mdk/SKILL.md +114 -0
- package/dist/skills/mdk/references/architecture.md +89 -0
- package/dist/skills/mdk/references/glossary.md +27 -0
- package/dist/skills/mdk/references/mdk-contract.schema.json +138 -0
- package/dist/skills/mdk/references/package-index.md +53 -0
- package/dist/skills/mdk/references/protocol.md +95 -0
- package/dist/skills/mdk-create-app/SKILL.md +231 -0
- package/dist/skills/mdk-deployment/SKILL.md +161 -0
- package/dist/skills/mdk-gateway-plugin/SKILL.md +192 -0
- package/dist/skills/mdk-gateway-plugin/references/controller-patterns.md +99 -0
- package/dist/skills/mdk-gateway-plugin/references/plugin-authoring.md +121 -0
- package/dist/skills/mdk-publish-security/SKILL.md +177 -0
- package/dist/skills/mdk-publish-security/references/vuln-classes.md +151 -0
- package/dist/skills/mdk-publish-security/scripts/scan-packages.mjs +544 -0
- package/dist/skills/mdk-site-sizing/SKILL.md +160 -0
- package/dist/skills/mdk-site-sizing/eval/held-out.json +104 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-100devices-2workers-mixed.json +24 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-100devices-2workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-10devices-1workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-15devices-2workers.json +24 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-200devices-4workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-500devices-4workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-50devices-2workers.json +23 -0
- package/dist/skills/mdk-site-sizing/references/envelope.json +284 -0
- package/dist/skills/mdk-site-sizing/references/envelope.md +42 -0
- package/dist/skills/mdk-site-sizing/scripts/compile-envelope.mjs +45 -0
- package/dist/skills/mdk-site-sizing/scripts/eval-held-out.mjs +50 -0
- package/dist/skills/mdk-site-sizing/scripts/lib.mjs +591 -0
- package/dist/skills/mdk-site-sizing/scripts/parse-site.mjs +25 -0
- package/dist/skills/mdk-site-sizing/scripts/size-site.mjs +49 -0
- package/dist/skills/mdk-ui-component/SKILL.md +207 -0
- package/dist/skills/mdk-ui-component/references/page-recipe.md +78 -0
- package/dist/skills/mdk-ui-component/references/ui-registry.json +1 -0
- package/dist/skills/mdk-worker-plugin/SKILL.md +198 -0
- package/dist/skills/mdk-worker-plugin/assets/mdk-contract.template.json +45 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/README.md +93 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/mdk-contract.json +26 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/mock/server.js +93 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/package.json +23 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/smoke.config.js +41 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/client.js +47 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/commands/reboot.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/commands/set-power-mode.js +10 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/hashrate-rt.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/power-mode.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/power.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/temperature.js +5 -0
- package/dist/skills/mdk-worker-plugin/references/contract-authoring.md +135 -0
- package/dist/skills/mdk-worker-plugin/references/device-families.md +96 -0
- package/dist/skills/mdk-worker-plugin/references/local-testing.md +134 -0
- package/dist/skills/mdk-worker-plugin/references/mdk-contract.schema.json +138 -0
- package/dist/skills/mdk-worker-plugin/references/worker-base-api.md +152 -0
- package/dist/skills/mdk-worker-plugin/scripts/validate-contract.mjs +129 -0
- package/dist/skills/mdk-worker-plugin/scripts/worker-smoke.mjs +256 -0
- package/package.json +47 -1
- package/src/assemble.mjs +11 -0
- package/src/index.d.ts +57 -0
- package/src/index.mjs +254 -0
- package/src/install.mjs +57 -0
- package/src/sources.map.json +34 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mdk-ui-component
|
|
3
|
+
description: >
|
|
4
|
+
Build a UI page/panel that renders data from an MDK Worker or Gateway
|
|
5
|
+
plugin. Use when the task mentions "UI / component / widget / dashboard",
|
|
6
|
+
"show telemetry", "chart / tile / heatmap", "render data from a
|
|
7
|
+
worker/plugin", or prompts like "create a UI to show <metric> for <device>".
|
|
8
|
+
metadata:
|
|
9
|
+
suite: mdk-developer-skill
|
|
10
|
+
mdk_version: "0.5.0"
|
|
11
|
+
license: Apache-2.0
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Build an MDK UI page for worker/plugin data
|
|
15
|
+
|
|
16
|
+
MDK UI shells (`apps/dashboard/`, scaffolded by `mdk create dashboard` — or
|
|
17
|
+
`apps/<name>/` if named explicitly) are **composition only**. Data comes from
|
|
18
|
+
a Gateway route; shaping lives in a hook; visuals come from
|
|
19
|
+
`@tetherto/mdk-react-devkit`. Never invent component prop names — look them
|
|
20
|
+
up in [`references/ui-registry.json`](./references/ui-registry.json).
|
|
21
|
+
|
|
22
|
+
A fresh scaffold ships no example page — build one from these layers (see the
|
|
23
|
+
shell's own [`USAGE.md`](../../../../../examples/mdk-ui-shell-template/USAGE.md), "The pattern every page should follow"):
|
|
24
|
+
|
|
25
|
+
| Layer | Owns |
|
|
26
|
+
| ---------------- | ------------------------------------------------------------------------------ |
|
|
27
|
+
| Gateway route | Your Gateway plugin's route — serves the data |
|
|
28
|
+
| Hook | app-local `src/hooks/use-<thing>.ts` — `useQuery` against the route, shapes the payload in `select` |
|
|
29
|
+
| Page | `apps/dashboard/src/pages/<Name>.tsx` — thin glue, no fetch/shaping |
|
|
30
|
+
| Panel | `apps/dashboard/src/components/<Name>Panel.tsx` — props in, markup out |
|
|
31
|
+
| Route | `apps/dashboard/src/routes.ts` — one line |
|
|
32
|
+
|
|
33
|
+
## Prerequisites (do these first)
|
|
34
|
+
|
|
35
|
+
1. **Data source exists.** If no `/api/...` route returns the metric, stop and
|
|
36
|
+
use `mdk-gateway-plugin` to create one. Confirm with `curl` that the JSON shape
|
|
37
|
+
is stable.
|
|
38
|
+
2. **Contract units known.** Read the Worker `mdk-contract.json` so labels/units
|
|
39
|
+
match (`power` → `W`, etc.). Resolve it from `mdk.yaml` → `spec.workers`:
|
|
40
|
+
local `package` → `<package>/mdk-contract.json` (or legacy
|
|
41
|
+
`<package>/plugin/mdk-contract.json`); npm package → the same under
|
|
42
|
+
`node_modules/<package>/`.
|
|
43
|
+
3. **Dashboard app exists.** If missing: `mdk create dashboard` (see
|
|
44
|
+
`mdk-deployment`).
|
|
45
|
+
|
|
46
|
+
## Workflow
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
flowchart TD
|
|
50
|
+
intent["Plain-language intent"]
|
|
51
|
+
route{"/api route returns the metric?"}
|
|
52
|
+
gateway["mdk-gateway-plugin: create the route"]
|
|
53
|
+
registry["Pick components from ui-registry.json"]
|
|
54
|
+
contract["Match labels/units to the worker mdk-contract.json"]
|
|
55
|
+
build["Build the hook, panel, and page"]
|
|
56
|
+
wire["Wire the route in routes.ts"]
|
|
57
|
+
|
|
58
|
+
intent --> route
|
|
59
|
+
route -->|no| gateway --> registry
|
|
60
|
+
route -->|yes| registry
|
|
61
|
+
registry --> contract
|
|
62
|
+
contract --> build --> wire
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 1. Pick the visual from the registry
|
|
66
|
+
|
|
67
|
+
Open [`references/ui-registry.json`](./references/ui-registry.json) (this skill). Use
|
|
68
|
+
`indexes.componentsByName` / `indexes.componentsByCategory`.
|
|
69
|
+
|
|
70
|
+
| Need | Typical component |
|
|
71
|
+
| ----------------------------- | ------------------- |
|
|
72
|
+
| Single numeric metric / total | `SingleStatCard` |
|
|
73
|
+
| Time-series | `LineChartCard` |
|
|
74
|
+
| Fallback text when null | `FALLBACK` constant |
|
|
75
|
+
|
|
76
|
+
Copy **exact** prop names and types from the registry entry. Example
|
|
77
|
+
`SingleStatCard`: `name`, `subtitle`, `value`, `unit`, `variant`, `flash`.
|
|
78
|
+
|
|
79
|
+
### 2. Add the page scaffold
|
|
80
|
+
|
|
81
|
+
Add the route to `src/routes.ts` yourself — append a one-line entry above the
|
|
82
|
+
`// mdk:routes-end` marker:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
// src/routes.ts
|
|
86
|
+
export const ROUTES: AppRoute[] = [
|
|
87
|
+
{ path: "/<page-name>", label: "<Page Name>", page: () => import("./pages/<PageName>") },
|
|
88
|
+
// mdk:routes-end
|
|
89
|
+
]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Keep each route on a **single line** and preserve the end marker; the file is
|
|
93
|
+
parsed line-wise by tooling. Do not add feature pages to `router.tsx`.
|
|
94
|
+
|
|
95
|
+
### 3. Create the data hook (shape the payload)
|
|
96
|
+
|
|
97
|
+
For a **project-local app** (one scaffolded under `apps/<app-name>` by `mdk create app`, referenced by
|
|
98
|
+
its package name in `mdk.yaml` → `spec.apps`), put the hook in the
|
|
99
|
+
dashboard. The Gateway serves the app's routes under `/apps/<app-name>` unless its entry sets
|
|
100
|
+
`mount: /`, so fetch `/apps/<app-name>/api/...`. In dev, the dashboard's Vite proxy must forward
|
|
101
|
+
`/apps` to the Gateway too; add `'/apps': gatewayUrl` next to `/api` in `vite.config.ts` if it is missing:
|
|
102
|
+
|
|
103
|
+
`src/hooks/use-<thing>.ts`
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { useQuery } from '@tetherto/mdk-react-adapter'
|
|
107
|
+
|
|
108
|
+
// types matching the route's JSON schema in mdk-plugin.json
|
|
109
|
+
export const useThing = () => {
|
|
110
|
+
const result = useQuery({
|
|
111
|
+
queryKey: ['thing'],
|
|
112
|
+
queryFn: async () => {
|
|
113
|
+
const res = await fetch('/apps/<app-name>/api/...')
|
|
114
|
+
if (!res.ok) throw new Error(`Failed (HTTP ${res.status})`)
|
|
115
|
+
const raw = await res.json()
|
|
116
|
+
// shape → render-ready; never leave shaping to the panel
|
|
117
|
+
return { /* ... */ }
|
|
118
|
+
},
|
|
119
|
+
refetchInterval: 5_000, // live tiles
|
|
120
|
+
})
|
|
121
|
+
return {
|
|
122
|
+
info: result.data ?? EMPTY,
|
|
123
|
+
isLoading: result.isLoading,
|
|
124
|
+
error: result.error,
|
|
125
|
+
refetch: () => { void result.refetch() },
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Rules:
|
|
131
|
+
|
|
132
|
+
- Import `useQuery` from `@tetherto/mdk-react-adapter`, **not** from
|
|
133
|
+
`@tanstack/react-query` directly.
|
|
134
|
+
- Hook owns fetch + shaping; panel stays presentational.
|
|
135
|
+
- For reusable MDK-wide endpoints, prefer adding the hook to
|
|
136
|
+
`@tetherto/mdk-react-adapter` (see the dashboard
|
|
137
|
+
[`USAGE.md`](../../../../../examples/mdk-ui-shell-template/USAGE.md)'s layered data
|
|
138
|
+
flow). App-local hooks are correct for project-local plugins.
|
|
139
|
+
|
|
140
|
+
### 4. Create the presentational panel
|
|
141
|
+
|
|
142
|
+
`src/components/<Name>Panel.tsx`
|
|
143
|
+
|
|
144
|
+
- Props: already-shaped `info` + `isLoading` + `error`
|
|
145
|
+
- No `fetch`, no stores, no inline transforms beyond trivial display
|
|
146
|
+
(`Math.round`)
|
|
147
|
+
- Handle three states: `error`, empty list, data
|
|
148
|
+
- Render registry components with verified props
|
|
149
|
+
|
|
150
|
+
### 5. Wire a thin page
|
|
151
|
+
|
|
152
|
+
`src/pages/<Name>.tsx`:
|
|
153
|
+
|
|
154
|
+
- Call the hook
|
|
155
|
+
- Wrap in `PageLayout` with title + optional Refresh action
|
|
156
|
+
- Pass hook outputs to the panel
|
|
157
|
+
- Page holds **no** fetch / shaping
|
|
158
|
+
|
|
159
|
+
### 6. Styles
|
|
160
|
+
|
|
161
|
+
Add a BEM block under the shell namespace in `src/App.scss`, e.g.
|
|
162
|
+
`.mdk-ui-shell-<name>`, matching the `className` on `PageLayout`. Reuse CSS
|
|
163
|
+
variables (`--mdk-color-*`) and mirror the existing blocks in that file for
|
|
164
|
+
structure (refresh button, card, error state).
|
|
165
|
+
|
|
166
|
+
## Composition rules (do not break)
|
|
167
|
+
|
|
168
|
+
From the dashboard [`USAGE.md`](../../../../../examples/mdk-ui-shell-template/USAGE.md):
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
Gateway plugin / foundation query
|
|
172
|
+
→ adapter or app hook (fetch + shape)
|
|
173
|
+
→ thin page (hook → panel)
|
|
174
|
+
→ presentational panel + @tetherto/mdk-react-devkit
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Forbidden:
|
|
178
|
+
|
|
179
|
+
| Don't | Do |
|
|
180
|
+
| -------------------------------- | -------------------------------------------------------------- |
|
|
181
|
+
| `fetch` inside a panel/component | Hook |
|
|
182
|
+
| Guess `SingleStatCard` props | [`references/ui-registry.json`](./references/ui-registry.json) |
|
|
183
|
+
| Import Ant Design / MUI | `@tetherto/mdk-react-devkit` |
|
|
184
|
+
| Edit `router.tsx` for feature pages | A one-line entry in `routes.ts` |
|
|
185
|
+
| Hard-code units ignoring the contract | Use `unit` from the API payload |
|
|
186
|
+
|
|
187
|
+
## Worked recipe: "UI to show `<metric>` for `<device family>`"
|
|
188
|
+
|
|
189
|
+
End-to-end chain this kind of prompt implies (router skill expands this):
|
|
190
|
+
|
|
191
|
+
1. `mdk-gateway-plugin` — ensure `GET /api/<domain>/<resource>` exists and
|
|
192
|
+
returns a shaped payload (e.g. `{ unit, total, devices[] }`) grounded in
|
|
193
|
+
the Worker contract's telemetry channel.
|
|
194
|
+
2. This skill — hook + `SingleStatCard` grid (total + per-device) + page +
|
|
195
|
+
route.
|
|
196
|
+
3. `mdk-deployment` — app listed in `mdk.yaml` → `spec.apps`, `mdk run` up (app runtime + Gateway),
|
|
197
|
+
Worker online with a device.
|
|
198
|
+
|
|
199
|
+
Detailed file checklist: [`references/page-recipe.md`](./references/page-recipe.md).
|
|
200
|
+
|
|
201
|
+
## Hand-off
|
|
202
|
+
|
|
203
|
+
| Problem | Skill |
|
|
204
|
+
| -------------------------- | --------------------------- |
|
|
205
|
+
| No API route / wrong shape | `mdk-gateway-plugin` |
|
|
206
|
+
| Stack not running / app not declared | `mdk-deployment` |
|
|
207
|
+
| Device / telemetry channel missing | `mdk-worker-plugin` |
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Page recipe — API-backed metric page
|
|
2
|
+
|
|
3
|
+
Checklist for a new dashboard page that shows live Worker/plugin data.
|
|
4
|
+
|
|
5
|
+
## Files to add or touch
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
<dashboard>/
|
|
9
|
+
├── src/hooks/use-<thing>.ts # fetch + shape (useQuery)
|
|
10
|
+
├── src/components/<Thing>Panel.tsx # presentational
|
|
11
|
+
├── src/pages/<Thing>.tsx # thin PageLayout glue
|
|
12
|
+
├── src/routes.ts # one-line route entry
|
|
13
|
+
└── src/App.scss # .mdk-ui-shell-<thing> block
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Optional: `src/constants/navigation.tsx` only if you need a custom nav icon
|
|
17
|
+
(default explorer icon is fine).
|
|
18
|
+
|
|
19
|
+
## Layer responsibilities
|
|
20
|
+
|
|
21
|
+
| Layer | Owns | Must not |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| Hook | `fetch`, JSON → typed `info`, polling | JSX |
|
|
24
|
+
| Panel | Markup, loading/empty/error UI, registry components | `fetch`, business rules |
|
|
25
|
+
| Page | Hook call, `PageLayout`, Refresh button | Shaping, direct HTTP |
|
|
26
|
+
| Plugin (backend) | Aggregation across Workers | UI concerns |
|
|
27
|
+
|
|
28
|
+
## Minimal page skeleton
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { PageLayout } from '../components/PageLayout'
|
|
32
|
+
import { ThingPanel } from '../components/ThingPanel'
|
|
33
|
+
import { useThing } from '../hooks/use-thing'
|
|
34
|
+
|
|
35
|
+
const Thing = () => {
|
|
36
|
+
const { info, isLoading, error, refetch } = useThing()
|
|
37
|
+
return (
|
|
38
|
+
<PageLayout
|
|
39
|
+
title="Thing"
|
|
40
|
+
className="mdk-ui-shell-thing"
|
|
41
|
+
actions={(
|
|
42
|
+
<button type="button" onClick={refetch} disabled={isLoading}>
|
|
43
|
+
{isLoading ? 'Refreshing…' : 'Refresh'}
|
|
44
|
+
</button>
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
<ThingPanel info={info} isLoading={isLoading} error={error} />
|
|
48
|
+
</PageLayout>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default Thing
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Route entry
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
{ path: '/thing', label: 'Thing', page: () => import('./pages/Thing') },
|
|
59
|
+
// mdk:routes-end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Keep on one line. Path becomes the sidebar href.
|
|
63
|
+
|
|
64
|
+
## Component lookup
|
|
65
|
+
|
|
66
|
+
Before writing JSX:
|
|
67
|
+
|
|
68
|
+
1. Open [`ui-registry.json`](./ui-registry.json).
|
|
69
|
+
2. Find the component by name.
|
|
70
|
+
3. Use only documented `props[]` names/types.
|
|
71
|
+
4. Prefer `tier: "agent-ready"` / `public: true` components.
|
|
72
|
+
|
|
73
|
+
## Composition rules
|
|
74
|
+
|
|
75
|
+
See the shell's own [`USAGE.md`](../../../../../../examples/mdk-ui-shell-template/USAGE.md) ("The pattern every page should follow") for the
|
|
76
|
+
layered data flow this checklist implements: a fresh scaffold ships no example
|
|
77
|
+
page, so there is no file in the project to copy — follow the layer
|
|
78
|
+
responsibilities above instead.
|