@polpo-ai/dashboard 0.1.3 → 0.1.4
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.
|
@@ -2,17 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
4
|
import { useParams } from "next/navigation";
|
|
5
|
-
import { useMutation,
|
|
6
|
-
import {
|
|
7
|
-
ArrowLeft,
|
|
8
|
-
BookMarked,
|
|
9
|
-
Check,
|
|
10
|
-
Download,
|
|
11
|
-
FilePlus2,
|
|
12
|
-
Link2,
|
|
13
|
-
Loader2,
|
|
14
|
-
Search,
|
|
15
|
-
} from "lucide-react";
|
|
5
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
|
+
import { ArrowLeft, Check, Download, FilePlus2, Link2, Loader2 } from "lucide-react";
|
|
16
7
|
import { usePolpoClient } from "../../lib/polpo-client";
|
|
17
8
|
import {
|
|
18
9
|
SkillsProvider,
|
|
@@ -31,41 +22,17 @@ import {
|
|
|
31
22
|
} from "../../components/ui/dialog";
|
|
32
23
|
|
|
33
24
|
/**
|
|
34
|
-
* SkillsInstallWizard —
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* -
|
|
38
|
-
*
|
|
39
|
-
* - from a URL → client.installSkills(githubUrl)
|
|
40
|
-
* - paste / create → @lumea-labs/skills <SkillCreateForm> over the
|
|
41
|
-
* Polpo-wired adapter (adapter.createSkill)
|
|
25
|
+
* SkillsInstallWizard — guided stepper that actually installs skills, dogfooding
|
|
26
|
+
* the SDK end to end:
|
|
27
|
+
* - From a URL → client.installSkills(githubUrl)
|
|
28
|
+
* - Paste/create → @lumea-labs/skills <SkillCreateForm> over a minimal
|
|
29
|
+
* SkillsAdapter built on the PolpoClient (adapter.createSkill).
|
|
42
30
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
31
|
+
* (Registry browse via Mastra's @mastra/skills-api is a future addition — it's a
|
|
32
|
+
* Node lib with bundled data, so it needs a server action, not a browser fetch.)
|
|
45
33
|
*/
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
type Source = "registry" | "url" | "paste";
|
|
50
|
-
|
|
51
|
-
interface RegistrySkill {
|
|
52
|
-
id?: string;
|
|
53
|
-
name: string;
|
|
54
|
-
description?: string;
|
|
55
|
-
owner?: string;
|
|
56
|
-
repo?: string;
|
|
57
|
-
repository?: string;
|
|
58
|
-
repositoryUrl?: string;
|
|
59
|
-
installs?: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Best-effort GitHub source for a skills.sh registry record. */
|
|
63
|
-
function sourceFor(s: RegistrySkill): string {
|
|
64
|
-
if (s.repositoryUrl) return s.repositoryUrl;
|
|
65
|
-
if (s.repository?.startsWith("http")) return s.repository;
|
|
66
|
-
if (s.owner && s.repo) return `https://github.com/${s.owner}/${s.repo}`;
|
|
67
|
-
return s.repository ?? s.name;
|
|
68
|
-
}
|
|
35
|
+
type Source = "url" | "paste";
|
|
69
36
|
|
|
70
37
|
export function SkillsInstallWizard() {
|
|
71
38
|
return <WizardDialog />;
|
|
@@ -76,10 +43,13 @@ function WizardDialog() {
|
|
|
76
43
|
const client = usePolpoClient(id);
|
|
77
44
|
const qc = useQueryClient();
|
|
78
45
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
46
|
+
const [open, setOpen] = useState(false);
|
|
47
|
+
const [source, setSource] = useState<Source | null>(null);
|
|
48
|
+
const [url, setUrl] = useState("");
|
|
49
|
+
const [done, setDone] = useState<string | null>(null);
|
|
50
|
+
|
|
51
|
+
// Minimal SkillsAdapter on the PolpoClient (same de-dup seam usePolpoClient
|
|
52
|
+
// resolves: OSS direct, cloud session-proxy). Only createSkill is needed.
|
|
83
53
|
const adapter = useMemo<SkillsAdapter>(
|
|
84
54
|
() => ({
|
|
85
55
|
createSkill: async (input: SkillCreateInput): Promise<Skill> => {
|
|
@@ -95,36 +65,6 @@ function WizardDialog() {
|
|
|
95
65
|
[client],
|
|
96
66
|
);
|
|
97
67
|
|
|
98
|
-
const [open, setOpen] = useState(false);
|
|
99
|
-
const [source, setSource] = useState<Source | null>(null);
|
|
100
|
-
const [search, setSearch] = useState("");
|
|
101
|
-
const [url, setUrl] = useState("");
|
|
102
|
-
const [done, setDone] = useState<string | null>(null);
|
|
103
|
-
|
|
104
|
-
const reset = () => {
|
|
105
|
-
setSource(null);
|
|
106
|
-
setSearch("");
|
|
107
|
-
setUrl("");
|
|
108
|
-
setDone(null);
|
|
109
|
-
install.reset();
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const { data: registry = [], isFetching, error: regError } = useQuery({
|
|
113
|
-
queryKey: ["skills-registry", search],
|
|
114
|
-
queryFn: async (): Promise<RegistrySkill[]> => {
|
|
115
|
-
const u = new URL(SKILLS_API);
|
|
116
|
-
u.searchParams.set("sortBy", "installs");
|
|
117
|
-
u.searchParams.set("pageSize", "30");
|
|
118
|
-
if (search.trim()) u.searchParams.set("query", search.trim());
|
|
119
|
-
const res = await fetch(u.toString());
|
|
120
|
-
if (!res.ok) throw new Error(`registry ${res.status}`);
|
|
121
|
-
const json = await res.json();
|
|
122
|
-
return (json.skills ?? json.data ?? json.results ?? json) as RegistrySkill[];
|
|
123
|
-
},
|
|
124
|
-
enabled: open && source === "registry",
|
|
125
|
-
staleTime: 60_000,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
68
|
const install = useMutation({
|
|
129
69
|
mutationFn: (src: string) => client.installSkills(src),
|
|
130
70
|
onSuccess: (_res, src) => {
|
|
@@ -133,6 +73,13 @@ function WizardDialog() {
|
|
|
133
73
|
},
|
|
134
74
|
});
|
|
135
75
|
|
|
76
|
+
const reset = () => {
|
|
77
|
+
setSource(null);
|
|
78
|
+
setUrl("");
|
|
79
|
+
setDone(null);
|
|
80
|
+
install.reset();
|
|
81
|
+
};
|
|
82
|
+
|
|
136
83
|
const afterCreate = () => {
|
|
137
84
|
qc.invalidateQueries({ queryKey: ["skills", id] });
|
|
138
85
|
setDone("created");
|
|
@@ -153,7 +100,6 @@ function WizardDialog() {
|
|
|
153
100
|
</DialogDescription>
|
|
154
101
|
</DialogHeader>
|
|
155
102
|
|
|
156
|
-
{/* ── Result ─────────────────────────────────────────── */}
|
|
157
103
|
{done ? (
|
|
158
104
|
<div className="flex flex-col items-center gap-3 py-8 text-center">
|
|
159
105
|
<div className="grid h-10 w-10 place-items-center rounded-full bg-emerald-500/15 text-emerald-500">
|
|
@@ -171,14 +117,7 @@ function WizardDialog() {
|
|
|
171
117
|
</div>
|
|
172
118
|
</div>
|
|
173
119
|
) : !source ? (
|
|
174
|
-
/* ── Step 1: choose a source ──────────────────────── */
|
|
175
120
|
<div className="grid gap-2 pt-1">
|
|
176
|
-
<SourceCard
|
|
177
|
-
icon={<BookMarked className="h-4 w-4" />}
|
|
178
|
-
title="Browse the registry"
|
|
179
|
-
desc="34k+ skills from skills.sh — search and install."
|
|
180
|
-
onClick={() => setSource("registry")}
|
|
181
|
-
/>
|
|
182
121
|
<SourceCard
|
|
183
122
|
icon={<Link2 className="h-4 w-4" />}
|
|
184
123
|
title="From a URL"
|
|
@@ -194,49 +133,13 @@ function WizardDialog() {
|
|
|
194
133
|
</div>
|
|
195
134
|
) : (
|
|
196
135
|
<div className="flex flex-col gap-3 pt-1">
|
|
197
|
-
<button
|
|
136
|
+
<button
|
|
137
|
+
onClick={() => { setSource(null); install.reset(); }}
|
|
138
|
+
className="inline-flex w-fit items-center gap-1 text-[11px] text-muted-foreground hover:text-foreground"
|
|
139
|
+
>
|
|
198
140
|
<ArrowLeft className="h-3 w-3" /> Back
|
|
199
141
|
</button>
|
|
200
142
|
|
|
201
|
-
{/* ── Registry ──────────────────────────────────── */}
|
|
202
|
-
{source === "registry" && (
|
|
203
|
-
<>
|
|
204
|
-
<div className="flex items-center gap-2 border border-border px-2.5">
|
|
205
|
-
<Search className="h-3.5 w-3.5 text-muted-foreground" />
|
|
206
|
-
<input
|
|
207
|
-
value={search}
|
|
208
|
-
onChange={(e) => setSearch(e.target.value)}
|
|
209
|
-
placeholder="Search skills…"
|
|
210
|
-
className="w-full bg-transparent py-2 text-sm outline-none"
|
|
211
|
-
/>
|
|
212
|
-
</div>
|
|
213
|
-
{isFetching ? (
|
|
214
|
-
<div className="flex items-center gap-2 py-6 text-xs text-muted-foreground"><Loader2 className="h-3.5 w-3.5 animate-spin" /> loading registry…</div>
|
|
215
|
-
) : regError ? (
|
|
216
|
-
<p className="py-6 text-center text-xs text-muted-foreground">Registry unavailable. Try the URL option.</p>
|
|
217
|
-
) : (
|
|
218
|
-
<ul className="flex max-h-[40vh] flex-col gap-1 overflow-y-auto">
|
|
219
|
-
{registry.map((s) => (
|
|
220
|
-
<li key={s.id ?? `${s.owner}/${s.repo}/${s.name}`} className="flex items-center justify-between gap-3 border border-border px-3 py-2">
|
|
221
|
-
<div className="min-w-0">
|
|
222
|
-
<div className="truncate text-sm font-medium">{s.name}</div>
|
|
223
|
-
{s.description && <div className="truncate text-[11px] text-muted-foreground">{s.description}</div>}
|
|
224
|
-
</div>
|
|
225
|
-
<button
|
|
226
|
-
disabled={install.isPending}
|
|
227
|
-
onClick={() => install.mutate(sourceFor(s))}
|
|
228
|
-
className="shrink-0 border border-foreground bg-foreground px-2.5 py-1 text-[11px] text-background hover:bg-foreground/90 disabled:opacity-50"
|
|
229
|
-
>
|
|
230
|
-
{install.isPending ? <Loader2 className="h-3 w-3 animate-spin" /> : "Install"}
|
|
231
|
-
</button>
|
|
232
|
-
</li>
|
|
233
|
-
))}
|
|
234
|
-
</ul>
|
|
235
|
-
)}
|
|
236
|
-
</>
|
|
237
|
-
)}
|
|
238
|
-
|
|
239
|
-
{/* ── URL ────────────────────────────────────────── */}
|
|
240
143
|
{source === "url" && (
|
|
241
144
|
<form
|
|
242
145
|
onSubmit={(e) => { e.preventDefault(); if (url.trim()) install.mutate(url.trim()); }}
|
|
@@ -261,16 +164,11 @@ function WizardDialog() {
|
|
|
261
164
|
</form>
|
|
262
165
|
)}
|
|
263
166
|
|
|
264
|
-
{/* ── Paste / create (dogfood lumea-agents UI) ──── */}
|
|
265
167
|
{source === "paste" && (
|
|
266
168
|
<SkillsProvider adapter={adapter}>
|
|
267
169
|
<SkillCreateForm onCreated={afterCreate} onCancel={() => setSource(null)} />
|
|
268
170
|
</SkillsProvider>
|
|
269
171
|
)}
|
|
270
|
-
|
|
271
|
-
{install.isError && source === "registry" && (
|
|
272
|
-
<p className="text-xs text-destructive">{(install.error as Error).message}</p>
|
|
273
|
-
)}
|
|
274
172
|
</div>
|
|
275
173
|
)}
|
|
276
174
|
</DialogContent>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polpo-ai/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -70,8 +70,8 @@
|
|
|
70
70
|
"tailwind-merge": "^3.5.0",
|
|
71
71
|
"tw-animate-css": "^1.4.0",
|
|
72
72
|
"@polpo-ai/core": "0.7.18",
|
|
73
|
-
"@polpo-ai/
|
|
74
|
-
"@polpo-ai/
|
|
73
|
+
"@polpo-ai/sdk": "0.7.18",
|
|
74
|
+
"@polpo-ai/react": "0.7.18"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@playwright/test": "^1.58.2",
|