@ebowwa/stack 0.1.2 → 0.1.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.
package/dist/index.js CHANGED
@@ -57619,7 +57619,71 @@ Prompt: ${prompt.slice(0, 100)}...`;
57619
57619
  if (!this.config.api)
57620
57620
  return;
57621
57621
  const port = this.config.api.port ?? 8911;
57622
- console.log(`[Stack] API would start on :${port} (implement with Bun.serve)`);
57622
+ const host = this.config.api.host ?? "0.0.0.0";
57623
+ const server = Bun.serve({
57624
+ port,
57625
+ host,
57626
+ fetch: async (req) => {
57627
+ const url = new URL(req.url);
57628
+ const path = url.pathname;
57629
+ const corsHeaders = {
57630
+ "Access-Control-Allow-Origin": "*",
57631
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
57632
+ "Access-Control-Allow-Headers": "Content-Type"
57633
+ };
57634
+ if (req.method === "OPTIONS") {
57635
+ return new Response(null, { headers: corsHeaders });
57636
+ }
57637
+ try {
57638
+ if (path === "/api/status" && req.method === "GET") {
57639
+ return Response.json(this.getStatusJSON(), { headers: corsHeaders });
57640
+ }
57641
+ if (path === "/api/ralph-loops" && req.method === "GET") {
57642
+ return Response.json(this.listRalphLoopsJSON(), { headers: corsHeaders });
57643
+ }
57644
+ if (path === "/api/ralph-loops" && req.method === "POST") {
57645
+ const body = await req.json();
57646
+ const prompt = body.prompt;
57647
+ if (!prompt) {
57648
+ return Response.json({ error: "Missing prompt" }, { status: 400, headers: corsHeaders });
57649
+ }
57650
+ const result = await this.startRalphLoop(prompt);
57651
+ return Response.json({ message: result }, { headers: corsHeaders });
57652
+ }
57653
+ const match = path.match(/^\/api\/ralph-loops\/(.+)$/);
57654
+ if (match && req.method === "DELETE") {
57655
+ const result = await this.stopRalphLoop(match[1]);
57656
+ return Response.json({ message: result }, { headers: corsHeaders });
57657
+ }
57658
+ if (path === "/health") {
57659
+ return Response.json({ status: "ok" }, { headers: corsHeaders });
57660
+ }
57661
+ return Response.json({ error: "Not found" }, { status: 404, headers: corsHeaders });
57662
+ } catch (error) {
57663
+ const errorMsg = error instanceof Error ? error.message : String(error);
57664
+ return Response.json({ error: errorMsg }, { status: 500, headers: corsHeaders });
57665
+ }
57666
+ }
57667
+ });
57668
+ console.log(`[Stack] API running on http://${host}:${port}`);
57669
+ }
57670
+ getStatusJSON() {
57671
+ return {
57672
+ node: this.config.node.name,
57673
+ channels: {
57674
+ ssh: this.state.channels.ssh,
57675
+ telegram: this.state.channels.telegram
57676
+ },
57677
+ api: {
57678
+ enabled: this.state.api.enabled,
57679
+ port: this.state.api.port
57680
+ },
57681
+ ralphLoops: this.state.ralphLoops.size,
57682
+ uptime: Math.floor((Date.now() - this.state.started.getTime()) / 1000)
57683
+ };
57684
+ }
57685
+ listRalphLoopsJSON() {
57686
+ return Array.from(this.state.ralphLoops.values());
57623
57687
  }
57624
57688
  async start() {
57625
57689
  console.log(`[Stack] Starting ${this.config.node.name}...`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ebowwa/stack",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Full-stack daemon orchestrator combining unified-router (cross-channel) and node-agent (Ralph orchestration)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -367,20 +367,96 @@ export class Stack {
367
367
  }
368
368
 
369
369
  // ============================================================
370
- // HTTP API (optional, delegates to node-agent)
370
+ // HTTP API
371
371
  // ============================================================
372
372
 
373
373
  private startAPI(): void {
374
374
  if (!this.config.api) return;
375
375
 
376
376
  const port = this.config.api.port ?? 8911;
377
- console.log(`[Stack] API would start on :${port} (implement with Bun.serve)`);
377
+ const host = this.config.api.host ?? "0.0.0.0";
378
+
379
+ const server = Bun.serve({
380
+ port,
381
+ host,
382
+ fetch: async (req) => {
383
+ const url = new URL(req.url);
384
+ const path = url.pathname;
385
+
386
+ // CORS headers
387
+ const corsHeaders = {
388
+ "Access-Control-Allow-Origin": "*",
389
+ "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
390
+ "Access-Control-Allow-Headers": "Content-Type",
391
+ };
392
+
393
+ if (req.method === "OPTIONS") {
394
+ return new Response(null, { headers: corsHeaders });
395
+ }
396
+
397
+ try {
398
+ // GET /api/status
399
+ if (path === "/api/status" && req.method === "GET") {
400
+ return Response.json(this.getStatusJSON(), { headers: corsHeaders });
401
+ }
402
+
403
+ // GET /api/ralph-loops
404
+ if (path === "/api/ralph-loops" && req.method === "GET") {
405
+ return Response.json(this.listRalphLoopsJSON(), { headers: corsHeaders });
406
+ }
407
+
408
+ // POST /api/ralph-loops
409
+ if (path === "/api/ralph-loops" && req.method === "POST") {
410
+ const body = await req.json();
411
+ const prompt = body.prompt;
412
+ if (!prompt) {
413
+ return Response.json({ error: "Missing prompt" }, { status: 400, headers: corsHeaders });
414
+ }
415
+ const result = await this.startRalphLoop(prompt);
416
+ return Response.json({ message: result }, { headers: corsHeaders });
417
+ }
418
+
419
+ // DELETE /api/ralph-loops/:id
420
+ const match = path.match(/^\/api\/ralph-loops\/(.+)$/);
421
+ if (match && req.method === "DELETE") {
422
+ const result = await this.stopRalphLoop(match[1]);
423
+ return Response.json({ message: result }, { headers: corsHeaders });
424
+ }
425
+
426
+ // GET /health
427
+ if (path === "/health") {
428
+ return Response.json({ status: "ok" }, { headers: corsHeaders });
429
+ }
430
+
431
+ return Response.json({ error: "Not found" }, { status: 404, headers: corsHeaders });
432
+ } catch (error) {
433
+ const errorMsg = error instanceof Error ? error.message : String(error);
434
+ return Response.json({ error: errorMsg }, { status: 500, headers: corsHeaders });
435
+ }
436
+ },
437
+ });
438
+
439
+ console.log(`[Stack] API running on http://${host}:${port}`);
440
+ }
441
+
442
+ private getStatusJSON(): object {
443
+ return {
444
+ node: this.config.node.name,
445
+ channels: {
446
+ ssh: this.state.channels.ssh,
447
+ telegram: this.state.channels.telegram,
448
+ },
449
+ api: {
450
+ enabled: this.state.api.enabled,
451
+ port: this.state.api.port,
452
+ },
453
+ ralphLoops: this.state.ralphLoops.size,
454
+ uptime: Math.floor((Date.now() - this.state.started.getTime()) / 1000),
455
+ };
456
+ }
378
457
 
379
- // TODO: Start HTTP server with routes:
380
- // GET /api/status - Stack status
381
- // GET /api/ralph-loops - List Ralph loops
382
- // POST /api/ralph-loops - Start Ralph loop
383
- // DELETE /api/ralph-loops/:id - Stop Ralph loop
458
+ private listRalphLoopsJSON(): object[] {
459
+ return Array.from(this.state.ralphLoops.values());
384
460
  }
385
461
 
386
462
  // ============================================================