@kb-labs/core-state-daemon 1.0.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.
package/README.md ADDED
@@ -0,0 +1,503 @@
1
+ # @kb-labs/state-daemon
2
+
3
+ HTTP daemon server for persistent cross-invocation state in KB Labs.
4
+
5
+ ## Overview
6
+
7
+ State Daemon provides a lightweight HTTP server that maintains persistent state across CLI command invocations, enabling fast in-memory caching with automatic TTL cleanup.
8
+
9
+ ### Features
10
+
11
+ - **Zero external dependencies**: Pure Node.js HTTP server
12
+ - **In-memory storage**: Fast key-value operations (~1ms)
13
+ - **Automatic TTL cleanup**: Background cleanup every 30s
14
+ - **HTTP REST API**: Simple GET/PUT/DELETE endpoints
15
+ - **Namespace isolation**: Per-plugin namespaces with statistics
16
+ - **Health monitoring**: `/health` and `/stats` endpoints
17
+ - **Graceful shutdown**: SIGTERM/SIGINT handling
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pnpm add @kb-labs/state-daemon
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Start Daemon
28
+
29
+ ```bash
30
+ # Default (localhost:7777)
31
+ kb-state-daemon
32
+
33
+ # Custom port
34
+ KB_STATE_DAEMON_PORT=8888 kb-state-daemon
35
+
36
+ # Custom host (be careful with security!)
37
+ KB_STATE_DAEMON_HOST=0.0.0.0 KB_STATE_DAEMON_PORT=7777 kb-state-daemon
38
+ ```
39
+
40
+ ### Environment Variables
41
+
42
+ | Variable | Default | Description |
43
+ |----------|---------|-------------|
44
+ | `KB_STATE_DAEMON_PORT` | `7777` | HTTP server port |
45
+ | `KB_STATE_DAEMON_HOST` | `localhost` | HTTP server host (use `0.0.0.0` for network access) |
46
+
47
+ ### Programmatic Usage
48
+
49
+ ```typescript
50
+ import { StateDaemonServer } from '@kb-labs/state-daemon';
51
+
52
+ const server = new StateDaemonServer({
53
+ port: 7777,
54
+ host: 'localhost',
55
+ });
56
+
57
+ await server.start();
58
+ console.log('State daemon running on localhost:7777');
59
+
60
+ // Graceful shutdown
61
+ process.on('SIGTERM', () => server.shutdown());
62
+ ```
63
+
64
+ ## HTTP API
65
+
66
+ ### Health Check
67
+
68
+ ```bash
69
+ GET /health
70
+ ```
71
+
72
+ **Response:**
73
+
74
+ ```json
75
+ {
76
+ "status": "ok",
77
+ "version": "0.1.0",
78
+ "stats": {
79
+ "uptime": 123456,
80
+ "totalEntries": 42,
81
+ "totalSize": 1024,
82
+ "hitRate": 0.85,
83
+ "missRate": 0.15,
84
+ "evictions": 5,
85
+ "namespaces": {
86
+ "mind": {
87
+ "entries": 30,
88
+ "sizeBytes": 768,
89
+ "lastAccess": 1638360000000
90
+ },
91
+ "workflow": {
92
+ "entries": 12,
93
+ "sizeBytes": 256,
94
+ "lastAccess": 1638360000000
95
+ }
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### Get Statistics
102
+
103
+ ```bash
104
+ GET /stats
105
+ ```
106
+
107
+ **Response:** Same as health stats.
108
+
109
+ ### Get Value
110
+
111
+ ```bash
112
+ GET /state/:key
113
+ ```
114
+
115
+ **Example:**
116
+
117
+ ```bash
118
+ curl http://localhost:7777/state/mind:query-123
119
+ ```
120
+
121
+ **Response (200 OK):**
122
+
123
+ ```json
124
+ {
125
+ "queryId": "Q-abc123",
126
+ "result": { ... },
127
+ "createdAt": "2025-11-29T12:00:00Z"
128
+ }
129
+ ```
130
+
131
+ **Response (404 Not Found):**
132
+
133
+ ```json
134
+ {
135
+ "error": "NOT_FOUND",
136
+ "message": "Key not found or expired"
137
+ }
138
+ ```
139
+
140
+ ### Set Value
141
+
142
+ ```bash
143
+ PUT /state/:key
144
+ Content-Type: application/json
145
+
146
+ {
147
+ "value": { ... },
148
+ "ttl": 60000 // Optional TTL in milliseconds
149
+ }
150
+ ```
151
+
152
+ **Example:**
153
+
154
+ ```bash
155
+ curl -X PUT http://localhost:7777/state/mind:query-123 \
156
+ -H "Content-Type: application/json" \
157
+ -d '{
158
+ "value": {"result": "cached data"},
159
+ "ttl": 60000
160
+ }'
161
+ ```
162
+
163
+ **Response (204 No Content)**
164
+
165
+ ### Delete Value
166
+
167
+ ```bash
168
+ DELETE /state/:key
169
+ ```
170
+
171
+ **Example:**
172
+
173
+ ```bash
174
+ curl -X DELETE http://localhost:7777/state/mind:query-123
175
+ ```
176
+
177
+ **Response (204 No Content)**
178
+
179
+ ### Clear Values
180
+
181
+ ```bash
182
+ POST /state/clear?pattern=<pattern>
183
+ ```
184
+
185
+ **Examples:**
186
+
187
+ ```bash
188
+ # Clear all entries
189
+ curl -X POST http://localhost:7777/state/clear
190
+
191
+ # Clear by namespace
192
+ curl -X POST http://localhost:7777/state/clear?pattern=mind:*
193
+
194
+ # Clear by prefix
195
+ curl -X POST http://localhost:7777/state/clear?pattern=mind:query-*
196
+ ```
197
+
198
+ **Response (204 No Content)**
199
+
200
+ ## Architecture
201
+
202
+ ### In-Memory Storage
203
+
204
+ ```typescript
205
+ // Stored as:
206
+ Map<string, CacheEntry>
207
+
208
+ interface CacheEntry {
209
+ value: unknown;
210
+ expiresAt: number;
211
+ namespace: string;
212
+ sizeBytes: number;
213
+ }
214
+ ```
215
+
216
+ ### TTL Cleanup
217
+
218
+ ```typescript
219
+ // Every 30 seconds
220
+ setInterval(() => {
221
+ const now = Date.now();
222
+ for (const [key, entry] of store.entries()) {
223
+ if (now > entry.expiresAt) {
224
+ store.delete(key);
225
+ evictions++;
226
+ }
227
+ }
228
+ }, 30000);
229
+ ```
230
+
231
+ ### Namespace Extraction
232
+
233
+ Keys are automatically parsed into namespaces:
234
+
235
+ - `mind:query-123` → namespace: `mind`
236
+ - `workflow:job-456` → namespace: `workflow`
237
+ - `cache:session-789` → namespace: `cache`
238
+
239
+ Statistics are tracked per namespace.
240
+
241
+ ## Performance
242
+
243
+ ### Benchmarks
244
+
245
+ | Operation | Latency | Throughput |
246
+ |-----------|---------|------------|
247
+ | GET | ~1ms | ~1000 ops/s |
248
+ | PUT | ~1ms | ~1000 ops/s |
249
+ | DELETE | ~0.5ms | ~2000 ops/s |
250
+
251
+ **Note:** Localhost performance. Network latency adds ~0.1-0.5ms.
252
+
253
+ ### Memory Usage
254
+
255
+ - **Overhead per entry:** ~100 bytes (key + metadata)
256
+ - **Default limit:** None (controlled by plugin quotas)
257
+ - **10,000 entries:** ~1 MB + data size
258
+
259
+ ### Comparison with File I/O
260
+
261
+ | Operation | Daemon | File I/O | Improvement |
262
+ |-----------|--------|----------|-------------|
263
+ | Cache read | 1ms | 10-50ms | 10-50x faster |
264
+ | Cache write | 1ms | 10-50ms | 10-50x faster |
265
+
266
+ ## Monitoring
267
+
268
+ ### Health Endpoint
269
+
270
+ ```bash
271
+ # Quick health check
272
+ curl -s http://localhost:7777/health | jq '.status'
273
+ # "ok"
274
+
275
+ # Full stats
276
+ curl -s http://localhost:7777/health | jq '.stats'
277
+ ```
278
+
279
+ ### Statistics
280
+
281
+ ```json
282
+ {
283
+ "uptime": 3600000, // 1 hour
284
+ "totalEntries": 1234,
285
+ "totalSize": 5242880, // ~5 MB
286
+ "hitRate": 0.85, // 85% cache hits
287
+ "missRate": 0.15, // 15% cache misses
288
+ "evictions": 42, // 42 entries evicted (expired)
289
+ "namespaces": {
290
+ "mind": {
291
+ "entries": 1000,
292
+ "sizeBytes": 4194304, // ~4 MB
293
+ "lastAccess": 1638360000000
294
+ }
295
+ }
296
+ }
297
+ ```
298
+
299
+ ## Lifecycle Management
300
+
301
+ ### Manual Start/Stop
302
+
303
+ ```bash
304
+ # Start
305
+ kb-state-daemon &
306
+ DAEMON_PID=$!
307
+
308
+ # Stop
309
+ kill $DAEMON_PID
310
+ ```
311
+
312
+ ### Systemd Service (Linux)
313
+
314
+ Create `/etc/systemd/system/kb-state-daemon.service`:
315
+
316
+ ```ini
317
+ [Unit]
318
+ Description=KB Labs State Daemon
319
+ After=network.target
320
+
321
+ [Service]
322
+ Type=simple
323
+ User=youruser
324
+ Environment="KB_STATE_DAEMON_PORT=7777"
325
+ Environment="KB_STATE_DAEMON_HOST=localhost"
326
+ ExecStart=/usr/local/bin/kb-state-daemon
327
+ Restart=on-failure
328
+ RestartSec=5s
329
+
330
+ [Install]
331
+ WantedBy=multi-user.target
332
+ ```
333
+
334
+ ```bash
335
+ sudo systemctl enable kb-state-daemon
336
+ sudo systemctl start kb-state-daemon
337
+ sudo systemctl status kb-state-daemon
338
+ ```
339
+
340
+ ### Launchd Service (macOS)
341
+
342
+ Create `~/Library/LaunchAgents/com.kb-labs.state-daemon.plist`:
343
+
344
+ ```xml
345
+ <?xml version="1.0" encoding="UTF-8"?>
346
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
347
+ <plist version="1.0">
348
+ <dict>
349
+ <key>Label</key>
350
+ <string>com.kb-labs.state-daemon</string>
351
+ <key>ProgramArguments</key>
352
+ <array>
353
+ <string>/usr/local/bin/kb-state-daemon</string>
354
+ </array>
355
+ <key>EnvironmentVariables</key>
356
+ <dict>
357
+ <key>KB_STATE_DAEMON_PORT</key>
358
+ <string>7777</string>
359
+ <key>KB_STATE_DAEMON_HOST</key>
360
+ <string>localhost</string>
361
+ </dict>
362
+ <key>RunAtLoad</key>
363
+ <true/>
364
+ <key>KeepAlive</key>
365
+ <true/>
366
+ <key>StandardOutPath</key>
367
+ <string>/tmp/kb-state-daemon.log</string>
368
+ <key>StandardErrorPath</key>
369
+ <string>/tmp/kb-state-daemon.error.log</string>
370
+ </dict>
371
+ </plist>
372
+ ```
373
+
374
+ ```bash
375
+ launchctl load ~/Library/LaunchAgents/com.kb-labs.state-daemon.plist
376
+ launchctl start com.kb-labs.state-daemon
377
+ launchctl list | grep kb-state-daemon
378
+ ```
379
+
380
+ ## Security Considerations
381
+
382
+ ### Network Access
383
+
384
+ **⚠️ Default: localhost only**
385
+
386
+ The daemon binds to `localhost` by default, preventing network access.
387
+
388
+ **❌ DO NOT expose to network without authentication:**
389
+
390
+ ```bash
391
+ # INSECURE - allows network access without auth
392
+ KB_STATE_DAEMON_HOST=0.0.0.0 kb-state-daemon
393
+ ```
394
+
395
+ ### Authentication
396
+
397
+ Currently, no authentication is implemented. The daemon should only be used on trusted localhost.
398
+
399
+ **Future improvements:**
400
+ - API key authentication
401
+ - JWT tokens
402
+ - TLS/SSL support
403
+ - Rate limiting
404
+
405
+ ### Namespace Isolation
406
+
407
+ Namespace isolation is enforced at the runtime level, not the daemon level. The daemon itself does not enforce permissions - it's a dumb key-value store.
408
+
409
+ **Permission enforcement:** Handled by `@kb-labs/plugin-runtime` via `createStateAPI()`.
410
+
411
+ ## Troubleshooting
412
+
413
+ ### Daemon Not Starting
414
+
415
+ ```bash
416
+ # Check if port is already in use
417
+ lsof -i :7777
418
+
419
+ # Try custom port
420
+ KB_STATE_DAEMON_PORT=8888 kb-state-daemon
421
+ ```
422
+
423
+ ### Connection Refused
424
+
425
+ ```bash
426
+ # Check if daemon is running
427
+ curl http://localhost:7777/health
428
+
429
+ # Check logs (if using systemd)
430
+ sudo journalctl -u kb-state-daemon -f
431
+
432
+ # Check logs (if using launchd)
433
+ tail -f /tmp/kb-state-daemon.log
434
+ ```
435
+
436
+ ### High Memory Usage
437
+
438
+ ```bash
439
+ # Check stats
440
+ curl -s http://localhost:7777/stats | jq '.totalSize'
441
+
442
+ # Clear specific namespace
443
+ curl -X POST http://localhost:7777/state/clear?pattern=mind:*
444
+ ```
445
+
446
+ ### Slow Performance
447
+
448
+ Daemon performance should be ~1ms per operation on localhost. If slower:
449
+
450
+ 1. **Network latency**: Check if using remote host instead of localhost
451
+ 2. **Large payloads**: Check `totalSize` in stats (>100 MB may cause slowdown)
452
+ 3. **System load**: Check CPU/memory usage
453
+
454
+ ## API Client Libraries
455
+
456
+ ### JavaScript/TypeScript
457
+
458
+ ```typescript
459
+ import { HTTPStateBroker } from '@kb-labs/state-broker';
460
+
461
+ const client = new HTTPStateBroker('http://localhost:7777');
462
+ await client.set('key', 'value', 60000);
463
+ const value = await client.get('key');
464
+ ```
465
+
466
+ ### Shell (curl)
467
+
468
+ ```bash
469
+ # Helper functions
470
+ kb_state_get() {
471
+ curl -s "http://localhost:7777/state/$1" | jq -r '.value'
472
+ }
473
+
474
+ kb_state_set() {
475
+ curl -s -X PUT "http://localhost:7777/state/$1" \
476
+ -H "Content-Type: application/json" \
477
+ -d "{\"value\":\"$2\",\"ttl\":$3}"
478
+ }
479
+
480
+ kb_state_delete() {
481
+ curl -s -X DELETE "http://localhost:7777/state/$1"
482
+ }
483
+
484
+ # Usage
485
+ kb_state_set "my-key" "my-value" 60000
486
+ kb_state_get "my-key"
487
+ kb_state_delete "my-key"
488
+ ```
489
+
490
+ ## Related Packages
491
+
492
+ - **@kb-labs/state-broker** - Client library for state daemon
493
+ - **@kb-labs/plugin-runtime** - Runtime integration with permissions
494
+ - **@kb-labs/plugin-manifest** - Permission type definitions
495
+
496
+ ## License
497
+
498
+ MIT
499
+
500
+ ## See Also
501
+
502
+ - [ADR-0037: State Broker for Persistent Cache](../../kb-labs-mind/docs/adr/0037-state-broker-persistent-cache.md)
503
+ - [State Broker README](../state-broker/README.md)
package/dist/bin.cjs ADDED
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var http = require('http');
5
+
6
+ // ../core-state-broker/dist/index.js
7
+ var InMemoryStateBroker = class {
8
+ constructor(cleanupIntervalMs = 3e4) {
9
+ this.cleanupIntervalMs = cleanupIntervalMs;
10
+ this.startTime = Date.now();
11
+ this.cleanupInterval = setInterval(() => this.cleanup(), cleanupIntervalMs);
12
+ }
13
+ store = /* @__PURE__ */ new Map();
14
+ cleanupInterval;
15
+ startTime;
16
+ hits = 0;
17
+ misses = 0;
18
+ evictions = 0;
19
+ async get(key) {
20
+ const entry = this.store.get(key);
21
+ if (!entry) {
22
+ this.misses++;
23
+ return null;
24
+ }
25
+ if (Date.now() > entry.expiresAt) {
26
+ this.store.delete(key);
27
+ this.misses++;
28
+ return null;
29
+ }
30
+ this.hits++;
31
+ return entry.value;
32
+ }
33
+ async set(key, value, ttl = 3e5) {
34
+ const namespace = this.extractNamespace(key);
35
+ const size = this.estimateSize(value);
36
+ this.store.set(key, {
37
+ value,
38
+ expiresAt: Date.now() + ttl,
39
+ size,
40
+ namespace
41
+ });
42
+ }
43
+ async delete(key) {
44
+ this.store.delete(key);
45
+ }
46
+ async clear(pattern) {
47
+ if (!pattern) {
48
+ this.store.clear();
49
+ return;
50
+ }
51
+ const prefix = pattern.replace("*", "");
52
+ for (const key of this.store.keys()) {
53
+ if (key.startsWith(prefix)) {
54
+ this.store.delete(key);
55
+ }
56
+ }
57
+ }
58
+ async getStats() {
59
+ const namespaces = {};
60
+ const byTenant = {};
61
+ let totalSize = 0;
62
+ const now = Date.now();
63
+ for (const [key, entry] of this.store.entries()) {
64
+ if (!namespaces[entry.namespace]) {
65
+ namespaces[entry.namespace] = { entries: 0, size: 0, oldestEntry: now };
66
+ }
67
+ const ns = namespaces[entry.namespace];
68
+ ns.entries++;
69
+ ns.size += entry.size;
70
+ totalSize += entry.size;
71
+ const entryAge = entry.expiresAt - now;
72
+ if (entryAge < ns.oldestEntry) {
73
+ ns.oldestEntry = entryAge;
74
+ }
75
+ const tenant = this.extractTenant(key);
76
+ if (!byTenant[tenant]) {
77
+ byTenant[tenant] = { entries: 0, size: 0, lastAccess: now };
78
+ }
79
+ byTenant[tenant].entries++;
80
+ byTenant[tenant].size += entry.size;
81
+ if (entry.expiresAt > byTenant[tenant].lastAccess) {
82
+ byTenant[tenant].lastAccess = entry.expiresAt;
83
+ }
84
+ }
85
+ const totalRequests = this.hits + this.misses;
86
+ const hitRate = totalRequests > 0 ? this.hits / totalRequests : 0;
87
+ const missRate = totalRequests > 0 ? this.misses / totalRequests : 0;
88
+ return {
89
+ uptime: Date.now() - this.startTime,
90
+ totalEntries: this.store.size,
91
+ totalSize,
92
+ hitRate,
93
+ missRate,
94
+ evictions: this.evictions,
95
+ namespaces,
96
+ byTenant
97
+ // ← New: stats by tenant
98
+ };
99
+ }
100
+ async getHealth() {
101
+ const stats = await this.getStats();
102
+ return {
103
+ status: "ok",
104
+ version: "0.1.0",
105
+ stats
106
+ };
107
+ }
108
+ async stop() {
109
+ clearInterval(this.cleanupInterval);
110
+ this.store.clear();
111
+ }
112
+ /**
113
+ * Cleanup expired entries
114
+ */
115
+ cleanup() {
116
+ const now = Date.now();
117
+ for (const [key, entry] of this.store.entries()) {
118
+ if (now > entry.expiresAt) {
119
+ this.store.delete(key);
120
+ this.evictions++;
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Extract namespace from key (format: namespace:key or tenant:tenantId:namespace:key)
126
+ */
127
+ extractNamespace(key) {
128
+ const parts = key.split(":");
129
+ if (parts[0] === "tenant" && parts.length >= 3) {
130
+ return parts[2] || "default";
131
+ }
132
+ return parts[0] || "default";
133
+ }
134
+ /**
135
+ * Extract tenant ID from key (format: tenant:tenantId:namespace:key)
136
+ * For backward compatibility, returns 'default' if no tenant prefix
137
+ */
138
+ extractTenant(key) {
139
+ const parts = key.split(":");
140
+ if (parts[0] === "tenant" && parts.length >= 2) {
141
+ return parts[1] || "default";
142
+ }
143
+ return "default";
144
+ }
145
+ /**
146
+ * Estimate size of value in bytes
147
+ */
148
+ estimateSize(value) {
149
+ try {
150
+ return JSON.stringify(value).length;
151
+ } catch {
152
+ return 0;
153
+ }
154
+ }
155
+ };
156
+
157
+ // src/server.ts
158
+ var StateDaemonServer = class {
159
+ constructor(config = {}) {
160
+ this.config = config;
161
+ this.broker = new InMemoryStateBroker();
162
+ }
163
+ broker;
164
+ jobsManager = null;
165
+ // DISABLED: JobsManager has missing dependencies
166
+ server = null;
167
+ isShuttingDown = false;
168
+ async start() {
169
+ const port2 = this.config.port ?? 7777;
170
+ const host2 = this.config.host ?? "localhost";
171
+ if (this.jobsManager) {
172
+ await this.jobsManager.initialize();
173
+ }
174
+ this.server = http.createServer((req, res) => this.handleRequest(req, res));
175
+ process.on("SIGTERM", () => this.shutdown());
176
+ process.on("SIGINT", () => this.shutdown());
177
+ return new Promise((resolve, reject) => {
178
+ this.server.listen(port2, host2, () => {
179
+ console.log(`State daemon listening on ${host2}:${port2}`);
180
+ if (this.jobsManager) {
181
+ console.log("Jobs manager enabled - HTTP endpoints available at /jobs");
182
+ }
183
+ resolve();
184
+ });
185
+ this.server.on("error", reject);
186
+ });
187
+ }
188
+ async stop() {
189
+ if (this.jobsManager) {
190
+ await this.jobsManager.dispose();
191
+ }
192
+ await this.broker.stop();
193
+ if (this.server) {
194
+ return new Promise((resolve) => {
195
+ this.server.close(() => resolve());
196
+ });
197
+ }
198
+ }
199
+ async shutdown() {
200
+ this.isShuttingDown = true;
201
+ console.log("Shutting down state daemon...");
202
+ await this.stop();
203
+ process.exit(0);
204
+ }
205
+ async handleRequest(req, res) {
206
+ res.setHeader("Access-Control-Allow-Origin", "*");
207
+ res.setHeader("Access-Control-Allow-Methods", "GET, PUT, DELETE, POST, OPTIONS");
208
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
209
+ if (req.method === "OPTIONS") {
210
+ res.writeHead(204);
211
+ res.end();
212
+ return;
213
+ }
214
+ const url = new URL(req.url, `http://${req.headers.host}`);
215
+ try {
216
+ if (req.method === "GET" && url.pathname === "/health") {
217
+ const health = await this.broker.getHealth();
218
+ res.writeHead(200, { "Content-Type": "application/json" });
219
+ res.end(JSON.stringify(health));
220
+ return;
221
+ }
222
+ if (req.method === "GET" && url.pathname === "/stats") {
223
+ const stats = await this.broker.getStats();
224
+ res.writeHead(200, { "Content-Type": "application/json" });
225
+ res.end(JSON.stringify(stats));
226
+ return;
227
+ }
228
+ if (req.method === "GET" && url.pathname.startsWith("/state/")) {
229
+ const key = decodeURIComponent(url.pathname.slice(7));
230
+ const value = await this.broker.get(key);
231
+ if (value === null) {
232
+ res.writeHead(404);
233
+ res.end();
234
+ return;
235
+ }
236
+ res.writeHead(200, { "Content-Type": "application/json" });
237
+ res.end(JSON.stringify(value));
238
+ return;
239
+ }
240
+ if (req.method === "PUT" && url.pathname.startsWith("/state/")) {
241
+ const key = decodeURIComponent(url.pathname.slice(7));
242
+ const body = await this.readBody(req);
243
+ const { value, ttl } = JSON.parse(body);
244
+ await this.broker.set(key, value, ttl);
245
+ res.writeHead(204);
246
+ res.end();
247
+ return;
248
+ }
249
+ if (req.method === "DELETE" && url.pathname.startsWith("/state/")) {
250
+ const key = decodeURIComponent(url.pathname.slice(7));
251
+ await this.broker.delete(key);
252
+ res.writeHead(204);
253
+ res.end();
254
+ return;
255
+ }
256
+ if (req.method === "POST" && url.pathname === "/state/clear") {
257
+ const pattern = url.searchParams.get("pattern") || void 0;
258
+ await this.broker.clear(pattern);
259
+ res.writeHead(204);
260
+ res.end();
261
+ return;
262
+ }
263
+ if (req.method === "GET" && url.pathname === "/jobs") {
264
+ if (!this.jobsManager) {
265
+ res.writeHead(503, { "Content-Type": "application/json" });
266
+ res.end(JSON.stringify({ error: "Jobs manager not enabled" }));
267
+ return;
268
+ }
269
+ const jobs = this.jobsManager.listJobs();
270
+ res.writeHead(200, { "Content-Type": "application/json" });
271
+ res.end(JSON.stringify({ jobs }));
272
+ return;
273
+ }
274
+ if (req.method === "POST" && url.pathname.startsWith("/jobs/") && url.pathname.endsWith("/trigger")) {
275
+ if (!this.jobsManager) {
276
+ res.writeHead(503, { "Content-Type": "application/json" });
277
+ res.end(JSON.stringify({ error: "Jobs manager not enabled" }));
278
+ return;
279
+ }
280
+ const id = decodeURIComponent(url.pathname.slice(6, -8));
281
+ try {
282
+ await this.jobsManager.triggerJob(id);
283
+ res.writeHead(200, { "Content-Type": "application/json" });
284
+ res.end(JSON.stringify({ ok: true, message: `Job ${id} triggered successfully` }));
285
+ } catch (error) {
286
+ res.writeHead(404, { "Content-Type": "application/json" });
287
+ res.end(JSON.stringify({
288
+ error: error instanceof Error ? error.message : "Job not found"
289
+ }));
290
+ }
291
+ return;
292
+ }
293
+ if (req.method === "GET" && url.pathname === "/jobs/stats") {
294
+ if (!this.jobsManager) {
295
+ res.writeHead(503, { "Content-Type": "application/json" });
296
+ res.end(JSON.stringify({ error: "Jobs manager not enabled" }));
297
+ return;
298
+ }
299
+ const stats = this.jobsManager.getStats();
300
+ res.writeHead(200, { "Content-Type": "application/json" });
301
+ res.end(JSON.stringify(stats));
302
+ return;
303
+ }
304
+ res.writeHead(404, { "Content-Type": "application/json" });
305
+ res.end(JSON.stringify({ error: "Not Found" }));
306
+ } catch (error) {
307
+ console.error("Request error:", error);
308
+ res.writeHead(500, { "Content-Type": "application/json" });
309
+ res.end(JSON.stringify({
310
+ error: error instanceof Error ? error.message : "Internal Server Error"
311
+ }));
312
+ }
313
+ }
314
+ async readBody(req) {
315
+ return new Promise((resolve, reject) => {
316
+ const chunks = [];
317
+ req.on("data", (chunk) => chunks.push(chunk));
318
+ req.on("end", () => resolve(Buffer.concat(chunks).toString()));
319
+ req.on("error", reject);
320
+ });
321
+ }
322
+ };
323
+
324
+ // src/bin.ts
325
+ var port = process.env.KB_STATE_DAEMON_PORT ? parseInt(process.env.KB_STATE_DAEMON_PORT, 10) : 7777;
326
+ var host = process.env.KB_STATE_DAEMON_HOST ?? "localhost";
327
+ var server = new StateDaemonServer({ port, host });
328
+ server.start().catch((error) => {
329
+ console.error("Failed to start state daemon:", error);
330
+ process.exit(1);
331
+ });
332
+ //# sourceMappingURL=bin.cjs.map
333
+ //# sourceMappingURL=bin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core-state-broker/src/backends/in-memory.ts","../src/server.ts","../src/bin.ts"],"names":["port","host","createServer"],"mappings":"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","file":"bin.cjs","sourcesContent":["/**\n * In-memory state broker with TTL cleanup\n */\n\nimport type { StateBroker, BrokerStats, HealthStatus } from '../index';\n\ninterface CacheEntry {\n value: unknown;\n expiresAt: number;\n size: number; // estimated size in bytes\n namespace: string;\n}\n\nexport class InMemoryStateBroker implements StateBroker {\n private store = new Map<string, CacheEntry>();\n private cleanupInterval: NodeJS.Timeout;\n private startTime: number;\n private hits = 0;\n private misses = 0;\n private evictions = 0;\n\n constructor(\n private cleanupIntervalMs = 30_000 // cleanup every 30s\n ) {\n this.startTime = Date.now();\n this.cleanupInterval = setInterval(() => this.cleanup(), cleanupIntervalMs);\n }\n\n async get<T>(key: string): Promise<T | null> {\n const entry = this.store.get(key);\n\n if (!entry) {\n this.misses++;\n return null;\n }\n\n // Check expiration\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n this.misses++;\n return null;\n }\n\n this.hits++;\n return entry.value as T;\n }\n\n async set<T>(key: string, value: T, ttl = 300_000): Promise<void> {\n const namespace = this.extractNamespace(key);\n const size = this.estimateSize(value);\n\n this.store.set(key, {\n value,\n expiresAt: Date.now() + ttl,\n size,\n namespace,\n });\n }\n\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n\n async clear(pattern?: string): Promise<void> {\n if (!pattern) {\n this.store.clear();\n return;\n }\n\n // Simple pattern matching (namespace prefix)\n const prefix = pattern.replace('*', '');\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.store.delete(key);\n }\n }\n }\n\n async getStats(): Promise<BrokerStats> {\n const namespaces: Record<string, { entries: number; size: number; oldestEntry: number }> = {};\n const byTenant: Record<string, { entries: number; size: number; lastAccess: number }> = {};\n let totalSize = 0;\n const now = Date.now();\n\n for (const [key, entry] of this.store.entries()) {\n // Namespace stats (existing)\n if (!namespaces[entry.namespace]) {\n namespaces[entry.namespace] = { entries: 0, size: 0, oldestEntry: now };\n }\n\n const ns = namespaces[entry.namespace]!;\n ns.entries++;\n ns.size += entry.size;\n totalSize += entry.size;\n\n const entryAge = entry.expiresAt - now;\n if (entryAge < ns.oldestEntry) {\n ns.oldestEntry = entryAge;\n }\n\n // Tenant stats (new - multi-tenancy support)\n const tenant = this.extractTenant(key);\n if (!byTenant[tenant]) {\n byTenant[tenant] = { entries: 0, size: 0, lastAccess: now };\n }\n byTenant[tenant].entries++;\n byTenant[tenant].size += entry.size;\n if (entry.expiresAt > byTenant[tenant].lastAccess) {\n byTenant[tenant].lastAccess = entry.expiresAt;\n }\n }\n\n const totalRequests = this.hits + this.misses;\n const hitRate = totalRequests > 0 ? this.hits / totalRequests : 0;\n const missRate = totalRequests > 0 ? this.misses / totalRequests : 0;\n\n return {\n uptime: Date.now() - this.startTime,\n totalEntries: this.store.size,\n totalSize,\n hitRate,\n missRate,\n evictions: this.evictions,\n namespaces,\n byTenant, // ← New: stats by tenant\n };\n }\n\n async getHealth(): Promise<HealthStatus> {\n const stats = await this.getStats();\n return {\n status: 'ok',\n version: '0.1.0',\n stats,\n };\n }\n\n async stop(): Promise<void> {\n clearInterval(this.cleanupInterval);\n this.store.clear();\n }\n\n /**\n * Cleanup expired entries\n */\n private cleanup() {\n const now = Date.now();\n for (const [key, entry] of this.store.entries()) {\n if (now > entry.expiresAt) {\n this.store.delete(key);\n this.evictions++;\n }\n }\n }\n\n /**\n * Extract namespace from key (format: namespace:key or tenant:tenantId:namespace:key)\n */\n private extractNamespace(key: string): string {\n const parts = key.split(':');\n // New format: tenant:default:mind:key → namespace: mind\n // Old format: mind:key → namespace: mind\n if (parts[0] === 'tenant' && parts.length >= 3) {\n return parts[2] || 'default';\n }\n return parts[0] || 'default';\n }\n\n /**\n * Extract tenant ID from key (format: tenant:tenantId:namespace:key)\n * For backward compatibility, returns 'default' if no tenant prefix\n */\n private extractTenant(key: string): string {\n const parts = key.split(':');\n // New format: tenant:default:mind:key → tenant: default\n // Old format: mind:key → tenant: default (backward compatible)\n if (parts[0] === 'tenant' && parts.length >= 2) {\n return parts[1] || 'default';\n }\n return 'default';\n }\n\n /**\n * Estimate size of value in bytes\n */\n private estimateSize(value: unknown): number {\n try {\n return JSON.stringify(value).length;\n } catch {\n return 0;\n }\n }\n}\n","/**\n * State daemon HTTP server\n */\n\nimport { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { InMemoryStateBroker } from '@kb-labs/core-state-broker';\n// import { JobsManager } from './jobs-manager'; // DISABLED: missing dependencies\n\nexport interface StateDaemonConfig {\n port?: number;\n host?: string;\n enableJobs?: boolean;\n}\n\nexport class StateDaemonServer {\n private broker: InMemoryStateBroker;\n private jobsManager: any | null = null; // DISABLED: JobsManager has missing dependencies\n private server: Server | null = null;\n private isShuttingDown = false;\n\n constructor(private config: StateDaemonConfig = {}) {\n this.broker = new InMemoryStateBroker();\n\n // Initialize jobs manager if enabled\n // DISABLED: JobsManager has missing dependencies\n // if (this.config.enableJobs !== false) {\n // const createLogger = (prefix: string = '[jobs]'): import('@kb-labs/core-platform').ILogger => ({\n // trace: (msg: string, meta?: Record<string, unknown>) => console.debug(prefix, '[trace]', msg, meta),\n // debug: (msg: string, meta?: Record<string, unknown>) => console.debug(prefix, msg, meta),\n // info: (msg: string, meta?: Record<string, unknown>) => console.log(prefix, msg, meta),\n // warn: (msg: string, meta?: Record<string, unknown>) => console.warn(prefix, msg, meta),\n // error: (msg: string, error?: Error, meta?: Record<string, unknown>) => console.error(prefix, msg, error, meta),\n // child: (bindings: Record<string, unknown>) => createLogger(`${prefix}:${JSON.stringify(bindings)}`),\n // });\n\n // this.jobsManager = new JobsManager({\n // logger: createLogger('[jobs]'),\n // });\n // }\n }\n\n async start(): Promise<void> {\n const port = this.config.port ?? 7777;\n const host = this.config.host ?? 'localhost';\n\n // Initialize jobs manager if enabled\n if (this.jobsManager) {\n await this.jobsManager.initialize();\n }\n\n this.server = createServer((req, res) => this.handleRequest(req, res));\n\n // Handle shutdown signals\n process.on('SIGTERM', () => this.shutdown());\n process.on('SIGINT', () => this.shutdown());\n\n return new Promise((resolve, reject) => {\n this.server!.listen(port, host, () => {\n console.log(`State daemon listening on ${host}:${port}`);\n if (this.jobsManager) {\n console.log('Jobs manager enabled - HTTP endpoints available at /jobs');\n }\n resolve();\n });\n\n this.server!.on('error', reject);\n });\n }\n\n async stop(): Promise<void> {\n // Dispose jobs manager\n if (this.jobsManager) {\n await this.jobsManager.dispose();\n }\n\n await this.broker.stop();\n\n if (this.server) {\n return new Promise((resolve) => {\n this.server!.close(() => resolve());\n });\n }\n }\n\n private async shutdown(): Promise<void> {\n this.isShuttingDown = true;\n console.log('Shutting down state daemon...');\n await this.stop();\n process.exit(0);\n }\n\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n // CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, PUT, DELETE, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n // Handle OPTIONS (preflight)\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = new URL(req.url!, `http://${req.headers.host}`);\n\n try {\n // GET /health\n if (req.method === 'GET' && url.pathname === '/health') {\n const health = await this.broker.getHealth();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(health));\n return;\n }\n\n // GET /stats\n if (req.method === 'GET' && url.pathname === '/stats') {\n const stats = await this.broker.getStats();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(stats));\n return;\n }\n\n // GET /state/:key\n if (req.method === 'GET' && url.pathname.startsWith('/state/')) {\n const key = decodeURIComponent(url.pathname.slice(7));\n const value = await this.broker.get(key);\n\n if (value === null) {\n res.writeHead(404);\n res.end();\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(value));\n return;\n }\n\n // PUT /state/:key\n if (req.method === 'PUT' && url.pathname.startsWith('/state/')) {\n const key = decodeURIComponent(url.pathname.slice(7));\n const body = await this.readBody(req);\n const { value, ttl } = JSON.parse(body);\n\n await this.broker.set(key, value, ttl);\n\n res.writeHead(204);\n res.end();\n return;\n }\n\n // DELETE /state/:key\n if (req.method === 'DELETE' && url.pathname.startsWith('/state/')) {\n const key = decodeURIComponent(url.pathname.slice(7));\n await this.broker.delete(key);\n\n res.writeHead(204);\n res.end();\n return;\n }\n\n // POST /state/clear\n if (req.method === 'POST' && url.pathname === '/state/clear') {\n const pattern = url.searchParams.get('pattern') || undefined;\n await this.broker.clear(pattern);\n\n res.writeHead(204);\n res.end();\n return;\n }\n\n // GET /jobs - List all registered jobs\n if (req.method === 'GET' && url.pathname === '/jobs') {\n if (!this.jobsManager) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Jobs manager not enabled' }));\n return;\n }\n\n const jobs = this.jobsManager.listJobs();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jobs }));\n return;\n }\n\n // POST /jobs/:id/trigger - Manually trigger a job\n if (req.method === 'POST' && url.pathname.startsWith('/jobs/') && url.pathname.endsWith('/trigger')) {\n if (!this.jobsManager) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Jobs manager not enabled' }));\n return;\n }\n\n const id = decodeURIComponent(url.pathname.slice(6, -8)); // Remove \"/jobs/\" and \"/trigger\"\n\n try {\n await this.jobsManager.triggerJob(id);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, message: `Job ${id} triggered successfully` }));\n } catch (error) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({\n error: error instanceof Error ? error.message : 'Job not found'\n }));\n }\n return;\n }\n\n // GET /jobs/stats - Get jobs statistics\n if (req.method === 'GET' && url.pathname === '/jobs/stats') {\n if (!this.jobsManager) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Jobs manager not enabled' }));\n return;\n }\n\n const stats = this.jobsManager.getStats();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(stats));\n return;\n }\n\n // 404 Not Found\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not Found' }));\n } catch (error) {\n console.error('Request error:', error);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({\n error: error instanceof Error ? error.message : 'Internal Server Error',\n }));\n }\n }\n\n private async readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString()));\n req.on('error', reject);\n });\n }\n}\n","/**\n * State daemon CLI\n */\n\nimport { StateDaemonServer } from './server';\n\nconst port = process.env.KB_STATE_DAEMON_PORT\n ? parseInt(process.env.KB_STATE_DAEMON_PORT, 10)\n : 7777;\n\nconst host = process.env.KB_STATE_DAEMON_HOST ?? 'localhost';\n\nconst server = new StateDaemonServer({ port, host });\n\nserver.start().catch((error) => {\n console.error('Failed to start state daemon:', error);\n process.exit(1);\n});\n"]}
package/dist/index.js ADDED
@@ -0,0 +1,173 @@
1
+ import { createServer } from 'http';
2
+ import { InMemoryStateBroker } from '@kb-labs/core-state-broker';
3
+
4
+ // src/server.ts
5
+ var StateDaemonServer = class {
6
+ constructor(config = {}) {
7
+ this.config = config;
8
+ this.broker = new InMemoryStateBroker();
9
+ }
10
+ broker;
11
+ jobsManager = null;
12
+ // DISABLED: JobsManager has missing dependencies
13
+ server = null;
14
+ isShuttingDown = false;
15
+ async start() {
16
+ const port = this.config.port ?? 7777;
17
+ const host = this.config.host ?? "localhost";
18
+ if (this.jobsManager) {
19
+ await this.jobsManager.initialize();
20
+ }
21
+ this.server = createServer((req, res) => this.handleRequest(req, res));
22
+ process.on("SIGTERM", () => this.shutdown());
23
+ process.on("SIGINT", () => this.shutdown());
24
+ return new Promise((resolve, reject) => {
25
+ this.server.listen(port, host, () => {
26
+ console.log(`State daemon listening on ${host}:${port}`);
27
+ if (this.jobsManager) {
28
+ console.log("Jobs manager enabled - HTTP endpoints available at /jobs");
29
+ }
30
+ resolve();
31
+ });
32
+ this.server.on("error", reject);
33
+ });
34
+ }
35
+ async stop() {
36
+ if (this.jobsManager) {
37
+ await this.jobsManager.dispose();
38
+ }
39
+ await this.broker.stop();
40
+ if (this.server) {
41
+ return new Promise((resolve) => {
42
+ this.server.close(() => resolve());
43
+ });
44
+ }
45
+ }
46
+ async shutdown() {
47
+ this.isShuttingDown = true;
48
+ console.log("Shutting down state daemon...");
49
+ await this.stop();
50
+ process.exit(0);
51
+ }
52
+ async handleRequest(req, res) {
53
+ res.setHeader("Access-Control-Allow-Origin", "*");
54
+ res.setHeader("Access-Control-Allow-Methods", "GET, PUT, DELETE, POST, OPTIONS");
55
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
56
+ if (req.method === "OPTIONS") {
57
+ res.writeHead(204);
58
+ res.end();
59
+ return;
60
+ }
61
+ const url = new URL(req.url, `http://${req.headers.host}`);
62
+ try {
63
+ if (req.method === "GET" && url.pathname === "/health") {
64
+ const health = await this.broker.getHealth();
65
+ res.writeHead(200, { "Content-Type": "application/json" });
66
+ res.end(JSON.stringify(health));
67
+ return;
68
+ }
69
+ if (req.method === "GET" && url.pathname === "/stats") {
70
+ const stats = await this.broker.getStats();
71
+ res.writeHead(200, { "Content-Type": "application/json" });
72
+ res.end(JSON.stringify(stats));
73
+ return;
74
+ }
75
+ if (req.method === "GET" && url.pathname.startsWith("/state/")) {
76
+ const key = decodeURIComponent(url.pathname.slice(7));
77
+ const value = await this.broker.get(key);
78
+ if (value === null) {
79
+ res.writeHead(404);
80
+ res.end();
81
+ return;
82
+ }
83
+ res.writeHead(200, { "Content-Type": "application/json" });
84
+ res.end(JSON.stringify(value));
85
+ return;
86
+ }
87
+ if (req.method === "PUT" && url.pathname.startsWith("/state/")) {
88
+ const key = decodeURIComponent(url.pathname.slice(7));
89
+ const body = await this.readBody(req);
90
+ const { value, ttl } = JSON.parse(body);
91
+ await this.broker.set(key, value, ttl);
92
+ res.writeHead(204);
93
+ res.end();
94
+ return;
95
+ }
96
+ if (req.method === "DELETE" && url.pathname.startsWith("/state/")) {
97
+ const key = decodeURIComponent(url.pathname.slice(7));
98
+ await this.broker.delete(key);
99
+ res.writeHead(204);
100
+ res.end();
101
+ return;
102
+ }
103
+ if (req.method === "POST" && url.pathname === "/state/clear") {
104
+ const pattern = url.searchParams.get("pattern") || void 0;
105
+ await this.broker.clear(pattern);
106
+ res.writeHead(204);
107
+ res.end();
108
+ return;
109
+ }
110
+ if (req.method === "GET" && url.pathname === "/jobs") {
111
+ if (!this.jobsManager) {
112
+ res.writeHead(503, { "Content-Type": "application/json" });
113
+ res.end(JSON.stringify({ error: "Jobs manager not enabled" }));
114
+ return;
115
+ }
116
+ const jobs = this.jobsManager.listJobs();
117
+ res.writeHead(200, { "Content-Type": "application/json" });
118
+ res.end(JSON.stringify({ jobs }));
119
+ return;
120
+ }
121
+ if (req.method === "POST" && url.pathname.startsWith("/jobs/") && url.pathname.endsWith("/trigger")) {
122
+ if (!this.jobsManager) {
123
+ res.writeHead(503, { "Content-Type": "application/json" });
124
+ res.end(JSON.stringify({ error: "Jobs manager not enabled" }));
125
+ return;
126
+ }
127
+ const id = decodeURIComponent(url.pathname.slice(6, -8));
128
+ try {
129
+ await this.jobsManager.triggerJob(id);
130
+ res.writeHead(200, { "Content-Type": "application/json" });
131
+ res.end(JSON.stringify({ ok: true, message: `Job ${id} triggered successfully` }));
132
+ } catch (error) {
133
+ res.writeHead(404, { "Content-Type": "application/json" });
134
+ res.end(JSON.stringify({
135
+ error: error instanceof Error ? error.message : "Job not found"
136
+ }));
137
+ }
138
+ return;
139
+ }
140
+ if (req.method === "GET" && url.pathname === "/jobs/stats") {
141
+ if (!this.jobsManager) {
142
+ res.writeHead(503, { "Content-Type": "application/json" });
143
+ res.end(JSON.stringify({ error: "Jobs manager not enabled" }));
144
+ return;
145
+ }
146
+ const stats = this.jobsManager.getStats();
147
+ res.writeHead(200, { "Content-Type": "application/json" });
148
+ res.end(JSON.stringify(stats));
149
+ return;
150
+ }
151
+ res.writeHead(404, { "Content-Type": "application/json" });
152
+ res.end(JSON.stringify({ error: "Not Found" }));
153
+ } catch (error) {
154
+ console.error("Request error:", error);
155
+ res.writeHead(500, { "Content-Type": "application/json" });
156
+ res.end(JSON.stringify({
157
+ error: error instanceof Error ? error.message : "Internal Server Error"
158
+ }));
159
+ }
160
+ }
161
+ async readBody(req) {
162
+ return new Promise((resolve, reject) => {
163
+ const chunks = [];
164
+ req.on("data", (chunk) => chunks.push(chunk));
165
+ req.on("end", () => resolve(Buffer.concat(chunks).toString()));
166
+ req.on("error", reject);
167
+ });
168
+ }
169
+ };
170
+
171
+ export { StateDaemonServer };
172
+ //# sourceMappingURL=index.js.map
173
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * State daemon HTTP server\n */\n\nimport { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { InMemoryStateBroker } from '@kb-labs/core-state-broker';\n// import { JobsManager } from './jobs-manager'; // DISABLED: missing dependencies\n\nexport interface StateDaemonConfig {\n port?: number;\n host?: string;\n enableJobs?: boolean;\n}\n\nexport class StateDaemonServer {\n private broker: InMemoryStateBroker;\n private jobsManager: any | null = null; // DISABLED: JobsManager has missing dependencies\n private server: Server | null = null;\n private isShuttingDown = false;\n\n constructor(private config: StateDaemonConfig = {}) {\n this.broker = new InMemoryStateBroker();\n\n // Initialize jobs manager if enabled\n // DISABLED: JobsManager has missing dependencies\n // if (this.config.enableJobs !== false) {\n // const createLogger = (prefix: string = '[jobs]'): import('@kb-labs/core-platform').ILogger => ({\n // trace: (msg: string, meta?: Record<string, unknown>) => console.debug(prefix, '[trace]', msg, meta),\n // debug: (msg: string, meta?: Record<string, unknown>) => console.debug(prefix, msg, meta),\n // info: (msg: string, meta?: Record<string, unknown>) => console.log(prefix, msg, meta),\n // warn: (msg: string, meta?: Record<string, unknown>) => console.warn(prefix, msg, meta),\n // error: (msg: string, error?: Error, meta?: Record<string, unknown>) => console.error(prefix, msg, error, meta),\n // child: (bindings: Record<string, unknown>) => createLogger(`${prefix}:${JSON.stringify(bindings)}`),\n // });\n\n // this.jobsManager = new JobsManager({\n // logger: createLogger('[jobs]'),\n // });\n // }\n }\n\n async start(): Promise<void> {\n const port = this.config.port ?? 7777;\n const host = this.config.host ?? 'localhost';\n\n // Initialize jobs manager if enabled\n if (this.jobsManager) {\n await this.jobsManager.initialize();\n }\n\n this.server = createServer((req, res) => this.handleRequest(req, res));\n\n // Handle shutdown signals\n process.on('SIGTERM', () => this.shutdown());\n process.on('SIGINT', () => this.shutdown());\n\n return new Promise((resolve, reject) => {\n this.server!.listen(port, host, () => {\n console.log(`State daemon listening on ${host}:${port}`);\n if (this.jobsManager) {\n console.log('Jobs manager enabled - HTTP endpoints available at /jobs');\n }\n resolve();\n });\n\n this.server!.on('error', reject);\n });\n }\n\n async stop(): Promise<void> {\n // Dispose jobs manager\n if (this.jobsManager) {\n await this.jobsManager.dispose();\n }\n\n await this.broker.stop();\n\n if (this.server) {\n return new Promise((resolve) => {\n this.server!.close(() => resolve());\n });\n }\n }\n\n private async shutdown(): Promise<void> {\n this.isShuttingDown = true;\n console.log('Shutting down state daemon...');\n await this.stop();\n process.exit(0);\n }\n\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\n // CORS headers\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'GET, PUT, DELETE, POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n // Handle OPTIONS (preflight)\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = new URL(req.url!, `http://${req.headers.host}`);\n\n try {\n // GET /health\n if (req.method === 'GET' && url.pathname === '/health') {\n const health = await this.broker.getHealth();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(health));\n return;\n }\n\n // GET /stats\n if (req.method === 'GET' && url.pathname === '/stats') {\n const stats = await this.broker.getStats();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(stats));\n return;\n }\n\n // GET /state/:key\n if (req.method === 'GET' && url.pathname.startsWith('/state/')) {\n const key = decodeURIComponent(url.pathname.slice(7));\n const value = await this.broker.get(key);\n\n if (value === null) {\n res.writeHead(404);\n res.end();\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(value));\n return;\n }\n\n // PUT /state/:key\n if (req.method === 'PUT' && url.pathname.startsWith('/state/')) {\n const key = decodeURIComponent(url.pathname.slice(7));\n const body = await this.readBody(req);\n const { value, ttl } = JSON.parse(body);\n\n await this.broker.set(key, value, ttl);\n\n res.writeHead(204);\n res.end();\n return;\n }\n\n // DELETE /state/:key\n if (req.method === 'DELETE' && url.pathname.startsWith('/state/')) {\n const key = decodeURIComponent(url.pathname.slice(7));\n await this.broker.delete(key);\n\n res.writeHead(204);\n res.end();\n return;\n }\n\n // POST /state/clear\n if (req.method === 'POST' && url.pathname === '/state/clear') {\n const pattern = url.searchParams.get('pattern') || undefined;\n await this.broker.clear(pattern);\n\n res.writeHead(204);\n res.end();\n return;\n }\n\n // GET /jobs - List all registered jobs\n if (req.method === 'GET' && url.pathname === '/jobs') {\n if (!this.jobsManager) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Jobs manager not enabled' }));\n return;\n }\n\n const jobs = this.jobsManager.listJobs();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ jobs }));\n return;\n }\n\n // POST /jobs/:id/trigger - Manually trigger a job\n if (req.method === 'POST' && url.pathname.startsWith('/jobs/') && url.pathname.endsWith('/trigger')) {\n if (!this.jobsManager) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Jobs manager not enabled' }));\n return;\n }\n\n const id = decodeURIComponent(url.pathname.slice(6, -8)); // Remove \"/jobs/\" and \"/trigger\"\n\n try {\n await this.jobsManager.triggerJob(id);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ ok: true, message: `Job ${id} triggered successfully` }));\n } catch (error) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({\n error: error instanceof Error ? error.message : 'Job not found'\n }));\n }\n return;\n }\n\n // GET /jobs/stats - Get jobs statistics\n if (req.method === 'GET' && url.pathname === '/jobs/stats') {\n if (!this.jobsManager) {\n res.writeHead(503, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Jobs manager not enabled' }));\n return;\n }\n\n const stats = this.jobsManager.getStats();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(stats));\n return;\n }\n\n // 404 Not Found\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not Found' }));\n } catch (error) {\n console.error('Request error:', error);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({\n error: error instanceof Error ? error.message : 'Internal Server Error',\n }));\n }\n }\n\n private async readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString()));\n req.on('error', reject);\n });\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@kb-labs/core-state-daemon",
3
+ "version": "1.0.0",
4
+ "description": "State daemon server for persistent cross-invocation state",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "kb-state-daemon": "./dist/bin.cjs"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "sideEffects": false,
22
+ "dependencies": {
23
+ "@kb-labs/core-state-broker": "link:../core-state-broker",
24
+ "@kb-labs/core-runtime": "link:../core-runtime",
25
+ "@kb-labs/core-platform": "link:../core-platform",
26
+ "@kb-labs/plugin-runtime": "link:../../../kb-labs-plugin/packages/plugin-runtime",
27
+ "@kb-labs/shared-command-kit": "link:../../../kb-labs-shared/packages/shared-command-kit"
28
+ },
29
+ "devDependencies": {
30
+ "@kb-labs/devkit": "link:../../../kb-labs-devkit",
31
+ "@types/node": "^24.3.3",
32
+ "rimraf": "^6.0.1",
33
+ "tsup": "^8.5.0",
34
+ "typescript": "^5.6.3",
35
+ "vitest": "^3.2.4"
36
+ },
37
+ "engines": {
38
+ "node": ">=20.0.0",
39
+ "pnpm": ">=9.0.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "scripts": {
45
+ "clean": "rimraf dist",
46
+ "build": "pnpm clean && tsup --config tsup.bin.config.ts && tsup --config tsup.lib.config.ts",
47
+ "dev": "tsup --config tsup.config.ts --watch",
48
+ "type-check": "tsc --noEmit",
49
+ "test": "vitest run --passWithNoTests",
50
+ "test:watch": "vitest",
51
+ "lint": "eslint src --ext .ts",
52
+ "lint:fix": "eslint . --fix"
53
+ }
54
+ }