rvlib-shipkit 0.6.0 → 0.8.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.
@@ -9,7 +9,11 @@ import { join } from 'path'
9
9
  import { graphHtml } from 'rvlib-shipkit/src/wiki/graph-page.ts'
10
10
  import type { MdGraph } from 'rvlib-shipkit/src/wiki/md-graph.ts'
11
11
  import type { StudioRoutes } from '../assets/server/studio-server.ts'
12
- import { landingPage, statusPage } from './hub-pages.ts'
12
+ import { withRail, type AttentionSection } from './hub-pages.ts'
13
+ // the hub UI is a Bun-native React app (HTML import, no vite, no build) —
14
+ // build/webapp doctrine; legacy pages (graphs, studio SPA) keep the
15
+ // server-injected rail until they migrate
16
+ import hubIndex from './hub-app/index.html'
13
17
 
14
18
  export type HubGraph = { title: string; load: () => MdGraph }
15
19
  export type HubTool = { path: string; label: string; desc: string }
@@ -25,6 +29,9 @@ export type HubDeps = {
25
29
  studio: StudioRoutes | null
26
30
  /** argv shelling the check run streamed by /status/run (test seam) */
27
31
  checkCmd: string[]
32
+ /** the / dashboard's data — everything red, gathered per request
33
+ * (checks · asks · assets · contracts · posts; run-serve builds it) */
34
+ attention: () => Promise<AttentionSection[]>
28
35
  }
29
36
  export type Hub = { server: ReturnType<typeof Bun.serve>; tools: HubTool[]; stop: () => void }
30
37
 
@@ -40,24 +47,33 @@ export function hubTools(d: Pick<HubDeps, 'wikiGraph' | 'agentGraph' | 'studio'>
40
47
 
41
48
  export function serveHub(d: HubDeps): Hub {
42
49
  const tools = hubTools(d)
43
- const html = (s: string): Response => new Response(s, { headers: { 'content-type': 'text/html; charset=utf-8' } })
50
+ // every HTML page carries the rail — one keystroke between surfaces;
51
+ // the red dot self-fetches client-side (no collector on passive navs)
52
+ const shell = (s: string, active: string): Response =>
53
+ new Response(withRail(s, { tools, active }), { headers: { 'content-type': 'text/html; charset=utf-8' } })
44
54
  const server: ReturnType<typeof Bun.serve> = Bun.serve({
45
55
  hostname: '127.0.0.1',
46
56
  port: d.port,
57
+ // / (attention dashboard) and /status are the React app — one HTML
58
+ // bundle, view picked by pathname client-side
59
+ routes: { '/': hubIndex, '/status': hubIndex },
47
60
  async fetch(req): Promise<Response> {
48
61
  const path = new URL(req.url).pathname
49
- if (path === '/') return html(landingPage({ name: d.name, port: server.port ?? d.port, tools }))
50
62
  // identity probe: lets a second launch tell "same repo, another
51
63
  // window" from "a DIFFERENT repo hashed onto my port"
52
64
  if (path === '/hub.json') return Response.json({ hub: 'shipkit', name: d.name, tools })
53
- if (path === '/graph/wiki') return html(graphHtml(d.wikiGraph.load(), { title: d.wikiGraph.title }))
54
- if (path === '/graph/wiki/graph.json') return Response.json(d.wikiGraph.load())
55
- if (d.agentGraph != null && path === '/graph/agent') return html(graphHtml(d.agentGraph.load(), { title: d.agentGraph.title }))
56
- if (d.agentGraph != null && path === '/graph/agent/graph.json') return Response.json(d.agentGraph.load())
57
- if (path === '/status') {
65
+ if (path === '/api/attention.json') {
66
+ const sections = await d.attention().catch((e: Error) => [{ title: 'attention collector broke', tone: 'red' as const, lines: [{ text: e.message }] }])
67
+ return Response.json({ sections })
68
+ }
69
+ if (path === '/api/status.json') {
58
70
  const p = join(d.root, 'STATUS.md')
59
- return html(statusPage({ name: d.name, statusMd: existsSync(p) ? await Bun.file(p).text() : null }))
71
+ return Response.json({ statusMd: existsSync(p) ? await Bun.file(p).text() : null })
60
72
  }
73
+ if (path === '/graph/wiki') return shell(graphHtml(d.wikiGraph.load(), { title: d.wikiGraph.title }), path)
74
+ if (path === '/graph/wiki/graph.json') return Response.json(d.wikiGraph.load())
75
+ if (d.agentGraph != null && path === '/graph/agent') return shell(graphHtml(d.agentGraph.load(), { title: d.agentGraph.title }), path)
76
+ if (d.agentGraph != null && path === '/graph/agent/graph.json') return Response.json(d.agentGraph.load())
61
77
  if (path === '/status/run' && req.method === 'POST') {
62
78
  // stream wire lines as the check emits them; sh merges stderr
63
79
  // so FAIL lines reach the page, not just the terminal
@@ -68,6 +84,11 @@ export function serveHub(d: HubDeps): Hub {
68
84
  // the studio page fetches RELATIVE urls — it must live under
69
85
  // the slash-terminated prefix for them to resolve
70
86
  if (path === '/assets') return new Response(null, { status: 307, headers: { location: '/assets/' } })
87
+ if (path === '/assets/' && req.method === 'GET') {
88
+ // the SPA document gets the rail too; API routes untouched
89
+ const res = await d.studio.handle(req, '/')
90
+ return shell(await res.text(), '/assets/')
91
+ }
71
92
  if (path.startsWith('/assets/')) return d.studio.handle(req, path.slice('/assets'.length))
72
93
  }
73
94
  return new Response('not found', { status: 404 })
package/src/shipkit.ts CHANGED
@@ -6,7 +6,8 @@ export type { RepoConfig, ServeConfig, FloorResolution, FloorSource } from './re
6
6
  export { serveHub, hubTools } from './serve/serve-hub.ts'
7
7
  export type { Hub, HubDeps, HubGraph, HubTool } from './serve/serve-hub.ts'
8
8
  export { runServe, resolveHubDeps, defaultHubPort } from './serve/run-serve.ts'
9
- export { landingPage, statusPage } from './serve/hub-pages.ts'
9
+ export { withRail, attentionRedCount } from './serve/hub-pages.ts'
10
+ export type { AttentionSection, AttentionLine } from './serve/hub-pages.ts'
10
11
  export { discoverChecks } from './core/discover-checks.ts'
11
12
  export type { DiscoveredCheck } from './core/discover-checks.ts'
12
13
  export { runTasks, selectTasks } from './core/task-runner.ts'
@@ -33,8 +34,8 @@ export type { Ask, CreateOpts } from './asks/asks-queue.ts'
33
34
  // assets surface (folded in from the standalone atelier lib; that npm
34
35
  // package is frozen). A repo's .rv-assets/kit.ts imports defineKit and
35
36
  // declares its kinds; `shipkit assets …` derives the rest.
36
- export { defineKit, defineKind, comfy, canvas, manual, mediaOf, MEDIA_EXTS, geometryPanel, gridPanel, customPanel, validateKit, missingPanelScripts, loadKit, findAsset, allAssets, categoriesOf } from './assets/kit-config.ts'
37
- export type { KitConfig, ResolvedKit, Kind, KindMeta, Media, AssetSpec, GeneratorSpec, ComfyGen, CanvasGen, ManualGen, PanelSpec, GeometryPanelSpec, GridPanelSpec, CustomPanelSpec, FoundAsset, ShipContext, ShipFn } from './assets/kit-config.ts'
37
+ export { defineKit, defineKind, comfy, canvas, manual, gemini, mediaOf, MEDIA_EXTS, geometryPanel, gridPanel, customPanel, validateKit, missingPanelScripts, loadKit, findAsset, allAssets, categoriesOf } from './assets/kit-config.ts'
38
+ export type { KitConfig, ResolvedKit, Kind, KindMeta, Media, AssetSpec, GeneratorSpec, ComfyGen, CanvasGen, ManualGen, GeminiGen, PanelSpec, GeometryPanelSpec, GridPanelSpec, CustomPanelSpec, FoundAsset, ShipContext, ShipFn } from './assets/kit-config.ts'
38
39
  export { identityKinds, renderIdentity, renderIdentityFromMark, NO_PICK_FIX, FAVICON_SIZE, OG_W, OG_H, LOGO_SIZE } from './assets/kits/identity-kit.ts'
39
40
  export type { Brand } from './assets/kits/identity-kit.ts'
40
41
  export { serveStudio, studioRoutes } from './assets/server/studio-server.ts'
@@ -57,3 +58,5 @@ export { lintMd } from './wiki/lint-md.ts'
57
58
  export { serveMdGraph } from './wiki/serve-graph.ts'
58
59
  export { graphHtml } from './wiki/graph-page.ts'
59
60
  export type { MdGraph, MdNode, MdEdge } from './wiki/md-graph.ts'
61
+ export { movePage } from './mv/mv-md.ts'
62
+ export type { MvIo, MvOpts } from './mv/mv-md.ts'
@@ -13,7 +13,7 @@ import { TuiStore } from './tui-store.ts'
13
13
 
14
14
  export async function runTui(root: string): Promise<number> {
15
15
  const c = await loadRepoConfig(root)
16
- // playbook (build/mobx): enforceActions warns on stray mutations — the
16
+ // playbook (build/mobx-mechanics): enforceActions warns on stray mutations — the
17
17
  // only signal. Read-warning flags stay off: useInput handler reads are
18
18
  // legitimately untracked and would false-positive.
19
19
  configure({ enforceActions: 'always' })
@@ -1,4 +1,4 @@
1
- // TUI state — MobX per the house playbook (build/state + build/mobx):
1
+ // TUI state — MobX per the house playbook (build/app-state-tree + build/mobx-mechanics):
2
2
  // `makeAutoObservable` WITH exceptions (the 2026-07-21 doctrine — no
3
3
  // decorators), runInAction around every post-await mutation. Panels are
4
4
  // plain data providers (no mobx): they signal mutations through update(),