@runcontext/cli 0.4.1 → 0.4.3
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.
|
@@ -167,9 +167,18 @@ async function startStudioServer(opts) {
|
|
|
167
167
|
const pages = await getPages();
|
|
168
168
|
let pagePath = url.pathname === "/" ? "index.html" : url.pathname.replace(/^\//, "");
|
|
169
169
|
if (!pagePath.endsWith(".html") && !pagePath.endsWith(".json")) {
|
|
170
|
-
pagePath
|
|
170
|
+
if (pages.has(pagePath + ".html")) {
|
|
171
|
+
pagePath += ".html";
|
|
172
|
+
} else {
|
|
173
|
+
pagePath = pagePath.replace(/\/$/, "") + "/index.html";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
let page = pages.get(pagePath);
|
|
177
|
+
if (!page && pagePath.endsWith("/index.html")) {
|
|
178
|
+
res.writeHead(302, { Location: "/" });
|
|
179
|
+
res.end();
|
|
180
|
+
return;
|
|
171
181
|
}
|
|
172
|
-
const page = pages.get(pagePath);
|
|
173
182
|
if (page) {
|
|
174
183
|
const ct = pagePath.endsWith(".json") ? "application/json" : "text/html; charset=utf-8";
|
|
175
184
|
res.writeHead(200, { "Content-Type": ct });
|
|
@@ -189,4 +198,4 @@ async function startStudioServer(opts) {
|
|
|
189
198
|
export {
|
|
190
199
|
startStudioServer
|
|
191
200
|
};
|
|
192
|
-
//# sourceMappingURL=server-
|
|
201
|
+
//# sourceMappingURL=server-IVDYRFDN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/studio/server.ts","../src/studio/sse.ts"],"sourcesContent":["import * as http from 'node:http';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n compile,\n loadConfig,\n emitManifest,\n LintEngine,\n ALL_RULES,\n applyYamlEdit,\n previewYamlEdit,\n type Manifest,\n type Diagnostic,\n} from '@runcontext/core';\nimport { SSEManager } from './sse.js';\n\nexport interface StudioServerOptions {\n contextDir: string;\n rootDir: string;\n port: number;\n host: string;\n}\n\nexport async function startStudioServer(opts: StudioServerOptions): Promise<{\n server: http.Server;\n sse: SSEManager;\n recompileAndBroadcast: () => Promise<void>;\n}> {\n const { contextDir, rootDir, port, host } = opts;\n const config = loadConfig(rootDir);\n const sse = new SSEManager();\n\n let cachedPages: Map<string, string> | null = null;\n let cachedManifest: Manifest | null = null;\n\n async function recompile(): Promise<{ manifest: Manifest; diagnostics: Diagnostic[] }> {\n const { graph, diagnostics: compileDiags } = await compile({ contextDir, config, rootDir });\n const engine = new LintEngine();\n for (const rule of ALL_RULES) engine.register(rule);\n const lintDiags = engine.run(graph);\n const manifest = emitManifest(graph, config);\n cachedManifest = manifest;\n cachedPages = null; // invalidate\n return { manifest, diagnostics: [...compileDiags, ...lintDiags] };\n }\n\n async function recompileAndBroadcast(): Promise<void> {\n const { manifest, diagnostics } = await recompile();\n sse.broadcast('update', {\n tiers: manifest.tiers,\n diagnosticCount: diagnostics.length,\n diagnostics: diagnostics.slice(0, 50),\n });\n }\n\n async function getPages(): Promise<Map<string, string>> {\n if (cachedPages) return cachedPages;\n if (!cachedManifest) await recompile();\n const { generateSite } = await import('@runcontext/site');\n cachedPages = generateSite(cachedManifest!, config.site, { studioMode: true });\n return cachedPages;\n }\n\n // Initial compile\n await recompile();\n\n function parseBody(req: http.IncomingMessage): Promise<Record<string, unknown>> {\n const MAX_BODY = 1_048_576; // 1 MB\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString();\n if (body.length > MAX_BODY) {\n reject(new Error('Payload too large'));\n req.destroy();\n }\n });\n req.on('end', () => {\n try {\n resolve(JSON.parse(body));\n } catch {\n reject(new Error('Invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n }\n\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host}`);\n\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n // --- API routes ---\n if (url.pathname === '/api/events' && req.method === 'GET') {\n sse.addClient(res);\n return;\n }\n\n if (url.pathname === '/api/manifest' && req.method === 'GET') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(cachedManifest));\n return;\n }\n\n if (url.pathname === '/api/preview' && req.method === 'POST') {\n const body = await parseBody(req);\n const file = body.file;\n const dotPath = body.path;\n const value = body.value;\n if (typeof file !== 'string' || typeof dotPath !== 'string') {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing required fields: file, path');\n return;\n }\n const filePath = path.resolve(rootDir, file);\n const resolvedRoot = path.resolve(rootDir);\n if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {\n res.writeHead(403);\n res.end('Forbidden');\n return;\n }\n const content = await fs.promises.readFile(filePath, 'utf-8');\n const preview = previewYamlEdit(content, dotPath, value);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ filename: file, ...preview }));\n return;\n }\n\n if (url.pathname === '/api/save' && req.method === 'POST') {\n const body = await parseBody(req);\n if (!Array.isArray(body.edits)) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing required field: edits (array)');\n return;\n }\n const edits = body.edits as Array<{ file: string; path: string; value: unknown }>;\n const resolvedRoot = path.resolve(rootDir);\n const results: Array<{ file: string; ok: boolean }> = [];\n for (const edit of edits) {\n const filePath = path.resolve(rootDir, edit.file);\n if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {\n results.push({ file: edit.file, ok: false });\n continue;\n }\n const content = await fs.promises.readFile(filePath, 'utf-8');\n const updated = applyYamlEdit(content, edit.path, edit.value);\n await fs.promises.writeFile(filePath, updated, 'utf-8');\n results.push({ file: edit.file, ok: true });\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n return;\n }\n\n // --- Static site pages ---\n const pages = await getPages();\n let pagePath = url.pathname === '/' ? 'index.html' : url.pathname.replace(/^\\//, '');\n if (!pagePath.endsWith('.html') && !pagePath.endsWith('.json')) {\n // Try exact path + .html, then directory index\n if (pages.has(pagePath + '.html')) {\n pagePath += '.html';\n } else {\n pagePath = pagePath.replace(/\\/$/, '') + '/index.html';\n }\n }\n // Redirect bare /models to /models/ index (or first model)\n let page = pages.get(pagePath);\n if (!page && pagePath.endsWith('/index.html')) {\n // No index page for this directory — redirect to site root\n res.writeHead(302, { Location: '/' });\n res.end();\n return;\n }\n if (page) {\n const ct = pagePath.endsWith('.json') ? 'application/json' : 'text/html; charset=utf-8';\n res.writeHead(200, { 'Content-Type': ct });\n res.end(page);\n return;\n }\n\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end((err as Error).message);\n }\n });\n\n server.listen(port, host);\n\n return { server, sse, recompileAndBroadcast };\n}\n","import type { ServerResponse } from 'node:http';\n\nexport class SSEManager {\n private clients: Set<ServerResponse> = new Set();\n\n addClient(res: ServerResponse): void {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n res.write('data: {\"type\":\"connected\"}\\n\\n');\n this.clients.add(res);\n res.on('close', () => this.clients.delete(res));\n }\n\n broadcast(event: string, data: unknown): void {\n const payload = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n for (const client of this.clients) {\n try {\n client.write(payload);\n } catch {\n this.clients.delete(client);\n }\n }\n }\n}\n"],"mappings":"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","names":["resolve"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runcontext/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Tell your AI agent to build your semantic layer. CLI for introspecting databases, curating metadata, and serving context via MCP.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eric Kittelson",
|
|
@@ -61,9 +61,9 @@
|
|
|
61
61
|
"commander": "^14.0.0",
|
|
62
62
|
"@clack/prompts": "^1.1.0",
|
|
63
63
|
"yaml": "^2.7.0",
|
|
64
|
-
"@runcontext/
|
|
65
|
-
"@runcontext/
|
|
66
|
-
"@runcontext/
|
|
64
|
+
"@runcontext/core": "^0.4.3",
|
|
65
|
+
"@runcontext/site": "^0.4.3",
|
|
66
|
+
"@runcontext/mcp": "^0.4.3"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/node": "^25.3.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/studio/server.ts","../src/studio/sse.ts"],"sourcesContent":["import * as http from 'node:http';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n compile,\n loadConfig,\n emitManifest,\n LintEngine,\n ALL_RULES,\n applyYamlEdit,\n previewYamlEdit,\n type Manifest,\n type Diagnostic,\n} from '@runcontext/core';\nimport { SSEManager } from './sse.js';\n\nexport interface StudioServerOptions {\n contextDir: string;\n rootDir: string;\n port: number;\n host: string;\n}\n\nexport async function startStudioServer(opts: StudioServerOptions): Promise<{\n server: http.Server;\n sse: SSEManager;\n recompileAndBroadcast: () => Promise<void>;\n}> {\n const { contextDir, rootDir, port, host } = opts;\n const config = loadConfig(rootDir);\n const sse = new SSEManager();\n\n let cachedPages: Map<string, string> | null = null;\n let cachedManifest: Manifest | null = null;\n\n async function recompile(): Promise<{ manifest: Manifest; diagnostics: Diagnostic[] }> {\n const { graph, diagnostics: compileDiags } = await compile({ contextDir, config, rootDir });\n const engine = new LintEngine();\n for (const rule of ALL_RULES) engine.register(rule);\n const lintDiags = engine.run(graph);\n const manifest = emitManifest(graph, config);\n cachedManifest = manifest;\n cachedPages = null; // invalidate\n return { manifest, diagnostics: [...compileDiags, ...lintDiags] };\n }\n\n async function recompileAndBroadcast(): Promise<void> {\n const { manifest, diagnostics } = await recompile();\n sse.broadcast('update', {\n tiers: manifest.tiers,\n diagnosticCount: diagnostics.length,\n diagnostics: diagnostics.slice(0, 50),\n });\n }\n\n async function getPages(): Promise<Map<string, string>> {\n if (cachedPages) return cachedPages;\n if (!cachedManifest) await recompile();\n const { generateSite } = await import('@runcontext/site');\n cachedPages = generateSite(cachedManifest!, config.site, { studioMode: true });\n return cachedPages;\n }\n\n // Initial compile\n await recompile();\n\n function parseBody(req: http.IncomingMessage): Promise<Record<string, unknown>> {\n const MAX_BODY = 1_048_576; // 1 MB\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString();\n if (body.length > MAX_BODY) {\n reject(new Error('Payload too large'));\n req.destroy();\n }\n });\n req.on('end', () => {\n try {\n resolve(JSON.parse(body));\n } catch {\n reject(new Error('Invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n }\n\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host}`);\n\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n try {\n // --- API routes ---\n if (url.pathname === '/api/events' && req.method === 'GET') {\n sse.addClient(res);\n return;\n }\n\n if (url.pathname === '/api/manifest' && req.method === 'GET') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(cachedManifest));\n return;\n }\n\n if (url.pathname === '/api/preview' && req.method === 'POST') {\n const body = await parseBody(req);\n const file = body.file;\n const dotPath = body.path;\n const value = body.value;\n if (typeof file !== 'string' || typeof dotPath !== 'string') {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing required fields: file, path');\n return;\n }\n const filePath = path.resolve(rootDir, file);\n const resolvedRoot = path.resolve(rootDir);\n if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {\n res.writeHead(403);\n res.end('Forbidden');\n return;\n }\n const content = await fs.promises.readFile(filePath, 'utf-8');\n const preview = previewYamlEdit(content, dotPath, value);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ filename: file, ...preview }));\n return;\n }\n\n if (url.pathname === '/api/save' && req.method === 'POST') {\n const body = await parseBody(req);\n if (!Array.isArray(body.edits)) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing required field: edits (array)');\n return;\n }\n const edits = body.edits as Array<{ file: string; path: string; value: unknown }>;\n const resolvedRoot = path.resolve(rootDir);\n const results: Array<{ file: string; ok: boolean }> = [];\n for (const edit of edits) {\n const filePath = path.resolve(rootDir, edit.file);\n if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {\n results.push({ file: edit.file, ok: false });\n continue;\n }\n const content = await fs.promises.readFile(filePath, 'utf-8');\n const updated = applyYamlEdit(content, edit.path, edit.value);\n await fs.promises.writeFile(filePath, updated, 'utf-8');\n results.push({ file: edit.file, ok: true });\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n return;\n }\n\n // --- Static site pages ---\n const pages = await getPages();\n let pagePath = url.pathname === '/' ? 'index.html' : url.pathname.replace(/^\\//, '');\n if (!pagePath.endsWith('.html') && !pagePath.endsWith('.json')) {\n pagePath += '.html';\n }\n const page = pages.get(pagePath);\n if (page) {\n const ct = pagePath.endsWith('.json') ? 'application/json' : 'text/html; charset=utf-8';\n res.writeHead(200, { 'Content-Type': ct });\n res.end(page);\n return;\n }\n\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not found');\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'text/plain' });\n res.end((err as Error).message);\n }\n });\n\n server.listen(port, host);\n\n return { server, sse, recompileAndBroadcast };\n}\n","import type { ServerResponse } from 'node:http';\n\nexport class SSEManager {\n private clients: Set<ServerResponse> = new Set();\n\n addClient(res: ServerResponse): void {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n res.write('data: {\"type\":\"connected\"}\\n\\n');\n this.clients.add(res);\n res.on('close', () => this.clients.delete(res));\n }\n\n broadcast(event: string, data: unknown): void {\n const payload = `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n for (const client of this.clients) {\n try {\n client.write(payload);\n } catch {\n this.clients.delete(client);\n }\n }\n }\n}\n"],"mappings":"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","names":["resolve"]}
|