@reactive-skills/runtime 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.
@@ -21,6 +21,7 @@ export declare class TelemetryServer {
21
21
  stop(): Promise<void>;
22
22
  private setCorsHeaders;
23
23
  private handleRequest;
24
+ private handleDashboard;
24
25
  private handleHealth;
25
26
  private handleState;
26
27
  private handleEventsHistory;
@@ -1,5 +1,6 @@
1
1
  import http from 'node:http';
2
2
  import { URL } from 'node:url';
3
+ import { renderDashboardHtml } from './dashboard.js';
3
4
  export class TelemetryServer {
4
5
  server = null;
5
6
  eventStore;
@@ -110,16 +111,21 @@ export class TelemetryServer {
110
111
  const hostHeader = req.headers.host || `${this.host}:${this.port}`;
111
112
  const parsedUrl = new URL(req.url || '/', `http://${hostHeader}`);
112
113
  const pathname = parsedUrl.pathname;
113
- if (req.method === 'GET' && (pathname === '/health' || pathname === '/status')) {
114
- this.handleHealth(res);
114
+ const isGetOrHead = req.method === 'GET' || req.method === 'HEAD';
115
+ if (isGetOrHead && (pathname === '/' || pathname === '/index.html')) {
116
+ this.handleDashboard(req, res);
115
117
  return;
116
118
  }
117
- if (req.method === 'GET' && pathname === '/state') {
118
- this.handleState(res);
119
+ if (isGetOrHead && (pathname === '/health' || pathname === '/status')) {
120
+ this.handleHealth(req, res);
119
121
  return;
120
122
  }
121
- if (req.method === 'GET' && pathname === '/events/history') {
122
- this.handleEventsHistory(parsedUrl, res);
123
+ if (isGetOrHead && pathname === '/state') {
124
+ this.handleState(req, res);
125
+ return;
126
+ }
127
+ if (isGetOrHead && pathname === '/events/history') {
128
+ this.handleEventsHistory(req, parsedUrl, res);
123
129
  return;
124
130
  }
125
131
  if (req.method === 'GET' && pathname === '/events') {
@@ -133,17 +139,43 @@ export class TelemetryServer {
133
139
  res.writeHead(404, { 'Content-Type': 'application/json' });
134
140
  res.end(JSON.stringify({ error: `Not found: ${pathname}` }));
135
141
  }
136
- handleHealth(res) {
142
+ handleDashboard(req, res) {
143
+ const html = renderDashboardHtml({
144
+ skillName: this.skillName,
145
+ port: this.getPort(),
146
+ host: this.host,
147
+ });
148
+ res.writeHead(200, {
149
+ 'Content-Type': 'text/html; charset=utf-8',
150
+ 'Content-Length': Buffer.byteLength(html),
151
+ });
152
+ if (req.method === 'HEAD') {
153
+ res.end();
154
+ }
155
+ else {
156
+ res.end(html);
157
+ }
158
+ }
159
+ handleHealth(req, res) {
137
160
  const payload = {
138
161
  status: 'ok',
139
162
  skillName: this.skillName,
140
163
  latestSeq: this.eventStore.getLatestSequence(),
141
164
  uptimeSeconds: Math.floor((Date.now() - this.startTime) / 1000),
142
165
  };
143
- res.writeHead(200, { 'Content-Type': 'application/json' });
144
- res.end(JSON.stringify(payload));
166
+ const body = JSON.stringify(payload);
167
+ res.writeHead(200, {
168
+ 'Content-Type': 'application/json',
169
+ 'Content-Length': Buffer.byteLength(body),
170
+ });
171
+ if (req.method === 'HEAD') {
172
+ res.end();
173
+ }
174
+ else {
175
+ res.end(body);
176
+ }
145
177
  }
146
- handleState(res) {
178
+ handleState(req, res) {
147
179
  const latestSeq = this.eventStore.getLatestSequence();
148
180
  const snapshot = this.eventStore.getLatestSnapshot();
149
181
  const activeState = this.fsmEngine ? this.fsmEngine.getCurrentState() : snapshot?.state;
@@ -155,10 +187,19 @@ export class TelemetryServer {
155
187
  context,
156
188
  snapshot,
157
189
  };
158
- res.writeHead(200, { 'Content-Type': 'application/json' });
159
- res.end(JSON.stringify(payload));
190
+ const body = JSON.stringify(payload);
191
+ res.writeHead(200, {
192
+ 'Content-Type': 'application/json',
193
+ 'Content-Length': Buffer.byteLength(body),
194
+ });
195
+ if (req.method === 'HEAD') {
196
+ res.end();
197
+ }
198
+ else {
199
+ res.end(body);
200
+ }
160
201
  }
161
- handleEventsHistory(url, res) {
202
+ handleEventsHistory(req, url, res) {
162
203
  const sinceSeqParam = url.searchParams.get('sinceSeq');
163
204
  const limitParam = url.searchParams.get('limit');
164
205
  const sinceSeq = sinceSeqParam !== null ? parseInt(sinceSeqParam, 10) : undefined;
@@ -173,8 +214,21 @@ export class TelemetryServer {
173
214
  if (limit !== undefined && !isNaN(limit) && limit > 0) {
174
215
  events = events.slice(-limit);
175
216
  }
176
- res.writeHead(200, { 'Content-Type': 'application/json' });
177
- res.end(JSON.stringify({ count: events.length, events }));
217
+ const payload = {
218
+ count: events.length,
219
+ events,
220
+ };
221
+ const body = JSON.stringify(payload);
222
+ res.writeHead(200, {
223
+ 'Content-Type': 'application/json',
224
+ 'Content-Length': Buffer.byteLength(body),
225
+ });
226
+ if (req.method === 'HEAD') {
227
+ res.end();
228
+ }
229
+ else {
230
+ res.end(body);
231
+ }
178
232
  }
179
233
  handleSseEvents(req, url, res) {
180
234
  res.writeHead(200, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactive-skills/runtime",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Reactive Skills Architecture (RSA) core runtime — FSM engine, event store, guard evaluator, projection engine, MCP server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",