@runcontext/ui 0.5.0 → 0.5.2

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/contextkit/contextkit/packages/ui/dist/index.cjs","../src/server.ts","../src/routes/api/brief.ts","../src/routes/api/sources.ts","../src/routes/api/upload.ts","../src/routes/api/pipeline.ts","../src/routes/api/products.ts"],"names":["Hono","PRODUCT_NAME_RE"],"mappings":"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","file":"/home/runner/work/contextkit/contextkit/packages/ui/dist/index.cjs","sourcesContent":[null,"import { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { cors } from 'hono/cors';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as url from 'node:url';\nimport { briefRoutes } from './routes/api/brief.js';\nimport { sourcesRoutes } from './routes/api/sources.js';\nimport { uploadRoutes } from './routes/api/upload.js';\nimport { pipelineRoutes } from './routes/api/pipeline.js';\nimport { productsRoutes } from './routes/api/products.js';\n\nexport interface UIServerOptions {\n rootDir: string;\n contextDir: string;\n port: number;\n host: string;\n}\n\nconst __dirname = path.dirname(url.fileURLToPath(import.meta.url));\nconst staticDir = path.resolve(__dirname, '..', 'static');\n\nexport function createApp(opts: UIServerOptions): Hono {\n const app = new Hono();\n\n app.use('*', cors());\n\n app.route('', briefRoutes(opts.contextDir));\n app.route('', sourcesRoutes(opts.rootDir, opts.contextDir));\n app.route('', uploadRoutes(opts.contextDir));\n app.route('', pipelineRoutes(opts.rootDir, opts.contextDir));\n app.route('', productsRoutes(opts.contextDir));\n\n app.get('/api/health', (c) => c.json({ ok: true }));\n\n // Static file serving (CSS, JS)\n app.get('/static/:filename', (c) => {\n const filename = c.req.param('filename');\n if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {\n return c.text('Not found', 404);\n }\n const filePath = path.join(staticDir, filename);\n if (!fs.existsSync(filePath)) return c.text('Not found', 404);\n\n const ext = path.extname(filename);\n const contentType =\n ext === '.css' ? 'text/css'\n : ext === '.js' ? 'application/javascript'\n : 'application/octet-stream';\n\n return c.body(fs.readFileSync(filePath), 200, { 'Content-Type': contentType });\n });\n\n app.get('/setup', (c) => {\n return c.html(setupPageHTML());\n });\n\n app.get('/', (c) => c.redirect('/setup'));\n\n return app;\n}\n\nfunction setupPageHTML(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>ContextKit — Build Your Data Product</title>\n <link rel=\"stylesheet\" href=\"/static/setup.css\" />\n</head>\n<body>\n <div class=\"wizard\">\n <header class=\"wizard-header\">\n <h1>ContextKit</h1>\n <p class=\"tagline\">Build your data product. AI handles the rest.</p>\n </header>\n\n <div class=\"progress-bar\">\n <div class=\"progress-step active\" data-step=\"1\"><span class=\"step-num\">1</span><span class=\"step-label\">Product</span></div>\n <div class=\"progress-step\" data-step=\"2\"><span class=\"step-num\">2</span><span class=\"step-label\">Owner</span></div>\n <div class=\"progress-step\" data-step=\"3\"><span class=\"step-num\">3</span><span class=\"step-label\">Context</span></div>\n <div class=\"progress-step\" data-step=\"4\"><span class=\"step-num\">4</span><span class=\"step-label\">Review</span></div>\n <div class=\"progress-step\" data-step=\"5\"><span class=\"step-num\">5</span><span class=\"step-label\">Build</span></div>\n </div>\n\n <!-- Step 1: Product Name + Description -->\n <div class=\"step active\" id=\"step-1\">\n <h2>Name your data product</h2>\n <div class=\"field\">\n <label for=\"product-name\">Product Name</label>\n <input type=\"text\" id=\"product-name\" class=\"input\" placeholder=\"e.g. player-engagement\" />\n <p class=\"hint\">Letters, numbers, dashes, underscores only</p>\n </div>\n <div class=\"field\">\n <label for=\"description\">Description</label>\n <div class=\"textarea-wrapper\">\n <textarea id=\"description\" class=\"textarea\" rows=\"4\" placeholder=\"Describe what this data product covers...\"></textarea>\n <button type=\"button\" id=\"voice-btn\" class=\"btn-icon\" title=\"Voice input\">\\u{1F3A4}</button>\n </div>\n </div>\n <div class=\"step-actions\">\n <div></div>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Next</button>\n </div>\n </div>\n\n <!-- Step 2: Owner -->\n <div class=\"step\" id=\"step-2\">\n <h2>Who owns this data?</h2>\n <div class=\"field\">\n <label for=\"owner-name\">Your Name</label>\n <input type=\"text\" id=\"owner-name\" class=\"input\" placeholder=\"e.g. Tyler\" />\n </div>\n <div class=\"field\">\n <label for=\"owner-team\">Team</label>\n <input type=\"text\" id=\"owner-team\" class=\"input\" placeholder=\"e.g. Analytics\" />\n </div>\n <div class=\"field\">\n <label for=\"owner-email\">Email</label>\n <input type=\"email\" id=\"owner-email\" class=\"input\" placeholder=\"e.g. tyler@company.com\" />\n </div>\n <div class=\"step-actions\">\n <button type=\"button\" class=\"btn btn-secondary\" data-prev>Back</button>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Next</button>\n </div>\n </div>\n\n <!-- Step 3: Sensitivity + Sources + Upload -->\n <div class=\"step\" id=\"step-3\">\n <h2>Context &amp; sensitivity</h2>\n <div class=\"field\">\n <label>Data Sensitivity</label>\n <div class=\"sensitivity-cards\">\n <div class=\"card\" data-sensitivity=\"public\">\n <strong>Public</strong>\n <p>Open data, no restrictions</p>\n </div>\n <div class=\"card selected\" data-sensitivity=\"internal\">\n <strong>Internal</strong>\n <p>Company use only</p>\n </div>\n <div class=\"card\" data-sensitivity=\"confidential\">\n <strong>Confidential</strong>\n <p>Need-to-know basis</p>\n </div>\n <div class=\"card\" data-sensitivity=\"restricted\">\n <strong>Restricted</strong>\n <p>Strict access controls</p>\n </div>\n </div>\n </div>\n\n <div class=\"field\">\n <label>Data Sources</label>\n <div id=\"sources-list\" class=\"source-cards\">\n <p class=\"muted\">Detecting data sources...</p>\n </div>\n </div>\n\n <div class=\"field\">\n <label>Documentation (optional)</label>\n <div id=\"upload-area\" class=\"upload-area\">\n <p>Drop files here or click to upload</p>\n <p class=\"hint\">Supports .md, .txt, .pdf, .csv, .json, .yaml, .sql</p>\n <input type=\"file\" id=\"file-input\" hidden multiple accept=\".md,.txt,.pdf,.csv,.json,.yaml,.yml,.sql,.html\" />\n </div>\n <div id=\"uploaded-files\"></div>\n </div>\n\n <div class=\"step-actions\">\n <button type=\"button\" class=\"btn btn-secondary\" data-prev>Back</button>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Next</button>\n </div>\n </div>\n\n <!-- Step 4: Review -->\n <div class=\"step\" id=\"step-4\">\n <h2>Review your data product</h2>\n <div id=\"review-content\" class=\"review-content\"></div>\n <div class=\"step-actions\">\n <button type=\"button\" class=\"btn btn-secondary\" data-prev>Back</button>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Build it</button>\n </div>\n </div>\n\n <!-- Step 5: Build Pipeline -->\n <div class=\"step\" id=\"step-5\">\n <h2>Building your semantic plane</h2>\n <div id=\"pipeline-timeline\" class=\"pipeline-timeline\"></div>\n <div id=\"pipeline-done\" class=\"pipeline-done\" style=\"display:none\">\n <p>Your semantic plane is live. AI agents can now query your data with context.</p>\n <p class=\"muted\">Powered by ContextKit \\u00B7 Open Semantic Interchange</p>\n </div>\n </div>\n\n <footer class=\"wizard-footer\">\n <p>Powered by ContextKit \\u00B7 Open Semantic Interchange</p>\n </footer>\n </div>\n <script src=\"/static/setup.js\"></script>\n</body>\n</html>`;\n}\n\nexport function startUIServer(opts: UIServerOptions): Promise<void> {\n return new Promise((resolve, reject) => {\n const app = createApp(opts);\n\n const server = serve({\n fetch: app.fetch,\n port: opts.port,\n hostname: opts.host,\n }, (info) => {\n console.log(`ContextKit UI running at http://${opts.host === '0.0.0.0' ? 'localhost' : opts.host}:${info.port}/setup`);\n resolve();\n });\n\n server.on('error', reject);\n });\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { stringify, parse } from 'yaml';\nimport { validateBrief, ContextBriefSchema, PRODUCT_NAME_RE } from '@runcontext/core';\n\nexport function briefRoutes(contextDir: string): Hono {\n const app = new Hono();\n\n app.post('/api/brief', async (c) => {\n const body = await c.req.json();\n const validation = validateBrief(body);\n if (!validation.ok) {\n return c.json({ error: 'Invalid brief', details: validation.errors }, 400);\n }\n const brief = ContextBriefSchema.parse(body);\n brief.created_at = brief.created_at || new Date().toISOString();\n const productDir = path.join(contextDir, 'products', brief.product_name);\n fs.mkdirSync(productDir, { recursive: true });\n fs.writeFileSync(path.join(productDir, 'context-brief.yaml'), stringify(brief), 'utf-8');\n return c.json({ ok: true, path: `products/${brief.product_name}/context-brief.yaml` });\n });\n\n app.get('/api/brief/:name', async (c) => {\n const name = c.req.param('name');\n if (!PRODUCT_NAME_RE.test(name)) {\n return c.json({ error: 'Invalid product name' }, 400);\n }\n const briefPath = path.join(contextDir, 'products', name, 'context-brief.yaml');\n if (!fs.existsSync(briefPath)) return c.json({ error: 'Not found' }, 404);\n return c.json(parse(fs.readFileSync(briefPath, 'utf-8')));\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface DetectedSource {\n name: string;\n adapter: string;\n origin: string;\n status: 'detected' | 'connected' | 'error';\n}\n\nexport function sourcesRoutes(rootDir: string, contextDir: string): Hono {\n const app = new Hono();\n\n app.get('/api/sources', (c) => {\n const sources: DetectedSource[] = [];\n\n // Check environment variables for common databases\n const envChecks: Array<{ env: string; adapter: string; name: string }> = [\n { env: 'DATABASE_URL', adapter: 'auto', name: 'Database (DATABASE_URL)' },\n { env: 'POSTGRES_URL', adapter: 'postgres', name: 'PostgreSQL' },\n { env: 'PG_CONNECTION_STRING', adapter: 'postgres', name: 'PostgreSQL' },\n { env: 'SNOWFLAKE_ACCOUNT', adapter: 'snowflake', name: 'Snowflake' },\n { env: 'BIGQUERY_PROJECT', adapter: 'bigquery', name: 'BigQuery' },\n { env: 'CLICKHOUSE_URL', adapter: 'clickhouse', name: 'ClickHouse' },\n { env: 'DATABRICKS_HOST', adapter: 'databricks', name: 'Databricks' },\n ];\n\n for (const check of envChecks) {\n if (process.env[check.env]) {\n sources.push({\n name: check.name,\n adapter: check.adapter,\n origin: `env:${check.env}`,\n status: 'detected',\n });\n }\n }\n\n // Check for local DuckDB files\n const duckdbFiles = ['*.duckdb', '*.db', '*.ddb'];\n for (const pattern of duckdbFiles) {\n const ext = pattern.replace('*', '');\n try {\n const files = fs.readdirSync(rootDir).filter((f) => f.endsWith(ext));\n for (const file of files) {\n sources.push({\n name: `DuckDB: ${file}`,\n adapter: 'duckdb',\n origin: `file:${file}`,\n status: 'detected',\n });\n }\n } catch {\n // ignore read errors\n }\n }\n\n // Check for SQLite files\n try {\n const sqliteFiles = fs.readdirSync(rootDir).filter((f) => f.endsWith('.sqlite') || f.endsWith('.sqlite3'));\n for (const file of sqliteFiles) {\n sources.push({\n name: `SQLite: ${file}`,\n adapter: 'sqlite',\n origin: `file:${file}`,\n status: 'detected',\n });\n }\n } catch {\n // ignore\n }\n\n return c.json(sources);\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { PRODUCT_NAME_RE } from '@runcontext/core';\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nconst ALLOWED_EXTENSIONS = ['.md', '.txt', '.pdf', '.csv', '.json', '.yaml', '.yml', '.sql', '.html'];\n\nexport function uploadRoutes(contextDir: string): Hono {\n const app = new Hono();\n\n app.post('/api/upload/:productName', async (c) => {\n const productName = c.req.param('productName');\n if (!PRODUCT_NAME_RE.test(productName)) {\n return c.json({ error: 'Invalid product name' }, 400);\n }\n\n const formData = await c.req.formData();\n const file = formData.get('file');\n if (!file || !(file instanceof File)) {\n return c.json({ error: 'No file provided' }, 400);\n }\n\n if (file.size > MAX_FILE_SIZE) {\n return c.json({ error: 'File too large (max 10MB)' }, 400);\n }\n\n const ext = path.extname(file.name).toLowerCase();\n if (!ALLOWED_EXTENSIONS.includes(ext)) {\n return c.json({ error: `File type ${ext} not allowed` }, 400);\n }\n\n // Sanitize filename\n const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');\n const docsDir = path.join(contextDir, 'products', productName, 'docs');\n fs.mkdirSync(docsDir, { recursive: true });\n\n const buffer = Buffer.from(await file.arrayBuffer());\n fs.writeFileSync(path.join(docsDir, safeName), buffer);\n\n return c.json({ ok: true, filename: safeName });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { randomUUID } from 'node:crypto';\n\nexport type PipelineStage =\n | 'introspect'\n | 'scaffold'\n | 'enrich-silver'\n | 'enrich-gold'\n | 'verify'\n | 'autofix'\n | 'agent-instructions';\n\nexport type StageStatus = 'pending' | 'running' | 'done' | 'error' | 'skipped';\n\nexport interface PipelineStageState {\n stage: PipelineStage;\n status: StageStatus;\n summary?: string;\n error?: string;\n startedAt?: string;\n completedAt?: string;\n}\n\nexport interface PipelineRun {\n id: string;\n productName: string;\n targetTier: 'bronze' | 'silver' | 'gold';\n status: 'running' | 'done' | 'error';\n stages: PipelineStageState[];\n createdAt: string;\n}\n\nconst ALL_STAGES: PipelineStage[] = [\n 'introspect',\n 'scaffold',\n 'enrich-silver',\n 'enrich-gold',\n 'verify',\n 'autofix',\n 'agent-instructions',\n];\n\n// In-memory store for pipeline runs\nconst runs = new Map<string, PipelineRun>();\n\nfunction stagesForTier(tier: 'bronze' | 'silver' | 'gold'): PipelineStage[] {\n const base: PipelineStage[] = ['introspect', 'scaffold'];\n if (tier === 'silver' || tier === 'gold') base.push('enrich-silver');\n if (tier === 'gold') base.push('enrich-gold');\n base.push('verify', 'autofix', 'agent-instructions');\n return base;\n}\n\nexport function pipelineRoutes(rootDir: string, contextDir: string): Hono {\n const app = new Hono();\n\n app.post('/api/pipeline/start', async (c) => {\n const body = await c.req.json();\n const { productName, targetTier, dataSource } = body;\n\n if (!productName || !targetTier) {\n return c.json({ error: 'productName and targetTier required' }, 400);\n }\n\n if (!['bronze', 'silver', 'gold'].includes(targetTier)) {\n return c.json({ error: 'targetTier must be bronze, silver, or gold' }, 400);\n }\n\n const id = randomUUID();\n const activeStages = stagesForTier(targetTier);\n const skippedStages = ALL_STAGES.filter((s) => !activeStages.includes(s));\n\n const run: PipelineRun = {\n id,\n productName,\n targetTier,\n status: 'running',\n stages: ALL_STAGES.map((stage) => ({\n stage,\n status: skippedStages.includes(stage) ? 'skipped' : 'pending',\n })),\n createdAt: new Date().toISOString(),\n };\n\n runs.set(id, run);\n\n // Start the pipeline asynchronously (non-blocking)\n executePipeline(run, rootDir, contextDir, dataSource).catch((err) => {\n run.status = 'error';\n const currentStage = run.stages.find((s) => s.status === 'running');\n if (currentStage) {\n currentStage.status = 'error';\n currentStage.error = err instanceof Error ? err.message : String(err);\n }\n });\n\n return c.json({ id, status: 'running' });\n });\n\n app.get('/api/pipeline/status/:id', (c) => {\n const run = runs.get(c.req.param('id'));\n if (!run) return c.json({ error: 'Not found' }, 404);\n return c.json(run);\n });\n\n return app;\n}\n\nasync function executePipeline(\n run: PipelineRun,\n _rootDir: string,\n _contextDir: string,\n _dataSource?: string,\n): Promise<void> {\n for (const stage of run.stages) {\n if (stage.status === 'skipped') continue;\n\n stage.status = 'running';\n stage.startedAt = new Date().toISOString();\n\n try {\n // TODO: Wire real setup step functions here\n // For now, each stage is a placeholder that completes immediately\n // Real implementation will call the corresponding setup step\n // e.g., for 'introspect': connect to data source and discover schema\n // e.g., for 'scaffold': generate .osi.yaml files from schema\n stage.status = 'done';\n stage.summary = `${stage.stage} completed`;\n stage.completedAt = new Date().toISOString();\n } catch (err) {\n stage.status = 'error';\n stage.error = err instanceof Error ? err.message : String(err);\n stage.completedAt = new Date().toISOString();\n run.status = 'error';\n return;\n }\n }\n\n run.status = 'done';\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parse } from 'yaml';\n\nexport interface ExistingProduct {\n name: string;\n description?: string;\n sensitivity?: string;\n hasBrief: boolean;\n}\n\nexport function productsRoutes(contextDir: string): Hono {\n const app = new Hono();\n\n app.get('/api/products', (c) => {\n const productsDir = path.join(contextDir, 'products');\n if (!fs.existsSync(productsDir)) {\n return c.json([]);\n }\n\n const products: ExistingProduct[] = [];\n const dirs = fs.readdirSync(productsDir).filter((name) => {\n const fullPath = path.join(productsDir, name);\n return fs.statSync(fullPath).isDirectory() && !name.startsWith('.');\n });\n\n for (const name of dirs) {\n const briefPath = path.join(productsDir, name, 'context-brief.yaml');\n let description: string | undefined;\n let sensitivity: string | undefined;\n let hasBrief = false;\n\n if (fs.existsSync(briefPath)) {\n hasBrief = true;\n try {\n const brief = parse(fs.readFileSync(briefPath, 'utf-8'));\n description = brief?.description;\n sensitivity = brief?.sensitivity;\n } catch {\n // ignore parse errors\n }\n }\n\n products.push({ name, description, sensitivity, hasBrief });\n }\n\n return c.json(products);\n });\n\n return app;\n}\n"]}
1
+ {"version":3,"sources":["/Users/erickittelson/Desktop/ContextKit/packages/ui/dist/index.cjs","../src/server.ts","../src/routes/api/brief.ts","../src/routes/api/sources.ts","../src/routes/api/upload.ts","../src/routes/api/pipeline.ts","../src/routes/api/products.ts"],"names":["Hono","PRODUCT_NAME_RE"],"mappings":"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","file":"/Users/erickittelson/Desktop/ContextKit/packages/ui/dist/index.cjs","sourcesContent":[null,"import { Hono } from 'hono';\nimport { serve } from '@hono/node-server';\nimport { cors } from 'hono/cors';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as url from 'node:url';\nimport { briefRoutes } from './routes/api/brief.js';\nimport { sourcesRoutes } from './routes/api/sources.js';\nimport { uploadRoutes } from './routes/api/upload.js';\nimport { pipelineRoutes } from './routes/api/pipeline.js';\nimport { productsRoutes } from './routes/api/products.js';\n\nexport interface UIServerOptions {\n rootDir: string;\n contextDir: string;\n port: number;\n host: string;\n}\n\nconst __dirname = path.dirname(url.fileURLToPath(import.meta.url));\nconst staticDir = path.resolve(__dirname, '..', 'static');\n\nexport function createApp(opts: UIServerOptions): Hono {\n const app = new Hono();\n\n app.use('*', cors());\n\n app.route('', briefRoutes(opts.contextDir));\n app.route('', sourcesRoutes(opts.rootDir, opts.contextDir));\n app.route('', uploadRoutes(opts.contextDir));\n app.route('', pipelineRoutes(opts.rootDir, opts.contextDir));\n app.route('', productsRoutes(opts.contextDir));\n\n app.get('/api/health', (c) => c.json({ ok: true }));\n\n // Static file serving (CSS, JS)\n app.get('/static/:filename', (c) => {\n const filename = c.req.param('filename');\n if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {\n return c.text('Not found', 404);\n }\n const filePath = path.join(staticDir, filename);\n if (!fs.existsSync(filePath)) return c.text('Not found', 404);\n\n const ext = path.extname(filename);\n const contentType =\n ext === '.css' ? 'text/css'\n : ext === '.js' ? 'application/javascript'\n : 'application/octet-stream';\n\n return c.body(fs.readFileSync(filePath), 200, { 'Content-Type': contentType });\n });\n\n app.get('/setup', (c) => {\n return c.html(setupPageHTML());\n });\n\n app.get('/', (c) => c.redirect('/setup'));\n\n return app;\n}\n\nfunction setupPageHTML(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>ContextKit — Build Your Data Product</title>\n <link rel=\"stylesheet\" href=\"/static/setup.css\" />\n</head>\n<body>\n <div class=\"wizard\">\n <header class=\"wizard-header\">\n <h1>ContextKit</h1>\n <p class=\"tagline\">Build your data product. AI handles the rest.</p>\n </header>\n\n <div class=\"progress-bar\">\n <div class=\"progress-step active\" data-step=\"1\"><span class=\"step-num\">1</span><span class=\"step-label\">Product</span></div>\n <div class=\"progress-step\" data-step=\"2\"><span class=\"step-num\">2</span><span class=\"step-label\">Owner</span></div>\n <div class=\"progress-step\" data-step=\"3\"><span class=\"step-num\">3</span><span class=\"step-label\">Context</span></div>\n <div class=\"progress-step\" data-step=\"4\"><span class=\"step-num\">4</span><span class=\"step-label\">Review</span></div>\n <div class=\"progress-step\" data-step=\"5\"><span class=\"step-num\">5</span><span class=\"step-label\">Build</span></div>\n </div>\n\n <!-- Step 1: Product Name + Description -->\n <div class=\"step active\" id=\"step-1\">\n <h2>Name your data product</h2>\n <div class=\"field\">\n <label for=\"product-name\">Product Name</label>\n <input type=\"text\" id=\"product-name\" class=\"input\" placeholder=\"e.g. player-engagement\" />\n <p class=\"hint\">Letters, numbers, dashes, underscores only</p>\n </div>\n <div class=\"field\">\n <label for=\"description\">Description</label>\n <div class=\"textarea-wrapper\">\n <textarea id=\"description\" class=\"textarea\" rows=\"4\" placeholder=\"Describe what this data product covers...\"></textarea>\n <button type=\"button\" id=\"voice-btn\" class=\"btn-icon\" title=\"Voice input\">\\u{1F3A4}</button>\n </div>\n </div>\n <div class=\"step-actions\">\n <div></div>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Next</button>\n </div>\n </div>\n\n <!-- Step 2: Owner -->\n <div class=\"step\" id=\"step-2\">\n <h2>Who owns this data?</h2>\n <div class=\"field\">\n <label for=\"owner-name\">Your Name</label>\n <input type=\"text\" id=\"owner-name\" class=\"input\" placeholder=\"e.g. Tyler\" />\n </div>\n <div class=\"field\">\n <label for=\"owner-team\">Team</label>\n <input type=\"text\" id=\"owner-team\" class=\"input\" placeholder=\"e.g. Analytics\" />\n </div>\n <div class=\"field\">\n <label for=\"owner-email\">Email</label>\n <input type=\"email\" id=\"owner-email\" class=\"input\" placeholder=\"e.g. tyler@company.com\" />\n </div>\n <div class=\"step-actions\">\n <button type=\"button\" class=\"btn btn-secondary\" data-prev>Back</button>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Next</button>\n </div>\n </div>\n\n <!-- Step 3: Sensitivity + Sources + Upload -->\n <div class=\"step\" id=\"step-3\">\n <h2>Context &amp; sensitivity</h2>\n <div class=\"field\">\n <label>Data Sensitivity</label>\n <div class=\"sensitivity-cards\">\n <div class=\"card\" data-sensitivity=\"public\">\n <strong>Public</strong>\n <p>Open data, no restrictions</p>\n </div>\n <div class=\"card selected\" data-sensitivity=\"internal\">\n <strong>Internal</strong>\n <p>Company use only</p>\n </div>\n <div class=\"card\" data-sensitivity=\"confidential\">\n <strong>Confidential</strong>\n <p>Need-to-know basis</p>\n </div>\n <div class=\"card\" data-sensitivity=\"restricted\">\n <strong>Restricted</strong>\n <p>Strict access controls</p>\n </div>\n </div>\n </div>\n\n <div class=\"field\">\n <label>Data Sources</label>\n <div id=\"sources-list\" class=\"source-cards\">\n <p class=\"muted\">Detecting data sources...</p>\n </div>\n </div>\n\n <div class=\"field\">\n <label>Documentation (optional)</label>\n <div id=\"upload-area\" class=\"upload-area\">\n <p>Drop files here or click to upload</p>\n <p class=\"hint\">Supports .md, .txt, .pdf, .csv, .json, .yaml, .sql</p>\n <input type=\"file\" id=\"file-input\" hidden multiple accept=\".md,.txt,.pdf,.csv,.json,.yaml,.yml,.sql,.html\" />\n </div>\n <div id=\"uploaded-files\"></div>\n </div>\n\n <div class=\"step-actions\">\n <button type=\"button\" class=\"btn btn-secondary\" data-prev>Back</button>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Next</button>\n </div>\n </div>\n\n <!-- Step 4: Review -->\n <div class=\"step\" id=\"step-4\">\n <h2>Review your data product</h2>\n <div id=\"review-content\" class=\"review-content\"></div>\n <div class=\"step-actions\">\n <button type=\"button\" class=\"btn btn-secondary\" data-prev>Back</button>\n <button type=\"button\" class=\"btn btn-primary\" data-next>Build it</button>\n </div>\n </div>\n\n <!-- Step 5: Build Pipeline -->\n <div class=\"step\" id=\"step-5\">\n <h2>Building your semantic plane</h2>\n <div id=\"pipeline-timeline\" class=\"pipeline-timeline\"></div>\n <div id=\"pipeline-done\" class=\"pipeline-done\" style=\"display:none\">\n <p>Your semantic plane is live. AI agents can now query your data with context.</p>\n <p class=\"muted\">Powered by ContextKit \\u00B7 Open Semantic Interchange</p>\n </div>\n </div>\n\n <footer class=\"wizard-footer\">\n <p>Powered by ContextKit \\u00B7 Open Semantic Interchange</p>\n </footer>\n </div>\n <script src=\"/static/setup.js\"></script>\n</body>\n</html>`;\n}\n\nexport function startUIServer(opts: UIServerOptions): Promise<void> {\n return new Promise((resolve, reject) => {\n const app = createApp(opts);\n\n const server = serve({\n fetch: app.fetch,\n port: opts.port,\n hostname: opts.host,\n }, (info) => {\n console.log(`ContextKit UI running at http://${opts.host === '0.0.0.0' ? 'localhost' : opts.host}:${info.port}/setup`);\n resolve();\n });\n\n server.on('error', reject);\n });\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { stringify, parse } from 'yaml';\nimport { validateBrief, ContextBriefSchema, PRODUCT_NAME_RE } from '@runcontext/core';\n\nexport function briefRoutes(contextDir: string): Hono {\n const app = new Hono();\n\n app.post('/api/brief', async (c) => {\n const body = await c.req.json();\n const validation = validateBrief(body);\n if (!validation.ok) {\n return c.json({ error: 'Invalid brief', details: validation.errors }, 400);\n }\n const brief = ContextBriefSchema.parse(body);\n brief.created_at = brief.created_at || new Date().toISOString();\n const productDir = path.join(contextDir, 'products', brief.product_name);\n fs.mkdirSync(productDir, { recursive: true });\n fs.writeFileSync(path.join(productDir, 'context-brief.yaml'), stringify(brief), 'utf-8');\n return c.json({ ok: true, path: `products/${brief.product_name}/context-brief.yaml` });\n });\n\n app.get('/api/brief/:name', async (c) => {\n const name = c.req.param('name');\n if (!PRODUCT_NAME_RE.test(name)) {\n return c.json({ error: 'Invalid product name' }, 400);\n }\n const briefPath = path.join(contextDir, 'products', name, 'context-brief.yaml');\n if (!fs.existsSync(briefPath)) return c.json({ error: 'Not found' }, 404);\n return c.json(parse(fs.readFileSync(briefPath, 'utf-8')));\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface DetectedSource {\n name: string;\n adapter: string;\n origin: string;\n status: 'detected' | 'connected' | 'error';\n}\n\nexport function sourcesRoutes(rootDir: string, contextDir: string): Hono {\n const app = new Hono();\n\n app.get('/api/sources', (c) => {\n const sources: DetectedSource[] = [];\n\n // Check environment variables for common databases\n const envChecks: Array<{ env: string; adapter: string; name: string }> = [\n { env: 'DATABASE_URL', adapter: 'auto', name: 'Database (DATABASE_URL)' },\n { env: 'POSTGRES_URL', adapter: 'postgres', name: 'PostgreSQL' },\n { env: 'PG_CONNECTION_STRING', adapter: 'postgres', name: 'PostgreSQL' },\n { env: 'SNOWFLAKE_ACCOUNT', adapter: 'snowflake', name: 'Snowflake' },\n { env: 'BIGQUERY_PROJECT', adapter: 'bigquery', name: 'BigQuery' },\n { env: 'CLICKHOUSE_URL', adapter: 'clickhouse', name: 'ClickHouse' },\n { env: 'DATABRICKS_HOST', adapter: 'databricks', name: 'Databricks' },\n ];\n\n for (const check of envChecks) {\n if (process.env[check.env]) {\n sources.push({\n name: check.name,\n adapter: check.adapter,\n origin: `env:${check.env}`,\n status: 'detected',\n });\n }\n }\n\n // Check for local DuckDB files\n const duckdbFiles = ['*.duckdb', '*.db', '*.ddb'];\n for (const pattern of duckdbFiles) {\n const ext = pattern.replace('*', '');\n try {\n const files = fs.readdirSync(rootDir).filter((f) => f.endsWith(ext));\n for (const file of files) {\n sources.push({\n name: `DuckDB: ${file}`,\n adapter: 'duckdb',\n origin: `file:${file}`,\n status: 'detected',\n });\n }\n } catch {\n // ignore read errors\n }\n }\n\n // Check for SQLite files\n try {\n const sqliteFiles = fs.readdirSync(rootDir).filter((f) => f.endsWith('.sqlite') || f.endsWith('.sqlite3'));\n for (const file of sqliteFiles) {\n sources.push({\n name: `SQLite: ${file}`,\n adapter: 'sqlite',\n origin: `file:${file}`,\n status: 'detected',\n });\n }\n } catch {\n // ignore\n }\n\n return c.json(sources);\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { PRODUCT_NAME_RE } from '@runcontext/core';\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\nconst ALLOWED_EXTENSIONS = ['.md', '.txt', '.pdf', '.csv', '.json', '.yaml', '.yml', '.sql', '.html'];\n\nexport function uploadRoutes(contextDir: string): Hono {\n const app = new Hono();\n\n app.post('/api/upload/:productName', async (c) => {\n const productName = c.req.param('productName');\n if (!PRODUCT_NAME_RE.test(productName)) {\n return c.json({ error: 'Invalid product name' }, 400);\n }\n\n const formData = await c.req.formData();\n const file = formData.get('file');\n if (!file || !(file instanceof File)) {\n return c.json({ error: 'No file provided' }, 400);\n }\n\n if (file.size > MAX_FILE_SIZE) {\n return c.json({ error: 'File too large (max 10MB)' }, 400);\n }\n\n const ext = path.extname(file.name).toLowerCase();\n if (!ALLOWED_EXTENSIONS.includes(ext)) {\n return c.json({ error: `File type ${ext} not allowed` }, 400);\n }\n\n // Sanitize filename\n const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');\n const docsDir = path.join(contextDir, 'products', productName, 'docs');\n fs.mkdirSync(docsDir, { recursive: true });\n\n const buffer = Buffer.from(await file.arrayBuffer());\n fs.writeFileSync(path.join(docsDir, safeName), buffer);\n\n return c.json({ ok: true, filename: safeName });\n });\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { randomUUID } from 'node:crypto';\n\nexport type PipelineStage =\n | 'introspect'\n | 'scaffold'\n | 'enrich-silver'\n | 'enrich-gold'\n | 'verify'\n | 'autofix'\n | 'agent-instructions';\n\nexport type StageStatus = 'pending' | 'running' | 'done' | 'error' | 'skipped';\n\nexport interface PipelineStageState {\n stage: PipelineStage;\n status: StageStatus;\n summary?: string;\n error?: string;\n startedAt?: string;\n completedAt?: string;\n}\n\nexport interface PipelineRun {\n id: string;\n productName: string;\n targetTier: 'bronze' | 'silver' | 'gold';\n status: 'running' | 'done' | 'error';\n stages: PipelineStageState[];\n createdAt: string;\n}\n\nconst ALL_STAGES: PipelineStage[] = [\n 'introspect',\n 'scaffold',\n 'enrich-silver',\n 'enrich-gold',\n 'verify',\n 'autofix',\n 'agent-instructions',\n];\n\n// In-memory store for pipeline runs\nconst runs = new Map<string, PipelineRun>();\n\nfunction stagesForTier(tier: 'bronze' | 'silver' | 'gold'): PipelineStage[] {\n const base: PipelineStage[] = ['introspect', 'scaffold'];\n if (tier === 'silver' || tier === 'gold') base.push('enrich-silver');\n if (tier === 'gold') base.push('enrich-gold');\n base.push('verify', 'autofix', 'agent-instructions');\n return base;\n}\n\nexport function pipelineRoutes(rootDir: string, contextDir: string): Hono {\n const app = new Hono();\n\n app.post('/api/pipeline/start', async (c) => {\n const body = await c.req.json();\n const { productName, targetTier, dataSource } = body;\n\n if (!productName || !targetTier) {\n return c.json({ error: 'productName and targetTier required' }, 400);\n }\n\n if (!['bronze', 'silver', 'gold'].includes(targetTier)) {\n return c.json({ error: 'targetTier must be bronze, silver, or gold' }, 400);\n }\n\n const id = randomUUID();\n const activeStages = stagesForTier(targetTier);\n const skippedStages = ALL_STAGES.filter((s) => !activeStages.includes(s));\n\n const run: PipelineRun = {\n id,\n productName,\n targetTier,\n status: 'running',\n stages: ALL_STAGES.map((stage) => ({\n stage,\n status: skippedStages.includes(stage) ? 'skipped' : 'pending',\n })),\n createdAt: new Date().toISOString(),\n };\n\n runs.set(id, run);\n\n // Start the pipeline asynchronously (non-blocking)\n executePipeline(run, rootDir, contextDir, dataSource).catch((err) => {\n run.status = 'error';\n const currentStage = run.stages.find((s) => s.status === 'running');\n if (currentStage) {\n currentStage.status = 'error';\n currentStage.error = err instanceof Error ? err.message : String(err);\n }\n });\n\n return c.json({ id, status: 'running' });\n });\n\n app.get('/api/pipeline/status/:id', (c) => {\n const run = runs.get(c.req.param('id'));\n if (!run) return c.json({ error: 'Not found' }, 404);\n return c.json(run);\n });\n\n return app;\n}\n\nasync function executePipeline(\n run: PipelineRun,\n _rootDir: string,\n _contextDir: string,\n _dataSource?: string,\n): Promise<void> {\n for (const stage of run.stages) {\n if (stage.status === 'skipped') continue;\n\n stage.status = 'running';\n stage.startedAt = new Date().toISOString();\n\n try {\n // TODO: Wire real setup step functions here\n // For now, each stage is a placeholder that completes immediately\n // Real implementation will call the corresponding setup step\n // e.g., for 'introspect': connect to data source and discover schema\n // e.g., for 'scaffold': generate .osi.yaml files from schema\n stage.status = 'done';\n stage.summary = `${stage.stage} completed`;\n stage.completedAt = new Date().toISOString();\n } catch (err) {\n stage.status = 'error';\n stage.error = err instanceof Error ? err.message : String(err);\n stage.completedAt = new Date().toISOString();\n run.status = 'error';\n return;\n }\n }\n\n run.status = 'done';\n}\n","import { Hono } from 'hono';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parse } from 'yaml';\n\nexport interface ExistingProduct {\n name: string;\n description?: string;\n sensitivity?: string;\n hasBrief: boolean;\n}\n\nexport function productsRoutes(contextDir: string): Hono {\n const app = new Hono();\n\n app.get('/api/products', (c) => {\n const productsDir = path.join(contextDir, 'products');\n if (!fs.existsSync(productsDir)) {\n return c.json([]);\n }\n\n const products: ExistingProduct[] = [];\n const dirs = fs.readdirSync(productsDir).filter((name) => {\n const fullPath = path.join(productsDir, name);\n return fs.statSync(fullPath).isDirectory() && !name.startsWith('.');\n });\n\n for (const name of dirs) {\n const briefPath = path.join(productsDir, name, 'context-brief.yaml');\n let description: string | undefined;\n let sensitivity: string | undefined;\n let hasBrief = false;\n\n if (fs.existsSync(briefPath)) {\n hasBrief = true;\n try {\n const brief = parse(fs.readFileSync(briefPath, 'utf-8'));\n description = brief?.description;\n sensitivity = brief?.sensitivity;\n } catch {\n // ignore parse errors\n }\n }\n\n products.push({ name, description, sensitivity, hasBrief });\n }\n\n return c.json(products);\n });\n\n return app;\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@runcontext/ui",
3
- "version": "0.5.0",
4
- "description": "Onboarding and studio UI for ContextKit",
3
+ "version": "0.5.2",
4
+ "description": "5-step browser wizard for creating data products and visual studio for curating metadata to Gold tier. Part of ContextKit.",
5
5
  "license": "MIT",
6
6
  "author": "Eric Kittelson",
7
7
  "homepage": "https://github.com/Quiet-Victory-Labs/contextkit",
@@ -15,9 +15,14 @@
15
15
  "node": ">=18.0.0"
16
16
  },
17
17
  "keywords": [
18
+ "contextkit",
18
19
  "onboarding",
19
20
  "data-product-builder",
20
- "wizard"
21
+ "wizard",
22
+ "semantic-plane",
23
+ "context-brief",
24
+ "browser-ui",
25
+ "metadata-editor"
21
26
  ],
22
27
  "type": "module",
23
28
  "main": "./dist/index.cjs",
@@ -43,7 +48,7 @@
43
48
  "@hono/node-server": "^1.19.11",
44
49
  "hono": "^4.12.5",
45
50
  "yaml": "^2.7.0",
46
- "@runcontext/core": "^0.5.0"
51
+ "@runcontext/core": "^0.5.2"
47
52
  },
48
53
  "devDependencies": {
49
54
  "@types/node": "^25.3.3",