modelroom 0.0.0-stage → 0.0.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +149 -2
  4. package/dist/bin/modelroom.d.ts +3 -0
  5. package/dist/bin/modelroom.d.ts.map +1 -0
  6. package/dist/bin/modelroom.js +242 -0
  7. package/dist/bin/modelroom.js.map +1 -0
  8. package/dist/lib/agents/index.d.ts +22 -0
  9. package/dist/lib/agents/index.d.ts.map +1 -0
  10. package/dist/lib/agents/index.js +68 -0
  11. package/dist/lib/agents/index.js.map +1 -0
  12. package/dist/lib/artifact.d.ts +22 -0
  13. package/dist/lib/artifact.d.ts.map +1 -0
  14. package/dist/lib/artifact.js +62 -0
  15. package/dist/lib/artifact.js.map +1 -0
  16. package/dist/lib/config.d.ts +53 -0
  17. package/dist/lib/config.d.ts.map +1 -0
  18. package/dist/lib/config.js +92 -0
  19. package/dist/lib/config.js.map +1 -0
  20. package/dist/lib/facets.d.ts +62 -0
  21. package/dist/lib/facets.d.ts.map +1 -0
  22. package/dist/lib/facets.js +181 -0
  23. package/dist/lib/facets.js.map +1 -0
  24. package/dist/lib/frontmatter.d.ts +41 -0
  25. package/dist/lib/frontmatter.d.ts.map +1 -0
  26. package/dist/lib/frontmatter.js +219 -0
  27. package/dist/lib/frontmatter.js.map +1 -0
  28. package/dist/lib/lint.d.ts +26 -0
  29. package/dist/lib/lint.d.ts.map +1 -0
  30. package/dist/lib/lint.js +139 -0
  31. package/dist/lib/lint.js.map +1 -0
  32. package/dist/lib/opencode.d.ts +42 -0
  33. package/dist/lib/opencode.d.ts.map +1 -0
  34. package/dist/lib/opencode.js +229 -0
  35. package/dist/lib/opencode.js.map +1 -0
  36. package/dist/lib/pkg.d.ts +2 -0
  37. package/dist/lib/pkg.d.ts.map +1 -0
  38. package/dist/lib/pkg.js +19 -0
  39. package/dist/lib/pkg.js.map +1 -0
  40. package/dist/lib/rooms.d.ts +63 -0
  41. package/dist/lib/rooms.d.ts.map +1 -0
  42. package/dist/lib/rooms.js +178 -0
  43. package/dist/lib/rooms.js.map +1 -0
  44. package/dist/lib/server.d.ts +53 -0
  45. package/dist/lib/server.d.ts.map +1 -0
  46. package/dist/lib/server.js +538 -0
  47. package/dist/lib/server.js.map +1 -0
  48. package/docs/01_concept.md +69 -0
  49. package/docs/02-convention.md +109 -0
  50. package/docs/03-cli.md +103 -0
  51. package/docs/04-agents.md +88 -0
  52. package/examples/rooms/sign-in/BEHAVIOUR.md +23 -0
  53. package/examples/rooms/sign-in/BRIEF.md +15 -0
  54. package/examples/rooms/sign-in/SPEC.md +14 -0
  55. package/examples/rooms/sign-in/index.html +56 -0
  56. package/package.json +83 -4
  57. package/templates/AGENTS.md +31 -0
  58. package/templates/modelroom.json +14 -0
  59. package/templates/room/BEHAVIOUR.md +19 -0
  60. package/templates/room/BRIEF.md +16 -0
  61. package/templates/room/SPEC.md +9 -0
  62. package/templates/room/index.html +53 -0
  63. package/ui/dashboard.html +332 -0
  64. package/ui/modelroom.css +2 -0
  65. package/ui/room.html +466 -0
  66. package/ui/settings.html +125 -0
@@ -0,0 +1,538 @@
1
+ // modelroom — the local server
2
+ //
3
+ // Serves the workspace (dashboard / room / settings), streams changes over SSE,
4
+ // and runs the coding agent you configure. It never writes anything except the
5
+ // room docs you allow (`cfg.docs`), and it never reads your application code.
6
+ import { createServer } from 'node:http';
7
+ import { watch } from 'node:fs';
8
+ import { readFile, mkdir, open } from 'node:fs/promises';
9
+ import { dirname, extname, join, normalize, relative, sep } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { findPackageRoot } from './pkg.js';
12
+ import { loadConfig, loadState, saveState, applySettings, } from './config.js';
13
+ import { listRoomNames, roomInfo, publicRoom, isRoomName } from './rooms.js';
14
+ import { buildFacets, searchRooms } from './facets.js';
15
+ import { getAdapter, stripAnsi } from './agents/index.js';
16
+ import { renderArtifact, playwrightAvailable } from './artifact.js';
17
+ import { sessionView } from './opencode.js';
18
+ function isRecord(value) {
19
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
20
+ }
21
+ const pkgDir = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
22
+ const uiDir = join(pkgDir, 'ui');
23
+ const MIME = {
24
+ '.html': 'text/html; charset=utf-8',
25
+ '.css': 'text/css; charset=utf-8',
26
+ '.js': 'text/javascript; charset=utf-8',
27
+ '.mjs': 'text/javascript; charset=utf-8',
28
+ '.json': 'application/json; charset=utf-8',
29
+ '.md': 'text/plain; charset=utf-8',
30
+ '.png': 'image/png',
31
+ '.jpg': 'image/jpeg',
32
+ '.jpeg': 'image/jpeg',
33
+ '.svg': 'image/svg+xml',
34
+ '.ico': 'image/x-icon',
35
+ '.txt': 'text/plain; charset=utf-8',
36
+ '.woff2': 'font/woff2',
37
+ };
38
+ export async function createModelroom(opts = {}) {
39
+ const cfg = await loadConfig(opts.cwd || process.cwd());
40
+ if (opts.port)
41
+ cfg.port = Number(opts.port);
42
+ if (opts.host)
43
+ cfg.host = String(opts.host);
44
+ await mkdir(cfg.stateDir, { recursive: true }); // keep state local, never in git
45
+ const state = await loadState(cfg);
46
+ // Settings saved from the UI are local state: re-apply them over modelroom.json
47
+ // so a saved model survives the next start.
48
+ applySettings(cfg, state.settings);
49
+ const clients = new Set();
50
+ const chats = new Map(); // room -> { startedAt }
51
+ let rendering = false;
52
+ const status = {
53
+ phase: 'idle',
54
+ message: 'ready',
55
+ since: Date.now(),
56
+ lastEvent: null,
57
+ log: [],
58
+ };
59
+ const persist = () => saveState(cfg, state).catch(() => { });
60
+ const sessionOf = (room) => {
61
+ const adapter = getAdapter(cfg.agent.adapter);
62
+ if (!state.sessions[room]) {
63
+ state.sessions[room] = adapter.newSessionId();
64
+ void persist();
65
+ }
66
+ return state.sessions[room];
67
+ };
68
+ const broadcast = (event, data) => {
69
+ const payload = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
70
+ for (const res of clients) {
71
+ try {
72
+ res.write(payload);
73
+ }
74
+ catch {
75
+ /* cleaned on close */
76
+ }
77
+ }
78
+ };
79
+ const setStatus = (phase, message) => {
80
+ status.phase = phase;
81
+ status.message = message;
82
+ status.since = Date.now();
83
+ broadcast('status', { phase, message, since: status.since });
84
+ };
85
+ // ── files ─────────────────────────────────────────────────────────────────
86
+ const send = (res, code, type, body) => {
87
+ res.writeHead(code, { 'content-type': type, 'cache-control': 'no-store' });
88
+ res.end(body);
89
+ };
90
+ const sendJson = (res, code, obj) => send(res, code, MIME['.json'], JSON.stringify(obj));
91
+ function withinRoot(target) {
92
+ if (target !== cfg.rootDir && !target.startsWith(cfg.rootDir + sep))
93
+ return false;
94
+ // Serve ordinary project files only: no dotfiles (secrets, .git, .modelroom), no deps.
95
+ const parts = relative(cfg.rootDir, target).split(sep);
96
+ return parts.every((p) => p && !p.startsWith('.') && p !== 'node_modules');
97
+ }
98
+ async function serveFile(res, urlPath) {
99
+ const rel = decodeURIComponent(urlPath.replace(/^\/files\/?/, ''));
100
+ const target = normalize(join(cfg.rootDir, rel));
101
+ if (!withinRoot(target))
102
+ return send(res, 403, 'text/plain; charset=utf-8', 'forbidden');
103
+ try {
104
+ const body = await readFile(target);
105
+ res.writeHead(200, {
106
+ 'content-type': MIME[extname(target).toLowerCase()] ?? 'application/octet-stream',
107
+ 'cache-control': 'no-store',
108
+ });
109
+ res.end(body); // byte-identical: no injection
110
+ }
111
+ catch {
112
+ send(res, 404, 'text/plain; charset=utf-8', `not found: ${rel}`);
113
+ }
114
+ }
115
+ async function serveUi(res, page) {
116
+ try {
117
+ return send(res, 200, MIME['.html'], await readFile(join(uiDir, page)));
118
+ }
119
+ catch (err) {
120
+ // Log locally; the browser gets no path or stack information.
121
+ process.stderr.write(`modelroom: cannot serve ${page}: ${err instanceof Error ? err.message : String(err)}\n`);
122
+ return send(res, 500, 'text/plain; charset=utf-8', 'internal error');
123
+ }
124
+ }
125
+ /** Static assets of the tool itself (the built stylesheet and friends). */
126
+ async function serveUiAsset(res, urlPath) {
127
+ const rel = decodeURIComponent(urlPath.replace(/^\/ui\/?/, ''));
128
+ const target = normalize(join(uiDir, rel));
129
+ if (target !== uiDir && !target.startsWith(uiDir + sep)) {
130
+ return send(res, 403, 'text/plain; charset=utf-8', 'forbidden');
131
+ }
132
+ try {
133
+ const body = await readFile(target);
134
+ res.writeHead(200, {
135
+ 'content-type': MIME[extname(target).toLowerCase()] ?? 'application/octet-stream',
136
+ 'cache-control': 'no-store',
137
+ });
138
+ res.end(body);
139
+ }
140
+ catch {
141
+ send(res, 404, 'text/plain; charset=utf-8', `not found: /ui/${rel}`);
142
+ }
143
+ }
144
+ // ── watching ──────────────────────────────────────────────────────────────
145
+ const watchers = [];
146
+ function startWatching() {
147
+ for (const dir of [cfg.roomsDir]) {
148
+ try {
149
+ watchers.push(watch(dir, { recursive: true }, (_e, name) => {
150
+ const file = name
151
+ ? relative(cfg.rootDir, join(dir, String(name)))
152
+ : relative(cfg.rootDir, dir);
153
+ const at = Date.now();
154
+ if (file.includes('.modelroom'))
155
+ return;
156
+ status.lastEvent = { file, at };
157
+ status.phase = 'change';
158
+ status.message = `changed: ${file}`;
159
+ status.since = at;
160
+ broadcast('change', { file, at });
161
+ broadcast('status', { phase: 'change', message: status.message, since: at });
162
+ }));
163
+ }
164
+ catch (err) {
165
+ const message = err instanceof Error ? err.message : String(err);
166
+ process.stderr.write(`modelroom: cannot watch ${dir}: ${message}\n`);
167
+ }
168
+ }
169
+ }
170
+ // ── chat ──────────────────────────────────────────────────────────────────
171
+ function runChat(room, prompt) {
172
+ if (!prompt || !prompt.trim())
173
+ return { started: false, reason: 'empty prompt' };
174
+ if (chats.has(room))
175
+ return { started: false, reason: 'this room is already running' };
176
+ const adapter = getAdapter(cfg.agent.adapter);
177
+ const sessionId = sessionOf(room);
178
+ chats.set(room, { startedAt: Date.now() });
179
+ setStatus('chat', `${room}: agent is working…`);
180
+ broadcast('chat-start', { room, sessionId, prompt, at: Date.now() });
181
+ broadcast('chat', {
182
+ room,
183
+ sessionId,
184
+ stream: 'meta',
185
+ chunk: `$ ${cfg.agent.command} (agent=${cfg.agent.agent || '-'}, model=${cfg.agent.model || 'default'}, session=${sessionId})\n`,
186
+ });
187
+ const child = adapter.run({
188
+ cfg,
189
+ cwd: cfg.rootDir,
190
+ prompt,
191
+ sessionId,
192
+ onChunk: (stream, chunk) => {
193
+ const text = stripAnsi(chunk);
194
+ if (text)
195
+ broadcast('chat', { room, sessionId, stream, chunk: text });
196
+ },
197
+ onExit: (code, err) => {
198
+ chats.delete(room);
199
+ if (err)
200
+ setStatus('error', err);
201
+ else
202
+ setStatus(code === 0 ? 'ok' : 'error', `${room}: agent exited (${code})`);
203
+ broadcast('chat-done', { room, sessionId, code, error: err || null, at: Date.now() });
204
+ },
205
+ });
206
+ return { started: true, sessionId, pid: child.pid };
207
+ }
208
+ // ── artifact render (optional) ────────────────────────────────────────────
209
+ async function render(room, device) {
210
+ if (rendering)
211
+ return { ok: false, error: 'already rendering' };
212
+ rendering = true;
213
+ setStatus('rendering', `${room}: rendering ${cfg.artifact.replace(/\.html?$/i, '.png')}…`);
214
+ broadcast('render-start', { room, device, at: Date.now() });
215
+ try {
216
+ const result = await renderArtifact({ cfg, room, device });
217
+ if (result.ok) {
218
+ setStatus('ok', `${room}: ${cfg.artifact.replace(/\.html?$/i, '.png')} updated`);
219
+ }
220
+ else {
221
+ setStatus('error', result.error);
222
+ }
223
+ broadcast('render-done', { room, ...result, at: Date.now() });
224
+ return result;
225
+ }
226
+ finally {
227
+ rendering = false;
228
+ }
229
+ }
230
+ /** Read text and its mtime through one handle, so they describe one file. */
231
+ async function readDoc(path) {
232
+ const fh = await open(path, 'r');
233
+ try {
234
+ const s = await fh.stat();
235
+ return { text: await fh.readFile('utf8'), mtime: s.mtimeMs };
236
+ }
237
+ finally {
238
+ await fh.close();
239
+ }
240
+ }
241
+ async function readBody(req, limit = 400_000) {
242
+ let size = 0;
243
+ const chunks = [];
244
+ for await (const chunk of req) {
245
+ const buf = chunk;
246
+ size += buf.length;
247
+ if (size > limit)
248
+ throw new Error('payload too large');
249
+ chunks.push(buf);
250
+ }
251
+ return Buffer.concat(chunks).toString('utf8');
252
+ }
253
+ /** Parse a JSON body; answers 400 itself and returns null when it is malformed. */
254
+ async function jsonBody(req, res, optional = false) {
255
+ let raw;
256
+ try {
257
+ raw = await readBody(req);
258
+ }
259
+ catch (err) {
260
+ const detail = err instanceof Error ? err.message : String(err);
261
+ process.stderr.write(`modelroom: cannot read request body: ${detail}\n`);
262
+ sendJson(res, 400, { error: 'request body could not be read' });
263
+ return null;
264
+ }
265
+ if (!raw.trim() && optional)
266
+ return {};
267
+ try {
268
+ const parsed = JSON.parse(raw || '{}');
269
+ if (!isRecord(parsed)) {
270
+ sendJson(res, 400, { error: 'body must be a JSON object' });
271
+ return null;
272
+ }
273
+ return parsed;
274
+ }
275
+ catch (err) {
276
+ const detail = err instanceof Error ? err.message : String(err);
277
+ process.stderr.write(`modelroom: invalid JSON body: ${detail}\n`);
278
+ sendJson(res, 400, { error: 'body must be valid JSON' });
279
+ return null;
280
+ }
281
+ }
282
+ // ── routing ───────────────────────────────────────────────────────────────
283
+ const server = createServer(async (req, res) => {
284
+ try {
285
+ const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
286
+ const path = url.pathname;
287
+ const names = await listRoomNames(cfg);
288
+ const param = url.searchParams.get('room') || '';
289
+ const room = isRoomName(param) && names.includes(param) ? param : (names[0] ?? null);
290
+ if (path === '/health')
291
+ return send(res, 200, 'text/plain; charset=utf-8', 'ok');
292
+ if (path === '/favicon.ico') {
293
+ res.writeHead(204);
294
+ return res.end();
295
+ }
296
+ if (path === '/events') {
297
+ res.writeHead(200, {
298
+ 'content-type': 'text/event-stream; charset=utf-8',
299
+ 'cache-control': 'no-store',
300
+ connection: 'keep-alive',
301
+ 'x-accel-buffering': 'no',
302
+ });
303
+ res.write('retry: 1000\n\n');
304
+ res.write(`event: hello\ndata: ${JSON.stringify({ config: publicConfig(cfg), status })}\n\n`);
305
+ clients.add(res);
306
+ req.on('close', () => clients.delete(res));
307
+ return;
308
+ }
309
+ if (path === '/api/rooms') {
310
+ const rooms = await Promise.all(names.map((n) => roomInfo(cfg, n)));
311
+ return sendJson(res, 200, {
312
+ count: rooms.length,
313
+ rooms: rooms.map(publicRoom),
314
+ facets: buildFacets(rooms),
315
+ config: publicConfig(cfg),
316
+ });
317
+ }
318
+ // Full-text search happens here, over the derived index in memory: the
319
+ // browser never downloads every body just to filter the list.
320
+ if (path === '/api/search') {
321
+ const q = url.searchParams.get('q') || '';
322
+ const rooms = await Promise.all(names.map((n) => roomInfo(cfg, n)));
323
+ return sendJson(res, 200, { q, hits: searchRooms(rooms, q) });
324
+ }
325
+ if (path === '/api/room') {
326
+ if (!room)
327
+ return sendJson(res, 404, { error: 'no rooms' });
328
+ const info = await roomInfo(cfg, room);
329
+ return sendJson(res, 200, {
330
+ room: publicRoom(info),
331
+ session: state.sessions[room] || null,
332
+ status,
333
+ config: publicConfig(cfg),
334
+ });
335
+ }
336
+ if (path === '/api/doc' && req.method === 'GET') {
337
+ const name = url.searchParams.get('name') || '';
338
+ if (!cfg.docs.includes(name)) {
339
+ return sendJson(res, 400, { error: `name must be one of ${cfg.docs.join(', ')}` });
340
+ }
341
+ if (!room)
342
+ return sendJson(res, 404, { error: 'no rooms' });
343
+ try {
344
+ const p = join(cfg.roomsDir, room, name);
345
+ const { text, mtime } = await readDoc(p);
346
+ return sendJson(res, 200, { room, name, text, mtime });
347
+ }
348
+ catch {
349
+ return sendJson(res, 200, { room, name, text: '', mtime: 0, missing: true });
350
+ }
351
+ }
352
+ if (path === '/api/doc' && req.method === 'PUT') {
353
+ const body = await jsonBody(req, res);
354
+ if (!body)
355
+ return;
356
+ const target = isRoomName(body.room) && names.includes(body.room) ? body.room : room;
357
+ const name = typeof body.name === 'string' ? body.name : '';
358
+ if (!target)
359
+ return sendJson(res, 404, { error: 'no rooms' });
360
+ if (!cfg.docs.includes(name)) {
361
+ return sendJson(res, 403, { error: 'this file is not editable' });
362
+ }
363
+ const p = join(cfg.roomsDir, target, name);
364
+ // Check and write through one handle: nobody can swap the file between
365
+ // the mtime comparison and the write.
366
+ // 'a+' creates the file when it is missing and never truncates on open,
367
+ // so there is no window between "does it exist" and the write.
368
+ const fh = await open(p, 'a+');
369
+ try {
370
+ const s = await fh.stat();
371
+ if (body.mtime && Number(body.mtime) !== s.mtimeMs) {
372
+ return sendJson(res, 409, {
373
+ error: 'changed elsewhere',
374
+ text: await fh.readFile('utf8'),
375
+ mtime: s.mtimeMs,
376
+ });
377
+ }
378
+ const text = typeof body.text === 'string' ? body.text : '';
379
+ await fh.truncate(0);
380
+ await fh.writeFile(text, 'utf8');
381
+ const written = await fh.stat();
382
+ broadcast('doc-saved', {
383
+ room: target,
384
+ name,
385
+ mtime: written.mtimeMs,
386
+ at: Date.now(),
387
+ });
388
+ return sendJson(res, 200, { room: target, name, mtime: written.mtimeMs });
389
+ }
390
+ finally {
391
+ await fh.close();
392
+ }
393
+ }
394
+ if (path === '/api/doc')
395
+ return sendJson(res, 405, { error: 'GET or PUT' });
396
+ if (path === '/api/chat' && req.method === 'POST') {
397
+ const body = await jsonBody(req, res);
398
+ if (!body)
399
+ return;
400
+ const target = isRoomName(body.room) && names.includes(body.room) ? body.room : room;
401
+ if (!target)
402
+ return sendJson(res, 404, { error: 'no rooms' });
403
+ const r = runChat(target, typeof body.prompt === 'string' ? body.prompt : '');
404
+ return sendJson(res, r.started ? 202 : 409, { room: target, ...r });
405
+ }
406
+ if (path === '/api/session' && req.method === 'POST') {
407
+ const body = await jsonBody(req, res, true);
408
+ if (!body)
409
+ return; // malformed JSON: jsonBody already answered 400
410
+ const target = isRoomName(body.room) && names.includes(body.room) ? body.room : room;
411
+ if (!target)
412
+ return sendJson(res, 404, { error: 'no rooms' });
413
+ if (chats.has(target))
414
+ return sendJson(res, 409, { error: 'room is running' });
415
+ const adapter = getAdapter(cfg.agent.adapter);
416
+ const id = adapter.newSessionId();
417
+ state.sessions[target] = id;
418
+ await persist();
419
+ return sendJson(res, 202, { room: target, sessionId: id });
420
+ }
421
+ // What the agent did in this room: cost, tokens, context, transcript, MCP.
422
+ // opencode-only, and best-effort — a failure just means `available: false`.
423
+ if (path === '/api/session' && req.method === 'GET') {
424
+ if (!room)
425
+ return sendJson(res, 404, { error: 'no rooms' });
426
+ if (cfg.agent.adapter !== 'opencode') {
427
+ return sendJson(res, 200, { available: false, room, session: null });
428
+ }
429
+ const sessionId = state.sessions[room] ?? null;
430
+ const view = await sessionView(cfg, sessionId);
431
+ const base = { available: Boolean(view), room, session: sessionId };
432
+ return sendJson(res, 200, view ? { ...base, ...view } : base);
433
+ }
434
+ if (path === '/api/settings' && req.method === 'POST') {
435
+ const body = await jsonBody(req, res);
436
+ if (!body)
437
+ return;
438
+ const applied = applySettings(cfg, body);
439
+ Object.assign(state.settings, applied); // keep them local, and for the next start
440
+ await persist();
441
+ broadcast('settings', { applied, at: Date.now() });
442
+ return sendJson(res, 200, { applied, config: publicConfig(cfg) });
443
+ }
444
+ if (path === '/api/artifact' && req.method === 'POST') {
445
+ const body = await jsonBody(req, res, true);
446
+ if (!body)
447
+ return; // malformed JSON: jsonBody already answered 400
448
+ const target = isRoomName(body.room) && names.includes(body.room) ? body.room : room;
449
+ if (!target)
450
+ return sendJson(res, 404, { error: 'no rooms' });
451
+ const device = typeof body.device === 'string' ? body.device : 'tablet';
452
+ const result = await render(target, device);
453
+ return sendJson(res, result.ok ? 200 : 501, result);
454
+ }
455
+ if (path.startsWith('/files/'))
456
+ return await serveFile(res, path);
457
+ if (path.startsWith('/ui/'))
458
+ return await serveUiAsset(res, path);
459
+ const page = path === '/'
460
+ ? 'dashboard.html'
461
+ : path === '/settings'
462
+ ? 'settings.html'
463
+ : path.startsWith('/room/')
464
+ ? 'room.html'
465
+ : null;
466
+ if (page)
467
+ return await serveUi(res, page);
468
+ send(res, 404, 'text/plain; charset=utf-8', 'not found');
469
+ }
470
+ catch (err) {
471
+ if (res.headersSent) {
472
+ try {
473
+ res.end();
474
+ }
475
+ catch {
476
+ /* already closed */
477
+ }
478
+ return;
479
+ }
480
+ const code = err instanceof URIError ? 400 : 500;
481
+ if (code === 500) {
482
+ // The detail stays in the terminal; the browser gets nothing to probe with.
483
+ const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
484
+ process.stderr.write(`modelroom: ${detail}\n`);
485
+ }
486
+ try {
487
+ send(res, code, 'text/plain; charset=utf-8', code === 400 ? 'bad request' : 'internal error');
488
+ }
489
+ catch {
490
+ /* nothing we can do */
491
+ }
492
+ }
493
+ });
494
+ const heartbeat = setInterval(() => {
495
+ for (const res of clients) {
496
+ try {
497
+ res.write(': ping\n\n');
498
+ }
499
+ catch {
500
+ clients.delete(res);
501
+ }
502
+ }
503
+ }, 15000);
504
+ heartbeat.unref();
505
+ return {
506
+ cfg,
507
+ server,
508
+ status,
509
+ startWatching,
510
+ listen: () => new Promise((resolveListen, reject) => {
511
+ server.once('error', reject);
512
+ server.listen(cfg.port, cfg.host, () => resolveListen({ url: `http://${cfg.host}:${cfg.port}`, cfg }));
513
+ }),
514
+ close: () => new Promise((r) => {
515
+ clearInterval(heartbeat);
516
+ for (const w of watchers)
517
+ w.close();
518
+ server.close(() => r());
519
+ }),
520
+ };
521
+ }
522
+ export function publicConfig(cfg) {
523
+ return {
524
+ root: cfg.root,
525
+ rooms: cfg.rooms,
526
+ artifact: cfg.artifact,
527
+ docs: cfg.docs,
528
+ agent: {
529
+ adapter: cfg.agent.adapter,
530
+ command: cfg.agent.command,
531
+ agent: cfg.agent.agent,
532
+ model: cfg.agent.model,
533
+ standalone: cfg.agent.standalone,
534
+ },
535
+ };
536
+ }
537
+ export { playwrightAvailable };
538
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../lib/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ # Concept
2
+
3
+ modelroom is the **design layer** for software built with coding agents. It is a convention
4
+ plus a small local server; it does not bundle a model or a chat product.
5
+
6
+ ## The model room
7
+
8
+ A model room in real estate is furnished, full scale, and convincing enough to decide in —
9
+ but it is not the apartment you will live in. The design layer works the same way:
10
+
11
+ - it is **not the real thing** (no database, no server clock, no permissions);
12
+ - it is **convincing enough** that a decision can be made in it;
13
+ - each room is **shown to someone** before the real build starts.
14
+
15
+ Everything below follows from that one property: because a room is not the real thing, the
16
+ promises that only the real thing can keep live somewhere else.
17
+
18
+ ## Layers and who each file is shown to
19
+
20
+ Files are named by **what they are**, and each one has one audience. The audience decides
21
+ the wording.
22
+
23
+ | File | Answers | Written by | Shown to | Checked by |
24
+ |---|---|---|---|---|
25
+ | `BRIEF.md` | Why does this exist? What matters most? | director | director, everyone | humans |
26
+ | `SPEC.md` | What is on the screen that you cannot see? (devices, states, constraints, references) | designer | designer, agent, implementer | humans |
27
+ | `BEHAVIOUR.md` | What does it do? (operations and results) | designer | tests, implementer | system tests |
28
+ | `index.html`, `index.png` | What it looks like | agent | director, owner, implementer | screenshots + humans |
29
+ | `principles.md`, `components.md`, `design-tokens/` | The shared interior rules | designer | everyone | humans + tokens |
30
+
31
+ Two rules keep this from drifting:
32
+
33
+ 1. **The artifact owns what is visible.** `SPEC.md` never lists what can be seen in
34
+ `index.html`; if a statement can be shown in a screenshot, it belongs to the artifact.
35
+ 2. **No statement lives in two files.** If a rule must reach another file, reference it
36
+ (one line, by id) instead of copying it.
37
+
38
+ ## One-way dependency
39
+
40
+ ```
41
+ app ──reads──▶ design (artifacts, tokens)
42
+ design ──✗──▶ app (the design layer never reads the application)
43
+ ```
44
+
45
+ The design layer must not read the application. A model room has no plumbing, so promises
46
+ that depend on the real system (records, rounding, closing, permissions) are owned by the
47
+ application layer and are never copied here. What the design layer needs to know is handed
48
+ to it — in a brief, in an issue, or in conversation.
49
+
50
+ This is enforceable, not aspirational: an agent working in the design directory can be
51
+ denied access outside it.
52
+
53
+ ## What "behaviour" means here
54
+
55
+ `BEHAVIOUR.md` holds promises that the room itself can keep — operations and results that a
56
+ system test can observe. Promises that need the real system (for example, that a record
57
+ carries the server's timestamp) belong to the application layer, not to this file.
58
+
59
+ ## Why this shape
60
+
61
+ Agents make writing cheap; they do not make **agreement** cheap. A workspace that keeps the
62
+ reason (`BRIEF`), the invisible spec (`SPEC`), the promise (`BEHAVIOUR`) and the result (the
63
+ artifact) in separate, single-purpose files lets several agents — and several people — work
64
+ on the same screen without overwriting one another's meaning.
65
+
66
+ ## Status
67
+
68
+ Early. This document records the design of the convention; see the repository README for
69
+ what is implemented today.