neo.mjs 11.14.0 → 11.16.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 (79) hide show
  1. package/.gemini/settings.json +4 -0
  2. package/.github/VISION.md +3 -3
  3. package/ROADMAP.md +9 -6
  4. package/ServiceWorker.mjs +2 -2
  5. package/ai/demo-agents/dev.mjs +20 -19
  6. package/ai/demo-agents/pm.mjs +7 -6
  7. package/ai/examples/test-app-worker.mjs +74 -0
  8. package/ai/mcp/client/mcp-cli.mjs +5 -4
  9. package/ai/mcp/server/github-workflow/config.mjs +14 -8
  10. package/ai/mcp/server/github-workflow/mcp-stdio.mjs +2 -1
  11. package/ai/mcp/server/knowledge-base/config.mjs +11 -13
  12. package/ai/mcp/server/knowledge-base/mcp-stdio.mjs +2 -1
  13. package/ai/mcp/server/memory-core/Server.mjs +0 -1
  14. package/ai/mcp/server/memory-core/config.mjs +12 -14
  15. package/ai/mcp/server/memory-core/mcp-stdio.mjs +2 -1
  16. package/ai/mcp/server/memory-core/services/HealthService.mjs +1 -2
  17. package/ai/mcp/server/memory-core/services/TextEmbeddingService.mjs +7 -4
  18. package/ai/mcp/server/neural-link/Server.mjs +118 -0
  19. package/ai/mcp/server/neural-link/config.mjs +106 -0
  20. package/ai/mcp/server/neural-link/logger.mjs +20 -0
  21. package/ai/mcp/server/neural-link/mcp-stdio.mjs +33 -0
  22. package/ai/mcp/server/neural-link/openapi.yaml +181 -0
  23. package/ai/mcp/server/neural-link/services/ConnectionService.mjs +265 -0
  24. package/ai/mcp/server/neural-link/services/toolService.mjs +25 -0
  25. package/ai/mcp/server/neural-link/test_connection_service.mjs +21 -0
  26. package/ai/services.mjs +68 -58
  27. package/apps/agentos/index.css +128 -0
  28. package/apps/agentos/resources/data/interventions.json +26 -0
  29. package/apps/colors/view/Viewport.mjs +5 -5
  30. package/apps/colors/view/ViewportController.mjs +1 -1
  31. package/apps/portal/index.html +1 -1
  32. package/apps/portal/llms.txt +2 -1
  33. package/apps/portal/sitemap.xml +8 -4
  34. package/apps/portal/view/home/FooterContainer.mjs +1 -1
  35. package/buildScripts/addConfig.mjs +6 -5
  36. package/buildScripts/buildAll.mjs +15 -13
  37. package/buildScripts/buildThemes.mjs +14 -13
  38. package/buildScripts/copyFolder.mjs +5 -4
  39. package/buildScripts/copySeoFiles.mjs +10 -10
  40. package/buildScripts/createApp.mjs +22 -21
  41. package/buildScripts/createAppMinimal.mjs +22 -21
  42. package/buildScripts/createClass.mjs +8 -7
  43. package/buildScripts/createComponent.mjs +6 -5
  44. package/buildScripts/generateSeoFiles.mjs +7 -6
  45. package/buildScripts/tools/createExample.mjs +3 -2
  46. package/buildScripts/tools/createScss.mjs +3 -2
  47. package/buildScripts/util/Sanitizer.mjs +13 -0
  48. package/buildScripts/webpack/buildThreads.mjs +10 -9
  49. package/package.json +3 -1
  50. package/resources/scss/src/apps/agentos/InterventionPanel.scss +35 -0
  51. package/resources/scss/src/apps/agentos/Viewport.scss +125 -0
  52. package/resources/scss/src/grid/header/Button.scss +13 -3
  53. package/resources/scss/theme-cyberpunk/Global.scss +25 -0
  54. package/resources/scss/theme-cyberpunk/apps/agentos/InterventionPanel.scss +6 -0
  55. package/resources/scss/theme-cyberpunk/apps/agentos/Viewport.scss +21 -0
  56. package/resources/scss/theme-cyberpunk/button/Base.scss +113 -0
  57. package/resources/scss/theme-cyberpunk/container/Panel.scss +5 -0
  58. package/resources/scss/theme-cyberpunk/grid/Body.scss +15 -0
  59. package/resources/scss/theme-cyberpunk/grid/Container.scss +7 -0
  60. package/resources/scss/theme-cyberpunk/grid/header/Button.scss +12 -0
  61. package/resources/scss/theme-cyberpunk/toolbar/Base.scss +4 -0
  62. package/resources/scss/theme-dark/apps/agentos/InterventionPanel.scss +6 -0
  63. package/resources/scss/theme-dark/apps/agentos/Viewport.scss +24 -0
  64. package/resources/scss/theme-dark/grid/header/Button.scss +10 -4
  65. package/resources/scss/theme-light/apps/agentos/InterventionPanel.scss +6 -0
  66. package/resources/scss/theme-light/apps/agentos/Viewport.scss +24 -0
  67. package/resources/scss/theme-light/grid/header/Button.scss +10 -4
  68. package/resources/scss/theme-neo-light/apps/agentos/InterventionPanel.scss +6 -0
  69. package/resources/scss/theme-neo-light/apps/agentos/Viewport.scss +24 -0
  70. package/resources/scss/theme-neo-light/grid/header/Button.scss +10 -4
  71. package/src/DefaultConfig.mjs +11 -2
  72. package/src/ai/Client.mjs +160 -0
  73. package/src/component/Base.mjs +1 -1
  74. package/src/dashboard/Container.mjs +6 -0
  75. package/src/data/connection/WebSocket.mjs +5 -3
  76. package/src/draggable/dashboard/SortZone.mjs +10 -3
  77. package/src/worker/App.mjs +1 -0
  78. package/test/playwright/ai/neural-link.spec.mjs +52 -0
  79. package/.github/.sync-metadata.json +0 -12579
@@ -18,6 +18,10 @@
18
18
  "neo.mjs-memory-core": {
19
19
  "command": "npm",
20
20
  "args" : ["run", "ai:mcp-server-memory-core"]
21
+ },
22
+ "neo.mjs-neural-link": {
23
+ "command": "npm",
24
+ "args" : ["run", "ai:mcp-server-neural-link"]
21
25
  }
22
26
  }
23
27
  }
package/.github/VISION.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Our vision is to build the platform for the next generation of web applications, **democratizing development** by making
4
4
  elite performance and architectural patterns accessible to all. We are building towards a future where the distinction
5
- between web and native applications disappears, and where the **interface for Humans and the interface for Agents are one and the same.**
5
+ between web and native applications disappears, and where the **interface for Humans and the interface for Agents are one and the same.** This convergence empowers not just developers, but **every user** to reshape their software tools to fit their needs, simply by asking.
6
6
 
7
7
  This vision stands on four core pillars:
8
8
 
@@ -35,8 +35,7 @@ This principle radically reduces complexity and enables a cohesive ecosystem of
35
35
  We are building the first platform architected for true AI collaboration. Our goal is to use AI as the great equalizer,
36
36
  breaking down the barriers to contributing to and using a sophisticated framework. This is achieved through **Context Engineering**: building an AI-native ecosystem where the agent possesses **persistent memory** of past interactions and **deep semantic knowledge** of the codebase. This allows the AI to explain complex source code, guide new developers, and make meaningful contributions on its own. **Our architecture is inherently AI-native because it is built on a JSON Blueprint.** Instead of JSX, we use a clean, serializable structure for defining component trees and
37
37
  the VDOM itself. This makes the entire application legible and manipulable in the native language of Large Language Models
38
- (LLMs), allowing an AI to orchestrate complex, multi-window interfaces and turning it from a simple code-completer into a
39
- true architectural partner. **The end game is a development team where the AI is a proactive senior partner, mentoring
38
+ (LLMs). This allows an AI to not just write code, but to **orchestrate the application at runtime**—injecting entire component trees or modifying state on the fly without a reload. **The end game is a development team where the AI is a proactive senior partner, mentoring
40
39
  developers and automating entire verticals of the workflow.**
41
40
 
42
41
  ### 4. The Agent Operating System (The Corporate HQ for AI)
@@ -49,6 +48,7 @@ a native Neo.mjs interface. This is our "Killer App":
49
48
  - **The "Headless" Workforce:** We are moving beyond black-box CLI wrappers. We provide a native **Headless Agent SDK** that spawns
50
49
  lightweight Node.js processes. These agents act as specialized employees—Strategic "CEOs" defining Epics, Tactical "PMs" breaking them down into tickets,
51
50
  and Execution "Drones" submitting PRs—communicating asynchronously via a **Ticket-Driven Protocol**.
51
+ - **The "Neural Link" (Runtime Orchestration):** We are bridging the gap between the Agent (Node.js) and the App (Browser). Agents can connect to running applications to inspect state, **diagnose errors**, recover crashed components, or evolve the UI in real-time based on user voice commands ("Self-Evolving Apps").
52
52
  - **The Feedback Loop:** Because the Command Center is built with Neo.mjs, and the Agents write Neo.mjs, the platform
53
53
  is recursive. Agents can improve their own control interface, creating a self-reinforcing cycle of improvement.
54
54
  **The end game is a new paradigm where you are not just a coder, but the Architect and CEO of an intelligent, automated software organization, managing it through a powerful, spatial interface built on the very technology you are deploying.**
package/ROADMAP.md CHANGED
@@ -48,15 +48,18 @@ We will build the **Neo Command Center** (`apps/agent-os`), a desktop-class UI t
48
48
  * **Human-in-the-Loop:** A "Plan Verification" mode where Strategic Agents propose a plan in the UI, and the human Chairman approves it before execution proceeds.
49
49
  * **Competitive Edge:** This leverages Neo.mjs's unique multi-window and shared-worker capabilities to provide an interface that single-tab competitors cannot match.
50
50
 
51
- ### Phase 4: The Neural Link (Bidirectional Agent Integration) - **[RESEARCH]**
51
+ ### Phase 4: The Self-Evolving App Platform (Runtime Orchestration) - **[ACTIVE RESEARCH]**
52
52
 
53
- **Goal:** Move beyond "Pull-based" tool use to a real-time, event-driven "Push/Pull" architecture.
53
+ **Goal:** Enable "Self-Healing" and "Self-Evolving" applications where AI Agents act as runtime operators.
54
54
 
55
- See [.github/AGENT_ARCHITECTURE.md](.github/AGENT_ARCHITECTURE.md) for the detailed technical specification.
55
+ We will evolve the **Neural Link** into a bidirectional bridge that allows Agents to not just write code, but **drive** the application at runtime:
56
56
 
57
- * **Bidirectional WebSocket RMA:** Enable Node.js Agents to invoke methods on Browser Apps (RPC) and vice-versa.
58
- * **Telemetry Bridge:** Push Worker console logs and framework events to Agents in real-time, bypassing the need for Chrome DevTools polling.
59
- * **Security Model:** A capability-based permission system for Agent-initiated browser actions.
57
+ * **Runtime Blueprints:** Agents can inject entire component trees (via JSON Blueprints) into running applications without a reload.
58
+ * **Automated Diagnostics (Dev):** Agents capture multi-thread error context to auto-generate bug reports or PRs.
59
+ * **State Recovery (User):** Agents detect crashes or silent failures (e.g., "dead clicks") and intervene to reset component state or guide the user.
60
+ * **Live Customization:** Non-technical users can verbally instruct Agents to modify the UI layout or behavior on the fly (e.g., "Move the chart to the right").
61
+ * **Persistence Layer:** Agent-driven changes are stored (e.g., in `localStorage` or a remote user profile), allowing runtime customizations to survive page reloads and become permanent user preferences.
62
+ * **Technical Spec:** See [.github/AGENT_ARCHITECTURE.md](.github/AGENT_ARCHITECTURE.md) for the detailed technical specification.
60
63
 
61
64
  ### Phase 5: Decoupling the Ecosystem (Future)
62
65
 
package/ServiceWorker.mjs CHANGED
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='11.14.0'
23
+ * @member {String} version='11.16.0'
24
24
  */
25
- version: '11.14.0'
25
+ version: '11.16.0'
26
26
  }
27
27
 
28
28
  /**
@@ -11,15 +11,16 @@
11
11
  * node ai/agents/dev.mjs --issue <number>
12
12
  */
13
13
 
14
- import { Command } from 'commander';
15
- import fs from 'fs-extra';
16
- import path from 'path';
17
- import { fileURLToPath } from 'url';
18
- import yaml from 'js-yaml';
19
- import dotenv from 'dotenv';
20
- import { exec } from 'child_process';
21
- import { promisify } from 'util';
22
- import { GoogleGenerativeAI, SchemaType } from '@google/generative-ai';
14
+ import {Command} from 'commander';
15
+ import fs from 'fs-extra';
16
+ import path from 'path';
17
+ import {fileURLToPath} from 'url';
18
+ import yaml from 'js-yaml';
19
+ import dotenv from 'dotenv';
20
+ import {exec} from 'child_process';
21
+ import {promisify} from 'util';
22
+ import {GoogleGenerativeAI, SchemaType} from '@google/generative-ai';
23
+ import {sanitizeInput} from '../../buildScripts/util/Sanitizer.mjs';
23
24
 
24
25
  import {
25
26
  GH_LocalFileService,
@@ -56,7 +57,7 @@ const program = new Command();
56
57
  program
57
58
  .name('dev-agent')
58
59
  .description('Autonomous Developer Agent -> Ticket to PR')
59
- .requiredOption('-i, --issue <number>', 'Issue Number to process')
60
+ .requiredOption('-i, --issue <number>', 'Issue Number to process', sanitizeInput)
60
61
  .option('-d, --dry-run', 'Simulate execution without pushing/PR')
61
62
  .parse(process.argv);
62
63
 
@@ -76,14 +77,14 @@ async function runGit(command) {
76
77
  function parseIssueContent(rawContent) {
77
78
  const parts = rawContent.split('---');
78
79
  if (parts.length < 3) return { title: 'Unknown', body: rawContent };
79
-
80
+
80
81
  const frontmatter = parts[1];
81
82
  const body = parts.slice(2).join('---').trim();
82
-
83
+
83
84
  let title = 'Unknown';
84
85
  const titleMatch = frontmatter.match(/^title:\s*(.*)$/m);
85
86
  if (titleMatch) title = titleMatch[1].trim().replace(/^['"](.*)['"]$/, '$1');
86
-
87
+
87
88
  return { title, body };
88
89
  }
89
90
 
@@ -107,7 +108,7 @@ async function parseYamlBody(body) {
107
108
 
108
109
  async function generateCode(rawIssueContent, task, contextFiles, kbContext) {
109
110
  console.log('🧠 Dev Agent is thinking...');
110
-
111
+
111
112
  const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
112
113
  const model = genAI.getGenerativeModel({
113
114
  model: MODEL_NAME,
@@ -169,7 +170,7 @@ async function run() {
169
170
  // 2. Fetch Issue (Local)
170
171
  const issueId = String(options.issue);
171
172
  console.log(`📥 Fetching Issue #${issueId}...`);
172
-
173
+
173
174
  const issueFile = await GH_LocalFileService.getIssueById(issueId);
174
175
  if (issueFile.error) throw new Error(`Issue #${issueId} not found locally.`);
175
176
 
@@ -240,17 +241,17 @@ async function run() {
240
241
  console.log('📦 Committing changes...');
241
242
  await runGit('git add .');
242
243
  await runGit(`git commit -m "feat: ${title}"`);
243
-
244
+
244
245
  console.log('⬆️ Pushing branch...');
245
246
  // Construct authenticated URL for push
246
247
  // Format: https://x-access-token:<TOKEN>@github.com/owner/repo.git
247
248
  let remoteUrl = await runGit('git remote get-url origin');
248
-
249
+
249
250
  // Strip existing auth if present (e.g. https://user:pass@...) and ensure .git suffix
250
251
  remoteUrl = remoteUrl.replace(/^https?:\/\/([^@]*@)?/, 'https://');
251
-
252
+
252
253
  const authenticatedUrl = remoteUrl.replace('https://', `https://x-access-token:${process.env.GH_TOKEN}@`);
253
-
254
+
254
255
  await runGit(`git push -u "${authenticatedUrl}" ${branchName}`);
255
256
 
256
257
  console.log('🔀 Creating Pull Request...');
@@ -11,11 +11,12 @@
11
11
  * node ai/agents/pm.mjs --epic <issue_number>
12
12
  */
13
13
 
14
- import { Command } from 'commander';
15
- import yaml from 'js-yaml';
16
- import dotenv from 'dotenv';
17
- import path from 'path';
18
- import { fileURLToPath } from 'url';
14
+ import {Command} from 'commander';
15
+ import yaml from 'js-yaml';
16
+ import dotenv from 'dotenv';
17
+ import path from 'path';
18
+ import {fileURLToPath} from 'url';
19
+ import {sanitizeInput} from '../../buildScripts/util/Sanitizer.mjs';
19
20
  import {
20
21
  GH_IssueService,
21
22
  GH_HealthService,
@@ -32,7 +33,7 @@ const program = new Command();
32
33
  program
33
34
  .name('pm-agent')
34
35
  .description('Autonomous PM Agent for breaking down Epics into Tickets')
35
- .requiredOption('-e, --epic <number>', 'Epic Issue Number to process')
36
+ .requiredOption('-e, --epic <number>', 'Epic Issue Number to process', sanitizeInput)
36
37
  .option('-d, --dry-run', 'Simulate execution without creating issues')
37
38
  .parse(process.argv);
38
39
 
@@ -0,0 +1,74 @@
1
+ import { AppWorker_BridgeService } from '../services.mjs';
2
+
3
+ /**
4
+ * Test script for the App Worker MCP Server.
5
+ * Verifies that the Node.js agent can talk to the browser app.
6
+ */
7
+ async function main() {
8
+ console.log('🧪 Testing App Worker Neural Link...');
9
+
10
+ // 1. Start the Bridge
11
+ console.log('[1] Starting Bridge Service...');
12
+ // Accessing the singleton instance starts the server
13
+ const bridge = AppWorker_BridgeService;
14
+
15
+ // Wait a moment for server to bind
16
+ await new Promise(resolve => setTimeout(resolve, 1000));
17
+
18
+ const status = bridge.getStatus();
19
+ console.log(` ✅ Bridge listening on port ${bridge.port}`);
20
+ console.log(` Clients connected: ${status.connectedClients}`);
21
+
22
+ if (status.connectedClients === 0) {
23
+ console.log(' ⚠️ No App Worker connected yet. Please reload the browser window.');
24
+ console.log(' Waiting for connection...');
25
+
26
+ // Poll for connection
27
+ let retries = 30;
28
+ while (retries > 0) {
29
+ await new Promise(resolve => setTimeout(resolve, 1000));
30
+ if (bridge.getStatus().connectedClients > 0) break;
31
+ process.stdout.write('.');
32
+ retries--;
33
+ }
34
+ console.log('');
35
+ }
36
+
37
+ if (bridge.getStatus().connectedClients === 0) {
38
+ console.error('❌ Timeout: App Worker did not connect.');
39
+ process.exit(1);
40
+ }
41
+
42
+ console.log(' ✅ App Worker Connected!');
43
+
44
+ // 2. Send Remote Command
45
+ console.log('[2] Sending Remote Command (createNeoInstance)...');
46
+
47
+ try {
48
+ const result = await bridge.evaluate({
49
+ method: 'Neo.worker.App.createNeoInstance',
50
+ params: [{
51
+ ntype: 'button',
52
+ text: 'Hello from Node.js!',
53
+ iconCls: 'fa fa-robot',
54
+ style: {
55
+ position: 'absolute',
56
+ top: '20px',
57
+ right: '20px',
58
+ zIndex: 1000
59
+ }
60
+ }]
61
+ });
62
+
63
+ console.log(' ✅ Command Sent. Result:', result);
64
+ console.log(' (Check browser to see the new button!)');
65
+
66
+ } catch (error) {
67
+ console.error(' ❌ Command Failed:', error);
68
+ }
69
+
70
+ // Keep process alive briefly to ensure message flush
71
+ setTimeout(() => process.exit(0), 1000);
72
+ }
73
+
74
+ main();
@@ -16,17 +16,18 @@ import * as core from '../../../src/core/_export.mjs'; // For Neo.core.B
16
16
  import InstanceManager from '../../../src/manager/Instance.mjs'; // For Neo.core.Base setup
17
17
  import Client from './Client.mjs';
18
18
  import aiConfig from './config.mjs';
19
+ import {sanitizeInput} from '../../../buildScripts/util/Sanitizer.mjs';
19
20
 
20
21
  const program = new Command();
21
22
 
22
23
  program
23
24
  .name('mcp-client')
24
25
  .description('CLI for interacting with MCP servers')
25
- .option('-s, --server <name>', 'Logical name of the MCP server to connect to (e.g., github-workflow)')
26
+ .option('-s, --server <name>', 'Logical name of the MCP server to connect to (e.g., github-workflow)', sanitizeInput)
26
27
  .option('-l, --list-tools', 'List available tools on the specified server')
27
- .option('-c, --config <path>', 'Path to an external client configuration file')
28
- .option('-t, --call-tool <toolName>', 'Name of the tool to call')
29
- .option('-a, --args <json>', 'JSON string of arguments for --call-tool', '{}')
28
+ .option('-c, --config <path>', 'Path to an external client configuration file', sanitizeInput)
29
+ .option('-t, --call-tool <toolName>', 'Name of the tool to call', sanitizeInput)
30
+ .option('-a, --args <json>', 'JSON string of arguments for --call-tool', '{}', sanitizeInput)
30
31
  .option('-d, --debug', 'Enable debug logging');
31
32
 
32
33
  program.parse(process.argv);
@@ -1,7 +1,13 @@
1
- import fs from 'fs/promises';
1
+ import fs from 'fs/promises';
2
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
3
4
  import Base from '../../../../src/core/Base.mjs';
4
5
 
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const packageRoot = path.resolve(__dirname, '../../../../');
9
+ const projectRoot = process.cwd() === '/' ? packageRoot : process.cwd();
10
+
5
11
  /**
6
12
  * Default configuration object.
7
13
  * Defines the structure and default values for the server configuration.
@@ -48,17 +54,17 @@ const defaultConfig = {
48
54
  * The path to the directory for active issues.
49
55
  * @type {string}
50
56
  */
51
- issuesDir: path.resolve(process.cwd(), '.github', 'ISSUE'),
57
+ issuesDir: path.resolve(projectRoot, '.github', 'ISSUE'),
52
58
  /**
53
59
  * The path to the directory for archived issues.
54
60
  * @type {string}
55
61
  */
56
- archiveDir: path.resolve(process.cwd(), '.github', 'ISSUE_ARCHIVE'),
62
+ archiveDir: path.resolve(projectRoot, '.github', 'ISSUE_ARCHIVE'),
57
63
  /**
58
64
  * The path to the synchronization metadata file.
59
65
  * @type {string}
60
66
  */
61
- metadataFile: path.resolve(process.cwd(), '.github', '.sync-metadata.json'),
67
+ metadataFile: path.resolve(projectRoot, '.github', '.sync-metadata.json'),
62
68
  /**
63
69
  * Labels that, when present on an issue, will cause it to be ignored and deleted locally.
64
70
  * @type {string[]}
@@ -73,7 +79,7 @@ const defaultConfig = {
73
79
  * The path to the directory for release notes.
74
80
  * @type {string}
75
81
  */
76
- releaseNotesDir: path.resolve(process.cwd(), '.github', 'RELEASE_NOTES'),
82
+ releaseNotesDir: path.resolve(projectRoot, '.github', 'RELEASE_NOTES'),
77
83
  /**
78
84
  * The default version directory to use for archiving issues when no release is found.
79
85
  * @type {string}
@@ -233,15 +239,15 @@ class Config extends Base {
233
239
 
234
240
  try {
235
241
  const absolutePath = path.resolve(filePath);
236
- const ext = path.extname(absolutePath);
237
- let customConfig;
242
+ const ext = path.extname(absolutePath);
243
+ let customConfig;
238
244
 
239
245
  if (ext === '.mjs' || ext === '.js') {
240
246
  const module = await import(absolutePath);
241
247
  customConfig = module.default;
242
248
  } else {
243
249
  const content = await fs.readFile(absolutePath, 'utf-8');
244
- customConfig = JSON.parse(content);
250
+ customConfig = JSON.parse(content);
245
251
  }
246
252
 
247
253
  // Deep merge custom config into the data object
@@ -5,13 +5,14 @@ import InstanceManager from '../../../../src/manager/Instance.mjs';
5
5
  import aiConfig from './config.mjs';
6
6
  import logger from './logger.mjs';
7
7
  import Server from './Server.mjs';
8
+ import {sanitizeInput} from '../../../../buildScripts/util/Sanitizer.mjs';
8
9
 
9
10
  const program = new Command();
10
11
 
11
12
  program
12
13
  .name('neo-github-workflow-mcp')
13
14
  .description('Neo.mjs GitHub Workflow MCP Server')
14
- .option('-c, --config <path>', 'Path to the configuration file')
15
+ .option('-c, --config <path>', 'Path to the configuration file', sanitizeInput)
15
16
  .option('-d, --debug', 'Enable debug logging')
16
17
  .parse(process.argv);
17
18
 
@@ -1,10 +1,8 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import {fileURLToPath} from 'url';
4
- import Base from '../../../../src/core/Base.mjs';
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import Base from '../../../../src/core/Base.mjs';
5
4
 
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
5
+ const cwd = process.cwd();
8
6
 
9
7
  /**
10
8
  * Default configuration object.
@@ -28,13 +26,13 @@ const defaultConfig = {
28
26
  * @returns {Object} The dummy embedding function satisfying IEmbeddingFunction
29
27
  */
30
28
  dummyEmbeddingFunction: {
31
- generate: () => null,
32
- name: 'dummy_embedding_function',
33
- getConfig: () => ({}),
29
+ generate : () => null,
30
+ name : 'dummy_embedding_function',
31
+ getConfig : () => ({}),
34
32
  constructor: {
35
33
  buildFromConfig: () => ({
36
- generate: () => null,
37
- name: 'dummy_embedding_function',
34
+ generate : () => null,
35
+ name : 'dummy_embedding_function',
38
36
  getConfig: () => ({})
39
37
  })
40
38
  }
@@ -53,12 +51,12 @@ const defaultConfig = {
53
51
  * The local persistence path for the agent knowledge-base server.
54
52
  * @type {string}
55
53
  */
56
- path: path.resolve(__dirname, '../../../../chroma-neo-knowledge-base'),
54
+ path: path.resolve(cwd, 'chroma-neo-knowledge-base'),
57
55
  /**
58
56
  * The path to the generated knowledge base JSONL file.
59
57
  * @type {string}
60
58
  */
61
- dataPath: path.resolve(__dirname, '../../../../dist/ai-knowledge-base.jsonl'),
59
+ dataPath: path.resolve(cwd, 'dist/ai-knowledge-base.jsonl'),
62
60
  /**
63
61
  * The name of the ChromaDB collection for the knowledge base.
64
62
  * @type {string}
@@ -5,13 +5,14 @@ import InstanceManager from '../../../../src/manager/Instance.mjs';
5
5
  import aiConfig from './config.mjs';
6
6
  import logger from './logger.mjs';
7
7
  import Server from './Server.mjs';
8
+ import {sanitizeInput} from '../../../../buildScripts/util/Sanitizer.mjs';
8
9
 
9
10
  const program = new Command();
10
11
 
11
12
  program
12
13
  .name('neo-knowledge-base-mcp')
13
14
  .description('Neo.mjs Knowledge Base MCP Server')
14
- .option('-c, --config <path>', 'Path to the configuration file')
15
+ .option('-c, --config <path>', 'Path to the configuration file', sanitizeInput)
15
16
  .option('-d, --debug', 'Enable debug logging')
16
17
  .parse(process.argv);
17
18
 
@@ -45,7 +45,6 @@ class Server extends Base {
45
45
 
46
46
  /**
47
47
  * Async initialization sequence.
48
- * Replaces the main() function of the previous procedural implementation.
49
48
  * @returns {Promise<void>}
50
49
  */
51
50
  async initAsync() {
@@ -1,10 +1,8 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import {fileURLToPath} from 'url';
4
- import Base from '../../../../src/core/Base.mjs';
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import Base from '../../../../src/core/Base.mjs';
5
4
 
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
5
+ const cwd = process.cwd();
8
6
 
9
7
  /**
10
8
  * Default configuration object.
@@ -28,13 +26,13 @@ const defaultConfig = {
28
26
  * @returns {Object} The dummy embedding function satisfying IEmbeddingFunction
29
27
  */
30
28
  dummyEmbeddingFunction: {
31
- generate: () => null,
32
- name: 'dummy_embedding_function',
33
- getConfig: () => ({}),
29
+ generate : () => null,
30
+ name : 'dummy_embedding_function',
31
+ getConfig : () => ({}),
34
32
  constructor: {
35
33
  buildFromConfig: () => ({
36
- generate: () => null,
37
- name: 'dummy_embedding_function',
34
+ generate : () => null,
35
+ name : 'dummy_embedding_function',
38
36
  getConfig: () => ({})
39
37
  })
40
38
  }
@@ -78,12 +76,12 @@ const defaultConfig = {
78
76
  * The local persistence path for the agent memory server.
79
77
  * @type {string}
80
78
  */
81
- path: path.resolve(__dirname, '../../../../chroma-neo-memory-core'),
79
+ path: path.resolve(cwd, 'chroma-neo-memory-core'),
82
80
  /**
83
81
  * The path to store memory backups.
84
82
  * @type {string}
85
83
  */
86
- backupPath: path.resolve(__dirname, '../../../../../dist/memory-backups')
84
+ backupPath: path.resolve(cwd, 'dist/memory-backups')
87
85
  },
88
86
  /**
89
87
  * Configuration for the AI agent's session summary database.
@@ -108,7 +106,7 @@ const defaultConfig = {
108
106
  * The path to store session summary backups.
109
107
  * @type {string}
110
108
  */
111
- backupPath: path.resolve(__dirname, '../../../../../dist/session-backups')
109
+ backupPath: path.resolve(cwd, 'dist/session-backups')
112
110
  }
113
111
  };
114
112
 
@@ -5,13 +5,14 @@ import InstanceManager from '../../../../src/manager/Instance.mjs';
5
5
  import aiConfig from './config.mjs';
6
6
  import logger from './logger.mjs';
7
7
  import Server from './Server.mjs';
8
+ import {sanitizeInput} from '../../../../buildScripts/util/Sanitizer.mjs';
8
9
 
9
10
  const program = new Command();
10
11
 
11
12
  program
12
13
  .name('neo-memory-core-mcp')
13
14
  .description('Neo.mjs Memory Core MCP Server')
14
- .option('-c, --config <path>', 'Path to the configuration file')
15
+ .option('-c, --config <path>', 'Path to the configuration file', sanitizeInput)
15
16
  .option('-d, --debug', 'Enable debug logging')
16
17
  .parse(process.argv);
17
18
 
@@ -2,7 +2,6 @@ import aiConfig from '../config.mjs';
2
2
  import Base from '../../../../../src/core/Base.mjs';
3
3
  import ChromaManager from './ChromaManager.mjs';
4
4
  import DatabaseLifecycleService from './DatabaseLifecycleService.mjs';
5
- import SessionService from './SessionService.mjs';
6
5
  import logger from '../logger.mjs';
7
6
 
8
7
  /**
@@ -208,7 +207,7 @@ class HealthService extends Base {
208
207
  status : 'healthy',
209
208
  timestamp: new Date().toISOString(),
210
209
  session : {
211
- currentId: SessionService.currentSessionId
210
+ currentId: Neo.ns('Neo.ai.mcp.server.memory-core.services.SessionService', false)?.currentSessionId
212
211
  },
213
212
  database : {
214
213
  process: DatabaseLifecycleService.getDatabaseStatus(),
@@ -1,6 +1,7 @@
1
1
  import {GoogleGenerativeAI} from '@google/generative-ai';
2
2
  import aiConfig from '../config.mjs';
3
3
  import Base from '../../../../../src/core/Base.mjs';
4
+ import logger from '../logger.mjs';
4
5
 
5
6
  /**
6
7
  * @summary Service for creating embedding vectors for text.
@@ -42,10 +43,8 @@ class TextEmbeddingService extends Base {
42
43
  const apiKey = process.env.GEMINI_API_KEY;
43
44
 
44
45
  if (!apiKey) {
45
- const error = new Error('The GEMINI_API_KEY environment variable must be set to use semantic search endpoints.');
46
- error.status = 503;
47
- error.code = 'missing_gemini_api_key';
48
- throw error;
46
+ logger.warn('⚠️ [TextEmbeddingService] GEMINI_API_KEY not set. Semantic search features will be unavailable.');
47
+ return;
49
48
  }
50
49
 
51
50
  const genAI = new GoogleGenerativeAI(apiKey);
@@ -58,6 +57,10 @@ class TextEmbeddingService extends Base {
58
57
  * @returns {Promise<number[]>}
59
58
  */
60
59
  async embedText(text) {
60
+ if (!process.env.GEMINI_API_KEY) {
61
+ throw new Error('Semantic search unavailable: GEMINI_API_KEY is missing.');
62
+ }
63
+
61
64
  const result = await this.embeddingModel.embedContent(text);
62
65
  return result.embedding.values;
63
66
  }