@yamo/memory-mesh 2.0.1
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/LICENSE +21 -0
- package/README.md +80 -0
- package/bin/memory_mesh.js +69 -0
- package/bin/scrubber.js +81 -0
- package/index.d.ts +111 -0
- package/lib/adapters/index.js +3 -0
- package/lib/embeddings/factory.js +150 -0
- package/lib/embeddings/index.js +2 -0
- package/lib/embeddings/service.js +586 -0
- package/lib/index.js +18 -0
- package/lib/lancedb/client.js +631 -0
- package/lib/lancedb/config.js +215 -0
- package/lib/lancedb/errors.js +144 -0
- package/lib/lancedb/index.js +4 -0
- package/lib/lancedb/schema.js +197 -0
- package/lib/memory/index.js +3 -0
- package/lib/memory/memory-context-manager.js +388 -0
- package/lib/memory/memory-mesh.js +910 -0
- package/lib/memory/memory-translator.js +130 -0
- package/lib/memory/migrate-memory.js +227 -0
- package/lib/memory/migrate-to-v2.js +120 -0
- package/lib/memory/scorer.js +85 -0
- package/lib/memory/vector-memory.js +364 -0
- package/lib/privacy/audit-logger.js +176 -0
- package/lib/privacy/dlp-redactor.js +72 -0
- package/lib/privacy/index.js +10 -0
- package/lib/reporting/skill-report-generator.js +283 -0
- package/lib/scrubber/.gitkeep +1 -0
- package/lib/scrubber/config/defaults.js +62 -0
- package/lib/scrubber/errors/scrubber-error.js +43 -0
- package/lib/scrubber/index.js +25 -0
- package/lib/scrubber/scrubber.js +130 -0
- package/lib/scrubber/stages/chunker.js +103 -0
- package/lib/scrubber/stages/metadata-annotator.js +74 -0
- package/lib/scrubber/stages/normalizer.js +59 -0
- package/lib/scrubber/stages/semantic-filter.js +61 -0
- package/lib/scrubber/stages/structural-cleaner.js +82 -0
- package/lib/scrubber/stages/validator.js +66 -0
- package/lib/scrubber/telemetry.js +66 -0
- package/lib/scrubber/utils/hash.js +39 -0
- package/lib/scrubber/utils/html-parser.js +45 -0
- package/lib/scrubber/utils/pattern-matcher.js +63 -0
- package/lib/scrubber/utils/token-counter.js +31 -0
- package/lib/search/filter.js +275 -0
- package/lib/search/hybrid.js +137 -0
- package/lib/search/index.js +3 -0
- package/lib/search/pattern-miner.js +160 -0
- package/lib/utils/error-sanitizer.js +84 -0
- package/lib/utils/handoff-validator.js +85 -0
- package/lib/utils/index.js +4 -0
- package/lib/utils/spinner.js +190 -0
- package/lib/utils/streaming-client.js +128 -0
- package/package.json +39 -0
- package/skills/SKILL.md +462 -0
- package/skills/skill-scrubber.yamo +41 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handoff Package Validator
|
|
6
|
+
* Verifies that a handoff package is complete and valid.
|
|
7
|
+
*/
|
|
8
|
+
class HandoffValidator {
|
|
9
|
+
constructor(packagePath) {
|
|
10
|
+
this.packagePath = packagePath;
|
|
11
|
+
this.requiredFiles = [
|
|
12
|
+
'context.md',
|
|
13
|
+
'memory.json',
|
|
14
|
+
'metadata.json'
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
validate() {
|
|
19
|
+
/** @type {{valid: boolean, errors: string[], warnings: string[], summary: string}} */
|
|
20
|
+
const result = {
|
|
21
|
+
valid: true,
|
|
22
|
+
errors: [],
|
|
23
|
+
warnings: [],
|
|
24
|
+
summary: ''
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(this.packagePath)) {
|
|
28
|
+
result.valid = false;
|
|
29
|
+
result.errors.push(`Package path not found: ${this.packagePath}`);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check required files
|
|
34
|
+
for (const file of this.requiredFiles) {
|
|
35
|
+
const filePath = path.join(this.packagePath, file);
|
|
36
|
+
if (!fs.existsSync(filePath)) {
|
|
37
|
+
result.valid = false;
|
|
38
|
+
result.errors.push(`Missing required file: ${file}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate metadata
|
|
43
|
+
const metadataPath = path.join(this.packagePath, 'metadata.json');
|
|
44
|
+
if (fs.existsSync(metadataPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
|
|
47
|
+
if (!metadata.timestamp) result.warnings.push('Metadata missing timestamp');
|
|
48
|
+
if (!metadata.source) result.warnings.push('Metadata missing source');
|
|
49
|
+
} catch (e) {
|
|
50
|
+
result.valid = false;
|
|
51
|
+
result.errors.push('Invalid metadata.json format');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
result.summary = result.valid
|
|
56
|
+
? `✅ Package valid with ${result.warnings.length} warnings`
|
|
57
|
+
: `❌ Package invalid with ${result.errors.length} errors`;
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// CLI usage
|
|
64
|
+
if (process.argv[1] === import.meta.url) {
|
|
65
|
+
const pkgPath = process.argv[2] || '.';
|
|
66
|
+
const validator = new HandoffValidator(pkgPath);
|
|
67
|
+
const result = validator.validate();
|
|
68
|
+
|
|
69
|
+
if (!result.valid) {
|
|
70
|
+
console.log(result.summary);
|
|
71
|
+
console.log('\n❌ Errors found:');
|
|
72
|
+
result.errors.forEach(err => console.log(` - ${err}`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
} else {
|
|
75
|
+
console.log(result.summary);
|
|
76
|
+
console.log('\n✅ Handoff package is valid!');
|
|
77
|
+
|
|
78
|
+
if (result.warnings.length > 0) {
|
|
79
|
+
console.log('\n⚠️ Warnings:');
|
|
80
|
+
result.warnings.forEach(warn => console.log(` - ${warn}`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default HandoffValidator;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as HandoffValidator } from './handoff-validator.js';
|
|
2
|
+
export { Spinner, ProgressBar, MultiSpinner } from './spinner.js';
|
|
3
|
+
export { StreamingClient, StreamingLLM } from './streaming-client.js';
|
|
4
|
+
export { sanitizeErrorMessage, sanitizeErrorForLogging, withSanitizedErrors } from './error-sanitizer.js';
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner and Progress Indicators
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
|
|
7
|
+
class Spinner {
|
|
8
|
+
constructor(text = 'Loading...', options = {}) {
|
|
9
|
+
this.text = text;
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
this.frames = options.frames || ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
this.interval = options.interval || 80;
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
this.stream = options.stream || process.stderr;
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
this.color = options.color || '\x1b[36m';
|
|
18
|
+
this.frameIndex = 0;
|
|
19
|
+
this.timer = null;
|
|
20
|
+
this.isSpinning = false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
start() {
|
|
24
|
+
if (this.isSpinning) return this;
|
|
25
|
+
this.isSpinning = true;
|
|
26
|
+
this.frameIndex = 0;
|
|
27
|
+
this.stream.write('\x1B[?25l');
|
|
28
|
+
this.timer = setInterval(() => {
|
|
29
|
+
const frame = this.frames[this.frameIndex];
|
|
30
|
+
this.stream.write(`\r${this.color}${frame}\x1b[0m ${this.text}`);
|
|
31
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
32
|
+
}, this.interval);
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
update(text) {
|
|
37
|
+
this.text = text;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
succeed(text) {
|
|
42
|
+
return this.stop('\x1b[32m✔\x1b[0m', text);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fail(text) {
|
|
46
|
+
return this.stop('\x1b[31m✖\x1b[0m', text);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
warn(text) {
|
|
50
|
+
return this.stop('\x1b[33m⚠\x1b[0m', text);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
info(text) {
|
|
54
|
+
return this.stop('\x1b[36mℹ\x1b[0m', text);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {string|null} symbol
|
|
59
|
+
* @param {string|null} text
|
|
60
|
+
*/
|
|
61
|
+
stop(symbol = null, text = null) {
|
|
62
|
+
if (!this.isSpinning) return this;
|
|
63
|
+
if (this.timer) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
clearInterval(this.timer);
|
|
66
|
+
}
|
|
67
|
+
this.isSpinning = false;
|
|
68
|
+
this.stream.write('\r\x1b[K');
|
|
69
|
+
this.stream.write('\x1B[?25h');
|
|
70
|
+
if (symbol && text) {
|
|
71
|
+
this.stream.write(`${symbol} ${text}\n`);
|
|
72
|
+
} else if (text) {
|
|
73
|
+
this.stream.write(`${text}\n`);
|
|
74
|
+
}
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
clear() {
|
|
79
|
+
if (!this.isSpinning) return this;
|
|
80
|
+
if (this.timer) {
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
clearInterval(this.timer);
|
|
83
|
+
}
|
|
84
|
+
this.isSpinning = false;
|
|
85
|
+
this.stream.write('\r\x1b[K');
|
|
86
|
+
this.stream.write('\x1B[?25h');
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class ProgressBar {
|
|
92
|
+
constructor(total, options = {}) {
|
|
93
|
+
this.total = total;
|
|
94
|
+
this.current = 0;
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
this.width = options.width || 40;
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
this.stream = options.stream || process.stderr;
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
this.format = options.format || ':bar :percent :current/:total';
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
this.complete = options.complete || '█';
|
|
103
|
+
// @ts-ignore
|
|
104
|
+
this.incomplete = options.incomplete || '░';
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
this.renderThrottle = options.renderThrottle || 16;
|
|
107
|
+
this.lastRender = 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
tick(amount = 1) {
|
|
111
|
+
this.current = Math.min(this.current + amount, this.total);
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
if (now - this.lastRender < this.renderThrottle && this.current < this.total) {
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
this.lastRender = now;
|
|
117
|
+
this.render();
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
render() {
|
|
122
|
+
const percent = Math.floor((this.current / this.total) * 100);
|
|
123
|
+
const completeLength = Math.floor(this.width * (this.current / this.total));
|
|
124
|
+
const incompleteLength = this.width - completeLength;
|
|
125
|
+
const bar = this.complete.repeat(completeLength) + this.incomplete.repeat(incompleteLength);
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
let output = this.format.replace(':bar', bar).replace(':percent', `${percent}%`).replace(':current', String(this.current)).replace(':total', String(this.total));
|
|
128
|
+
this.stream.write(`\r${output}`);
|
|
129
|
+
if (this.current >= this.total) {
|
|
130
|
+
this.stream.write('\n');
|
|
131
|
+
}
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
update(current) {
|
|
136
|
+
this.current = Math.min(current, this.total);
|
|
137
|
+
this.render();
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
class MultiSpinner {
|
|
143
|
+
constructor() {
|
|
144
|
+
this.spinners = new Map();
|
|
145
|
+
this.stream = process.stderr;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
add(key, text, status = 'pending') {
|
|
149
|
+
this.spinners.set(key, { text, status, startTime: Date.now() });
|
|
150
|
+
this.render();
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
update(key, status, text = null) {
|
|
155
|
+
const spinner = this.spinners.get(key);
|
|
156
|
+
if (spinner) {
|
|
157
|
+
spinner.status = status;
|
|
158
|
+
if (text) spinner.text = text;
|
|
159
|
+
this.render();
|
|
160
|
+
}
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
succeed(key, text) { return this.update(key, 'success', text); }
|
|
165
|
+
fail(key, text) { return this.update(key, 'fail', text); }
|
|
166
|
+
warn(key, text) { return this.update(key, 'warn', text); }
|
|
167
|
+
|
|
168
|
+
render() {
|
|
169
|
+
this.stream.write('\r\x1b[K');
|
|
170
|
+
const lines = [];
|
|
171
|
+
for (const [key, spinner] of this.spinners) {
|
|
172
|
+
// @ts-ignore
|
|
173
|
+
const symbol = { pending: '\x1b[36m⋯\x1b[0m', success: '\x1b[32m✔\x1b[0m', fail: '\x1b[31m✖\x1b[0m', warn: '\x1b[33m⚠\x1b[0m' }[spinner.status];
|
|
174
|
+
const duration = ((Date.now() - spinner.startTime) / 1000).toFixed(1);
|
|
175
|
+
lines.push(`${symbol} ${spinner.text} (${duration}s)`);
|
|
176
|
+
}
|
|
177
|
+
this.stream.write(lines.join('\n'));
|
|
178
|
+
if (lines.length > 1) {
|
|
179
|
+
this.stream.write(`\x1b[${lines.length - 1}A`);
|
|
180
|
+
}
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
done() {
|
|
185
|
+
this.stream.write('\n'.repeat(this.spinners.size));
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export { Spinner, ProgressBar, MultiSpinner };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import https from "https";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Streaming LLM Client
|
|
6
|
+
*/
|
|
7
|
+
class StreamingClient {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.buffer = '';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
stream(payload, onToken, onComplete, onError) {
|
|
14
|
+
const url = new URL(this.config.endpoint);
|
|
15
|
+
|
|
16
|
+
/** @type {any} */
|
|
17
|
+
const options = {
|
|
18
|
+
hostname: url.hostname,
|
|
19
|
+
path: url.pathname,
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
24
|
+
'Accept': 'text/event-stream'
|
|
25
|
+
},
|
|
26
|
+
rejectUnauthorized: true,
|
|
27
|
+
minVersion: 'TLSv1.2'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const req = https.request(options, (res) => {
|
|
31
|
+
let fullResponse = '';
|
|
32
|
+
res.on('data', (chunk) => {
|
|
33
|
+
this.buffer += chunk.toString();
|
|
34
|
+
const lines = this.buffer.split('\n');
|
|
35
|
+
this.buffer = lines.pop() || '';
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
if (line.startsWith('data: ')) {
|
|
38
|
+
const data = line.slice(6);
|
|
39
|
+
if (data === '[DONE]') continue;
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(data);
|
|
42
|
+
const token = this.extractToken(parsed, this.config.schema);
|
|
43
|
+
if (token) {
|
|
44
|
+
fullResponse += token;
|
|
45
|
+
onToken(token, parsed);
|
|
46
|
+
}
|
|
47
|
+
} catch (e) { /* ignore */ }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
res.on('end', () => { if (onComplete) onComplete(fullResponse); });
|
|
52
|
+
res.on('error', (e) => { if (onError) onError(e); });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
req.on('error', (e) => { if (onError) onError(e); });
|
|
56
|
+
req.setTimeout(60000, () => {
|
|
57
|
+
req.destroy();
|
|
58
|
+
if (onError) onError(new Error('Timeout'));
|
|
59
|
+
});
|
|
60
|
+
req.write(JSON.stringify({ ...payload, stream: true }));
|
|
61
|
+
req.end();
|
|
62
|
+
return req;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
extractToken(data, schema) {
|
|
66
|
+
switch (schema) {
|
|
67
|
+
case 'openai': return data.choices?.[0]?.delta?.content || '';
|
|
68
|
+
default: return data.choices?.[0]?.delta?.content || data.delta?.text || '';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async request(payload) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const url = new URL(this.config.endpoint);
|
|
75
|
+
|
|
76
|
+
/** @type {any} */
|
|
77
|
+
const options = {
|
|
78
|
+
hostname: url.hostname,
|
|
79
|
+
path: url.pathname,
|
|
80
|
+
method: 'POST',
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
'Authorization': `Bearer ${this.config.apiKey}`
|
|
84
|
+
},
|
|
85
|
+
rejectUnauthorized: true,
|
|
86
|
+
minVersion: 'TLSv1.2'
|
|
87
|
+
};
|
|
88
|
+
const req = https.request(options, (res) => {
|
|
89
|
+
let data = '';
|
|
90
|
+
res.on('data', (chunk) => data += chunk);
|
|
91
|
+
res.on('end', () => {
|
|
92
|
+
try { resolve(JSON.parse(data)); } catch (e) { reject(e); }
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
req.on('error', reject);
|
|
96
|
+
req.write(JSON.stringify(payload));
|
|
97
|
+
req.end();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class StreamingLLM {
|
|
103
|
+
constructor(config) {
|
|
104
|
+
this.client = new StreamingClient(config);
|
|
105
|
+
this.config = config;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async streamToConsole(payload, options = {}) {
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
const prefix = options.prefix || '';
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
let tokens = [];
|
|
113
|
+
this.client.stream(payload, (token) => {
|
|
114
|
+
process.stdout.write(token);
|
|
115
|
+
// @ts-ignore
|
|
116
|
+
tokens.push(token);
|
|
117
|
+
}, (full) => {
|
|
118
|
+
process.stdout.write('\n');
|
|
119
|
+
resolve({ content: full, tokens: tokens.length });
|
|
120
|
+
}, (e) => {
|
|
121
|
+
process.stdout.write('\n');
|
|
122
|
+
reject(e);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { StreamingClient, StreamingLLM };
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yamo/memory-mesh",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Portable semantic memory system with Layer 0 Scrubber for YAMO agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/memory/index.js",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"memory-mesh": "./bin/memory_mesh.js",
|
|
10
|
+
"scrubber": "./bin/scrubber.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib/",
|
|
14
|
+
"bin/",
|
|
15
|
+
"skills/",
|
|
16
|
+
"index.d.ts"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "npm run type-check",
|
|
20
|
+
"type-check": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@lancedb/lancedb": "^0.23.0",
|
|
24
|
+
"@xenova/transformers": "^2.17.0",
|
|
25
|
+
"apache-arrow": "^17.0.0",
|
|
26
|
+
"onnxruntime-node": "^1.18.0"
|
|
27
|
+
},
|
|
28
|
+
"author": "Soverane Labs",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^25.0.9",
|
|
35
|
+
"cohere-ai": "^7.20.0",
|
|
36
|
+
"openai": "^6.16.0",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
}
|
|
39
|
+
}
|