@runcontext/cli 0.4.2 → 0.4.4
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/dist/index.js +78 -60
- package/dist/index.js.map +1 -1
- package/dist/server-MQUOYUAX.js +322 -0
- package/dist/server-MQUOYUAX.js.map +1 -0
- package/package.json +4 -4
- package/dist/server-DEKWPP3H.js +0 -192
- package/dist/server-DEKWPP3H.js.map +0 -1
|
@@ -0,0 +1,322 @@
|
|
|
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
|
+
const { getAstroProjectDir } = await import("@runcontext/site");
|
|
51
|
+
const astroDir = getAstroProjectDir();
|
|
52
|
+
const astroDataDir = path.join(astroDir, "src", "data");
|
|
53
|
+
let cachedManifest = null;
|
|
54
|
+
async function writeAstroData(manifest) {
|
|
55
|
+
fs.mkdirSync(astroDataDir, { recursive: true });
|
|
56
|
+
await fs.promises.writeFile(
|
|
57
|
+
path.join(astroDataDir, "manifest.json"),
|
|
58
|
+
JSON.stringify(manifest, null, 2),
|
|
59
|
+
"utf-8"
|
|
60
|
+
);
|
|
61
|
+
await fs.promises.writeFile(
|
|
62
|
+
path.join(astroDataDir, "site-config.json"),
|
|
63
|
+
JSON.stringify({
|
|
64
|
+
title: config.site?.title ?? "ContextKit",
|
|
65
|
+
studioMode: true
|
|
66
|
+
}),
|
|
67
|
+
"utf-8"
|
|
68
|
+
);
|
|
69
|
+
const { buildSearchIndex } = await import("@runcontext/site");
|
|
70
|
+
const searchIndex = buildSearchIndex(manifest, "");
|
|
71
|
+
const publicDir = path.join(astroDir, "public");
|
|
72
|
+
fs.mkdirSync(publicDir, { recursive: true });
|
|
73
|
+
await fs.promises.writeFile(
|
|
74
|
+
path.join(publicDir, "search-index.json"),
|
|
75
|
+
JSON.stringify(searchIndex, null, 2),
|
|
76
|
+
"utf-8"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
async function recompile() {
|
|
80
|
+
const { graph, diagnostics: compileDiags } = await compile({ contextDir, config, rootDir });
|
|
81
|
+
const engine = new LintEngine();
|
|
82
|
+
for (const rule of ALL_RULES) engine.register(rule);
|
|
83
|
+
const lintDiags = engine.run(graph);
|
|
84
|
+
const manifest = emitManifest(graph, config);
|
|
85
|
+
cachedManifest = manifest;
|
|
86
|
+
await writeAstroData(manifest);
|
|
87
|
+
return { manifest, diagnostics: [...compileDiags, ...lintDiags] };
|
|
88
|
+
}
|
|
89
|
+
async function recompileAndBroadcast() {
|
|
90
|
+
const { manifest, diagnostics } = await recompile();
|
|
91
|
+
sse.broadcast("update", {
|
|
92
|
+
tiers: manifest.tiers,
|
|
93
|
+
diagnosticCount: diagnostics.length,
|
|
94
|
+
diagnostics: diagnostics.slice(0, 50)
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
await recompile();
|
|
98
|
+
const astroPort = port + 1;
|
|
99
|
+
const { execFile } = await import("child_process");
|
|
100
|
+
const npx = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
101
|
+
const astroProc = execFile(
|
|
102
|
+
npx,
|
|
103
|
+
["astro", "dev", "--port", String(astroPort), "--host", host],
|
|
104
|
+
{ cwd: astroDir }
|
|
105
|
+
);
|
|
106
|
+
astroProc.stderr?.on("data", (chunk) => {
|
|
107
|
+
const msg = chunk.toString();
|
|
108
|
+
if (msg.includes("Error") || msg.includes("error")) {
|
|
109
|
+
process.stderr.write(msg);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
await new Promise((resolve2) => {
|
|
113
|
+
let resolved = false;
|
|
114
|
+
const checkInterval = setInterval(async () => {
|
|
115
|
+
try {
|
|
116
|
+
const testReq = http.request(
|
|
117
|
+
{ hostname: host === "0.0.0.0" ? "127.0.0.1" : host, port: astroPort, path: "/", method: "HEAD", timeout: 500 },
|
|
118
|
+
(res) => {
|
|
119
|
+
res.resume();
|
|
120
|
+
if (!resolved) {
|
|
121
|
+
resolved = true;
|
|
122
|
+
clearInterval(checkInterval);
|
|
123
|
+
resolve2();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
testReq.on("error", () => {
|
|
128
|
+
});
|
|
129
|
+
testReq.end();
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}, 300);
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
if (!resolved) {
|
|
135
|
+
resolved = true;
|
|
136
|
+
clearInterval(checkInterval);
|
|
137
|
+
resolve2();
|
|
138
|
+
}
|
|
139
|
+
}, 15e3);
|
|
140
|
+
});
|
|
141
|
+
function parseBody(req) {
|
|
142
|
+
const MAX_BODY = 1048576;
|
|
143
|
+
return new Promise((resolve2, reject) => {
|
|
144
|
+
let body = "";
|
|
145
|
+
req.on("data", (chunk) => {
|
|
146
|
+
body += chunk.toString();
|
|
147
|
+
if (body.length > MAX_BODY) {
|
|
148
|
+
reject(new Error("Payload too large"));
|
|
149
|
+
req.destroy();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
req.on("end", () => {
|
|
153
|
+
try {
|
|
154
|
+
resolve2(JSON.parse(body));
|
|
155
|
+
} catch {
|
|
156
|
+
reject(new Error("Invalid JSON"));
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
req.on("error", reject);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function proxyToAstro(req, res) {
|
|
163
|
+
const proxyReq = http.request(
|
|
164
|
+
{
|
|
165
|
+
hostname: host === "0.0.0.0" ? "127.0.0.1" : host,
|
|
166
|
+
port: astroPort,
|
|
167
|
+
path: req.url,
|
|
168
|
+
method: req.method,
|
|
169
|
+
headers: req.headers
|
|
170
|
+
},
|
|
171
|
+
(proxyRes) => {
|
|
172
|
+
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
173
|
+
proxyRes.pipe(res);
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
proxyReq.on("error", () => {
|
|
177
|
+
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
178
|
+
res.end("Astro dev server unavailable");
|
|
179
|
+
});
|
|
180
|
+
req.pipe(proxyReq);
|
|
181
|
+
}
|
|
182
|
+
const server = http.createServer(async (req, res) => {
|
|
183
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
184
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
185
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
186
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
187
|
+
if (req.method === "OPTIONS") {
|
|
188
|
+
res.writeHead(204);
|
|
189
|
+
res.end();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
if (url.pathname === "/api/events" && req.method === "GET") {
|
|
194
|
+
sse.addClient(res);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (url.pathname === "/api/manifest" && req.method === "GET") {
|
|
198
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
199
|
+
res.end(JSON.stringify(cachedManifest));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (url.pathname === "/api/preview" && req.method === "POST") {
|
|
203
|
+
const body = await parseBody(req);
|
|
204
|
+
const file = body.file;
|
|
205
|
+
const dotPath = body.path;
|
|
206
|
+
const value = body.value;
|
|
207
|
+
if (typeof file !== "string" || typeof dotPath !== "string") {
|
|
208
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
209
|
+
res.end(JSON.stringify({ error: "Missing required fields: file, path" }));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const filePath = path.resolve(rootDir, file);
|
|
213
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
214
|
+
if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {
|
|
215
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
216
|
+
res.end(JSON.stringify({ error: "Forbidden" }));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
220
|
+
const preview = previewYamlEdit(content, dotPath, value);
|
|
221
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
222
|
+
res.end(JSON.stringify({ filename: file, ...preview }));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (url.pathname === "/api/rename-model" && req.method === "POST") {
|
|
226
|
+
const body = await parseBody(req);
|
|
227
|
+
const oldName = body.oldName;
|
|
228
|
+
const newName = body.newName;
|
|
229
|
+
if (typeof oldName !== "string" || typeof newName !== "string" || !newName.trim()) {
|
|
230
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
231
|
+
res.end(JSON.stringify({ error: "Missing required fields: oldName, newName" }));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const safeName = newName.trim().replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
235
|
+
const resolvedCtx = path.resolve(contextDir);
|
|
236
|
+
const renamed = [];
|
|
237
|
+
const osiOld = path.join(resolvedCtx, "models", `${oldName}.osi.yaml`);
|
|
238
|
+
const osiNew = path.join(resolvedCtx, "models", `${safeName}.osi.yaml`);
|
|
239
|
+
if (fs.existsSync(osiOld)) {
|
|
240
|
+
let content = await fs.promises.readFile(osiOld, "utf-8");
|
|
241
|
+
content = content.replace(
|
|
242
|
+
new RegExp(`(name:\\s*["']?)${oldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(["']?)`, "g"),
|
|
243
|
+
`$1${safeName}$2`
|
|
244
|
+
);
|
|
245
|
+
await fs.promises.writeFile(osiOld, content, "utf-8");
|
|
246
|
+
await fs.promises.rename(osiOld, osiNew);
|
|
247
|
+
renamed.push(`models/${safeName}.osi.yaml`);
|
|
248
|
+
}
|
|
249
|
+
const govOld = path.join(resolvedCtx, "governance", `${oldName}.governance.yaml`);
|
|
250
|
+
const govNew = path.join(resolvedCtx, "governance", `${safeName}.governance.yaml`);
|
|
251
|
+
if (fs.existsSync(govOld)) {
|
|
252
|
+
let content = await fs.promises.readFile(govOld, "utf-8");
|
|
253
|
+
content = content.replace(/^model:\s*.*$/m, `model: ${safeName}`);
|
|
254
|
+
await fs.promises.writeFile(govOld, content, "utf-8");
|
|
255
|
+
await fs.promises.rename(govOld, govNew);
|
|
256
|
+
renamed.push(`governance/${safeName}.governance.yaml`);
|
|
257
|
+
}
|
|
258
|
+
const rulesOld = path.join(resolvedCtx, "rules", `${oldName}.rules.yaml`);
|
|
259
|
+
const rulesNew = path.join(resolvedCtx, "rules", `${safeName}.rules.yaml`);
|
|
260
|
+
if (fs.existsSync(rulesOld)) {
|
|
261
|
+
let content = await fs.promises.readFile(rulesOld, "utf-8");
|
|
262
|
+
content = content.replace(/^model:\s*.*$/m, `model: ${safeName}`);
|
|
263
|
+
await fs.promises.writeFile(rulesOld, content, "utf-8");
|
|
264
|
+
await fs.promises.rename(rulesOld, rulesNew);
|
|
265
|
+
renamed.push(`rules/${safeName}.rules.yaml`);
|
|
266
|
+
}
|
|
267
|
+
const linOld = path.join(resolvedCtx, "lineage", `${oldName}.lineage.yaml`);
|
|
268
|
+
const linNew = path.join(resolvedCtx, "lineage", `${safeName}.lineage.yaml`);
|
|
269
|
+
if (fs.existsSync(linOld)) {
|
|
270
|
+
let content = await fs.promises.readFile(linOld, "utf-8");
|
|
271
|
+
content = content.replace(/^model:\s*.*$/m, `model: ${safeName}`);
|
|
272
|
+
await fs.promises.writeFile(linOld, content, "utf-8");
|
|
273
|
+
await fs.promises.rename(linOld, linNew);
|
|
274
|
+
renamed.push(`lineage/${safeName}.lineage.yaml`);
|
|
275
|
+
}
|
|
276
|
+
await recompileAndBroadcast();
|
|
277
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
278
|
+
res.end(JSON.stringify({ ok: true, newName: safeName, renamed }));
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (url.pathname === "/api/save" && req.method === "POST") {
|
|
282
|
+
const body = await parseBody(req);
|
|
283
|
+
if (!Array.isArray(body.edits)) {
|
|
284
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
285
|
+
res.end(JSON.stringify({ error: "Missing required field: edits (array)" }));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const edits = body.edits;
|
|
289
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
290
|
+
const results = [];
|
|
291
|
+
for (const edit of edits) {
|
|
292
|
+
const filePath = path.resolve(rootDir, edit.file);
|
|
293
|
+
if (!filePath.startsWith(resolvedRoot + path.sep) && filePath !== resolvedRoot) {
|
|
294
|
+
results.push({ file: edit.file, ok: false });
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const content = await fs.promises.readFile(filePath, "utf-8");
|
|
298
|
+
const updated = applyYamlEdit(content, edit.path, edit.value);
|
|
299
|
+
await fs.promises.writeFile(filePath, updated, "utf-8");
|
|
300
|
+
results.push({ file: edit.file, ok: true });
|
|
301
|
+
}
|
|
302
|
+
await recompileAndBroadcast();
|
|
303
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
304
|
+
res.end(JSON.stringify({ results }));
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
proxyToAstro(req, res);
|
|
308
|
+
} catch (err) {
|
|
309
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
310
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
server.listen(port, host);
|
|
314
|
+
server.on("close", () => {
|
|
315
|
+
astroProc.kill();
|
|
316
|
+
});
|
|
317
|
+
return { server, sse, recompileAndBroadcast };
|
|
318
|
+
}
|
|
319
|
+
export {
|
|
320
|
+
startStudioServer
|
|
321
|
+
};
|
|
322
|
+
//# sourceMappingURL=server-MQUOYUAX.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 // Resolve Astro project directory\n const { getAstroProjectDir } = await import('@runcontext/site');\n const astroDir = getAstroProjectDir();\n const astroDataDir = path.join(astroDir, 'src', 'data');\n\n let cachedManifest: Manifest | null = null;\n\n // Write manifest data to Astro's data directory so Astro pages can import it\n async function writeAstroData(manifest: Manifest): Promise<void> {\n fs.mkdirSync(astroDataDir, { recursive: true });\n await fs.promises.writeFile(\n path.join(astroDataDir, 'manifest.json'),\n JSON.stringify(manifest, null, 2),\n 'utf-8',\n );\n await fs.promises.writeFile(\n path.join(astroDataDir, 'site-config.json'),\n JSON.stringify({\n title: config.site?.title ?? 'ContextKit',\n studioMode: true,\n }),\n 'utf-8',\n );\n\n // Also write search index to public dir\n const { buildSearchIndex } = await import('@runcontext/site');\n const searchIndex = buildSearchIndex(manifest, '');\n const publicDir = path.join(astroDir, 'public');\n fs.mkdirSync(publicDir, { recursive: true });\n await fs.promises.writeFile(\n path.join(publicDir, 'search-index.json'),\n JSON.stringify(searchIndex, null, 2),\n 'utf-8',\n );\n }\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\n // Write data for Astro to consume\n await writeAstroData(manifest);\n\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 // Initial compile — writes manifest to Astro data dir\n await recompile();\n\n // Start Astro dev server on an internal port\n const astroPort = port + 1;\n const { execFile } = await import('node:child_process');\n const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n const astroProc = execFile(\n npx,\n ['astro', 'dev', '--port', String(astroPort), '--host', host],\n { cwd: astroDir },\n );\n astroProc.stderr?.on('data', (chunk: Buffer) => {\n const msg = chunk.toString();\n // Suppress noisy astro startup logs unless it's an error\n if (msg.includes('Error') || msg.includes('error')) {\n process.stderr.write(msg);\n }\n });\n\n // Wait for Astro dev server to be ready\n await new Promise<void>((resolve) => {\n let resolved = false;\n const checkInterval = setInterval(async () => {\n try {\n const testReq = http.request(\n { hostname: host === '0.0.0.0' ? '127.0.0.1' : host, port: astroPort, path: '/', method: 'HEAD', timeout: 500 },\n (res) => {\n res.resume();\n if (!resolved) {\n resolved = true;\n clearInterval(checkInterval);\n resolve();\n }\n },\n );\n testReq.on('error', () => {}); // ignore connection errors during startup\n testReq.end();\n } catch {\n // Not ready yet\n }\n }, 300);\n // Timeout after 15 seconds\n setTimeout(() => {\n if (!resolved) {\n resolved = true;\n clearInterval(checkInterval);\n resolve();\n }\n }, 15_000);\n });\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 // Proxy a request to the Astro dev server\n function proxyToAstro(req: http.IncomingMessage, res: http.ServerResponse): void {\n const proxyReq = http.request(\n {\n hostname: host === '0.0.0.0' ? '127.0.0.1' : host,\n port: astroPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n },\n (proxyRes) => {\n res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);\n proxyRes.pipe(res);\n },\n );\n proxyReq.on('error', () => {\n res.writeHead(502, { 'Content-Type': 'text/plain' });\n res.end('Astro dev server unavailable');\n });\n req.pipe(proxyReq);\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 (handled locally) ---\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': 'application/json' });\n res.end(JSON.stringify({ error: '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, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: '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/rename-model' && req.method === 'POST') {\n const body = await parseBody(req);\n const oldName = body.oldName;\n const newName = body.newName;\n if (typeof oldName !== 'string' || typeof newName !== 'string' || !newName.trim()) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Missing required fields: oldName, newName' }));\n return;\n }\n const safeName = newName.trim().replace(/[^a-zA-Z0-9_-]/g, '-');\n const resolvedCtx = path.resolve(contextDir);\n const renamed: string[] = [];\n\n // Rename model OSI file\n const osiOld = path.join(resolvedCtx, 'models', `${oldName}.osi.yaml`);\n const osiNew = path.join(resolvedCtx, 'models', `${safeName}.osi.yaml`);\n if (fs.existsSync(osiOld)) {\n let content = await fs.promises.readFile(osiOld, 'utf-8');\n content = content.replace(\n new RegExp(`(name:\\\\s*[\"']?)${oldName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}([\"']?)`, 'g'),\n `$1${safeName}$2`,\n );\n await fs.promises.writeFile(osiOld, content, 'utf-8');\n await fs.promises.rename(osiOld, osiNew);\n renamed.push(`models/${safeName}.osi.yaml`);\n }\n\n // Rename governance file\n const govOld = path.join(resolvedCtx, 'governance', `${oldName}.governance.yaml`);\n const govNew = path.join(resolvedCtx, 'governance', `${safeName}.governance.yaml`);\n if (fs.existsSync(govOld)) {\n let content = await fs.promises.readFile(govOld, 'utf-8');\n content = content.replace(/^model:\\s*.*$/m, `model: ${safeName}`);\n await fs.promises.writeFile(govOld, content, 'utf-8');\n await fs.promises.rename(govOld, govNew);\n renamed.push(`governance/${safeName}.governance.yaml`);\n }\n\n // Rename rules file\n const rulesOld = path.join(resolvedCtx, 'rules', `${oldName}.rules.yaml`);\n const rulesNew = path.join(resolvedCtx, 'rules', `${safeName}.rules.yaml`);\n if (fs.existsSync(rulesOld)) {\n let content = await fs.promises.readFile(rulesOld, 'utf-8');\n content = content.replace(/^model:\\s*.*$/m, `model: ${safeName}`);\n await fs.promises.writeFile(rulesOld, content, 'utf-8');\n await fs.promises.rename(rulesOld, rulesNew);\n renamed.push(`rules/${safeName}.rules.yaml`);\n }\n\n // Rename lineage file\n const linOld = path.join(resolvedCtx, 'lineage', `${oldName}.lineage.yaml`);\n const linNew = path.join(resolvedCtx, 'lineage', `${safeName}.lineage.yaml`);\n if (fs.existsSync(linOld)) {\n let content = await fs.promises.readFile(linOld, 'utf-8');\n content = content.replace(/^model:\\s*.*$/m, `model: ${safeName}`);\n await fs.promises.writeFile(linOld, content, 'utf-8');\n await fs.promises.rename(linOld, linNew);\n renamed.push(`lineage/${safeName}.lineage.yaml`);\n }\n\n // Recompile after rename\n await recompileAndBroadcast();\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, newName: safeName, renamed }));\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': 'application/json' });\n res.end(JSON.stringify({ error: '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 // Recompile after save so Astro picks up changes\n await recompileAndBroadcast();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ results }));\n return;\n }\n\n // --- All other requests: proxy to Astro dev server ---\n proxyToAstro(req, res);\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: (err as Error).message }));\n }\n });\n\n server.listen(port, host);\n\n // Clean up Astro process on server close\n server.on('close', () => {\n astroProc.kill();\n });\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.4",
|
|
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/core": "^0.4.
|
|
65
|
-
"@runcontext/
|
|
66
|
-
"@runcontext/
|
|
64
|
+
"@runcontext/core": "^0.4.4",
|
|
65
|
+
"@runcontext/mcp": "^0.4.4",
|
|
66
|
+
"@runcontext/site": "^0.4.4"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@types/node": "^25.3.3",
|
package/dist/server-DEKWPP3H.js
DELETED
|
@@ -1,192 +0,0 @@
|
|
|
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
|
|
@@ -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"]}
|