@juspay/neurolink 8.42.0 → 8.43.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.
Files changed (162) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +26 -17
  3. package/dist/cli/commands/serve.d.ts +24 -0
  4. package/dist/cli/commands/serve.js +565 -0
  5. package/dist/cli/commands/server.d.ts +26 -0
  6. package/dist/cli/commands/server.js +750 -0
  7. package/dist/cli/parser.js +7 -1
  8. package/dist/cli/utils/serverUtils.d.ts +71 -0
  9. package/dist/cli/utils/serverUtils.js +149 -0
  10. package/dist/index.d.ts +20 -0
  11. package/dist/index.js +42 -0
  12. package/dist/lib/index.d.ts +20 -0
  13. package/dist/lib/index.js +42 -0
  14. package/dist/lib/neurolink.d.ts +33 -4
  15. package/dist/lib/neurolink.js +41 -12
  16. package/dist/lib/server/abstract/baseServerAdapter.d.ts +171 -0
  17. package/dist/lib/server/abstract/baseServerAdapter.js +541 -0
  18. package/dist/lib/server/adapters/expressAdapter.d.ts +71 -0
  19. package/dist/lib/server/adapters/expressAdapter.js +487 -0
  20. package/dist/lib/server/adapters/fastifyAdapter.d.ts +71 -0
  21. package/dist/lib/server/adapters/fastifyAdapter.js +468 -0
  22. package/dist/lib/server/adapters/honoAdapter.d.ts +72 -0
  23. package/dist/lib/server/adapters/honoAdapter.js +567 -0
  24. package/dist/lib/server/adapters/index.d.ts +7 -0
  25. package/dist/lib/server/adapters/index.js +8 -0
  26. package/dist/lib/server/adapters/koaAdapter.d.ts +79 -0
  27. package/dist/lib/server/adapters/koaAdapter.js +511 -0
  28. package/dist/lib/server/errors.d.ts +193 -0
  29. package/dist/lib/server/errors.js +487 -0
  30. package/dist/lib/server/factory/serverAdapterFactory.d.ts +73 -0
  31. package/dist/lib/server/factory/serverAdapterFactory.js +161 -0
  32. package/dist/lib/server/index.d.ts +44 -0
  33. package/dist/lib/server/index.js +105 -0
  34. package/dist/lib/server/middleware/abortSignal.d.ts +61 -0
  35. package/dist/lib/server/middleware/abortSignal.js +112 -0
  36. package/dist/lib/server/middleware/auth.d.ts +228 -0
  37. package/dist/lib/server/middleware/auth.js +389 -0
  38. package/dist/lib/server/middleware/cache.d.ts +208 -0
  39. package/dist/lib/server/middleware/cache.js +357 -0
  40. package/dist/lib/server/middleware/common.d.ts +108 -0
  41. package/dist/lib/server/middleware/common.js +250 -0
  42. package/dist/lib/server/middleware/deprecation.d.ts +58 -0
  43. package/dist/lib/server/middleware/deprecation.js +191 -0
  44. package/dist/lib/server/middleware/index.d.ts +12 -0
  45. package/dist/lib/server/middleware/index.js +13 -0
  46. package/dist/lib/server/middleware/mcpBodyAttachment.d.ts +42 -0
  47. package/dist/lib/server/middleware/mcpBodyAttachment.js +64 -0
  48. package/dist/lib/server/middleware/rateLimit.d.ts +148 -0
  49. package/dist/lib/server/middleware/rateLimit.js +228 -0
  50. package/dist/lib/server/middleware/validation.d.ts +147 -0
  51. package/dist/lib/server/middleware/validation.js +389 -0
  52. package/dist/lib/server/openapi/generator.d.ts +107 -0
  53. package/dist/lib/server/openapi/generator.js +399 -0
  54. package/dist/lib/server/openapi/index.d.ts +7 -0
  55. package/dist/lib/server/openapi/index.js +37 -0
  56. package/dist/lib/server/openapi/schemas.d.ts +94 -0
  57. package/dist/lib/server/openapi/schemas.js +696 -0
  58. package/dist/lib/server/openapi/templates.d.ts +108 -0
  59. package/dist/lib/server/openapi/templates.js +375 -0
  60. package/dist/lib/server/routes/agentRoutes.d.ts +9 -0
  61. package/dist/lib/server/routes/agentRoutes.js +113 -0
  62. package/dist/lib/server/routes/healthRoutes.d.ts +9 -0
  63. package/dist/lib/server/routes/healthRoutes.js +171 -0
  64. package/dist/lib/server/routes/index.d.ts +42 -0
  65. package/dist/lib/server/routes/index.js +50 -0
  66. package/dist/lib/server/routes/mcpRoutes.d.ts +9 -0
  67. package/dist/lib/server/routes/mcpRoutes.js +315 -0
  68. package/dist/lib/server/routes/memoryRoutes.d.ts +11 -0
  69. package/dist/lib/server/routes/memoryRoutes.js +345 -0
  70. package/dist/lib/server/routes/openApiRoutes.d.ts +34 -0
  71. package/dist/lib/server/routes/openApiRoutes.js +127 -0
  72. package/dist/lib/server/routes/toolRoutes.d.ts +9 -0
  73. package/dist/lib/server/routes/toolRoutes.js +200 -0
  74. package/dist/lib/server/streaming/dataStream.d.ts +344 -0
  75. package/dist/lib/server/streaming/dataStream.js +484 -0
  76. package/dist/lib/server/streaming/index.d.ts +5 -0
  77. package/dist/lib/server/streaming/index.js +12 -0
  78. package/dist/lib/server/types.d.ts +800 -0
  79. package/dist/lib/server/types.js +68 -0
  80. package/dist/lib/server/utils/redaction.d.ts +75 -0
  81. package/dist/lib/server/utils/redaction.js +335 -0
  82. package/dist/lib/server/utils/validation.d.ts +215 -0
  83. package/dist/lib/server/utils/validation.js +225 -0
  84. package/dist/lib/server/websocket/WebSocketHandler.d.ts +107 -0
  85. package/dist/lib/server/websocket/WebSocketHandler.js +384 -0
  86. package/dist/lib/server/websocket/index.d.ts +4 -0
  87. package/dist/lib/server/websocket/index.js +5 -0
  88. package/dist/neurolink.d.ts +33 -4
  89. package/dist/neurolink.js +41 -12
  90. package/dist/server/abstract/baseServerAdapter.d.ts +171 -0
  91. package/dist/server/abstract/baseServerAdapter.js +540 -0
  92. package/dist/server/adapters/expressAdapter.d.ts +71 -0
  93. package/dist/server/adapters/expressAdapter.js +486 -0
  94. package/dist/server/adapters/fastifyAdapter.d.ts +71 -0
  95. package/dist/server/adapters/fastifyAdapter.js +467 -0
  96. package/dist/server/adapters/honoAdapter.d.ts +72 -0
  97. package/dist/server/adapters/honoAdapter.js +566 -0
  98. package/dist/server/adapters/index.d.ts +7 -0
  99. package/dist/server/adapters/index.js +7 -0
  100. package/dist/server/adapters/koaAdapter.d.ts +79 -0
  101. package/dist/server/adapters/koaAdapter.js +510 -0
  102. package/dist/server/errors.d.ts +193 -0
  103. package/dist/server/errors.js +486 -0
  104. package/dist/server/factory/serverAdapterFactory.d.ts +73 -0
  105. package/dist/server/factory/serverAdapterFactory.js +160 -0
  106. package/dist/server/index.d.ts +44 -0
  107. package/dist/server/index.js +104 -0
  108. package/dist/server/middleware/abortSignal.d.ts +61 -0
  109. package/dist/server/middleware/abortSignal.js +111 -0
  110. package/dist/server/middleware/auth.d.ts +228 -0
  111. package/dist/server/middleware/auth.js +388 -0
  112. package/dist/server/middleware/cache.d.ts +208 -0
  113. package/dist/server/middleware/cache.js +356 -0
  114. package/dist/server/middleware/common.d.ts +108 -0
  115. package/dist/server/middleware/common.js +249 -0
  116. package/dist/server/middleware/deprecation.d.ts +58 -0
  117. package/dist/server/middleware/deprecation.js +190 -0
  118. package/dist/server/middleware/index.d.ts +12 -0
  119. package/dist/server/middleware/index.js +12 -0
  120. package/dist/server/middleware/mcpBodyAttachment.d.ts +42 -0
  121. package/dist/server/middleware/mcpBodyAttachment.js +63 -0
  122. package/dist/server/middleware/rateLimit.d.ts +148 -0
  123. package/dist/server/middleware/rateLimit.js +227 -0
  124. package/dist/server/middleware/validation.d.ts +147 -0
  125. package/dist/server/middleware/validation.js +388 -0
  126. package/dist/server/openapi/generator.d.ts +107 -0
  127. package/dist/server/openapi/generator.js +398 -0
  128. package/dist/server/openapi/index.d.ts +7 -0
  129. package/dist/server/openapi/index.js +36 -0
  130. package/dist/server/openapi/schemas.d.ts +94 -0
  131. package/dist/server/openapi/schemas.js +695 -0
  132. package/dist/server/openapi/templates.d.ts +108 -0
  133. package/dist/server/openapi/templates.js +374 -0
  134. package/dist/server/routes/agentRoutes.d.ts +9 -0
  135. package/dist/server/routes/agentRoutes.js +112 -0
  136. package/dist/server/routes/healthRoutes.d.ts +9 -0
  137. package/dist/server/routes/healthRoutes.js +170 -0
  138. package/dist/server/routes/index.d.ts +42 -0
  139. package/dist/server/routes/index.js +49 -0
  140. package/dist/server/routes/mcpRoutes.d.ts +9 -0
  141. package/dist/server/routes/mcpRoutes.js +314 -0
  142. package/dist/server/routes/memoryRoutes.d.ts +11 -0
  143. package/dist/server/routes/memoryRoutes.js +344 -0
  144. package/dist/server/routes/openApiRoutes.d.ts +34 -0
  145. package/dist/server/routes/openApiRoutes.js +126 -0
  146. package/dist/server/routes/toolRoutes.d.ts +9 -0
  147. package/dist/server/routes/toolRoutes.js +199 -0
  148. package/dist/server/streaming/dataStream.d.ts +344 -0
  149. package/dist/server/streaming/dataStream.js +483 -0
  150. package/dist/server/streaming/index.d.ts +5 -0
  151. package/dist/server/streaming/index.js +11 -0
  152. package/dist/server/types.d.ts +800 -0
  153. package/dist/server/types.js +67 -0
  154. package/dist/server/utils/redaction.d.ts +75 -0
  155. package/dist/server/utils/redaction.js +334 -0
  156. package/dist/server/utils/validation.d.ts +215 -0
  157. package/dist/server/utils/validation.js +224 -0
  158. package/dist/server/websocket/WebSocketHandler.d.ts +107 -0
  159. package/dist/server/websocket/WebSocketHandler.js +383 -0
  160. package/dist/server/websocket/index.d.ts +4 -0
  161. package/dist/server/websocket/index.js +4 -0
  162. package/package.json +21 -2
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [8.43.0](https://github.com/juspay/neurolink/compare/v8.42.0...v8.43.0) (2026-02-02)
2
+
3
+ ### Features
4
+
5
+ - **(server):** implement multi-framework HTTP server adapters with full CLI support ([1651938](https://github.com/juspay/neurolink/commit/16519387ac4b6480ac779eb7b5cd90eb2cdee6e7))
6
+
1
7
  ## [8.42.0](https://github.com/juspay/neurolink/compare/v8.41.1...v8.42.0) (2026-02-02)
2
8
 
3
9
  ### Features
package/README.md CHANGED
@@ -35,13 +35,15 @@ Extracted from production systems at Juspay and battle-tested at enterprise scal
35
35
 
36
36
  ## What's New (Q1 2026)
37
37
 
38
- | Feature | Version | Description | Guide |
39
- | ---------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
40
- | **Title Generation Events** | v8.38.0 | Emit `conversation:titleGenerated` event when conversation title is generated. Supports custom title prompts via `NEUROLINK_TITLE_PROMPT`. | [Conversation Memory Guide](docs/conversation-memory.md) |
41
- | **Video Generation with Veo** | v8.32.0 | Video generation using Veo 3.1 (`veo-3.1`). Realistic video generation with many parameter options | [Video Generation Guide](docs/features/video-generation.md) |
42
- | **Image Generation with Gemini** | v8.31.0 | Native image generation using Gemini 2.0 Flash Experimental (`imagen-3.0-generate-002`). High-quality image synthesis directly from Google AI. | [Image Generation Guide](docs/image-generation-streaming.md) |
43
- | **HTTP/Streamable HTTP Transport** | v8.29.0 | Connect to remote MCP servers via HTTP with authentication headers, automatic retry with exponential backoff, and configurable rate limiting. | [HTTP Transport Guide](docs/mcp-http-transport.md) |
44
-
38
+ | Feature | Version | Description | Guide |
39
+ | ---------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
40
+ | **Server Adapters** | v8.41.0 | Multi-framework HTTP server with Hono, Express, Fastify, Koa support. Full CLI for server management with foreground/background modes. | [Server Adapters Guide](docs/guides/server-adapters/index.md) |
41
+ | **Title Generation Events** | v8.38.0 | Emit `conversation:titleGenerated` event when conversation title is generated. Supports custom title prompts via `NEUROLINK_TITLE_PROMPT`. | [Conversation Memory Guide](docs/conversation-memory.md) |
42
+ | **Video Generation with Veo** | v8.32.0 | Video generation using Veo 3.1 (`veo-3.1`). Realistic video generation with many parameter options | [Video Generation Guide](docs/features/video-generation.md) |
43
+ | **Image Generation with Gemini** | v8.31.0 | Native image generation using Gemini 2.0 Flash Experimental (`imagen-3.0-generate-002`). High-quality image synthesis directly from Google AI. | [Image Generation Guide](docs/image-generation-streaming.md) |
44
+ | **HTTP/Streamable HTTP Transport** | v8.29.0 | Connect to remote MCP servers via HTTP with authentication headers, automatic retry with exponential backoff, and configurable rate limiting. | [HTTP Transport Guide](docs/mcp-http-transport.md) |
45
+
46
+ - **Server Adapters** – Deploy NeuroLink as an HTTP API server with your framework of choice (Hono, Express, Fastify, Koa). Full CLI support with `serve` and `server` commands for foreground/background modes, route management, and OpenAPI generation. → [Server Adapters Guide](docs/guides/server-adapters/index.md)
45
47
  - **Title Generation Events** – Emit real-time events when conversation titles are auto-generated. Listen to `conversation:titleGenerated` for session tracking. → [Conversation Memory Guide](docs/conversation-memory.md#title-generation-events)
46
48
  - **Custom Title Prompts** – Customize conversation title generation with `NEUROLINK_TITLE_PROMPT` environment variable. Use `${userMessage}` placeholder for dynamic prompts. → [Conversation Memory Guide](docs/conversation-memory.md#customizing-the-title-prompt)
47
49
  - **Video Generation** – Transform images into 8-second videos with synchronized audio using Google Veo 3.1 via Vertex AI. Supports 720p/1080p resolutions, portrait/landscape aspect ratios. → [Video Generation Guide](docs/features/video-generation.md)
@@ -335,16 +337,23 @@ node your-app.js
335
337
 
336
338
  **15+ commands** for every workflow:
337
339
 
338
- | Command | Purpose | Example | Documentation |
339
- | ---------- | ---------------------------------- | -------------------------- | ----------------------------------------- |
340
- | `setup` | Interactive provider configuration | `neurolink setup` | [Setup Guide](docs/cli/index.md) |
341
- | `generate` | Text generation | `neurolink gen "Hello"` | [Generate](docs/cli/commands.md#generate) |
342
- | `stream` | Streaming generation | `neurolink stream "Story"` | [Stream](docs/cli/commands.md#stream) |
343
- | `status` | Provider health check | `neurolink status` | [Status](docs/cli/commands.md#status) |
344
- | `loop` | Interactive session | `neurolink loop` | [Loop](docs/cli/commands.md#loop) |
345
- | `mcp` | MCP server management | `neurolink mcp discover` | [MCP CLI](docs/cli/commands.md#mcp) |
346
- | `models` | Model listing | `neurolink models` | [Models](docs/cli/commands.md#models) |
347
- | `eval` | Model evaluation | `neurolink eval` | [Eval](docs/cli/commands.md#eval) |
340
+ | Command | Purpose | Example | Documentation |
341
+ | ---------------- | ------------------------------------ | -------------------------- | ----------------------------------------- |
342
+ | `setup` | Interactive provider configuration | `neurolink setup` | [Setup Guide](docs/cli/index.md) |
343
+ | `generate` | Text generation | `neurolink gen "Hello"` | [Generate](docs/cli/commands.md#generate) |
344
+ | `stream` | Streaming generation | `neurolink stream "Story"` | [Stream](docs/cli/commands.md#stream) |
345
+ | `status` | Provider health check | `neurolink status` | [Status](docs/cli/commands.md#status) |
346
+ | `loop` | Interactive session | `neurolink loop` | [Loop](docs/cli/commands.md#loop) |
347
+ | `mcp` | MCP server management | `neurolink mcp discover` | [MCP CLI](docs/cli/commands.md#mcp) |
348
+ | `models` | Model listing | `neurolink models` | [Models](docs/cli/commands.md#models) |
349
+ | `eval` | Model evaluation | `neurolink eval` | [Eval](docs/cli/commands.md#eval) |
350
+ | `serve` | Start HTTP server in foreground mode | `neurolink serve` | [Serve](docs/cli/commands.md#serve) |
351
+ | `server start` | Start HTTP server in background mode | `neurolink server start` | [Server](docs/cli/commands.md#server) |
352
+ | `server stop` | Stop running background server | `neurolink server stop` | [Server](docs/cli/commands.md#server) |
353
+ | `server status` | Show server status information | `neurolink server status` | [Server](docs/cli/commands.md#server) |
354
+ | `server routes` | List all registered API routes | `neurolink server routes` | [Server](docs/cli/commands.md#server) |
355
+ | `server config` | View or modify server configuration | `neurolink server config` | [Server](docs/cli/commands.md#server) |
356
+ | `server openapi` | Generate OpenAPI specification | `neurolink server openapi` | [Server](docs/cli/commands.md#server) |
348
357
 
349
358
  **[📖 Complete CLI Reference](docs/cli/commands.md)** - All commands and options
350
359
 
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Serve CLI Commands for NeuroLink
3
+ * Simplified HTTP server management commands for Server Adapters feature
4
+ *
5
+ * Usage:
6
+ * neurolink serve --framework <hono|express|fastify|koa> --port <n>
7
+ * neurolink serve --config <file>
8
+ * neurolink serve --cors --rate-limit
9
+ * neurolink serve status
10
+ */
11
+ import type { CommandModule } from "yargs";
12
+ /**
13
+ * Serve CLI command factory
14
+ */
15
+ export declare class ServeCommandFactory {
16
+ /**
17
+ * Create the main serve command
18
+ */
19
+ static createServeCommands(): CommandModule;
20
+ private static buildStatusOptions;
21
+ private static executeServe;
22
+ private static executeStatus;
23
+ }
24
+ export default ServeCommandFactory;
@@ -0,0 +1,565 @@
1
+ /**
2
+ * Serve CLI Commands for NeuroLink
3
+ * Simplified HTTP server management commands for Server Adapters feature
4
+ *
5
+ * Usage:
6
+ * neurolink serve --framework <hono|express|fastify|koa> --port <n>
7
+ * neurolink serve --config <file>
8
+ * neurolink serve --cors --rate-limit
9
+ * neurolink serve status
10
+ */
11
+ import chalk from "chalk";
12
+ import ora from "ora";
13
+ import fs from "fs";
14
+ import path from "path";
15
+ import { logger } from "../../lib/utils/logger.js";
16
+ import { NeuroLink } from "../../lib/neurolink.js";
17
+ import { withTimeout } from "../../lib/utils/errorHandling.js";
18
+ import { isProcessRunning, formatUptime, StateFileManager, } from "../utils/serverUtils.js";
19
+ import { ConfigurationError, ServerStartError, } from "../../lib/server/errors.js";
20
+ // ============================================
21
+ // State Management
22
+ // ============================================
23
+ // Use StateFileManager for serve state persistence
24
+ const serveStateManager = new StateFileManager("serve-state.json");
25
+ function saveServeState(state) {
26
+ serveStateManager.save(state);
27
+ }
28
+ function loadServeState() {
29
+ return serveStateManager.load();
30
+ }
31
+ function clearServeState() {
32
+ serveStateManager.clear();
33
+ }
34
+ function loadConfigFile(configPath) {
35
+ const absolutePath = path.isAbsolute(configPath)
36
+ ? configPath
37
+ : path.resolve(process.cwd(), configPath);
38
+ if (!fs.existsSync(absolutePath)) {
39
+ throw new ConfigurationError(`Config file not found: ${absolutePath}`, {
40
+ configPath,
41
+ absolutePath,
42
+ });
43
+ }
44
+ const content = fs.readFileSync(absolutePath, "utf8");
45
+ // Support both JSON and JS/TS module format (for JSON only at runtime)
46
+ if (absolutePath.endsWith(".json")) {
47
+ return JSON.parse(content);
48
+ }
49
+ throw new ConfigurationError("Only JSON config files are supported. Use .json extension.", { configPath, absolutePath });
50
+ }
51
+ // ============================================
52
+ // Watch Mode Utilities
53
+ // ============================================
54
+ /**
55
+ * Directories to watch for changes in watch mode
56
+ */
57
+ const WATCH_DIRS = ["src", "lib"];
58
+ /**
59
+ * File extensions to watch for changes
60
+ */
61
+ const WATCH_EXTENSIONS = [".ts", ".js", ".json"];
62
+ /**
63
+ * Debounce time for file changes (ms)
64
+ */
65
+ const WATCH_DEBOUNCE_MS = 500;
66
+ /**
67
+ * Create a file watcher for watch mode
68
+ * Returns a cleanup function to stop watching
69
+ */
70
+ function createFileWatcher(onRestart, quiet) {
71
+ const watchers = [];
72
+ let debounceTimer = null;
73
+ let isRestarting = false;
74
+ const handleChange = (eventType, filename) => {
75
+ // Skip if no filename or if it doesn't match our extensions
76
+ if (!filename) {
77
+ return;
78
+ }
79
+ const ext = path.extname(filename);
80
+ if (!WATCH_EXTENSIONS.includes(ext)) {
81
+ return;
82
+ }
83
+ // Debounce rapid changes
84
+ if (debounceTimer) {
85
+ clearTimeout(debounceTimer);
86
+ }
87
+ debounceTimer = setTimeout(async () => {
88
+ if (isRestarting) {
89
+ return;
90
+ }
91
+ isRestarting = true;
92
+ if (!quiet) {
93
+ logger.always("");
94
+ logger.always(chalk.yellow(`File changed: ${filename}. Restarting server...`));
95
+ }
96
+ try {
97
+ await onRestart();
98
+ }
99
+ finally {
100
+ isRestarting = false;
101
+ }
102
+ }, WATCH_DEBOUNCE_MS);
103
+ };
104
+ // Watch each directory
105
+ const cwd = process.cwd();
106
+ for (const dir of WATCH_DIRS) {
107
+ const watchPath = path.join(cwd, dir);
108
+ if (fs.existsSync(watchPath)) {
109
+ try {
110
+ const watcher = fs.watch(watchPath, { recursive: true }, handleChange);
111
+ watchers.push(watcher);
112
+ }
113
+ catch {
114
+ // Ignore errors for directories that can't be watched
115
+ }
116
+ }
117
+ }
118
+ // Return cleanup function
119
+ return () => {
120
+ if (debounceTimer) {
121
+ clearTimeout(debounceTimer);
122
+ }
123
+ for (const watcher of watchers) {
124
+ watcher.close();
125
+ }
126
+ };
127
+ }
128
+ // ============================================
129
+ // Serve Command Factory
130
+ // ============================================
131
+ /**
132
+ * Serve CLI command factory
133
+ */
134
+ export class ServeCommandFactory {
135
+ /**
136
+ * Create the main serve command
137
+ */
138
+ static createServeCommands() {
139
+ return {
140
+ command: "serve [subcommand]",
141
+ describe: "Start NeuroLink HTTP server with server adapters",
142
+ builder: (yargs) => {
143
+ return yargs
144
+ .command("status", "Show server status", (yargs) => this.buildStatusOptions(yargs), (argv) => this.executeStatus(argv))
145
+ .option("port", {
146
+ type: "number",
147
+ alias: "p",
148
+ default: 3000,
149
+ description: "Port to listen on",
150
+ })
151
+ .option("host", {
152
+ type: "string",
153
+ alias: "H",
154
+ default: "0.0.0.0",
155
+ description: "Host to bind to",
156
+ })
157
+ .option("framework", {
158
+ type: "string",
159
+ alias: "f",
160
+ choices: ["hono", "express", "fastify", "koa"],
161
+ default: "hono",
162
+ description: "Web framework to use (hono recommended)",
163
+ })
164
+ .option("basePath", {
165
+ type: "string",
166
+ alias: "b",
167
+ default: "/api",
168
+ description: "Base path for all routes",
169
+ })
170
+ .option("cors", {
171
+ type: "boolean",
172
+ default: true,
173
+ description: "Enable CORS middleware",
174
+ })
175
+ .option("rate-limit", {
176
+ type: "number",
177
+ alias: "rateLimit",
178
+ default: 100,
179
+ description: "Rate limit (requests per 15 min window, 0 to disable)",
180
+ })
181
+ .option("swagger", {
182
+ type: "boolean",
183
+ default: false,
184
+ description: "Enable OpenAPI/Swagger documentation",
185
+ })
186
+ .option("config", {
187
+ type: "string",
188
+ alias: "c",
189
+ description: "Path to server config file (JSON)",
190
+ })
191
+ .option("watch", {
192
+ type: "boolean",
193
+ alias: "w",
194
+ default: false,
195
+ description: "Watch mode (restart server on file changes)",
196
+ })
197
+ .option("quiet", {
198
+ type: "boolean",
199
+ alias: "q",
200
+ default: false,
201
+ description: "Suppress non-essential output",
202
+ })
203
+ .option("debug", {
204
+ type: "boolean",
205
+ alias: "d",
206
+ default: false,
207
+ description: "Enable debug output",
208
+ })
209
+ .example("neurolink serve", "Start server with default settings (Hono on port 3000)")
210
+ .example("neurolink serve --framework express --port 8080", "Start Express server on port 8080")
211
+ .example("neurolink serve --config server.config.json", "Start server with config file")
212
+ .example("neurolink serve --cors --rate-limit 50", "Start server with CORS and rate limiting (50 req/15min)")
213
+ .example("neurolink serve --swagger", "Start server with OpenAPI documentation enabled")
214
+ .example("neurolink serve --watch", "Start server in watch mode (restart on changes)")
215
+ .example("neurolink serve status", "Show server status")
216
+ .help();
217
+ },
218
+ handler: async (argv) => {
219
+ // If subcommand is provided (like 'status'), it will be handled by the subcommand
220
+ // Otherwise, start the server
221
+ if (!argv.subcommand || argv.subcommand === "serve") {
222
+ await this.executeServe(argv);
223
+ }
224
+ },
225
+ };
226
+ }
227
+ static buildStatusOptions(yargs) {
228
+ return yargs
229
+ .option("format", {
230
+ type: "string",
231
+ choices: ["text", "json"],
232
+ default: "text",
233
+ description: "Output format",
234
+ })
235
+ .option("quiet", {
236
+ type: "boolean",
237
+ alias: "q",
238
+ default: false,
239
+ description: "Suppress non-essential output",
240
+ })
241
+ .example("neurolink serve status", "Show server status")
242
+ .example("neurolink serve status --format json", "Show status as JSON");
243
+ }
244
+ // ============================================
245
+ // Command Executors
246
+ // ============================================
247
+ static async executeServe(argv) {
248
+ const spinner = argv.quiet
249
+ ? null
250
+ : ora("Starting NeuroLink server...").start();
251
+ try {
252
+ // Check if server is already running
253
+ const existingState = loadServeState();
254
+ if (existingState && isProcessRunning(existingState.pid)) {
255
+ if (spinner) {
256
+ spinner.fail(chalk.red(`Server already running on port ${existingState.port} (PID: ${existingState.pid})`));
257
+ }
258
+ logger.always(chalk.yellow("Use 'neurolink server stop' or kill the process to stop it first"));
259
+ process.exit(1);
260
+ }
261
+ // Load config file if provided
262
+ let fileConfig = {};
263
+ if (argv.config) {
264
+ try {
265
+ fileConfig = loadConfigFile(argv.config);
266
+ if (spinner) {
267
+ spinner.text = `Loading config from ${argv.config}...`;
268
+ }
269
+ }
270
+ catch (configError) {
271
+ if (spinner) {
272
+ spinner.fail(chalk.red("Failed to load config file"));
273
+ }
274
+ logger.error(chalk.red(`Error: ${configError instanceof Error ? configError.message : String(configError)}`));
275
+ process.exit(1);
276
+ }
277
+ }
278
+ // Merge CLI args with file config (CLI takes precedence)
279
+ const port = argv.port ?? fileConfig.port ?? 3000;
280
+ const host = argv.host ?? fileConfig.host ?? "0.0.0.0";
281
+ const framework = argv.framework ?? fileConfig.framework ?? "hono";
282
+ const basePath = argv.basePath ?? fileConfig.basePath ?? "/api";
283
+ const corsEnabled = argv.cors ?? fileConfig.cors?.enabled ?? true;
284
+ // Rate limit: argv.rateLimit is a number (0 to disable, >0 for max requests)
285
+ const rateLimitValue = argv.rateLimit ?? fileConfig.rateLimit?.maxRequests ?? 100;
286
+ const rateLimitEnabled = rateLimitValue > 0;
287
+ // Swagger/OpenAPI documentation
288
+ const swaggerEnabled = argv.swagger ?? fileConfig.enableSwagger ?? false;
289
+ // Create NeuroLink instance
290
+ const neurolink = new NeuroLink();
291
+ // Dynamically import server module
292
+ const { createServer, registerAllRoutes } = await import("../../lib/server/index.js");
293
+ // Build server adapter config
294
+ // Default config values
295
+ const defaultConfig = {
296
+ cors: { enabled: true },
297
+ rateLimit: { enabled: true, maxRequests: 100 },
298
+ };
299
+ const serverConfig = {
300
+ port,
301
+ host,
302
+ basePath,
303
+ cors: {
304
+ ...defaultConfig.cors,
305
+ ...(fileConfig.cors || {}),
306
+ // CLI flag should override config file
307
+ enabled: argv.cors !== undefined
308
+ ? argv.cors
309
+ : (fileConfig.cors?.enabled ?? defaultConfig.cors.enabled),
310
+ },
311
+ rateLimit: {
312
+ ...defaultConfig.rateLimit,
313
+ ...(fileConfig.rateLimit || {}),
314
+ // CLI flags should override config file
315
+ enabled: argv.rateLimit !== undefined
316
+ ? argv.rateLimit > 0
317
+ : (fileConfig.rateLimit?.enabled ??
318
+ defaultConfig.rateLimit.enabled),
319
+ maxRequests: argv.rateLimit !== undefined
320
+ ? argv.rateLimit
321
+ : (fileConfig.rateLimit?.maxRequests ??
322
+ defaultConfig.rateLimit.maxRequests),
323
+ },
324
+ bodyParser: fileConfig.bodyParser,
325
+ logging: fileConfig.logging,
326
+ timeout: fileConfig.timeout,
327
+ enableMetrics: fileConfig.enableMetrics ?? true,
328
+ enableSwagger: argv.swagger !== undefined
329
+ ? argv.swagger
330
+ : (fileConfig.enableSwagger ?? false),
331
+ disableBuiltInHealth: true, // We register health routes separately
332
+ };
333
+ if (spinner) {
334
+ spinner.text = `Creating ${framework} server...`;
335
+ }
336
+ // Create server using ServerAdapterFactory
337
+ // Use a mutable reference wrapper so signal handlers always access the current server
338
+ // This is necessary because watch mode replaces the server on restart
339
+ const serverRef = {
340
+ current: await createServer(neurolink, {
341
+ framework: framework,
342
+ config: serverConfig,
343
+ }),
344
+ };
345
+ // Register all routes
346
+ registerAllRoutes(serverRef.current, basePath);
347
+ if (spinner) {
348
+ spinner.text = "Initializing server...";
349
+ }
350
+ // Initialize and start with timeout
351
+ await withTimeout(serverRef.current.initialize(), 30000, new ServerStartError("Server initialization timed out after 30 seconds", undefined, port, host));
352
+ await withTimeout(serverRef.current.start(), 30000, new ServerStartError("Server startup timed out after 30 seconds", undefined, port, host));
353
+ // Save state
354
+ const state = {
355
+ pid: process.pid,
356
+ port,
357
+ host,
358
+ framework,
359
+ startTime: new Date().toISOString(),
360
+ basePath,
361
+ configFile: argv.config,
362
+ };
363
+ saveServeState(state);
364
+ if (spinner) {
365
+ spinner.succeed(chalk.green("NeuroLink server started successfully"));
366
+ }
367
+ const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
368
+ logger.always("");
369
+ logger.always(chalk.bold.cyan("NeuroLink Server"));
370
+ logger.always(chalk.gray("=".repeat(50)));
371
+ logger.always("");
372
+ logger.always(` ${chalk.bold("URL:")} ${chalk.cyan(url)}`);
373
+ logger.always(` ${chalk.bold("Framework:")} ${chalk.cyan(framework)}`);
374
+ logger.always(` ${chalk.bold("Base Path:")} ${chalk.cyan(basePath)}`);
375
+ logger.always(` ${chalk.bold("PID:")} ${chalk.cyan(state.pid)}`);
376
+ if (argv.config) {
377
+ logger.always(` ${chalk.bold("Config:")} ${chalk.cyan(argv.config)}`);
378
+ }
379
+ logger.always("");
380
+ logger.always(chalk.bold("Middleware:"));
381
+ logger.always(` CORS: ${corsEnabled ? chalk.green("enabled") : chalk.yellow("disabled")}`);
382
+ logger.always(` Rate Limit: ${rateLimitEnabled ? chalk.green(`enabled (${rateLimitValue} req/15min)`) : chalk.yellow("disabled")}`);
383
+ logger.always(` Swagger: ${swaggerEnabled ? chalk.green("enabled") : chalk.yellow("disabled")}`);
384
+ if (argv.watch) {
385
+ logger.always(` Watch Mode: ${chalk.green("enabled")}`);
386
+ }
387
+ logger.always("");
388
+ logger.always(chalk.bold("Available Endpoints:"));
389
+ logger.always(chalk.gray(" Health & Monitoring:"));
390
+ logger.always(` ${chalk.green("GET")} ${basePath}/health`);
391
+ logger.always(` ${chalk.green("GET")} ${basePath}/ready`);
392
+ logger.always(` ${chalk.green("GET")} ${basePath}/metrics`);
393
+ logger.always("");
394
+ logger.always(chalk.gray(" Agent API:"));
395
+ logger.always(` ${chalk.blue("POST")} ${basePath}/agent/execute`);
396
+ logger.always(` ${chalk.blue("POST")} ${basePath}/agent/stream`);
397
+ logger.always(` ${chalk.green("GET")} ${basePath}/agent/providers`);
398
+ logger.always("");
399
+ logger.always(chalk.gray(" Tools & MCP:"));
400
+ logger.always(` ${chalk.green("GET")} ${basePath}/tools`);
401
+ logger.always(` ${chalk.blue("POST")} ${basePath}/tools/:name/execute`);
402
+ logger.always(` ${chalk.green("GET")} ${basePath}/mcp/servers`);
403
+ logger.always("");
404
+ logger.always(chalk.gray(" Memory:"));
405
+ logger.always(` ${chalk.green("GET")} ${basePath}/memory/sessions`);
406
+ logger.always(` ${chalk.green("GET")} ${basePath}/memory/sessions/:id`);
407
+ if (swaggerEnabled) {
408
+ logger.always("");
409
+ logger.always(chalk.gray(" OpenAPI Documentation:"));
410
+ logger.always(` ${chalk.green("GET")} ${basePath}/openapi.json`);
411
+ logger.always(` ${chalk.cyan("INFO")} Swagger UI available at ${url}${basePath}/docs`);
412
+ }
413
+ logger.always("");
414
+ logger.always(chalk.gray("Press Ctrl+C to stop the server"));
415
+ logger.always("");
416
+ // Set up watch mode if enabled
417
+ let stopWatcher = null;
418
+ if (argv.watch) {
419
+ const restartServer = async () => {
420
+ try {
421
+ // Stop current server with timeout
422
+ await withTimeout(serverRef.current.stop(), 30000, new ServerStartError("Server stop timed out during restart", undefined, port, host));
423
+ // Re-import server module with cache busting for watch mode
424
+ // Append timestamp query to force ESM to re-evaluate the module
425
+ const timestamp = Date.now();
426
+ const { createServer: createNewServer, registerAllRoutes: registerNewRoutes, } = await import(`../../lib/server/index.js?t=${timestamp}`);
427
+ // Create new server
428
+ const newServer = await createNewServer(new NeuroLink(), {
429
+ framework: framework,
430
+ config: serverConfig,
431
+ });
432
+ registerNewRoutes(newServer, basePath);
433
+ // Initialize and start with timeouts
434
+ await withTimeout(newServer.initialize(), 30000, new ServerStartError("Server initialization timed out during restart", undefined, port, host));
435
+ await withTimeout(newServer.start(), 30000, new ServerStartError("Server startup timed out during restart", undefined, port, host));
436
+ // Update the reference so signal handlers use the new server instance
437
+ serverRef.current = newServer;
438
+ logger.always(chalk.green("Server restarted successfully"));
439
+ }
440
+ catch (restartError) {
441
+ logger.error(chalk.red(`Error restarting server: ${restartError instanceof Error ? restartError.message : String(restartError)}`));
442
+ }
443
+ };
444
+ stopWatcher = createFileWatcher(restartServer, argv.quiet ?? false);
445
+ logger.always(chalk.gray("Watching for file changes in src/ and lib/..."));
446
+ logger.always("");
447
+ }
448
+ // Keep process running and handle graceful shutdown
449
+ // Signal handlers access serverRef.current to always get the latest server instance
450
+ process.on("SIGINT", async () => {
451
+ logger.always("");
452
+ logger.always(chalk.yellow("Shutting down server..."));
453
+ try {
454
+ // Stop file watcher if active
455
+ if (stopWatcher) {
456
+ stopWatcher();
457
+ }
458
+ await serverRef.current.stop();
459
+ clearServeState();
460
+ logger.always(chalk.green("Server stopped gracefully"));
461
+ process.exit(0);
462
+ }
463
+ catch (error) {
464
+ logger.error(chalk.red(`Error stopping server: ${error instanceof Error ? error.message : String(error)}`));
465
+ process.exit(1);
466
+ }
467
+ });
468
+ process.on("SIGTERM", async () => {
469
+ try {
470
+ if (stopWatcher) {
471
+ stopWatcher();
472
+ }
473
+ await withTimeout(serverRef.current.stop(), 30000, new Error("Server stop timed out during SIGTERM"));
474
+ clearServeState();
475
+ process.exit(0);
476
+ }
477
+ catch (error) {
478
+ logger.error(chalk.red(`Error stopping server: ${error instanceof Error ? error.message : String(error)}`));
479
+ clearServeState();
480
+ process.exit(1);
481
+ }
482
+ });
483
+ }
484
+ catch (error) {
485
+ if (spinner) {
486
+ spinner.fail(chalk.red("Failed to start server"));
487
+ }
488
+ logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
489
+ if (argv.debug && error instanceof Error && error.stack) {
490
+ logger.error(chalk.gray(error.stack));
491
+ }
492
+ logger.always("");
493
+ logger.always(chalk.bold("Troubleshooting:"));
494
+ logger.always(" 1. Check if the port is already in use");
495
+ logger.always(" 2. Verify the framework is installed (npm install hono/express/fastify/koa)");
496
+ logger.always(" 3. Check your config file format if using --config");
497
+ logger.always(" 4. Run with --debug for more information");
498
+ logger.always("");
499
+ process.exit(1);
500
+ }
501
+ }
502
+ static async executeStatus(argv) {
503
+ try {
504
+ const state = loadServeState();
505
+ const status = {
506
+ running: false,
507
+ pid: null,
508
+ port: null,
509
+ host: null,
510
+ framework: null,
511
+ basePath: null,
512
+ uptime: null,
513
+ startTime: null,
514
+ configFile: null,
515
+ url: null,
516
+ };
517
+ if (state && isProcessRunning(state.pid)) {
518
+ status.running = true;
519
+ status.pid = state.pid;
520
+ status.port = state.port;
521
+ status.host = state.host;
522
+ status.framework = state.framework;
523
+ status.basePath = state.basePath;
524
+ status.startTime = state.startTime;
525
+ status.uptime = Date.now() - new Date(state.startTime).getTime();
526
+ status.configFile = state.configFile ?? null;
527
+ status.url = `http://${state.host === "0.0.0.0" ? "localhost" : state.host}:${state.port}`;
528
+ }
529
+ if (argv.format === "json") {
530
+ logger.always(JSON.stringify(status, null, 2));
531
+ return;
532
+ }
533
+ // Text format
534
+ logger.always("");
535
+ logger.always(chalk.bold.cyan("NeuroLink Server Status"));
536
+ logger.always(chalk.gray("=".repeat(50)));
537
+ logger.always("");
538
+ if (status.running) {
539
+ logger.always(` ${chalk.bold("Status:")} ${chalk.green("RUNNING")}`);
540
+ logger.always(` ${chalk.bold("PID:")} ${chalk.cyan(status.pid)}`);
541
+ logger.always(` ${chalk.bold("URL:")} ${chalk.cyan(status.url)}`);
542
+ logger.always(` ${chalk.bold("Framework:")} ${chalk.cyan(status.framework)}`);
543
+ logger.always(` ${chalk.bold("Base Path:")} ${chalk.cyan(status.basePath)}`);
544
+ logger.always(` ${chalk.bold("Started:")} ${chalk.cyan(status.startTime)}`);
545
+ logger.always(` ${chalk.bold("Uptime:")} ${chalk.cyan(formatUptime(status.uptime ?? 0))}`);
546
+ if (status.configFile) {
547
+ logger.always(` ${chalk.bold("Config:")} ${chalk.cyan(status.configFile)}`);
548
+ }
549
+ }
550
+ else {
551
+ logger.always(` ${chalk.bold("Status:")} ${chalk.yellow("NOT RUNNING")}`);
552
+ logger.always("");
553
+ logger.always(chalk.gray(" Start the server with: neurolink serve"));
554
+ logger.always(chalk.gray(" Or with custom options: neurolink serve --port 8080 --framework express"));
555
+ }
556
+ logger.always("");
557
+ }
558
+ catch (error) {
559
+ logger.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
560
+ process.exit(1);
561
+ }
562
+ }
563
+ }
564
+ export default ServeCommandFactory;
565
+ //# sourceMappingURL=serve.js.map