@juspay/neurolink 7.48.1 → 7.49.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 +13 -0
- package/README.md +215 -16
- package/dist/agent/directTools.d.ts +55 -0
- package/dist/agent/directTools.js +266 -0
- package/dist/cli/factories/commandFactory.d.ts +2 -0
- package/dist/cli/factories/commandFactory.js +130 -16
- package/dist/cli/index.js +0 -0
- package/dist/cli/loop/conversationSelector.d.ts +45 -0
- package/dist/cli/loop/conversationSelector.js +222 -0
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/loop/session.d.ts +36 -8
- package/dist/cli/loop/session.js +257 -61
- package/dist/core/baseProvider.js +9 -2
- package/dist/core/evaluation.js +5 -2
- package/dist/factories/providerRegistry.js +2 -2
- package/dist/lib/agent/directTools.d.ts +55 -0
- package/dist/lib/agent/directTools.js +266 -0
- package/dist/lib/core/baseProvider.js +9 -2
- package/dist/lib/core/evaluation.js +5 -2
- package/dist/lib/factories/providerRegistry.js +2 -2
- package/dist/lib/mcp/factory.d.ts +2 -157
- package/dist/lib/mcp/flexibleToolValidator.d.ts +1 -5
- package/dist/lib/mcp/index.d.ts +3 -2
- package/dist/lib/mcp/mcpCircuitBreaker.d.ts +1 -75
- package/dist/lib/mcp/mcpClientFactory.d.ts +1 -20
- package/dist/lib/mcp/mcpClientFactory.js +1 -0
- package/dist/lib/mcp/registry.d.ts +3 -10
- package/dist/lib/mcp/servers/agent/directToolsServer.d.ts +1 -1
- package/dist/lib/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
- package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
- package/dist/lib/mcp/toolDiscoveryService.d.ts +3 -84
- package/dist/lib/mcp/toolRegistry.d.ts +2 -24
- package/dist/lib/middleware/builtin/guardrails.d.ts +5 -16
- package/dist/lib/middleware/builtin/guardrails.js +44 -39
- package/dist/lib/middleware/utils/guardrailsUtils.d.ts +64 -0
- package/dist/lib/middleware/utils/guardrailsUtils.js +387 -0
- package/dist/lib/neurolink.d.ts +1 -1
- package/dist/lib/providers/anthropic.js +46 -3
- package/dist/lib/providers/azureOpenai.js +8 -2
- package/dist/lib/providers/googleAiStudio.js +8 -2
- package/dist/lib/providers/googleVertex.js +11 -2
- package/dist/lib/providers/huggingFace.js +1 -1
- package/dist/lib/providers/litellm.js +1 -1
- package/dist/lib/providers/mistral.js +1 -1
- package/dist/lib/providers/openAI.js +46 -3
- package/dist/lib/session/globalSessionState.d.ts +26 -0
- package/dist/lib/session/globalSessionState.js +49 -0
- package/dist/lib/types/cli.d.ts +28 -0
- package/dist/lib/types/content.d.ts +18 -5
- package/dist/lib/types/contextTypes.d.ts +1 -1
- package/dist/lib/types/conversation.d.ts +55 -4
- package/dist/lib/types/fileTypes.d.ts +65 -0
- package/dist/lib/types/fileTypes.js +4 -0
- package/dist/lib/types/generateTypes.d.ts +12 -0
- package/dist/lib/types/guardrails.d.ts +103 -0
- package/dist/lib/types/guardrails.js +1 -0
- package/dist/lib/types/index.d.ts +4 -2
- package/dist/lib/types/index.js +4 -0
- package/dist/lib/types/mcpTypes.d.ts +407 -14
- package/dist/lib/types/streamTypes.d.ts +7 -0
- package/dist/lib/types/tools.d.ts +132 -35
- package/dist/lib/utils/csvProcessor.d.ts +68 -0
- package/dist/lib/utils/csvProcessor.js +277 -0
- package/dist/lib/utils/fileDetector.d.ts +57 -0
- package/dist/lib/utils/fileDetector.js +457 -0
- package/dist/lib/utils/imageProcessor.d.ts +10 -0
- package/dist/lib/utils/imageProcessor.js +22 -0
- package/dist/lib/utils/loopUtils.d.ts +71 -0
- package/dist/lib/utils/loopUtils.js +262 -0
- package/dist/lib/utils/messageBuilder.d.ts +2 -1
- package/dist/lib/utils/messageBuilder.js +197 -2
- package/dist/lib/utils/optionsUtils.d.ts +1 -1
- package/dist/mcp/factory.d.ts +2 -157
- package/dist/mcp/flexibleToolValidator.d.ts +1 -5
- package/dist/mcp/index.d.ts +3 -2
- package/dist/mcp/mcpCircuitBreaker.d.ts +1 -75
- package/dist/mcp/mcpClientFactory.d.ts +1 -20
- package/dist/mcp/mcpClientFactory.js +1 -0
- package/dist/mcp/registry.d.ts +3 -10
- package/dist/mcp/servers/agent/directToolsServer.d.ts +1 -1
- package/dist/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
- package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
- package/dist/mcp/toolDiscoveryService.d.ts +3 -84
- package/dist/mcp/toolRegistry.d.ts +2 -24
- package/dist/middleware/builtin/guardrails.d.ts +5 -16
- package/dist/middleware/builtin/guardrails.js +44 -39
- package/dist/middleware/utils/guardrailsUtils.d.ts +64 -0
- package/dist/middleware/utils/guardrailsUtils.js +387 -0
- package/dist/neurolink.d.ts +1 -1
- package/dist/providers/anthropic.js +46 -3
- package/dist/providers/azureOpenai.js +8 -2
- package/dist/providers/googleAiStudio.js +8 -2
- package/dist/providers/googleVertex.js +11 -2
- package/dist/providers/huggingFace.js +1 -1
- package/dist/providers/litellm.js +1 -1
- package/dist/providers/mistral.js +1 -1
- package/dist/providers/openAI.js +46 -3
- package/dist/session/globalSessionState.d.ts +26 -0
- package/dist/session/globalSessionState.js +49 -0
- package/dist/types/cli.d.ts +28 -0
- package/dist/types/content.d.ts +18 -5
- package/dist/types/contextTypes.d.ts +1 -1
- package/dist/types/conversation.d.ts +55 -4
- package/dist/types/fileTypes.d.ts +65 -0
- package/dist/types/fileTypes.js +4 -0
- package/dist/types/generateTypes.d.ts +12 -0
- package/dist/types/guardrails.d.ts +103 -0
- package/dist/types/guardrails.js +1 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +4 -0
- package/dist/types/mcpTypes.d.ts +407 -14
- package/dist/types/modelTypes.d.ts +6 -6
- package/dist/types/streamTypes.d.ts +7 -0
- package/dist/types/tools.d.ts +132 -35
- package/dist/utils/csvProcessor.d.ts +68 -0
- package/dist/utils/csvProcessor.js +277 -0
- package/dist/utils/fileDetector.d.ts +57 -0
- package/dist/utils/fileDetector.js +457 -0
- package/dist/utils/imageProcessor.d.ts +10 -0
- package/dist/utils/imageProcessor.js +22 -0
- package/dist/utils/loopUtils.d.ts +71 -0
- package/dist/utils/loopUtils.js +262 -0
- package/dist/utils/messageBuilder.d.ts +2 -1
- package/dist/utils/messageBuilder.js +197 -2
- package/dist/utils/optionsUtils.d.ts +1 -1
- package/package.json +9 -3
- package/dist/lib/mcp/contracts/mcpContract.d.ts +0 -106
- package/dist/lib/mcp/contracts/mcpContract.js +0 -5
- package/dist/mcp/contracts/mcpContract.d.ts +0 -106
- package/dist/mcp/contracts/mcpContract.js +0 -5
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Type Detection Utility
|
|
3
|
+
* Centralized file detection for all multimodal file types
|
|
4
|
+
* Uses multi-strategy approach for reliable type identification
|
|
5
|
+
*/
|
|
6
|
+
import { request } from "undici";
|
|
7
|
+
import { readFile, stat } from "fs/promises";
|
|
8
|
+
import { logger } from "./logger.js";
|
|
9
|
+
import { CSVProcessor } from "./csvProcessor.js";
|
|
10
|
+
import { ImageProcessor } from "./imageProcessor.js";
|
|
11
|
+
/**
|
|
12
|
+
* Format file size in human-readable units
|
|
13
|
+
*/
|
|
14
|
+
function formatFileSize(bytes) {
|
|
15
|
+
if (bytes < 1024) {
|
|
16
|
+
return `${bytes} bytes`;
|
|
17
|
+
}
|
|
18
|
+
if (bytes < 1024 * 1024) {
|
|
19
|
+
return `${(bytes / 1024).toFixed(2)} KB`;
|
|
20
|
+
}
|
|
21
|
+
if (bytes < 1024 * 1024 * 1024) {
|
|
22
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
23
|
+
}
|
|
24
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Centralized file type detection and processing
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // Auto-detect and process any file
|
|
32
|
+
* const result = await FileDetector.detectAndProcess("data.csv");
|
|
33
|
+
* console.log(result.type); // 'csv'
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class FileDetector {
|
|
37
|
+
/**
|
|
38
|
+
* Auto-detect file type and process in one call
|
|
39
|
+
*
|
|
40
|
+
* Runs detection strategies in priority order:
|
|
41
|
+
* 1. MagicBytesStrategy (95% confidence) - Binary file headers
|
|
42
|
+
* 2. MimeTypeStrategy (85% confidence) - HTTP Content-Type for URLs
|
|
43
|
+
* 3. ExtensionStrategy (70% confidence) - File extension
|
|
44
|
+
* 4. ContentHeuristicStrategy (75% confidence) - Content analysis
|
|
45
|
+
*
|
|
46
|
+
* @param input - File path, URL, Buffer, or data URI
|
|
47
|
+
* @param options - Detection and processing options
|
|
48
|
+
* @returns Processed file result with type and content
|
|
49
|
+
*/
|
|
50
|
+
static async detectAndProcess(input, options) {
|
|
51
|
+
const detection = await this.detect(input, options);
|
|
52
|
+
if (options?.allowedTypes &&
|
|
53
|
+
!options.allowedTypes.includes(detection.type)) {
|
|
54
|
+
throw new Error(`File type ${detection.type} not allowed. Allowed: ${options.allowedTypes.join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
const content = await this.loadContent(input, detection, options);
|
|
57
|
+
// Extract CSV-specific options from FileDetectorOptions
|
|
58
|
+
const csvOptions = options?.csvOptions;
|
|
59
|
+
return await this.processFile(content, detection, csvOptions);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Detect file type using multi-strategy approach
|
|
63
|
+
* Stops at first strategy with confidence >= threshold (default: 80%)
|
|
64
|
+
*/
|
|
65
|
+
static async detect(input, options) {
|
|
66
|
+
const confidenceThreshold = options?.confidenceThreshold ?? 80;
|
|
67
|
+
const strategies = [
|
|
68
|
+
new MagicBytesStrategy(),
|
|
69
|
+
new MimeTypeStrategy(),
|
|
70
|
+
new ExtensionStrategy(),
|
|
71
|
+
new ContentHeuristicStrategy(),
|
|
72
|
+
];
|
|
73
|
+
let best = null;
|
|
74
|
+
for (const strategy of strategies) {
|
|
75
|
+
const result = await strategy.detect(input);
|
|
76
|
+
if (!best || result.metadata.confidence > best.metadata.confidence) {
|
|
77
|
+
best = result;
|
|
78
|
+
}
|
|
79
|
+
if (result.metadata.confidence >= confidenceThreshold) {
|
|
80
|
+
logger.info(`[FileDetector] Type: ${result.type} (${result.metadata.confidence}%)`);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
logger.warn(`[FileDetector] Low confidence: ${best?.type ?? "unknown"} (${best?.metadata.confidence ?? 0}%)`);
|
|
85
|
+
return best;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Load file content from various sources
|
|
89
|
+
*/
|
|
90
|
+
static async loadContent(input, detection, options) {
|
|
91
|
+
let source = detection.source;
|
|
92
|
+
if (source === "buffer" && !Buffer.isBuffer(input)) {
|
|
93
|
+
if (typeof input === "string") {
|
|
94
|
+
if (input.startsWith("data:")) {
|
|
95
|
+
source = "datauri";
|
|
96
|
+
}
|
|
97
|
+
else if (input.startsWith("http://") ||
|
|
98
|
+
input.startsWith("https://")) {
|
|
99
|
+
source = "url";
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
source = "path";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
switch (source) {
|
|
107
|
+
case "url":
|
|
108
|
+
return await this.loadFromURL(input, options);
|
|
109
|
+
case "path":
|
|
110
|
+
return await this.loadFromPath(input, options);
|
|
111
|
+
case "buffer":
|
|
112
|
+
return input;
|
|
113
|
+
case "datauri":
|
|
114
|
+
return this.loadFromDataURI(input);
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`Unknown source: ${source}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Route to appropriate processor
|
|
121
|
+
*/
|
|
122
|
+
static async processFile(content, detection, options) {
|
|
123
|
+
switch (detection.type) {
|
|
124
|
+
case "csv":
|
|
125
|
+
return await CSVProcessor.process(content, options);
|
|
126
|
+
case "image":
|
|
127
|
+
return await ImageProcessor.process(content);
|
|
128
|
+
case "text":
|
|
129
|
+
return {
|
|
130
|
+
type: "text",
|
|
131
|
+
content: content.toString("utf-8"),
|
|
132
|
+
mimeType: "text/plain",
|
|
133
|
+
metadata: detection.metadata,
|
|
134
|
+
};
|
|
135
|
+
default:
|
|
136
|
+
throw new Error(`Unsupported file type: ${detection.type}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Load file from URL
|
|
141
|
+
*/
|
|
142
|
+
static async loadFromURL(url, options) {
|
|
143
|
+
const maxSize = options?.maxSize || 10 * 1024 * 1024;
|
|
144
|
+
const timeout = options?.timeout || 30000;
|
|
145
|
+
const response = await request(url, {
|
|
146
|
+
method: "GET",
|
|
147
|
+
headersTimeout: timeout,
|
|
148
|
+
bodyTimeout: timeout,
|
|
149
|
+
maxRedirections: 5,
|
|
150
|
+
});
|
|
151
|
+
if (response.statusCode !== 200) {
|
|
152
|
+
throw new Error(`HTTP ${response.statusCode}`);
|
|
153
|
+
}
|
|
154
|
+
const chunks = [];
|
|
155
|
+
let totalSize = 0;
|
|
156
|
+
for await (const chunk of response.body) {
|
|
157
|
+
totalSize += chunk.length;
|
|
158
|
+
if (totalSize > maxSize) {
|
|
159
|
+
throw new Error(`File too large: ${formatFileSize(totalSize)} (max: ${formatFileSize(maxSize)})`);
|
|
160
|
+
}
|
|
161
|
+
chunks.push(chunk);
|
|
162
|
+
}
|
|
163
|
+
return Buffer.concat(chunks);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Load file from filesystem path
|
|
167
|
+
*/
|
|
168
|
+
static async loadFromPath(path, options) {
|
|
169
|
+
const maxSize = options?.maxSize || 10 * 1024 * 1024;
|
|
170
|
+
const statInfo = await stat(path);
|
|
171
|
+
if (!statInfo.isFile()) {
|
|
172
|
+
throw new Error("Not a file");
|
|
173
|
+
}
|
|
174
|
+
if (statInfo.size > maxSize) {
|
|
175
|
+
throw new Error(`File too large: ${formatFileSize(statInfo.size)} (max: ${formatFileSize(maxSize)})`);
|
|
176
|
+
}
|
|
177
|
+
return await readFile(path);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Load file from data URI
|
|
181
|
+
*/
|
|
182
|
+
static loadFromDataURI(dataUri) {
|
|
183
|
+
const match = dataUri.match(/^data:([^;]+);base64,(.+)$/);
|
|
184
|
+
if (!match) {
|
|
185
|
+
throw new Error("Invalid data URI format");
|
|
186
|
+
}
|
|
187
|
+
return Buffer.from(match[2], "base64");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Strategy 1: Magic Bytes Detection (95% confidence)
|
|
192
|
+
* Detects file type from binary file headers
|
|
193
|
+
*/
|
|
194
|
+
class MagicBytesStrategy {
|
|
195
|
+
async detect(input) {
|
|
196
|
+
if (!Buffer.isBuffer(input)) {
|
|
197
|
+
return this.unknown();
|
|
198
|
+
}
|
|
199
|
+
if (this.isPNG(input)) {
|
|
200
|
+
return this.result("image", "image/png", 95);
|
|
201
|
+
}
|
|
202
|
+
if (this.isJPEG(input)) {
|
|
203
|
+
return this.result("image", "image/jpeg", 95);
|
|
204
|
+
}
|
|
205
|
+
if (this.isGIF(input)) {
|
|
206
|
+
return this.result("image", "image/gif", 95);
|
|
207
|
+
}
|
|
208
|
+
if (this.isWebP(input)) {
|
|
209
|
+
return this.result("image", "image/webp", 95);
|
|
210
|
+
}
|
|
211
|
+
if (this.isPDF(input)) {
|
|
212
|
+
return this.result("pdf", "application/pdf", 95);
|
|
213
|
+
}
|
|
214
|
+
return this.unknown();
|
|
215
|
+
}
|
|
216
|
+
isPNG(buf) {
|
|
217
|
+
return (buf.length >= 4 &&
|
|
218
|
+
buf[0] === 0x89 &&
|
|
219
|
+
buf[1] === 0x50 &&
|
|
220
|
+
buf[2] === 0x4e &&
|
|
221
|
+
buf[3] === 0x47);
|
|
222
|
+
}
|
|
223
|
+
isJPEG(buf) {
|
|
224
|
+
return (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff);
|
|
225
|
+
}
|
|
226
|
+
isGIF(buf) {
|
|
227
|
+
return (buf.length >= 4 &&
|
|
228
|
+
buf[0] === 0x47 &&
|
|
229
|
+
buf[1] === 0x49 &&
|
|
230
|
+
buf[2] === 0x46 &&
|
|
231
|
+
buf[3] === 0x38);
|
|
232
|
+
}
|
|
233
|
+
isWebP(buf) {
|
|
234
|
+
return (buf.length >= 12 &&
|
|
235
|
+
buf.slice(0, 4).toString() === "RIFF" &&
|
|
236
|
+
buf.slice(8, 12).toString() === "WEBP");
|
|
237
|
+
}
|
|
238
|
+
isPDF(buf) {
|
|
239
|
+
return buf.length >= 5 && buf.slice(0, 5).toString() === "%PDF-";
|
|
240
|
+
}
|
|
241
|
+
result(type, mime, confidence) {
|
|
242
|
+
return {
|
|
243
|
+
type,
|
|
244
|
+
mimeType: mime,
|
|
245
|
+
extension: null,
|
|
246
|
+
source: "buffer",
|
|
247
|
+
metadata: { confidence },
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
unknown() {
|
|
251
|
+
return {
|
|
252
|
+
type: "unknown",
|
|
253
|
+
mimeType: "application/octet-stream",
|
|
254
|
+
extension: null,
|
|
255
|
+
source: "buffer",
|
|
256
|
+
metadata: { confidence: 0 },
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Strategy 2: MIME Type Detection (85% confidence)
|
|
262
|
+
* Detects file type from HTTP Content-Type headers
|
|
263
|
+
*/
|
|
264
|
+
class MimeTypeStrategy {
|
|
265
|
+
async detect(input) {
|
|
266
|
+
if (typeof input !== "string" || !this.isURL(input)) {
|
|
267
|
+
return this.unknown();
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
const response = await request(input, {
|
|
271
|
+
method: "HEAD",
|
|
272
|
+
headersTimeout: 5000,
|
|
273
|
+
bodyTimeout: 5000,
|
|
274
|
+
maxRedirections: 5,
|
|
275
|
+
});
|
|
276
|
+
const contentType = response.headers["content-type"] || "";
|
|
277
|
+
const type = this.mimeToFileType(contentType);
|
|
278
|
+
return {
|
|
279
|
+
type,
|
|
280
|
+
mimeType: contentType.split(";")[0].trim(),
|
|
281
|
+
extension: null,
|
|
282
|
+
source: "url",
|
|
283
|
+
metadata: { confidence: type !== "unknown" ? 85 : 0 },
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return this.unknown();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
mimeToFileType(mime) {
|
|
291
|
+
if (mime.includes("text/csv")) {
|
|
292
|
+
return "csv";
|
|
293
|
+
}
|
|
294
|
+
if (mime.includes("text/tab-separated-values")) {
|
|
295
|
+
return "csv";
|
|
296
|
+
}
|
|
297
|
+
if (mime.includes("image/")) {
|
|
298
|
+
return "image";
|
|
299
|
+
}
|
|
300
|
+
if (mime.includes("application/pdf")) {
|
|
301
|
+
return "pdf";
|
|
302
|
+
}
|
|
303
|
+
if (mime.includes("text/plain")) {
|
|
304
|
+
return "text";
|
|
305
|
+
}
|
|
306
|
+
return "unknown";
|
|
307
|
+
}
|
|
308
|
+
isURL(str) {
|
|
309
|
+
return str.startsWith("http://") || str.startsWith("https://");
|
|
310
|
+
}
|
|
311
|
+
unknown() {
|
|
312
|
+
return {
|
|
313
|
+
type: "unknown",
|
|
314
|
+
mimeType: "application/octet-stream",
|
|
315
|
+
extension: null,
|
|
316
|
+
source: "buffer",
|
|
317
|
+
metadata: { confidence: 0 },
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Strategy 3: Extension Detection (70% confidence)
|
|
323
|
+
* Detects file type from file extension
|
|
324
|
+
*/
|
|
325
|
+
class ExtensionStrategy {
|
|
326
|
+
async detect(input) {
|
|
327
|
+
if (typeof input !== "string") {
|
|
328
|
+
return this.unknown();
|
|
329
|
+
}
|
|
330
|
+
const ext = this.getExtension(input);
|
|
331
|
+
if (!ext) {
|
|
332
|
+
return this.unknown();
|
|
333
|
+
}
|
|
334
|
+
const typeMap = {
|
|
335
|
+
csv: "csv",
|
|
336
|
+
tsv: "csv",
|
|
337
|
+
jpg: "image",
|
|
338
|
+
jpeg: "image",
|
|
339
|
+
png: "image",
|
|
340
|
+
gif: "image",
|
|
341
|
+
webp: "image",
|
|
342
|
+
bmp: "image",
|
|
343
|
+
tiff: "image",
|
|
344
|
+
tif: "image",
|
|
345
|
+
svg: "image",
|
|
346
|
+
avif: "image",
|
|
347
|
+
pdf: "pdf",
|
|
348
|
+
txt: "text",
|
|
349
|
+
md: "text",
|
|
350
|
+
};
|
|
351
|
+
const type = typeMap[ext.toLowerCase()];
|
|
352
|
+
return {
|
|
353
|
+
type: type || "unknown",
|
|
354
|
+
mimeType: this.getMimeType(ext),
|
|
355
|
+
extension: ext,
|
|
356
|
+
source: this.detectSource(input),
|
|
357
|
+
metadata: { confidence: type ? 70 : 0 },
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
getExtension(input) {
|
|
361
|
+
if (this.isURL(input)) {
|
|
362
|
+
const url = new URL(input);
|
|
363
|
+
const match = url.pathname.match(/\.([^.]+)$/);
|
|
364
|
+
return match ? match[1] : null;
|
|
365
|
+
}
|
|
366
|
+
const match = input.match(/\.([^.]+)$/);
|
|
367
|
+
return match ? match[1] : null;
|
|
368
|
+
}
|
|
369
|
+
isURL(str) {
|
|
370
|
+
return str.startsWith("http://") || str.startsWith("https://");
|
|
371
|
+
}
|
|
372
|
+
detectSource(input) {
|
|
373
|
+
if (input.startsWith("data:")) {
|
|
374
|
+
return "datauri";
|
|
375
|
+
}
|
|
376
|
+
if (this.isURL(input)) {
|
|
377
|
+
return "url";
|
|
378
|
+
}
|
|
379
|
+
return "path";
|
|
380
|
+
}
|
|
381
|
+
getMimeType(ext) {
|
|
382
|
+
const mimeMap = {
|
|
383
|
+
csv: "text/csv",
|
|
384
|
+
tsv: "text/tab-separated-values",
|
|
385
|
+
jpg: "image/jpeg",
|
|
386
|
+
jpeg: "image/jpeg",
|
|
387
|
+
png: "image/png",
|
|
388
|
+
gif: "image/gif",
|
|
389
|
+
webp: "image/webp",
|
|
390
|
+
bmp: "image/bmp",
|
|
391
|
+
tiff: "image/tiff",
|
|
392
|
+
tif: "image/tiff",
|
|
393
|
+
svg: "image/svg+xml",
|
|
394
|
+
avif: "image/avif",
|
|
395
|
+
pdf: "application/pdf",
|
|
396
|
+
txt: "text/plain",
|
|
397
|
+
md: "text/markdown",
|
|
398
|
+
};
|
|
399
|
+
return mimeMap[ext.toLowerCase()] || "application/octet-stream";
|
|
400
|
+
}
|
|
401
|
+
unknown() {
|
|
402
|
+
return {
|
|
403
|
+
type: "unknown",
|
|
404
|
+
mimeType: "application/octet-stream",
|
|
405
|
+
extension: null,
|
|
406
|
+
source: "buffer",
|
|
407
|
+
metadata: { confidence: 0 },
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Strategy 4: Content Heuristics (75% confidence)
|
|
413
|
+
* Detects file type by analyzing content patterns
|
|
414
|
+
*/
|
|
415
|
+
class ContentHeuristicStrategy {
|
|
416
|
+
async detect(input) {
|
|
417
|
+
if (!Buffer.isBuffer(input)) {
|
|
418
|
+
return this.unknown();
|
|
419
|
+
}
|
|
420
|
+
const sample = input.toString("utf-8", 0, Math.min(1000, input.length));
|
|
421
|
+
if (this.looksLikeCSV(sample)) {
|
|
422
|
+
return this.result("csv", "text/csv", 75);
|
|
423
|
+
}
|
|
424
|
+
return this.unknown();
|
|
425
|
+
}
|
|
426
|
+
looksLikeCSV(text) {
|
|
427
|
+
const lines = text.split("\n").slice(0, 5);
|
|
428
|
+
if (lines.length < 2) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
const hasCommas = lines.every((line) => line.includes(","));
|
|
432
|
+
if (!hasCommas) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
const columnCounts = lines.map((line) => line.split(",").length);
|
|
436
|
+
const uniqueCounts = new Set(columnCounts);
|
|
437
|
+
return uniqueCounts.size === 1 && columnCounts[0] >= 2;
|
|
438
|
+
}
|
|
439
|
+
result(type, mime, confidence) {
|
|
440
|
+
return {
|
|
441
|
+
type,
|
|
442
|
+
mimeType: mime,
|
|
443
|
+
extension: null,
|
|
444
|
+
source: "buffer",
|
|
445
|
+
metadata: { confidence },
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
unknown() {
|
|
449
|
+
return {
|
|
450
|
+
type: "unknown",
|
|
451
|
+
mimeType: "application/octet-stream",
|
|
452
|
+
extension: null,
|
|
453
|
+
source: "buffer",
|
|
454
|
+
metadata: { confidence: 0 },
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
@@ -3,10 +3,20 @@
|
|
|
3
3
|
* Handles format conversion for different AI providers
|
|
4
4
|
*/
|
|
5
5
|
import type { ProcessedImage } from "../types/content.js";
|
|
6
|
+
import type { FileProcessingResult } from "../types/fileTypes.js";
|
|
6
7
|
/**
|
|
7
8
|
* Image processor class for handling provider-specific image formatting
|
|
8
9
|
*/
|
|
9
10
|
export declare class ImageProcessor {
|
|
11
|
+
/**
|
|
12
|
+
* Process image Buffer (unified interface)
|
|
13
|
+
* Matches CSVProcessor.process() signature for consistency
|
|
14
|
+
*
|
|
15
|
+
* @param content - Image file as Buffer
|
|
16
|
+
* @param options - Processing options (unused for now)
|
|
17
|
+
* @returns Processed image as data URI
|
|
18
|
+
*/
|
|
19
|
+
static process(content: Buffer, _options?: unknown): Promise<FileProcessingResult>;
|
|
10
20
|
/**
|
|
11
21
|
* Process image for OpenAI (requires data URI format)
|
|
12
22
|
*/
|
|
@@ -7,6 +7,28 @@ import { logger } from "./logger.js";
|
|
|
7
7
|
* Image processor class for handling provider-specific image formatting
|
|
8
8
|
*/
|
|
9
9
|
export class ImageProcessor {
|
|
10
|
+
/**
|
|
11
|
+
* Process image Buffer (unified interface)
|
|
12
|
+
* Matches CSVProcessor.process() signature for consistency
|
|
13
|
+
*
|
|
14
|
+
* @param content - Image file as Buffer
|
|
15
|
+
* @param options - Processing options (unused for now)
|
|
16
|
+
* @returns Processed image as data URI
|
|
17
|
+
*/
|
|
18
|
+
static async process(content, _options) {
|
|
19
|
+
const mediaType = this.detectImageType(content);
|
|
20
|
+
const base64 = content.toString("base64");
|
|
21
|
+
const dataUri = `data:${mediaType};base64,${base64}`;
|
|
22
|
+
return {
|
|
23
|
+
type: "image",
|
|
24
|
+
content: dataUri,
|
|
25
|
+
mimeType: mediaType,
|
|
26
|
+
metadata: {
|
|
27
|
+
confidence: 100,
|
|
28
|
+
size: content.length,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
10
32
|
/**
|
|
11
33
|
* Process image for OpenAI (requires data URI format)
|
|
12
34
|
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop Mode Utilities
|
|
3
|
+
* Utilities specific to CLI loop mode session management and restoration
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatMessage, ConversationData } from "../types/conversation.js";
|
|
6
|
+
import type { SessionRestoreResult } from "../types/cli.js";
|
|
7
|
+
/**
|
|
8
|
+
* Verify that conversation context is accessible and properly loaded
|
|
9
|
+
* Uses the global session to access the NeuroLink instance
|
|
10
|
+
*/
|
|
11
|
+
export declare function verifyConversationContext(sessionId: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Get conversation context for display (first few and last few messages)
|
|
14
|
+
* Uses the global session to access the NeuroLink instance
|
|
15
|
+
*/
|
|
16
|
+
export declare function getConversationPreview(sessionId: string, previewCount?: number): Promise<ChatMessage[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Generate a title from content by truncating to appropriate length
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateConversationTitle(content: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Truncate text content to specified length with ellipsis
|
|
23
|
+
*/
|
|
24
|
+
export declare function truncateText(content: string, maxLength: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Format timestamp as human-readable relative time
|
|
27
|
+
* Uses Intl.RelativeTimeFormat for natural language output
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatTimeAgo(timestamp: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Get appropriate icon for content based on regex patterns
|
|
32
|
+
*/
|
|
33
|
+
export declare function getContentIcon(content: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Display session restoration status message
|
|
36
|
+
*/
|
|
37
|
+
export declare function displaySessionMessage(result: SessionRestoreResult): void;
|
|
38
|
+
/**
|
|
39
|
+
* Load command history from the global history file
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadCommandHistory(): Promise<string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Save a command to the global history file
|
|
44
|
+
*/
|
|
45
|
+
export declare function saveCommandToHistory(command: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Display conversation preview with formatted messages
|
|
48
|
+
*/
|
|
49
|
+
export declare function displayConversationPreview(preview: ChatMessage[], maxPreview?: number): void;
|
|
50
|
+
/**
|
|
51
|
+
* Parse a string value to its appropriate type (string, number, or boolean)
|
|
52
|
+
* Useful for parsing user input from CLI commands
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseValue(value: string): string | number | boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Restore session variables from conversation metadata
|
|
57
|
+
* Extracts and sets session variables stored in conversation metadata
|
|
58
|
+
*/
|
|
59
|
+
export declare function restoreSessionVariables(conversationData: ConversationData): Promise<void>;
|
|
60
|
+
export declare const HISTORY_FILE: string;
|
|
61
|
+
export declare const LOOP_CACHE_CONFIG: {
|
|
62
|
+
readonly TTL_MS: number;
|
|
63
|
+
};
|
|
64
|
+
export declare const LOOP_DISPLAY_LIMITS: {
|
|
65
|
+
readonly MAX_CONVERSATIONS: 20;
|
|
66
|
+
readonly CONTENT_LENGTH: 50;
|
|
67
|
+
readonly TITLE_LENGTH: 40;
|
|
68
|
+
readonly SESSION_ID_DISPLAY: 12;
|
|
69
|
+
readonly SESSION_ID_SHORT: 8;
|
|
70
|
+
readonly PAGE_SIZE: 15;
|
|
71
|
+
};
|