@runcontext/cli 0.3.4 → 0.4.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.
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/studio/server.ts
4
+ import * as http from "http";
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import {
8
+ compile,
9
+ loadConfig,
10
+ emitManifest,
11
+ LintEngine,
12
+ ALL_RULES,
13
+ applyYamlEdit,
14
+ previewYamlEdit
15
+ } from "@runcontext/core";
16
+
17
+ // src/studio/sse.ts
18
+ var SSEManager = class {
19
+ clients = /* @__PURE__ */ new Set();
20
+ addClient(res) {
21
+ res.writeHead(200, {
22
+ "Content-Type": "text/event-stream",
23
+ "Cache-Control": "no-cache",
24
+ Connection: "keep-alive"
25
+ });
26
+ res.write('data: {"type":"connected"}\n\n');
27
+ this.clients.add(res);
28
+ res.on("close", () => this.clients.delete(res));
29
+ }
30
+ broadcast(event, data) {
31
+ const payload = `event: ${event}
32
+ data: ${JSON.stringify(data)}
33
+
34
+ `;
35
+ for (const client of this.clients) {
36
+ try {
37
+ client.write(payload);
38
+ } catch {
39
+ this.clients.delete(client);
40
+ }
41
+ }
42
+ }
43
+ };
44
+
45
+ // src/studio/server.ts
46
+ async function startStudioServer(opts) {
47
+ const { contextDir, rootDir, port, host } = opts;
48
+ const config = loadConfig(rootDir);
49
+ const sse = new SSEManager();
50
+ let cachedPages = null;
51
+ let cachedManifest = null;
52
+ async function recompile() {
53
+ const { graph, diagnostics: compileDiags } = await compile({ contextDir, config, rootDir });
54
+ const engine = new LintEngine();
55
+ for (const rule of ALL_RULES) engine.register(rule);
56
+ const lintDiags = engine.run(graph);
57
+ const manifest = emitManifest(graph, config);
58
+ cachedManifest = manifest;
59
+ cachedPages = null;
60
+ return { manifest, diagnostics: [...compileDiags, ...lintDiags] };
61
+ }
62
+ async function recompileAndBroadcast() {
63
+ const { manifest, diagnostics } = await recompile();
64
+ sse.broadcast("update", {
65
+ tiers: manifest.tiers,
66
+ diagnosticCount: diagnostics.length,
67
+ diagnostics: diagnostics.slice(0, 50)
68
+ });
69
+ }
70
+ async function getPages() {
71
+ if (cachedPages) return cachedPages;
72
+ if (!cachedManifest) await recompile();
73
+ const { generateSite } = await import("@runcontext/site");
74
+ cachedPages = generateSite(cachedManifest, config.site, { studioMode: true });
75
+ return cachedPages;
76
+ }
77
+ await recompile();
78
+ function parseBody(req) {
79
+ const MAX_BODY = 1048576;
80
+ return new Promise((resolve2, reject) => {
81
+ let body = "";
82
+ req.on("data", (chunk) => {
83
+ body += chunk.toString();
84
+ if (body.length > MAX_BODY) {
85
+ reject(new Error("Payload too large"));
86
+ req.destroy();
87
+ }
88
+ });
89
+ req.on("end", () => {
90
+ try {
91
+ resolve2(JSON.parse(body));
92
+ } catch {
93
+ reject(new Error("Invalid JSON"));
94
+ }
95
+ });
96
+ req.on("error", reject);
97
+ });
98
+ }
99
+ const server = http.createServer(async (req, res) => {
100
+ const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
101
+ res.setHeader("Access-Control-Allow-Origin", "*");
102
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
103
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
104
+ if (req.method === "OPTIONS") {
105
+ res.writeHead(204);
106
+ res.end();
107
+ return;
108
+ }
109
+ try {
110
+ if (url.pathname === "/api/events" && req.method === "GET") {
111
+ sse.addClient(res);
112
+ return;
113
+ }
114
+ if (url.pathname === "/api/manifest" && req.method === "GET") {
115
+ res.writeHead(200, { "Content-Type": "application/json" });
116
+ res.end(JSON.stringify(cachedManifest));
117
+ return;
118
+ }
119
+ if (url.pathname === "/api/preview" && req.method === "POST") {
120
+ const body = await parseBody(req);
121
+ const file = body.file;
122
+ const dotPath = body.path;
123
+ const value = body.value;
124
+ if (typeof file !== "string" || typeof dotPath !== "string") {
125
+ res.writeHead(400, { "Content-Type": "text/plain" });
126
+ res.end("Missing required fields: file, path");
127
+ return;
128
+ }
129
+ const filePath = path.resolve(rootDir, file);
130
+ const resolvedRoot = path.resolve(rootDir);
131
+ if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {
132
+ res.writeHead(403);
133
+ res.end("Forbidden");
134
+ return;
135
+ }
136
+ const content = await fs.promises.readFile(filePath, "utf-8");
137
+ const preview = previewYamlEdit(content, dotPath, value);
138
+ res.writeHead(200, { "Content-Type": "application/json" });
139
+ res.end(JSON.stringify({ filename: file, ...preview }));
140
+ return;
141
+ }
142
+ if (url.pathname === "/api/save" && req.method === "POST") {
143
+ const body = await parseBody(req);
144
+ if (!Array.isArray(body.edits)) {
145
+ res.writeHead(400, { "Content-Type": "text/plain" });
146
+ res.end("Missing required field: edits (array)");
147
+ return;
148
+ }
149
+ const edits = body.edits;
150
+ const resolvedRoot = path.resolve(rootDir);
151
+ const results = [];
152
+ for (const edit of edits) {
153
+ const filePath = path.resolve(rootDir, edit.file);
154
+ if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {
155
+ results.push({ file: edit.file, ok: false });
156
+ continue;
157
+ }
158
+ const content = await fs.promises.readFile(filePath, "utf-8");
159
+ const updated = applyYamlEdit(content, edit.path, edit.value);
160
+ await fs.promises.writeFile(filePath, updated, "utf-8");
161
+ results.push({ file: edit.file, ok: true });
162
+ }
163
+ res.writeHead(200, { "Content-Type": "application/json" });
164
+ res.end(JSON.stringify({ results }));
165
+ return;
166
+ }
167
+ const pages = await getPages();
168
+ let pagePath = url.pathname === "/" ? "index.html" : url.pathname.replace(/^\//, "");
169
+ if (!pagePath.endsWith(".html") && !pagePath.endsWith(".json")) {
170
+ pagePath += ".html";
171
+ }
172
+ const page = pages.get(pagePath);
173
+ if (page) {
174
+ const ct = pagePath.endsWith(".json") ? "application/json" : "text/html; charset=utf-8";
175
+ res.writeHead(200, { "Content-Type": ct });
176
+ res.end(page);
177
+ return;
178
+ }
179
+ res.writeHead(404, { "Content-Type": "text/plain" });
180
+ res.end("Not found");
181
+ } catch (err) {
182
+ res.writeHead(500, { "Content-Type": "text/plain" });
183
+ res.end(err.message);
184
+ }
185
+ });
186
+ server.listen(port, host);
187
+ return { server, sse, recompileAndBroadcast };
188
+ }
189
+ export {
190
+ startStudioServer
191
+ };
192
+ //# sourceMappingURL=server-DEKWPP3H.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 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"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@runcontext/cli",
3
- "version": "0.3.4",
4
- "description": "CLI for ContextKit lint, build, fix, and serve institutional context",
3
+ "version": "0.4.0",
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",
7
7
  "homepage": "https://github.com/erickittelson/ContextKit",
@@ -10,13 +10,43 @@
10
10
  "url": "https://github.com/erickittelson/ContextKit.git",
11
11
  "directory": "packages/cli"
12
12
  },
13
+ "bugs": "https://github.com/erickittelson/ContextKit/issues",
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ },
13
17
  "keywords": [
14
18
  "contextkit",
15
19
  "cli",
20
+ "semantic-layer",
21
+ "metadata",
22
+ "governance",
23
+ "mcp",
24
+ "mcp-server",
25
+ "model-context-protocol",
26
+ "ai",
27
+ "ai-agent",
28
+ "ai-tools",
29
+ "llm",
30
+ "data-catalog",
31
+ "data-governance",
32
+ "dbt",
33
+ "osi",
34
+ "open-semantic-interchange",
16
35
  "lint",
17
- "build",
18
- "yaml",
19
- "mcp"
36
+ "database",
37
+ "database-introspection",
38
+ "duckdb",
39
+ "postgres",
40
+ "mysql",
41
+ "snowflake",
42
+ "bigquery",
43
+ "clickhouse",
44
+ "databricks",
45
+ "sqlite",
46
+ "sql",
47
+ "cursor",
48
+ "claude",
49
+ "windsurf"
20
50
  ],
21
51
  "type": "module",
22
52
  "bin": {
@@ -26,14 +56,14 @@
26
56
  "dist"
27
57
  ],
28
58
  "dependencies": {
29
- "@runcontext/core": "^0.3.4",
30
- "@runcontext/mcp": "^0.3.4",
31
- "@runcontext/site": "^0.3.4",
32
59
  "chalk": "^5.4.0",
33
60
  "chokidar": "^4.0.0",
34
61
  "commander": "^14.0.0",
35
62
  "@clack/prompts": "^1.1.0",
36
- "yaml": "^2.7.0"
63
+ "yaml": "^2.7.0",
64
+ "@runcontext/core": "^0.4.0",
65
+ "@runcontext/mcp": "^0.4.0",
66
+ "@runcontext/site": "^0.4.0"
37
67
  },
38
68
  "devDependencies": {
39
69
  "@types/node": "^25.3.3",