neuralos 3.9.5 → 3.9.6

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 (68) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +48 -347
  3. package/cli.js +78 -0
  4. package/engine.js +124 -0
  5. package/index.js +160 -0
  6. package/package.json +37 -43
  7. package/test.js +20 -0
  8. package/tools.js +47 -0
  9. package/bin/gybackend.cjs +0 -411663
  10. package/plugins/agentspan-bridge/agentspan-bridge.extreme.spec.ts +0 -328
  11. package/plugins/agentspan-bridge/agentspan-bridge.phase2.extreme.spec.ts +0 -282
  12. package/plugins/agentspan-bridge/conductorClient.mjs +0 -182
  13. package/plugins/agentspan-bridge/index.mjs +0 -389
  14. package/plugins/agentspan-bridge/playbookToWorkflowDef.mjs +0 -209
  15. package/plugins/agentspan-bridge/plugin.json +0 -27
  16. package/plugins/fraudops/index.d.mts +0 -9
  17. package/plugins/fraudops/index.mjs +0 -185
  18. package/plugins/fraudops/plugin.json +0 -10
  19. package/plugins/iam-connector/index.d.mts +0 -10
  20. package/plugins/iam-connector/index.mjs +0 -229
  21. package/plugins/iam-connector/plugin.json +0 -10
  22. package/plugins/mitmproxy-bridge/index.d.mts +0 -20
  23. package/plugins/mitmproxy-bridge/index.mjs +0 -256
  24. package/plugins/mitmproxy-bridge/plugin.json +0 -17
  25. package/plugins/monid-bridge/index.mjs +0 -228
  26. package/plugins/monid-bridge/monid-bridge.extreme.spec.mjs +0 -75
  27. package/plugins/monid-bridge/plugin.json +0 -15
  28. package/plugins/netdata-rterm/index.mjs +0 -229
  29. package/plugins/netdata-rterm/netdata-rterm.extreme.spec.ts +0 -252
  30. package/plugins/netdata-rterm/plugin.json +0 -22
  31. package/plugins/netexec-bridge/index.d.mts +0 -28
  32. package/plugins/netexec-bridge/index.mjs +0 -325
  33. package/plugins/netexec-bridge/plugin.json +0 -20
  34. package/plugins/neuralos/index.mjs +0 -137
  35. package/plugins/neuralos/neuralos.extreme.spec.mts +0 -386
  36. package/plugins/neuralos/neuralosEngine.mjs +0 -369
  37. package/plugins/neuralos/plugin.json +0 -15
  38. package/plugins/numbat-bridge/index.mjs +0 -229
  39. package/plugins/numbat-bridge/numbat-bridge.extreme.spec.mjs +0 -148
  40. package/plugins/numbat-bridge/plugin.json +0 -21
  41. package/plugins/patch-manager/index.d.mts +0 -10
  42. package/plugins/patch-manager/index.mjs +0 -288
  43. package/plugins/patch-manager/plugin.json +0 -10
  44. package/plugins/promptfoo-redteam/index.d.mts +0 -15
  45. package/plugins/promptfoo-redteam/index.mjs +0 -277
  46. package/plugins/promptfoo-redteam/plugin.json +0 -16
  47. package/plugins/reactorpro-bridge/index.mjs +0 -499
  48. package/plugins/reactorpro-bridge/plugin.json +0 -26
  49. package/plugins/reactorpro-bridge/reactorpro-bridge.extreme.spec.mts +0 -409
  50. package/plugins/reactorpro-bridge/reactorproAgent.mjs +0 -399
  51. package/plugins/request-router/index.d.mts +0 -9
  52. package/plugins/request-router/index.mjs +0 -239
  53. package/plugins/request-router/plugin.json +0 -10
  54. package/plugins/sample-k8s-slo/index.ts +0 -58
  55. package/plugins/sample-k8s-slo/plugin.json +0 -11
  56. package/plugins/sop-assistant/index.d.mts +0 -9
  57. package/plugins/sop-assistant/index.mjs +0 -325
  58. package/plugins/sop-assistant/plugin.json +0 -10
  59. package/plugins/synapse-bridge/index.mjs +0 -484
  60. package/plugins/synapse-bridge/plugin.json +0 -29
  61. package/plugins/synapse-bridge/synapse-bridge.extreme.spec.mjs +0 -204
  62. package/plugins/synapse-bridge/synapseAgent.extreme.spec.mjs +0 -236
  63. package/plugins/synapse-bridge/synapseAgent.mjs +0 -254
  64. package/plugins/web-intel/index.mjs +0 -381
  65. package/plugins/web-intel/plugin.json +0 -28
  66. package/plugins/web-intel/sidecar.mjs +0 -134
  67. package/plugins/web-intel/web-intel.extreme.spec.ts +0 -263
  68. package/plugins/web-intel/wigoloClient.mjs +0 -145
package/cli.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { Needle } = require('./index');
7
+
8
+ function usage(code = 0) {
9
+ const text = `neuralos ${require('./package.json').version} — offline tool-calling + embeddings engine
10
+
11
+ Usage:
12
+ neuralos run --tools <tools.json> [--impl <impl.js>] [--system <text>]
13
+ [--query <text>] [--max-steps <n>] [--max-new-tokens <n>]
14
+ neuralos embed --text <text>
15
+ neuralos info
16
+
17
+ tools.json: array of { name, description, parameters }
18
+ impl.js: optional module exporting { toolName: (args) => result } functions
19
+ `;
20
+ process.stdout.write(text);
21
+ process.exit(code);
22
+ }
23
+
24
+ function argValue(flag, argv) {
25
+ const i = argv.indexOf(flag);
26
+ return i >= 0 ? argv[i + 1] : undefined;
27
+ }
28
+
29
+ const argv = process.argv.slice(2);
30
+ const command = argv[0];
31
+
32
+ try {
33
+ if (!command || command === '--help' || command === '-h') usage(0);
34
+
35
+ if (command === 'info') {
36
+ const info = require('./index').engineInfo();
37
+ console.log(JSON.stringify(info, null, 2));
38
+ process.exit(0);
39
+ }
40
+
41
+ if (command === 'embed') {
42
+ const text = argValue('--text', argv);
43
+ if (!text) usage(1);
44
+ const agent = new Needle();
45
+ process.stdout.write(JSON.stringify(agent.embed(text)) + '\n');
46
+ process.exit(0);
47
+ }
48
+
49
+ if (command === 'run') {
50
+ const toolsPath = argValue('--tools', argv);
51
+ const implPath = argValue('--impl', argv);
52
+ const query = argValue('--query', argv) || '';
53
+ const system = argValue('--system', argv) || '';
54
+ const maxSteps = Number(argValue('--max-steps', argv) || 8);
55
+ const maxNewTokens = Number(argValue('--max-new-tokens', argv) || 512);
56
+ if (!toolsPath) usage(1);
57
+
58
+ const schemas = JSON.parse(fs.readFileSync(path.resolve(toolsPath), 'utf8'));
59
+ const tools = schemas.map((schema) => {
60
+ const entry = { ...schema };
61
+ if (implPath) {
62
+ const impls = require(path.resolve(implPath));
63
+ if (typeof impls[schema.name] === 'function') {
64
+ entry.implementation = impls[schema.name];
65
+ }
66
+ }
67
+ return entry;
68
+ });
69
+ const agent = new Needle({ tools, system });
70
+ process.stdout.write(JSON.stringify(agent.run(query, { maxSteps, maxNewTokens }), null, 2) + '\n');
71
+ process.exit(0);
72
+ }
73
+
74
+ usage(1);
75
+ } catch (err) {
76
+ process.stderr.write(`neuralos: ${err && err.message ? err.message : err}\n`);
77
+ process.exit(1);
78
+ }
package/engine.js ADDED
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * neuralOS engine loader — locates the platform engine package
5
+ * (libneedle3 shared library + needle3.cact weights), binds the C ABI
6
+ * through koffi, and loads the base weights once per process.
7
+ *
8
+ * The C ABI mirrors needle/__init__.py in the Python distribution:
9
+ * int needle_init(const char *system, const char *tools, const char *tool_index)
10
+ * int needle_complete(const char *text, int max_new_tokens, char *buffer, int buffer_len)
11
+ * int needle_embed(const char *text, float *out, int dim)
12
+ * void needle_reset(void)
13
+ * int needle_load(const char *data, unsigned long long len)
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const koffi = require('koffi');
19
+
20
+ const PLATFORM_PACKAGES = {
21
+ 'darwin-arm64': { pkg: 'neuralos-darwin-arm64', lib: 'libneedle3.dylib' },
22
+ 'darwin-x64': { pkg: 'neuralos-darwin-x64', lib: 'libneedle3.dylib' },
23
+ 'linux-x64-gnu': { pkg: 'neuralos-linux-x64-gnu', lib: 'libneedle3.so' },
24
+ 'linux-arm64-gnu': { pkg: 'neuralos-linux-arm64-gnu', lib: 'libneedle3.so' },
25
+ 'win32-x64': { pkg: 'neuralos-win32-x64', lib: 'libneedle3.dll' },
26
+ };
27
+
28
+ function platformKey() {
29
+ const p = process.platform;
30
+ const a = process.arch;
31
+ if (p === 'darwin') return a === 'arm64' ? 'darwin-arm64' : (a === 'x64' ? 'darwin-x64' : null);
32
+ if (p === 'win32') return a === 'x64' ? 'win32-x64' : null;
33
+ if (p === 'linux') {
34
+ if (a !== 'x64' && a !== 'arm64') return null;
35
+ // musl (Alpine) engines are not bundled; detect via process.report.
36
+ try {
37
+ const report = process.report && process.report.getReport && process.report.getReport();
38
+ if (report && report.header && !report.header.glibcVersionRuntime) return null;
39
+ } catch (_) { /* assume glibc */ }
40
+ return a === 'x64' ? 'linux-x64-gnu' : 'linux-arm64-gnu';
41
+ }
42
+ return null;
43
+ }
44
+
45
+ function platformPackageDir() {
46
+ const key = platformKey();
47
+ if (!key) {
48
+ throw new Error(
49
+ `neuralOS: no bundled engine for ${process.platform}/${process.arch}. ` +
50
+ 'Supported: darwin arm64/x64, linux x64/arm64 (glibc), win32 x64.');
51
+ }
52
+ const spec = PLATFORM_PACKAGES[key];
53
+ let dir;
54
+ try {
55
+ dir = path.dirname(require.resolve(spec.pkg + '/package.json'));
56
+ } catch (err) {
57
+ throw new Error(
58
+ `neuralOS: engine package ${spec.pkg} is not installed. Reinstall neuralos ` +
59
+ `(optional dependencies must be allowed: npm i neuralos --include=optional).`);
60
+ }
61
+ return { dir, lib: spec.lib, key };
62
+ }
63
+
64
+ let _engine = null;
65
+
66
+ function engine() {
67
+ if (_engine) return _engine;
68
+ const { dir, lib: libName } = platformPackageDir();
69
+ const libPath = path.join(dir, 'bin', libName);
70
+ const weightsPath = path.join(dir, 'bin', 'needle3.cact');
71
+ if (!fs.existsSync(libPath)) throw new Error(`neuralOS: engine library missing: ${libPath}`);
72
+ if (!fs.existsSync(weightsPath)) throw new Error(`neuralOS: weights missing: ${weightsPath}`);
73
+
74
+ const lib = koffi.load(libPath);
75
+ const needleInit = lib.func('int needle_init(const char *system, const char *tools, const char *tool_index)');
76
+ const needleComplete = lib.func('int needle_complete(const char *text, int max_new_tokens, _Out_ char *buffer, int buffer_len)');
77
+ const needleEmbed = lib.func('int needle_embed(const char *text, float *out, int dim)');
78
+ const needleReset = lib.func('void needle_reset()');
79
+ const needleLoad = lib.func('int needle_load(const char *data, unsigned long long len)');
80
+
81
+ const weights = fs.readFileSync(weightsPath);
82
+ const dataPtr = koffi.as(weights, 'const char *');
83
+ if (needleLoad(dataPtr, weights.length) < 0) {
84
+ throw new Error(`neuralOS: needle_load failed for ${weightsPath}`);
85
+ }
86
+
87
+ _engine = {
88
+ libPath,
89
+ weightsPath,
90
+ init: (systemText, toolsJson, toolIndexPath) =>
91
+ needleInit(systemText, toolsJson, toolIndexPath || null),
92
+ complete: (text, maxNewTokens, buf, len) =>
93
+ needleComplete(text, maxNewTokens, buf, len),
94
+ embed: (text, out, dim) => needleEmbed(text, out, dim),
95
+ reset: () => needleReset(),
96
+ allocBuffer: (n) => koffi.alloc('char', n),
97
+ allocFloats: (n) => koffi.alloc('float', n),
98
+ // koffi.decode(ptr, 'string') would deref the pointer as char**; the
99
+ // correct read of a raw char buffer is 'char' + length, NUL-trimmed.
100
+ decodeString: (ptr, n) => {
101
+ const bytes = Buffer.from(koffi.decode(ptr, 'char', n));
102
+ const end = bytes.indexOf(0);
103
+ return bytes.subarray(0, end === -1 ? bytes.length : end).toString('utf8');
104
+ },
105
+ decodeFloats: (ptr, n) => koffi.decode(ptr, 'float', n),
106
+ };
107
+ return _engine;
108
+ }
109
+
110
+ /** The currently bound agent (needle_init is engine-global, like Python's). */
111
+ let _active = null;
112
+
113
+ function bindAgent(agent) {
114
+ const e = engine();
115
+ if (_active === agent) return e;
116
+ if (e.init(agent.systemText, agent.toolsJson, null) < 0) {
117
+ _active = null;
118
+ throw new Error('neuralOS: needle_init failed');
119
+ }
120
+ _active = agent;
121
+ return e;
122
+ }
123
+
124
+ module.exports = { engine, bindAgent, platformKey, platformPackageDir, PLATFORM_PACKAGES };
package/index.js ADDED
@@ -0,0 +1,160 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * neuralOS — the automation foundation model for tiny devices, for Node.js.
5
+ *
6
+ * Offline tool-calling, structured extraction and embeddings on the bundled
7
+ * libneedle3 engine + needle3.cact weights (installed via the platform
8
+ * optional dependency for your OS/arch).
9
+ *
10
+ * const { Needle, tool } = require('neuralos');
11
+ *
12
+ * const getWeather = tool({
13
+ * name: 'get_weather',
14
+ * description: 'Get the current weather for a city.',
15
+ * parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
16
+ * }, (args) => ({ city: args.city, temp_c: 27, sky: 'clear' }));
17
+ *
18
+ * const agent = new Needle({ tools: [getWeather] });
19
+ * console.log(agent.run("What's the weather in Lagos?"));
20
+ */
21
+
22
+ const engineLoader = require('./engine');
23
+ const { normalizeTools, tool } = require('./tools');
24
+
25
+ const UNRESET_TURNS = 4;
26
+
27
+ function withDateFact(system, autoDate) {
28
+ const text = system || '';
29
+ if (!autoDate) return text;
30
+ // Mirror Python: skip when the caller already supplied a date fact/ISO stamp.
31
+ if (/\bdate\s*:/.test(text) || /\d{4}-\d{2}-\d{2}/.test(text)) return text;
32
+ const now = new Date();
33
+ const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
34
+ const pad = (n) => String(n).padStart(2, '0');
35
+ const fact =
36
+ `date: ${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ` +
37
+ `${days[now.getDay()]} ${pad(now.getHours())}:${pad(now.getMinutes())}`;
38
+ if (!text.trim()) return fact;
39
+ return fact + '; ' + text;
40
+ }
41
+
42
+ class Needle {
43
+ constructor({
44
+ tools = [],
45
+ system = '',
46
+ autoDate = true,
47
+ stateless = false,
48
+ bufferSize = 65536,
49
+ } = {}) {
50
+ this._functions = new Map();
51
+ this._schemas = normalizeTools(tools, this._functions);
52
+ this.systemText = withDateFact(system, autoDate);
53
+ this.toolsJson = JSON.stringify(this._schemas);
54
+ this._stateless = !!stateless;
55
+ this._turns = 0;
56
+ this._bufferSize = bufferSize;
57
+ this._bind();
58
+ }
59
+
60
+ _bind() {
61
+ engineLoader.bindAgent(this);
62
+ }
63
+
64
+ _countQuery() {
65
+ this._turns++;
66
+ if (this._turns === UNRESET_TURNS + 1) {
67
+ console.warn(
68
+ `neuralOS: ${UNRESET_TURNS} queries on this agent without reset(); every turn stays in the ` +
69
+ 'conversation, so unrelated queries lose accuracy. Call reset() between independent queries.');
70
+ }
71
+ }
72
+
73
+ complete(text = '', maxNewTokens = 512) {
74
+ if (this._stateless) this.reset();
75
+ this._countQuery();
76
+ return this._complete(text, maxNewTokens);
77
+ }
78
+
79
+ _complete(text, maxNewTokens, parse = true) {
80
+ this._bind();
81
+ const e = engineLoader.engine();
82
+ const buffer = e.allocBuffer(this._bufferSize);
83
+ const rc = e.complete(text || '', Math.trunc(maxNewTokens), buffer, this._bufferSize);
84
+ if (rc < 0) {
85
+ const detail = e.decodeString(buffer, this._bufferSize);
86
+ throw new Error(detail || `neuralOS: needle_complete failed (code ${rc})`);
87
+ }
88
+ const raw = e.decodeString(buffer, this._bufferSize);
89
+ if (!parse) return raw;
90
+ let response;
91
+ try {
92
+ response = JSON.parse(raw);
93
+ } catch (err) {
94
+ throw new Error(`neuralOS: engine returned an unparseable envelope (${err.message})`);
95
+ }
96
+ return response;
97
+ }
98
+
99
+ /** Execute tool calls until the model responds. Mirrors Python's run(). */
100
+ run(query = '', { maxSteps = 8, maxNewTokens = 512 } = {}) {
101
+ if (this._stateless) this.reset();
102
+ this._countQuery();
103
+ let response = this._complete(query, maxNewTokens);
104
+ const executed = [];
105
+ for (let step = 0; step < maxSteps; step++) {
106
+ const calls = response.function_calls || [];
107
+ if (response.type !== 'call' || !calls.length) break;
108
+ const results = [];
109
+ for (const call of calls) {
110
+ const name = String(call.name);
111
+ const fn = this._functions.get(name);
112
+ if (!fn) {
113
+ results.push({ error: 'unknown tool: ' + name });
114
+ continue;
115
+ }
116
+ try {
117
+ results.push(fn(call.arguments || {}));
118
+ } catch (err) {
119
+ results.push({ error: String((err && err.message) || err) });
120
+ }
121
+ }
122
+ executed.push(...results);
123
+ response = this._complete(JSON.stringify(results), maxNewTokens, false);
124
+ try { response = JSON.parse(response); } catch (_) { /* handled below */ }
125
+ if (typeof response !== 'object' || response === null) {
126
+ throw new Error('neuralOS: engine returned an unparseable envelope');
127
+ }
128
+ }
129
+ response.results = executed;
130
+ return response;
131
+ }
132
+
133
+ embed(text = '') {
134
+ this._bind();
135
+ const e = engineLoader.engine();
136
+ const dim = e.embed(text || '', null, 0);
137
+ if (dim <= 0) throw new Error(`neuralOS: needle_embed failed (code ${dim})`);
138
+ const out = e.allocFloats(dim);
139
+ const rc = e.embed(text || '', out, dim);
140
+ if (rc !== dim) throw new Error(`neuralOS: needle_embed failed (code ${rc})`);
141
+ return e.decodeFloats(out, dim);
142
+ }
143
+
144
+ reset() {
145
+ this._bind();
146
+ engineLoader.engine().reset();
147
+ this._turns = 0;
148
+ }
149
+ }
150
+
151
+ module.exports = {
152
+ Needle,
153
+ tool,
154
+ version: require('./package.json').version,
155
+ platformKey: engineLoader.platformKey,
156
+ engineInfo: () => {
157
+ const e = engineLoader.engine();
158
+ return { lib: e.libPath, weights: e.weightsPath };
159
+ },
160
+ };
package/package.json CHANGED
@@ -1,55 +1,49 @@
1
1
  {
2
- "main": "bin/gybackend.cjs",
2
+ "name": "neuralos",
3
+ "version": "3.9.6",
4
+ "description": "neuralOS - automation foundation model for tiny devices: offline tool-calling + embeddings engine for Node.js, weights bundled",
5
+ "main": "index.js",
3
6
  "bin": {
4
- "gybackend": "bin/gybackend.cjs"
7
+ "neuralos": "cli.js"
5
8
  },
9
+ "files": [
10
+ "index.js",
11
+ "engine.js",
12
+ "tools.js",
13
+ "cli.js",
14
+ "test.js",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
6
18
  "engines": {
7
19
  "node": ">=18"
8
20
  },
9
- "dependencies": {
10
- "@nats-io/jetstream": "^3.4.0",
11
- "@nats-io/kv": "^3.4.0",
12
- "@nats-io/transport-node": "^3.4.0",
13
- "better-sqlite3": "^12.11.1"
14
- },
15
- "optionalDependencies": {
16
- "node-pty": "1.2.0-beta.3",
17
- "serialport": "^12.0.0",
18
- "ssh2": "^1.17.0"
21
+ "license": "Apache-2.0",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/DrOlu/neuralOS.git"
19
25
  },
26
+ "homepage": "https://github.com/DrOlu/neuralOS#readme",
20
27
  "keywords": [
21
- "forward-deployed-engineer",
22
- "fde",
23
- "ai-terminal",
24
- "ai-agent",
25
- "ssh",
26
- "winrm",
27
- "serial-console",
28
- "cisco",
29
- "network-automation",
30
- "sre",
31
- "observability",
32
- "incident-response",
33
- "change-management",
34
- "infrastructure-automation",
35
- "runbooks",
36
28
  "neuralos",
37
- "aiops",
38
- "ai-sre",
39
- "agentic-ai",
40
- "chatops",
41
- "self-healing",
42
- "runbook-automation",
43
- "closed-loop-remediation",
44
- "mcp"
29
+ "needle",
30
+ "llm",
31
+ "tool-calling",
32
+ "structured-extraction",
33
+ "embeddings",
34
+ "on-device",
35
+ "offline",
36
+ "tinyml",
37
+ "ai"
45
38
  ],
46
- "license": "MIT",
47
- "homepage": "https://rterm.app",
48
- "repository": {
49
- "type": "git",
50
- "url": "git+https://github.com/DrOlu/RTerm.git"
39
+ "dependencies": {
40
+ "koffi": "^2.9.0"
51
41
  },
52
- "name": "neuralos",
53
- "version": "3.9.5",
54
- "description": "Standalone neuralOS backend (rterm-backend): AI-native terminal & agentic-AI operations platform. Ships the neuralOS plugin — on-device data agents (verified probe menus over real data sources, engine + weights auto-provisioned on first use)."
42
+ "optionalDependencies": {
43
+ "neuralos-darwin-arm64": "3.9.6",
44
+ "neuralos-darwin-x64": "3.9.6",
45
+ "neuralos-linux-x64-gnu": "3.9.6",
46
+ "neuralos-linux-arm64-gnu": "3.9.6",
47
+ "neuralos-win32-x64": "3.9.6"
48
+ }
55
49
  }
package/test.js ADDED
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const { Needle, tool } = require('./index');
4
+
5
+ const getWeather = tool({
6
+ name: 'get_weather',
7
+ description: 'Get the current weather for a city.',
8
+ parameters: {
9
+ type: 'object',
10
+ properties: { city: { type: 'string' } },
11
+ required: ['city'],
12
+ },
13
+ }, (args) => ({ city: args.city, temp_c: 27, sky: 'clear' }));
14
+
15
+ const agent = new Needle({ tools: [getWeather] });
16
+ const result = agent.run('What is the weather in Lagos?');
17
+ console.log(JSON.stringify(result.results));
18
+
19
+ const vector = agent.embed('hello world');
20
+ console.log('embed dim:', vector.length);
package/tools.js ADDED
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Tool schema normalization. A tool is one of:
5
+ * - a schema object: { name, description, parameters, implementation? }
6
+ * (parameters is a JSON schema object; implementation is optional)
7
+ * - a function with a `.needleSchema` property (schema + implementation in one)
8
+ *
9
+ * Tool functions receive ONE argument: the arguments object the model produced.
10
+ */
11
+
12
+ function normalizeTools(tools, functions) {
13
+ const schemas = [];
14
+ for (const entry of tools || []) {
15
+ if (typeof entry === 'function' && entry.needleSchema) {
16
+ const schema = entry.needleSchema;
17
+ if (!schema || typeof schema.name !== 'string') {
18
+ throw new TypeError('neuralOS: function.needleSchema must be {name, description, parameters}');
19
+ }
20
+ schemas.push(schema);
21
+ functions.set(schema.name, entry);
22
+ } else if (entry && typeof entry === 'object' && typeof entry.name === 'string') {
23
+ schemas.push({
24
+ name: entry.name,
25
+ description: entry.description || '',
26
+ parameters: entry.parameters || { type: 'object', properties: {} },
27
+ });
28
+ if (typeof entry.implementation === 'function') {
29
+ functions.set(entry.name, entry.implementation);
30
+ }
31
+ } else {
32
+ throw new TypeError(
33
+ 'neuralOS: each tool must be a schema object {name, description, parameters, implementation?} ' +
34
+ 'or a function with a .needleSchema property');
35
+ }
36
+ }
37
+ return schemas;
38
+ }
39
+
40
+ /** A tiny `tool` helper, mirroring the Python decorator ergonomics. */
41
+ function tool(schema, fn) {
42
+ const wrapped = (args) => fn(args);
43
+ wrapped.needleSchema = schema;
44
+ return wrapped;
45
+ }
46
+
47
+ module.exports = { normalizeTools, tool };