rvlib-shipkit 0.5.0 → 0.7.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.
- package/CHANGELOG.md +58 -1
- package/package.json +3 -2
- package/src/assets/gen/derived.ts +83 -0
- package/src/assets/gen/gemini-gen.ts +77 -0
- package/src/assets/gen/generate-roll.ts +14 -1
- package/src/assets/kit-config.ts +47 -3
- package/src/assets/roll-workspace.ts +3 -1
- package/src/assets/run-assets.ts +84 -27
- package/src/assets/server/studio-page.ts +66 -18
- package/src/assets/server/studio-server.ts +73 -11
- package/src/serve/hub-app/hub-app.tsx +156 -0
- package/src/serve/hub-app/hub.css +41 -0
- package/src/serve/hub-app/index.html +13 -0
- package/src/serve/hub-pages.ts +39 -78
- package/src/serve/run-serve.ts +83 -4
- package/src/serve/serve-hub.ts +30 -9
- package/src/shipkit.ts +5 -4
package/src/serve/hub-pages.ts
CHANGED
|
@@ -1,84 +1,45 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Shared hub-page plumbing. The hub UI itself is the Bun-native React app
|
|
2
|
+
// in hub-app/ (HTML import, served by serve-hub.ts — build/webapp
|
|
3
|
+
// doctrine). This module keeps what the LEGACY pages (graph pages, studio
|
|
4
|
+
// SPA) still need until they migrate: the server-injected rail, and the
|
|
5
|
+
// attention section types the collector + React app share.
|
|
5
6
|
import type { HubTool } from './serve-hub.ts'
|
|
6
7
|
|
|
7
|
-
const esc = (s: string): string => s.replace(/&/g, '&').replace(/</g, '<')
|
|
8
|
+
const esc = (s: string): string => s.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"')
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
:
|
|
11
|
-
--accent:#e88c28;--gold:#ffcf5c;--teal:#58c0b0;--red:#e05252}
|
|
12
|
-
*{box-sizing:border-box}
|
|
13
|
-
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.6 system-ui,sans-serif;min-height:100vh}
|
|
14
|
-
.wrap{max-width:860px;margin:0 auto;padding:40px 24px}
|
|
15
|
-
h1{font-size:20px;margin:0}
|
|
16
|
-
h1 small{color:var(--dim);font-weight:400;font-size:13px;margin-left:10px}
|
|
17
|
-
.crumb{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);margin-bottom:6px}
|
|
18
|
-
.crumb a{color:var(--accent);text-decoration:none}.crumb a:hover{color:var(--gold)}
|
|
19
|
-
`
|
|
10
|
+
// ── the rail: thin fixed tab bar, injected into legacy hub-served pages ───
|
|
11
|
+
const RAIL_ICONS: Record<string, string> = { '/': '⚡', '/graph/wiki': '🕸', '/graph/agent': '🤖', '/assets/': '🖼', '/status': '📊' }
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
padding:16px;text-decoration:none;color:var(--ink)}
|
|
31
|
-
.card:hover{border-color:var(--accent);background:var(--panel2)}
|
|
32
|
-
.card b{font-size:15px}.card span{font-size:13px;color:var(--dim)}
|
|
33
|
-
.card code{font:11px ui-monospace,Menlo,monospace;color:var(--teal)}
|
|
34
|
-
</style></head><body><div class=wrap>
|
|
35
|
-
<div class=crumb>shipkit hub</div>
|
|
36
|
-
<h1>${esc(o.name)}<small>http://localhost:${o.port}</small></h1>
|
|
37
|
-
<div class=cards>
|
|
38
|
-
${cards}
|
|
39
|
-
</div>
|
|
40
|
-
</div></body></html>`
|
|
41
|
-
}
|
|
13
|
+
const RAIL_CSS = `
|
|
14
|
+
.skrail{position:fixed;left:0;top:0;bottom:0;width:46px;z-index:9999;display:flex;flex-direction:column;
|
|
15
|
+
align-items:center;padding-top:10px;gap:4px;background:#17140f;border-right:1px solid #332b22}
|
|
16
|
+
.skrail a{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:9px;
|
|
17
|
+
font-size:17px;text-decoration:none;border-left:2px solid transparent;filter:grayscale(35%);opacity:.75;position:relative}
|
|
18
|
+
.skrail a:hover{background:#262019;filter:none;opacity:1}
|
|
19
|
+
.skrail a.on{background:#262019;border-left-color:#e88c28;filter:none;opacity:1}
|
|
20
|
+
.skrail a i{position:absolute;top:4px;right:4px;width:8px;height:8px;border-radius:50%;background:#e05252}
|
|
21
|
+
body{padding-left:46px}`
|
|
42
22
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<div class=bar><button id=run>run check</button><span id=note>shells <code>shipkit check</code>, wire lines stream in below</span></div>
|
|
59
|
-
<pre id=out></pre>
|
|
60
|
-
<script>
|
|
61
|
-
const out=document.getElementById('out'),btn=document.getElementById('run'),note=document.getElementById('note')
|
|
62
|
-
// wire-line coloring: ok/WARN/FAIL/skip prefixes → the house palette
|
|
63
|
-
const esc=s=>s.replace(/&/g,'&').replace(/</g,'<')
|
|
64
|
-
const cls=l=>{const t=l.trimStart()
|
|
65
|
-
if(t.startsWith('FAIL')||t.startsWith('✗'))return 'fail'
|
|
66
|
-
if(t.startsWith('WARN')||t.startsWith('⚠'))return 'warn'
|
|
67
|
-
if(t.startsWith('ok')||t.startsWith('✓'))return 'ok'
|
|
68
|
-
if(t.startsWith('skip')||t.startsWith('fix:')||t.startsWith('#'))return 'dim'
|
|
69
|
-
return ''}
|
|
70
|
-
const paint=text=>{out.innerHTML=text.split('\\n').map(l=>'<span class="'+cls(l)+'">'+esc(l)+'</span>').join('\\n')}
|
|
71
|
-
paint(${JSON.stringify(body).replaceAll('<', '\\u003c')})
|
|
72
|
-
btn.onclick=async()=>{
|
|
73
|
-
btn.disabled=true;note.textContent='running…';let text=''
|
|
74
|
-
try{
|
|
75
|
-
const r=await fetch('status/run',{method:'POST'})
|
|
76
|
-
const rd=r.body.getReader(),dec=new TextDecoder()
|
|
77
|
-
for(;;){const{done,value}=await rd.read();if(done)break;text+=dec.decode(value,{stream:true});paint(text)}
|
|
78
|
-
note.textContent='done — STATUS.md refreshed on disk'
|
|
79
|
-
}catch(e){text+='\\nFAIL stream broke: '+e;paint(text);note.textContent='stream broke'}
|
|
80
|
-
btn.disabled=false
|
|
81
|
-
}
|
|
82
|
-
</script>
|
|
83
|
-
</div></body></html>`
|
|
23
|
+
/** Inject the rail into a legacy hub-served HTML document (post-<body>,
|
|
24
|
+
* fixed position — the page needs no layout knowledge). The React app
|
|
25
|
+
* renders its own identical rail from /hub.json. */
|
|
26
|
+
export function withRail(html: string, o: { tools: HubTool[]; active: string }): string {
|
|
27
|
+
const items = [{ path: '/', label: 'attention', desc: 'everything red' }, ...o.tools.filter((t) => t.path !== '/')]
|
|
28
|
+
.map((t) => {
|
|
29
|
+
const on = o.active === t.path || (t.path !== '/' && o.active.startsWith(t.path))
|
|
30
|
+
const dot = t.path === '/' ? '<i id=skdot style="display:none"></i>' : ''
|
|
31
|
+
return `<a class="${on ? 'on' : ''}" href="${esc(t.path)}" title="${esc(t.label)} — ${esc(t.desc)}">${RAIL_ICONS[t.path] ?? '·'}${dot}</a>`
|
|
32
|
+
})
|
|
33
|
+
.join('')
|
|
34
|
+
// the red dot fetches client-side: a passive page nav must never run the
|
|
35
|
+
// attention collector server-side (cost + heal side-effects)
|
|
36
|
+
const dotScript = `<script>fetch('/api/attention.json').then(r=>r.json()).then(j=>{const n=(j.sections||[]).filter(s=>s.tone==='red').reduce((a,s)=>a+s.lines.length,0);if(n>0)document.getElementById('skdot').style.display=''}).catch(()=>{})</script>`
|
|
37
|
+
return html.replace(/<body([^>]*)>/, `<body$1><style>${RAIL_CSS}</style><nav class=skrail>${items}</nav>${dotScript}`)
|
|
84
38
|
}
|
|
39
|
+
|
|
40
|
+
// ── attention data shapes (collector in run-serve, rendering in hub-app) ──
|
|
41
|
+
export type AttentionLine = { text: string; href?: string }
|
|
42
|
+
export type AttentionSection = { title: string; tone: 'red' | 'warn'; lines: AttentionLine[]; href?: string }
|
|
43
|
+
|
|
44
|
+
export const attentionRedCount = (sections: AttentionSection[]): number =>
|
|
45
|
+
sections.filter((s) => s.tone === 'red').reduce((n, s) => n + s.lines.length, 0)
|
package/src/serve/run-serve.ts
CHANGED
|
@@ -5,14 +5,20 @@
|
|
|
5
5
|
// kit.ts loads. Port: --port > serve.port > a deterministic house-range
|
|
6
6
|
// default (4800-4979, hashed from the repo name — stable across launches;
|
|
7
7
|
// pin serve.port when two repos collide).
|
|
8
|
-
import { existsSync } from 'fs'
|
|
8
|
+
import { existsSync, readdirSync, statSync } from 'fs'
|
|
9
9
|
import { join } from 'path'
|
|
10
10
|
import { fileURLToPath } from 'url'
|
|
11
|
-
import { loadKit } from '../assets/kit-config.ts'
|
|
11
|
+
import { loadKit, allAssets, type ResolvedKit } from '../assets/kit-config.ts'
|
|
12
|
+
import { isStale } from '../assets/gen/derived.ts'
|
|
13
|
+
import { assetDir, listRolls, readPick } from '../assets/roll-workspace.ts'
|
|
14
|
+
import { healIdentity } from '../assets/run-assets.ts'
|
|
12
15
|
import { page as studioPage } from '../assets/server/studio-page.ts'
|
|
13
16
|
import { studioRoutes, type StudioRoutes } from '../assets/server/studio-server.ts'
|
|
14
|
-
import {
|
|
17
|
+
import { openAsks } from '../asks/asks-queue.ts'
|
|
18
|
+
import { evaluateContracts } from '../contracts/contracts-engine.ts'
|
|
19
|
+
import { activeContracts, contractsConfigFrom, loadRepoConfig, type RepoConfig } from '../repo-config.ts'
|
|
15
20
|
import { scanMdGraph } from '../wiki/scan-md.ts'
|
|
21
|
+
import type { AttentionSection } from './hub-pages.ts'
|
|
16
22
|
import { serveHub, type HubDeps } from './serve-hub.ts'
|
|
17
23
|
|
|
18
24
|
export type ServeIo = { print: (l: string) => void; printErr: (l: string) => void }
|
|
@@ -32,12 +38,15 @@ export async function resolveHubDeps(root: string, c: RepoConfig, io: ServeIo, o
|
|
|
32
38
|
? { title: `${c.name} agent/`, load: () => scanMdGraph({ root: agentRoot, index: c.serve?.agentIndex }) }
|
|
33
39
|
: null
|
|
34
40
|
let studio: StudioRoutes | null = null
|
|
41
|
+
let kit: ResolvedKit | null = null
|
|
35
42
|
if (existsSync(join(root, '.rv-assets/kit.ts'))) {
|
|
36
43
|
// a broken kit must not take the graphs and status down with it —
|
|
37
44
|
// WARN + mount everything else
|
|
38
45
|
try {
|
|
39
|
-
|
|
46
|
+
kit = await loadKit(root)
|
|
47
|
+
studio = await studioRoutes({ kit, root, page: studioPage })
|
|
40
48
|
} catch (e) {
|
|
49
|
+
kit = null
|
|
41
50
|
io.print(`WARN assets studio not mounted: ${(e as Error).message}`)
|
|
42
51
|
}
|
|
43
52
|
}
|
|
@@ -50,6 +59,76 @@ export async function resolveHubDeps(root: string, c: RepoConfig, io: ServeIo, o
|
|
|
50
59
|
agentGraph,
|
|
51
60
|
studio,
|
|
52
61
|
checkCmd: [process.execPath, cli, 'check'],
|
|
62
|
+
attention: attentionCollector(root, c, kit),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── the / dashboard's data: everything red, one collector per source, each
|
|
67
|
+
// failure-isolated (a broken source reports itself, never blanks the page)
|
|
68
|
+
export function attentionCollector(root: string, c: RepoConfig, kit: ResolvedKit | null): () => Promise<AttentionSection[]> {
|
|
69
|
+
const guard = async (title: string, tone: 'red' | 'warn', body: () => Promise<AttentionSection['lines']>): Promise<AttentionSection> => {
|
|
70
|
+
try { return { title, tone, lines: await body() } } catch (e) { return { title, tone: 'red', lines: [{ text: `collector broke: ${(e as Error).message}` }] } }
|
|
71
|
+
}
|
|
72
|
+
return async () => {
|
|
73
|
+
const sections = await Promise.all([
|
|
74
|
+
guard('failing checks', 'red', async () => {
|
|
75
|
+
const p = join(root, 'STATUS.md')
|
|
76
|
+
if (!existsSync(p)) return []
|
|
77
|
+
// a cred FAIL whose ask already sits in the asks section is
|
|
78
|
+
// ONE action shown twice — the ask (his hands) wins
|
|
79
|
+
const askDir = join(root, c.asks?.dir ?? 'asks')
|
|
80
|
+
const askText = existsSync(askDir) ? openAsks(askDir).map((a) => a.title.toLowerCase()).join(' ') : ''
|
|
81
|
+
return (await Bun.file(p).text()).split('\n')
|
|
82
|
+
.filter((l) => l.trimStart().startsWith('FAIL'))
|
|
83
|
+
.filter((l) => {
|
|
84
|
+
const cred = l.match(/cred rv\/([a-z0-9-]+)\/[a-z0-9-]+ missing/)?.[1]
|
|
85
|
+
return cred == null || !askText.includes(cred)
|
|
86
|
+
})
|
|
87
|
+
.map((text) => ({ text, href: '/status' }))
|
|
88
|
+
}),
|
|
89
|
+
guard('asks — waiting on you', 'red', async () => {
|
|
90
|
+
const dir = join(root, c.asks?.dir ?? 'asks')
|
|
91
|
+
if (!existsSync(dir)) return [] // read path must not mint an asks/ dir
|
|
92
|
+
return openAsks(dir).map((a) => ({ text: `${a.title}${a.kind !== '' ? ` (${a.kind})` : ''}` }))
|
|
93
|
+
}),
|
|
94
|
+
guard('assets', 'red', async () => {
|
|
95
|
+
// self-heal first: identity re-renders itself on brand.ts
|
|
96
|
+
// drift; only pick-blocked (taste) surfaces below
|
|
97
|
+
const idn = await healIdentity(root)
|
|
98
|
+
const lines: AttentionSection['lines'] = []
|
|
99
|
+
if (idn.state === 'blocked') lines.push({ text: `identity — ${idn.detail ?? 'blocked'}`, href: '/assets/#identity-logo' })
|
|
100
|
+
if (kit == null) return lines
|
|
101
|
+
for (const f of allAssets(kit)) {
|
|
102
|
+
const dir = assetDir(join(root, kit.workspace), f.asset.id)
|
|
103
|
+
const rolls = listRolls(dir)
|
|
104
|
+
const manual = f.kind.generate.backend === 'manual'
|
|
105
|
+
if (manual && rolls.length === 0) lines.push({ text: `${f.asset.id} — manual, nothing uploaded`, href: `/assets/#${f.asset.id}` })
|
|
106
|
+
else if (rolls.length > 0 && (await readPick(dir)).pick == null) lines.push({ text: `${f.asset.id} — ${rolls.length} roll(s), none picked`, href: `/assets/#${f.asset.id}` })
|
|
107
|
+
else if (await isStale(kit, join(root, kit.workspace), f)) lines.push({ text: `${f.asset.id} — stale (a parent was re-picked)`, href: `/assets/#${f.asset.id}` })
|
|
108
|
+
}
|
|
109
|
+
return lines
|
|
110
|
+
}),
|
|
111
|
+
guard('contracts', 'red', async () => {
|
|
112
|
+
const r = await evaluateContracts(activeContracts(c), contractsConfigFrom(c, root))
|
|
113
|
+
return r.lines.filter((l) => l.kind === 'fail').map((l) => ({ text: l.text }))
|
|
114
|
+
}),
|
|
115
|
+
guard('contract warnings', 'warn', async () => {
|
|
116
|
+
const r = await evaluateContracts(activeContracts(c), contractsConfigFrom(c, root))
|
|
117
|
+
return r.lines.filter((l) => l.kind === 'warn').map((l) => ({ text: l.text }))
|
|
118
|
+
}),
|
|
119
|
+
guard('posts to write', 'warn', async () => {
|
|
120
|
+
const social = join(root, '.rv-social')
|
|
121
|
+
if (!existsSync(social)) return []
|
|
122
|
+
const lines: AttentionSection['lines'] = []
|
|
123
|
+
for (const ch of readdirSync(social)) {
|
|
124
|
+
const d = join(social, ch)
|
|
125
|
+
if (!statSync(d).isDirectory()) continue
|
|
126
|
+
for (const f of readdirSync(d).filter((x) => x.endsWith('.md'))) lines.push({ text: `${ch}/${f}` })
|
|
127
|
+
}
|
|
128
|
+
return lines
|
|
129
|
+
}),
|
|
130
|
+
])
|
|
131
|
+
return sections
|
|
53
132
|
}
|
|
54
133
|
}
|
|
55
134
|
|
package/src/serve/serve-hub.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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 === '/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
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 {
|
|
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, geometryPanel, gridPanel, customPanel, validateKit, missingPanelScripts, loadKit, findAsset, allAssets, categoriesOf } from './assets/kit-config.ts'
|
|
37
|
-
export type { KitConfig, ResolvedKit, Kind, KindMeta, AssetSpec, GeneratorSpec, ComfyGen, CanvasGen, 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'
|
|
@@ -50,7 +51,7 @@ export { loadWorkflows, workflowFor } from './assets/gen/workflow-loader.ts'
|
|
|
50
51
|
export type { Workflow, Sidekick, Binding, Graph } from './assets/gen/workflow-loader.ts'
|
|
51
52
|
export { toPng, alphaHeight } from './assets/image-ops.ts'
|
|
52
53
|
export { updateJson, readJson } from './assets/json-store.ts'
|
|
53
|
-
export { assetDir, listRolls, isRollFile, readPick, updatePick, metaPath, pickPath, readPromptOverlay, updatePromptOverlay, promptPath } from './assets/roll-workspace.ts'
|
|
54
|
+
export { assetDir, listRolls, isRollFile, isAudioRoll, readPick, updatePick, metaPath, pickPath, readPromptOverlay, updatePromptOverlay, promptPath } from './assets/roll-workspace.ts'
|
|
54
55
|
export type { PickState, PromptOverlay } from './assets/roll-workspace.ts'
|
|
55
56
|
export { scanMdGraph } from './wiki/scan-md.ts'
|
|
56
57
|
export { lintMd } from './wiki/lint-md.ts'
|