@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.
- package/CHANGELOG.md +6 -0
- package/README.md +26 -17
- package/dist/cli/commands/serve.d.ts +24 -0
- package/dist/cli/commands/serve.js +565 -0
- package/dist/cli/commands/server.d.ts +26 -0
- package/dist/cli/commands/server.js +750 -0
- package/dist/cli/parser.js +7 -1
- package/dist/cli/utils/serverUtils.d.ts +71 -0
- package/dist/cli/utils/serverUtils.js +149 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +42 -0
- package/dist/lib/index.d.ts +20 -0
- package/dist/lib/index.js +42 -0
- package/dist/lib/neurolink.d.ts +33 -4
- package/dist/lib/neurolink.js +41 -12
- package/dist/lib/server/abstract/baseServerAdapter.d.ts +171 -0
- package/dist/lib/server/abstract/baseServerAdapter.js +541 -0
- package/dist/lib/server/adapters/expressAdapter.d.ts +71 -0
- package/dist/lib/server/adapters/expressAdapter.js +487 -0
- package/dist/lib/server/adapters/fastifyAdapter.d.ts +71 -0
- package/dist/lib/server/adapters/fastifyAdapter.js +468 -0
- package/dist/lib/server/adapters/honoAdapter.d.ts +72 -0
- package/dist/lib/server/adapters/honoAdapter.js +567 -0
- package/dist/lib/server/adapters/index.d.ts +7 -0
- package/dist/lib/server/adapters/index.js +8 -0
- package/dist/lib/server/adapters/koaAdapter.d.ts +79 -0
- package/dist/lib/server/adapters/koaAdapter.js +511 -0
- package/dist/lib/server/errors.d.ts +193 -0
- package/dist/lib/server/errors.js +487 -0
- package/dist/lib/server/factory/serverAdapterFactory.d.ts +73 -0
- package/dist/lib/server/factory/serverAdapterFactory.js +161 -0
- package/dist/lib/server/index.d.ts +44 -0
- package/dist/lib/server/index.js +105 -0
- package/dist/lib/server/middleware/abortSignal.d.ts +61 -0
- package/dist/lib/server/middleware/abortSignal.js +112 -0
- package/dist/lib/server/middleware/auth.d.ts +228 -0
- package/dist/lib/server/middleware/auth.js +389 -0
- package/dist/lib/server/middleware/cache.d.ts +208 -0
- package/dist/lib/server/middleware/cache.js +357 -0
- package/dist/lib/server/middleware/common.d.ts +108 -0
- package/dist/lib/server/middleware/common.js +250 -0
- package/dist/lib/server/middleware/deprecation.d.ts +58 -0
- package/dist/lib/server/middleware/deprecation.js +191 -0
- package/dist/lib/server/middleware/index.d.ts +12 -0
- package/dist/lib/server/middleware/index.js +13 -0
- package/dist/lib/server/middleware/mcpBodyAttachment.d.ts +42 -0
- package/dist/lib/server/middleware/mcpBodyAttachment.js +64 -0
- package/dist/lib/server/middleware/rateLimit.d.ts +148 -0
- package/dist/lib/server/middleware/rateLimit.js +228 -0
- package/dist/lib/server/middleware/validation.d.ts +147 -0
- package/dist/lib/server/middleware/validation.js +389 -0
- package/dist/lib/server/openapi/generator.d.ts +107 -0
- package/dist/lib/server/openapi/generator.js +399 -0
- package/dist/lib/server/openapi/index.d.ts +7 -0
- package/dist/lib/server/openapi/index.js +37 -0
- package/dist/lib/server/openapi/schemas.d.ts +94 -0
- package/dist/lib/server/openapi/schemas.js +696 -0
- package/dist/lib/server/openapi/templates.d.ts +108 -0
- package/dist/lib/server/openapi/templates.js +375 -0
- package/dist/lib/server/routes/agentRoutes.d.ts +9 -0
- package/dist/lib/server/routes/agentRoutes.js +113 -0
- package/dist/lib/server/routes/healthRoutes.d.ts +9 -0
- package/dist/lib/server/routes/healthRoutes.js +171 -0
- package/dist/lib/server/routes/index.d.ts +42 -0
- package/dist/lib/server/routes/index.js +50 -0
- package/dist/lib/server/routes/mcpRoutes.d.ts +9 -0
- package/dist/lib/server/routes/mcpRoutes.js +315 -0
- package/dist/lib/server/routes/memoryRoutes.d.ts +11 -0
- package/dist/lib/server/routes/memoryRoutes.js +345 -0
- package/dist/lib/server/routes/openApiRoutes.d.ts +34 -0
- package/dist/lib/server/routes/openApiRoutes.js +127 -0
- package/dist/lib/server/routes/toolRoutes.d.ts +9 -0
- package/dist/lib/server/routes/toolRoutes.js +200 -0
- package/dist/lib/server/streaming/dataStream.d.ts +344 -0
- package/dist/lib/server/streaming/dataStream.js +484 -0
- package/dist/lib/server/streaming/index.d.ts +5 -0
- package/dist/lib/server/streaming/index.js +12 -0
- package/dist/lib/server/types.d.ts +800 -0
- package/dist/lib/server/types.js +68 -0
- package/dist/lib/server/utils/redaction.d.ts +75 -0
- package/dist/lib/server/utils/redaction.js +335 -0
- package/dist/lib/server/utils/validation.d.ts +215 -0
- package/dist/lib/server/utils/validation.js +225 -0
- package/dist/lib/server/websocket/WebSocketHandler.d.ts +107 -0
- package/dist/lib/server/websocket/WebSocketHandler.js +384 -0
- package/dist/lib/server/websocket/index.d.ts +4 -0
- package/dist/lib/server/websocket/index.js +5 -0
- package/dist/neurolink.d.ts +33 -4
- package/dist/neurolink.js +41 -12
- package/dist/server/abstract/baseServerAdapter.d.ts +171 -0
- package/dist/server/abstract/baseServerAdapter.js +540 -0
- package/dist/server/adapters/expressAdapter.d.ts +71 -0
- package/dist/server/adapters/expressAdapter.js +486 -0
- package/dist/server/adapters/fastifyAdapter.d.ts +71 -0
- package/dist/server/adapters/fastifyAdapter.js +467 -0
- package/dist/server/adapters/honoAdapter.d.ts +72 -0
- package/dist/server/adapters/honoAdapter.js +566 -0
- package/dist/server/adapters/index.d.ts +7 -0
- package/dist/server/adapters/index.js +7 -0
- package/dist/server/adapters/koaAdapter.d.ts +79 -0
- package/dist/server/adapters/koaAdapter.js +510 -0
- package/dist/server/errors.d.ts +193 -0
- package/dist/server/errors.js +486 -0
- package/dist/server/factory/serverAdapterFactory.d.ts +73 -0
- package/dist/server/factory/serverAdapterFactory.js +160 -0
- package/dist/server/index.d.ts +44 -0
- package/dist/server/index.js +104 -0
- package/dist/server/middleware/abortSignal.d.ts +61 -0
- package/dist/server/middleware/abortSignal.js +111 -0
- package/dist/server/middleware/auth.d.ts +228 -0
- package/dist/server/middleware/auth.js +388 -0
- package/dist/server/middleware/cache.d.ts +208 -0
- package/dist/server/middleware/cache.js +356 -0
- package/dist/server/middleware/common.d.ts +108 -0
- package/dist/server/middleware/common.js +249 -0
- package/dist/server/middleware/deprecation.d.ts +58 -0
- package/dist/server/middleware/deprecation.js +190 -0
- package/dist/server/middleware/index.d.ts +12 -0
- package/dist/server/middleware/index.js +12 -0
- package/dist/server/middleware/mcpBodyAttachment.d.ts +42 -0
- package/dist/server/middleware/mcpBodyAttachment.js +63 -0
- package/dist/server/middleware/rateLimit.d.ts +148 -0
- package/dist/server/middleware/rateLimit.js +227 -0
- package/dist/server/middleware/validation.d.ts +147 -0
- package/dist/server/middleware/validation.js +388 -0
- package/dist/server/openapi/generator.d.ts +107 -0
- package/dist/server/openapi/generator.js +398 -0
- package/dist/server/openapi/index.d.ts +7 -0
- package/dist/server/openapi/index.js +36 -0
- package/dist/server/openapi/schemas.d.ts +94 -0
- package/dist/server/openapi/schemas.js +695 -0
- package/dist/server/openapi/templates.d.ts +108 -0
- package/dist/server/openapi/templates.js +374 -0
- package/dist/server/routes/agentRoutes.d.ts +9 -0
- package/dist/server/routes/agentRoutes.js +112 -0
- package/dist/server/routes/healthRoutes.d.ts +9 -0
- package/dist/server/routes/healthRoutes.js +170 -0
- package/dist/server/routes/index.d.ts +42 -0
- package/dist/server/routes/index.js +49 -0
- package/dist/server/routes/mcpRoutes.d.ts +9 -0
- package/dist/server/routes/mcpRoutes.js +314 -0
- package/dist/server/routes/memoryRoutes.d.ts +11 -0
- package/dist/server/routes/memoryRoutes.js +344 -0
- package/dist/server/routes/openApiRoutes.d.ts +34 -0
- package/dist/server/routes/openApiRoutes.js +126 -0
- package/dist/server/routes/toolRoutes.d.ts +9 -0
- package/dist/server/routes/toolRoutes.js +199 -0
- package/dist/server/streaming/dataStream.d.ts +344 -0
- package/dist/server/streaming/dataStream.js +483 -0
- package/dist/server/streaming/index.d.ts +5 -0
- package/dist/server/streaming/index.js +11 -0
- package/dist/server/types.d.ts +800 -0
- package/dist/server/types.js +67 -0
- package/dist/server/utils/redaction.d.ts +75 -0
- package/dist/server/utils/redaction.js +334 -0
- package/dist/server/utils/validation.d.ts +215 -0
- package/dist/server/utils/validation.js +224 -0
- package/dist/server/websocket/WebSocketHandler.d.ts +107 -0
- package/dist/server/websocket/WebSocketHandler.js +383 -0
- package/dist/server/websocket/index.d.ts +4 -0
- package/dist/server/websocket/index.js +4 -0
- 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
|
-
| **
|
|
41
|
-
| **
|
|
42
|
-
| **
|
|
43
|
-
| **
|
|
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
|
|
339
|
-
|
|
|
340
|
-
| `setup`
|
|
341
|
-
| `generate`
|
|
342
|
-
| `stream`
|
|
343
|
-
| `status`
|
|
344
|
-
| `loop`
|
|
345
|
-
| `mcp`
|
|
346
|
-
| `models`
|
|
347
|
-
| `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
|