selftune 0.2.26 → 0.2.28

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>selftune — Dashboard</title>
7
7
  <link rel="icon" type="image/png" href="/favicon.png" />
8
- <script type="module" crossorigin src="/assets/index-BIZDNtRp.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-MMLFlnVn.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-Dw2cE7zH.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-C5oyHiV1.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-ui-B3BPIYy7.js">
@@ -185,6 +185,50 @@ const MIME_TYPES: Record<string, string> = {
185
185
  ".ico": "image/x-icon",
186
186
  };
187
187
 
188
+ async function serveSpaShell(spaDir: string | null): Promise<Response> {
189
+ if (!spaDir) {
190
+ return new Response("Dashboard build not found. Run `bun run build:dashboard` first.", {
191
+ status: 503,
192
+ headers: { "Content-Type": "text/plain; charset=utf-8", ...corsHeaders() },
193
+ });
194
+ }
195
+
196
+ const indexPath = join(spaDir, "index.html");
197
+ const indexFile = Bun.file(indexPath);
198
+ if (!(await indexFile.exists())) {
199
+ return new Response(
200
+ "Dashboard assets are updating. Retry in a moment or run `selftune dashboard --restart`.",
201
+ {
202
+ status: 503,
203
+ headers: {
204
+ "Content-Type": "text/plain; charset=utf-8",
205
+ "Retry-After": "1",
206
+ ...corsHeaders(),
207
+ },
208
+ },
209
+ );
210
+ }
211
+
212
+ try {
213
+ const html = await indexFile.text();
214
+ return new Response(html, {
215
+ headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders() },
216
+ });
217
+ } catch {
218
+ return new Response(
219
+ "Dashboard assets are updating. Retry in a moment or run `selftune dashboard --restart`.",
220
+ {
221
+ status: 503,
222
+ headers: {
223
+ "Content-Type": "text/plain; charset=utf-8",
224
+ "Retry-After": "1",
225
+ ...corsHeaders(),
226
+ },
227
+ },
228
+ );
229
+ }
230
+ }
231
+
188
232
  async function computeStatusFromDb(): Promise<StatusResult> {
189
233
  const db = getDb();
190
234
  const telemetry = querySessionTelemetry(db);
@@ -519,16 +563,7 @@ export async function startDashboardServer(
519
563
 
520
564
  // ---- GET / ---- Serve SPA shell
521
565
  if (url.pathname === "/" && req.method === "GET") {
522
- if (spaDir) {
523
- const html = await Bun.file(join(spaDir, "index.html")).text();
524
- return new Response(html, {
525
- headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders() },
526
- });
527
- }
528
- return new Response("Dashboard build not found. Run `bun run build:dashboard` first.", {
529
- status: 503,
530
- headers: { "Content-Type": "text/plain; charset=utf-8", ...corsHeaders() },
531
- });
566
+ return serveSpaShell(spaDir);
532
567
  }
533
568
 
534
569
  // ---- POST /api/actions/{watch,evolve,rollback,watchlist} ----
@@ -672,10 +707,7 @@ export async function startDashboardServer(
672
707
 
673
708
  // ---- SPA fallback ----
674
709
  if (spaDir && req.method === "GET" && !url.pathname.startsWith("/api/")) {
675
- const html = await Bun.file(join(spaDir, "index.html")).text();
676
- return new Response(html, {
677
- headers: { "Content-Type": "text/html; charset=utf-8", ...corsHeaders() },
678
- });
710
+ return serveSpaShell(spaDir);
679
711
  }
680
712
 
681
713
  return new Response("Not Found", { status: 404, headers: corsHeaders() });
@@ -581,7 +581,13 @@ export function formatAlphaStatus(info: AlphaStatusInfo | null): string {
581
581
  lines.push(` Failed: ${info.stats.failed}`);
582
582
  lines.push(` Sent: ${info.stats.sent}`);
583
583
 
584
- if (info.lastError) {
584
+ const lastErrorIsCurrent =
585
+ info.lastError &&
586
+ (!info.lastSuccess ||
587
+ new Date(info.lastError.updated_at).getTime() >
588
+ new Date(info.lastSuccess.updated_at).getTime());
589
+
590
+ if (lastErrorIsCurrent) {
585
591
  lines.push(` Last error: ${info.lastError.last_error ?? "unknown"}`);
586
592
  }
587
593
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selftune",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Skill-level observability and self-improvement for AI agents — monitors skill routing, detects missed triggers, and evolves descriptions automatically",
5
5
  "keywords": [
6
6
  "agent",
@@ -213,27 +213,27 @@ export const DASHBOARD_ROUTE_MANIFEST: readonly DashboardRouteManifestEntry[] =
213
213
  {
214
214
  id: "registry",
215
215
  label: "Registry",
216
- tooltip: "Team skill registry",
216
+ tooltip: "Cloud skill registry",
217
217
  icon: PackageIcon,
218
218
  feature: "registry",
219
219
  discoverableFeature: "registry",
220
- lockedTitle: "Team Registry lives in Selftune Cloud",
220
+ lockedTitle: "Cloud Registry lives in Selftune Cloud",
221
221
  lockedBody:
222
222
  "Publish versioned skills, watch installations across projects, and roll back bad versions from a single cloud workspace.",
223
223
  lockedHighlights: [
224
224
  "Version timeline with rollback controls",
225
225
  "Installation map across your team",
226
- "Managed publish flow for shared skills",
226
+ "Managed publish flow for Pro and Team creators",
227
227
  ],
228
- lockedPrimaryCtaLabel: "Upgrade to Team",
229
- lockedPrimaryCtaHref: "https://selftune.dev/pricing",
230
- lockedSecondaryCtaLabel: "Read registry docs",
231
- lockedSecondaryCtaHref: "https://docs.selftune.dev/cloud/registry",
228
+ lockedPrimaryCtaLabel: "Read registry docs",
229
+ lockedPrimaryCtaHref: "https://docs.selftune.dev/cloud/registry",
230
+ lockedSecondaryCtaLabel: "View cloud plans",
231
+ lockedSecondaryCtaHref: "https://selftune.dev/pricing",
232
232
  hosts: {
233
233
  cloud: {
234
234
  path: "/registry",
235
235
  title: "Registry",
236
- badge: "Team",
236
+ badge: "Cloud",
237
237
  backHref: "/",
238
238
  backLabel: "Dashboard",
239
239
  activePatterns: [{ mode: "exact", value: "/registry" }],
@@ -86,6 +86,7 @@ describe("OverviewCompositionSurface", () => {
86
86
  expect(html).toContain("After Feed");
87
87
  expect(html.indexOf("Comparison 1")).toBeLessThan(html.indexOf("Before Feed"));
88
88
  expect(html.indexOf("Run Summary 4")).toBeLessThan(html.indexOf("After Feed"));
89
+ expect(html).toContain('<div class="col-span-12"><div>Before Feed</div></div>');
89
90
  });
90
91
 
91
92
  it("omits the comparison block when there are no rows", () => {
@@ -48,7 +48,7 @@ export function OverviewCompositionSurface({
48
48
  <OverviewComparisonSurface {...comparison} renderSkillLink={renderSkillLink} />
49
49
  </div>
50
50
  ) : null}
51
- {sectionsBeforeFeed}
51
+ {sectionsBeforeFeed ? <div className="col-span-12">{sectionsBeforeFeed}</div> : null}
52
52
  </>
53
53
  ) : null;
54
54
 
@@ -28,6 +28,7 @@ export interface SkillReportScaffoldProps {
28
28
  summary?: ReactNode;
29
29
  showOnboardingBanner?: boolean;
30
30
  guideButtonLabel?: string;
31
+ prioritizeChildren?: boolean;
31
32
  nextAction: SkillReportNextAction;
32
33
  trustState: TrustState;
33
34
  coverage?: TrustFields["coverage"];
@@ -52,6 +53,7 @@ export function SkillReportScaffold({
52
53
  summary,
53
54
  showOnboardingBanner = false,
54
55
  guideButtonLabel = "How to read this page",
56
+ prioritizeChildren = false,
55
57
  nextAction,
56
58
  trustState,
57
59
  coverage,
@@ -115,6 +117,8 @@ export function SkillReportScaffold({
115
117
  }
116
118
  />
117
119
 
120
+ {prioritizeChildren && children ? <div className="space-y-4">{children}</div> : null}
121
+
118
122
  <SkillTrustNarrativePanel
119
123
  trustState={trustState}
120
124
  coverage={coverage}
@@ -141,7 +145,7 @@ export function SkillReportScaffold({
141
145
  />
142
146
  </div>
143
147
 
144
- {children ? (
148
+ {children && !prioritizeChildren ? (
145
149
  <div className="space-y-4 border-t border-border/10 pt-4">{children}</div>
146
150
  ) : null}
147
151
  </div>
package/skill/SKILL.md CHANGED
@@ -13,7 +13,7 @@ description: >
13
13
  even if they don't say "selftune" explicitly.
14
14
  metadata:
15
15
  author: selftune-dev
16
- version: 0.2.26
16
+ version: 0.2.28
17
17
  category: developer-tools
18
18
  ---
19
19
 
@@ -55,7 +55,7 @@ Manage versioned skill distribution across your team. Push skill folders to the
55
55
  ## Prerequisites
56
56
 
57
57
  - Must be authenticated (`selftune alpha upload` to set up API key)
58
- - Push and rollback require Team plan and admin role
58
+ - Push and rollback require Pro plan or higher and admin role
59
59
  - Install requires Pro plan or higher
60
60
 
61
61
  ## Output Format