@n0zer0d4y/vulcan-file-ops 1.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/CHANGELOG.md +27 -0
- package/LICENSE +21 -0
- package/README.md +747 -0
- package/dist/cli.js +31 -0
- package/dist/index.js +3 -0
- package/dist/server/index.js +648 -0
- package/dist/tools/filesystem-tools.js +905 -0
- package/dist/tools/read-tools.js +234 -0
- package/dist/tools/search-tools.js +156 -0
- package/dist/tools/shell-tool.js +191 -0
- package/dist/tools/write-tools.js +256 -0
- package/dist/types/index.js +368 -0
- package/dist/utils/command-path-extraction.js +200 -0
- package/dist/utils/command-validation.js +121 -0
- package/dist/utils/document-parser.js +171 -0
- package/dist/utils/docx-writer.js +30 -0
- package/dist/utils/html-to-document.js +334 -0
- package/dist/utils/lib.js +944 -0
- package/dist/utils/path-utils.js +92 -0
- package/dist/utils/path-validation.js +76 -0
- package/dist/utils/pdf-writer.js +94 -0
- package/dist/utils/roots-utils.js +71 -0
- package/dist/utils/shell-execution.js +77 -0
- package/package.json +73 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import os from 'os';
|
|
3
|
+
/**
|
|
4
|
+
* Converts WSL or Unix-style Windows paths to Windows format
|
|
5
|
+
* @param p The path to convert
|
|
6
|
+
* @returns Converted Windows path
|
|
7
|
+
*/
|
|
8
|
+
export function convertToWindowsPath(p) {
|
|
9
|
+
// Handle WSL paths (/mnt/c/...)
|
|
10
|
+
if (p.startsWith('/mnt/')) {
|
|
11
|
+
const driveLetter = p.charAt(5).toUpperCase();
|
|
12
|
+
const pathPart = p.slice(6).replace(/\//g, '\\');
|
|
13
|
+
return `${driveLetter}:${pathPart}`;
|
|
14
|
+
}
|
|
15
|
+
// Handle Unix-style Windows paths (/c/...)
|
|
16
|
+
if (p.match(/^\/[a-zA-Z]\//)) {
|
|
17
|
+
const driveLetter = p.charAt(1).toUpperCase();
|
|
18
|
+
const pathPart = p.slice(2).replace(/\//g, '\\');
|
|
19
|
+
return `${driveLetter}:${pathPart}`;
|
|
20
|
+
}
|
|
21
|
+
// Handle standard Windows paths, ensuring backslashes
|
|
22
|
+
if (p.match(/^[a-zA-Z]:/)) {
|
|
23
|
+
return p.replace(/\//g, '\\');
|
|
24
|
+
}
|
|
25
|
+
// Leave non-Windows paths unchanged
|
|
26
|
+
return p;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalizes path by standardizing format while preserving OS-specific behavior
|
|
30
|
+
* @param p The path to normalize
|
|
31
|
+
* @returns Normalized path
|
|
32
|
+
*/
|
|
33
|
+
export function normalizePath(p) {
|
|
34
|
+
// Remove any surrounding quotes and whitespace
|
|
35
|
+
p = p.trim().replace(/^["']|["']$/g, '');
|
|
36
|
+
// Check if this is a Unix path (starts with / but not a Windows or WSL path)
|
|
37
|
+
const isUnixPath = p.startsWith('/') &&
|
|
38
|
+
!p.match(/^\/mnt\/[a-z]\//i) &&
|
|
39
|
+
!p.match(/^\/[a-zA-Z]\//);
|
|
40
|
+
if (isUnixPath) {
|
|
41
|
+
// For Unix paths, just normalize without converting to Windows format
|
|
42
|
+
// Replace double slashes with single slashes and remove trailing slashes
|
|
43
|
+
return p.replace(/\/+/g, '/').replace(/\/+$/, '');
|
|
44
|
+
}
|
|
45
|
+
// Convert WSL or Unix-style Windows paths to Windows format
|
|
46
|
+
p = convertToWindowsPath(p);
|
|
47
|
+
// Handle double backslashes, preserving leading UNC \\
|
|
48
|
+
if (p.startsWith('\\\\')) {
|
|
49
|
+
// For UNC paths, first normalize any excessive leading backslashes to exactly \\
|
|
50
|
+
// Then normalize double backslashes in the rest of the path
|
|
51
|
+
let uncPath = p;
|
|
52
|
+
// Replace multiple leading backslashes with exactly two
|
|
53
|
+
uncPath = uncPath.replace(/^\\{2,}/, '\\\\');
|
|
54
|
+
// Now normalize any remaining double backslashes in the rest of the path
|
|
55
|
+
const restOfPath = uncPath.substring(2).replace(/\\\\/g, '\\');
|
|
56
|
+
p = '\\\\' + restOfPath;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// For non-UNC paths, normalize all double backslashes
|
|
60
|
+
p = p.replace(/\\\\/g, '\\');
|
|
61
|
+
}
|
|
62
|
+
// Use Node's path normalization, which handles . and .. segments
|
|
63
|
+
let normalized = path.normalize(p);
|
|
64
|
+
// Fix UNC paths after normalization (path.normalize can remove a leading backslash)
|
|
65
|
+
if (p.startsWith('\\\\') && !normalized.startsWith('\\\\')) {
|
|
66
|
+
normalized = '\\' + normalized;
|
|
67
|
+
}
|
|
68
|
+
// Handle Windows paths: convert slashes and ensure drive letter is capitalized
|
|
69
|
+
if (normalized.match(/^[a-zA-Z]:/)) {
|
|
70
|
+
let result = normalized.replace(/\//g, '\\');
|
|
71
|
+
// Capitalize drive letter if present
|
|
72
|
+
if (/^[a-z]:/.test(result)) {
|
|
73
|
+
result = result.charAt(0).toUpperCase() + result.slice(1);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
// For all other paths (including relative paths), convert forward slashes to backslashes
|
|
78
|
+
// This ensures relative paths like "some/relative/path" become "some\\relative\\path"
|
|
79
|
+
return normalized.replace(/\//g, '\\');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Expands home directory tildes in paths
|
|
83
|
+
* @param filepath The path to expand
|
|
84
|
+
* @returns Expanded path
|
|
85
|
+
*/
|
|
86
|
+
export function expandHome(filepath) {
|
|
87
|
+
if (filepath.startsWith('~/') || filepath === '~') {
|
|
88
|
+
return path.join(os.homedir(), filepath.slice(1));
|
|
89
|
+
}
|
|
90
|
+
return filepath;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=path-utils.js.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if an absolute path is within any of the allowed directories.
|
|
4
|
+
*
|
|
5
|
+
* @param absolutePath - The absolute path to check (will be normalized)
|
|
6
|
+
* @param allowedDirectories - Array of absolute allowed directory paths (will be normalized)
|
|
7
|
+
* @returns true if the path is within an allowed directory, false otherwise
|
|
8
|
+
* @throws Error if given relative paths after normalization
|
|
9
|
+
*/
|
|
10
|
+
export function isPathWithinAllowedDirectories(absolutePath, allowedDirectories) {
|
|
11
|
+
// Type validation
|
|
12
|
+
if (typeof absolutePath !== 'string' || !Array.isArray(allowedDirectories)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// Reject empty inputs
|
|
16
|
+
if (!absolutePath || allowedDirectories.length === 0) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
// Reject null bytes (forbidden in paths)
|
|
20
|
+
if (absolutePath.includes('\x00')) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
// Normalize the input path
|
|
24
|
+
let normalizedPath;
|
|
25
|
+
try {
|
|
26
|
+
normalizedPath = path.resolve(path.normalize(absolutePath));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
// Verify it's absolute after normalization
|
|
32
|
+
if (!path.isAbsolute(normalizedPath)) {
|
|
33
|
+
throw new Error('Path must be absolute after normalization');
|
|
34
|
+
}
|
|
35
|
+
// Check against each allowed directory
|
|
36
|
+
return allowedDirectories.some(dir => {
|
|
37
|
+
if (typeof dir !== 'string' || !dir) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Reject null bytes in allowed dirs
|
|
41
|
+
if (dir.includes('\x00')) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Normalize the allowed directory
|
|
45
|
+
let normalizedDir;
|
|
46
|
+
try {
|
|
47
|
+
normalizedDir = path.resolve(path.normalize(dir));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
// Verify allowed directory is absolute after normalization
|
|
53
|
+
if (!path.isAbsolute(normalizedDir)) {
|
|
54
|
+
throw new Error('Allowed directories must be absolute paths after normalization');
|
|
55
|
+
}
|
|
56
|
+
// Check if normalizedPath is within normalizedDir
|
|
57
|
+
// Path is inside if it's the same or a subdirectory
|
|
58
|
+
if (normalizedPath === normalizedDir) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
// Special case for root directory to avoid double slash
|
|
62
|
+
// On Windows, we need to check if both paths are on the same drive
|
|
63
|
+
if (normalizedDir === path.sep) {
|
|
64
|
+
return normalizedPath.startsWith(path.sep);
|
|
65
|
+
}
|
|
66
|
+
// On Windows, also check for drive root (e.g., "C:\")
|
|
67
|
+
if (path.sep === '\\' && normalizedDir.match(/^[A-Za-z]:\\?$/)) {
|
|
68
|
+
// Ensure both paths are on the same drive
|
|
69
|
+
const dirDrive = normalizedDir.charAt(0).toLowerCase();
|
|
70
|
+
const pathDrive = normalizedPath.charAt(0).toLowerCase();
|
|
71
|
+
return pathDrive === dirDrive && normalizedPath.startsWith(normalizedDir.replace(/\\?$/, '\\'));
|
|
72
|
+
}
|
|
73
|
+
return normalizedPath.startsWith(normalizedDir + path.sep);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=path-validation.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
|
|
2
|
+
/**
|
|
3
|
+
* Create a new PDF document
|
|
4
|
+
*/
|
|
5
|
+
export async function createPDF(options = {}) {
|
|
6
|
+
const pdfDoc = await PDFDocument.create();
|
|
7
|
+
if (options.title)
|
|
8
|
+
pdfDoc.setTitle(options.title);
|
|
9
|
+
if (options.author)
|
|
10
|
+
pdfDoc.setAuthor(options.author);
|
|
11
|
+
if (options.subject)
|
|
12
|
+
pdfDoc.setSubject(options.subject);
|
|
13
|
+
if (options.keywords)
|
|
14
|
+
pdfDoc.setKeywords(options.keywords);
|
|
15
|
+
return pdfDoc;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Save PDF to buffer
|
|
19
|
+
*/
|
|
20
|
+
export async function savePDFToBuffer(pdfDoc) {
|
|
21
|
+
const pdfBytes = await pdfDoc.save();
|
|
22
|
+
return Buffer.from(pdfBytes);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create simple text-based PDF from string content
|
|
26
|
+
* This is the main function called by write_file tool
|
|
27
|
+
*/
|
|
28
|
+
export async function createSimpleTextPDF(content, options = {}) {
|
|
29
|
+
const pdfDoc = await createPDF(options);
|
|
30
|
+
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
31
|
+
// A4 size: 595 x 842 points
|
|
32
|
+
const pageWidth = 595;
|
|
33
|
+
const pageHeight = 842;
|
|
34
|
+
const margin = 50;
|
|
35
|
+
const fontSize = 12;
|
|
36
|
+
const lineHeight = 14;
|
|
37
|
+
const maxWidth = pageWidth - 2 * margin;
|
|
38
|
+
let page = pdfDoc.addPage([pageWidth, pageHeight]);
|
|
39
|
+
let y = pageHeight - margin;
|
|
40
|
+
// Split content into lines
|
|
41
|
+
const lines = content.split("\n");
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
// Check if we need a new page
|
|
44
|
+
if (y < margin) {
|
|
45
|
+
page = pdfDoc.addPage([pageWidth, pageHeight]);
|
|
46
|
+
y = pageHeight - margin;
|
|
47
|
+
}
|
|
48
|
+
// Handle empty lines
|
|
49
|
+
if (line.trim() === "") {
|
|
50
|
+
y -= lineHeight;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Simple word wrapping
|
|
54
|
+
const words = line.split(" ");
|
|
55
|
+
let currentLine = "";
|
|
56
|
+
for (const word of words) {
|
|
57
|
+
const testLine = currentLine + (currentLine ? " " : "") + word;
|
|
58
|
+
const textWidth = font.widthOfTextAtSize(testLine, fontSize);
|
|
59
|
+
if (textWidth > maxWidth && currentLine) {
|
|
60
|
+
// Draw current line and start new one
|
|
61
|
+
page.drawText(currentLine, {
|
|
62
|
+
x: margin,
|
|
63
|
+
y,
|
|
64
|
+
size: fontSize,
|
|
65
|
+
font,
|
|
66
|
+
color: rgb(0, 0, 0),
|
|
67
|
+
});
|
|
68
|
+
y -= lineHeight;
|
|
69
|
+
currentLine = word;
|
|
70
|
+
// Check if we need a new page
|
|
71
|
+
if (y < margin) {
|
|
72
|
+
page = pdfDoc.addPage([pageWidth, pageHeight]);
|
|
73
|
+
y = pageHeight - margin;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
currentLine = testLine;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Draw remaining text
|
|
81
|
+
if (currentLine) {
|
|
82
|
+
page.drawText(currentLine, {
|
|
83
|
+
x: margin,
|
|
84
|
+
y,
|
|
85
|
+
size: fontSize,
|
|
86
|
+
font,
|
|
87
|
+
color: rgb(0, 0, 0),
|
|
88
|
+
});
|
|
89
|
+
y -= lineHeight;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return await savePDFToBuffer(pdfDoc);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=pdf-writer.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { normalizePath } from './path-utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Converts a root URI to a normalized directory path with basic security validation.
|
|
7
|
+
* @param rootUri - File URI (file://...) or plain directory path
|
|
8
|
+
* @returns Promise resolving to validated path or null if invalid
|
|
9
|
+
*/
|
|
10
|
+
async function parseRootUri(rootUri) {
|
|
11
|
+
try {
|
|
12
|
+
const rawPath = rootUri.startsWith('file://') ? rootUri.slice(7) : rootUri;
|
|
13
|
+
const expandedPath = rawPath.startsWith('~/') || rawPath === '~'
|
|
14
|
+
? path.join(os.homedir(), rawPath.slice(1))
|
|
15
|
+
: rawPath;
|
|
16
|
+
const absolutePath = path.resolve(expandedPath);
|
|
17
|
+
const resolvedPath = await fs.realpath(absolutePath);
|
|
18
|
+
return normalizePath(resolvedPath);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null; // Path doesn't exist or other error
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Formats error message for directory validation failures.
|
|
26
|
+
* @param dir - Directory path that failed validation
|
|
27
|
+
* @param error - Error that occurred during validation
|
|
28
|
+
* @param reason - Specific reason for failure
|
|
29
|
+
* @returns Formatted error message
|
|
30
|
+
*/
|
|
31
|
+
function formatDirectoryError(dir, error, reason) {
|
|
32
|
+
if (reason) {
|
|
33
|
+
return `Skipping ${reason}: ${dir}`;
|
|
34
|
+
}
|
|
35
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
36
|
+
return `Skipping invalid directory: ${dir} due to error: ${message}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolves requested root directories from MCP root specifications.
|
|
40
|
+
*
|
|
41
|
+
* Converts root URI specifications (file:// URIs or plain paths) into normalized
|
|
42
|
+
* directory paths, validating that each path exists and is a directory.
|
|
43
|
+
* Includes symlink resolution for security.
|
|
44
|
+
*
|
|
45
|
+
* @param requestedRoots - Array of root specifications with URI and optional name
|
|
46
|
+
* @returns Promise resolving to array of validated directory paths
|
|
47
|
+
*/
|
|
48
|
+
export async function getValidRootDirectories(requestedRoots) {
|
|
49
|
+
const validatedDirectories = [];
|
|
50
|
+
for (const requestedRoot of requestedRoots) {
|
|
51
|
+
const resolvedPath = await parseRootUri(requestedRoot.uri);
|
|
52
|
+
if (!resolvedPath) {
|
|
53
|
+
console.error(formatDirectoryError(requestedRoot.uri, undefined, 'invalid path or inaccessible'));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
const stats = await fs.stat(resolvedPath);
|
|
58
|
+
if (stats.isDirectory()) {
|
|
59
|
+
validatedDirectories.push(resolvedPath);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.error(formatDirectoryError(resolvedPath, undefined, 'non-directory root'));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error(formatDirectoryError(resolvedPath, error));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return validatedDirectories;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=roots-utils.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { getShellConfig } from "./command-validation.js";
|
|
3
|
+
/**
|
|
4
|
+
* Execute a shell command with proper security and resource management
|
|
5
|
+
*/
|
|
6
|
+
export async function executeShellCommand(command, options = {}) {
|
|
7
|
+
const { workdir = process.cwd(), timeout = 30000, env } = options;
|
|
8
|
+
const shellConfig = getShellConfig();
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
let stdout = "";
|
|
11
|
+
let stderr = "";
|
|
12
|
+
let timedOut = false;
|
|
13
|
+
let resolved = false;
|
|
14
|
+
// Spawn the shell process
|
|
15
|
+
const child = spawn(shellConfig.shell, [...shellConfig.args, command], {
|
|
16
|
+
cwd: workdir,
|
|
17
|
+
env: { ...process.env, ...env },
|
|
18
|
+
shell: false, // Already using explicit shell
|
|
19
|
+
windowsHide: true,
|
|
20
|
+
});
|
|
21
|
+
// Set up timeout
|
|
22
|
+
const timeoutHandle = setTimeout(() => {
|
|
23
|
+
if (!resolved) {
|
|
24
|
+
timedOut = true;
|
|
25
|
+
child.kill("SIGTERM");
|
|
26
|
+
// Force kill after 5 seconds if SIGTERM doesn't work
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
if (!resolved) {
|
|
29
|
+
child.kill("SIGKILL");
|
|
30
|
+
}
|
|
31
|
+
}, 5000);
|
|
32
|
+
}
|
|
33
|
+
}, timeout);
|
|
34
|
+
// Capture stdout
|
|
35
|
+
if (child.stdout) {
|
|
36
|
+
child.stdout.on("data", (data) => {
|
|
37
|
+
stdout += data.toString();
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Capture stderr
|
|
41
|
+
if (child.stderr) {
|
|
42
|
+
child.stderr.on("data", (data) => {
|
|
43
|
+
stderr += data.toString();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// Handle process completion
|
|
47
|
+
child.on("close", (exitCode, signal) => {
|
|
48
|
+
if (resolved)
|
|
49
|
+
return;
|
|
50
|
+
resolved = true;
|
|
51
|
+
clearTimeout(timeoutHandle);
|
|
52
|
+
resolve({
|
|
53
|
+
stdout: stdout.trim(),
|
|
54
|
+
stderr: stderr.trim(),
|
|
55
|
+
exitCode,
|
|
56
|
+
signal,
|
|
57
|
+
timedOut,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// Handle errors
|
|
61
|
+
child.on("error", (error) => {
|
|
62
|
+
if (resolved)
|
|
63
|
+
return;
|
|
64
|
+
resolved = true;
|
|
65
|
+
clearTimeout(timeoutHandle);
|
|
66
|
+
resolve({
|
|
67
|
+
stdout: stdout.trim(),
|
|
68
|
+
stderr: stderr.trim(),
|
|
69
|
+
exitCode: null,
|
|
70
|
+
signal: null,
|
|
71
|
+
timedOut: false,
|
|
72
|
+
error,
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=shell-execution.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@n0zer0d4y/vulcan-file-ops",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "MCP server that gives Claude Desktop and other AI assistants filesystem superpowers—read, write, edit, and manage files like AI coding assistants",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Lloyd Barcatan",
|
|
7
|
+
"homepage": "https://github.com/n0zer0d4y/vulcan-file-ops",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/n0zer0d4y/vulcan-file-ops.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/n0zer0d4y/vulcan-file-ops/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"vulcan-file-ops": "dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/cli.js",
|
|
21
|
+
"dist/index.js",
|
|
22
|
+
"dist/server/index.js",
|
|
23
|
+
"dist/tools/*.js",
|
|
24
|
+
"dist/utils/*.js",
|
|
25
|
+
"dist/types/index.js",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"CHANGELOG.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc && shx chmod +x dist/*.js",
|
|
32
|
+
"prepare": "npm run build",
|
|
33
|
+
"prepublishOnly": "npm test",
|
|
34
|
+
"watch": "tsc --watch",
|
|
35
|
+
"test": "jest --config=jest.config.cjs",
|
|
36
|
+
"test:coverage": "jest --config=jest.config.cjs --coverage",
|
|
37
|
+
"start": "node dist/index.js",
|
|
38
|
+
"lint:json": "node -e \"JSON.parse(require('fs').readFileSync('package.json'))\" && echo \"package.json is valid JSON\"",
|
|
39
|
+
"lint": "npm run lint:json"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.20.0",
|
|
43
|
+
"@turbodocx/html-to-docx": "^1.16.0",
|
|
44
|
+
"diff": "^8.0.2",
|
|
45
|
+
"docx": "^9.5.1",
|
|
46
|
+
"dotenv": "^17.2.3",
|
|
47
|
+
"glob": "^11.0.3",
|
|
48
|
+
"html-to-pdfmake": "^2.5.31",
|
|
49
|
+
"jsdom": "^27.0.1",
|
|
50
|
+
"mammoth": "^1.11.0",
|
|
51
|
+
"minimatch": "^10.0.3",
|
|
52
|
+
"officeparser": "^5.2.1",
|
|
53
|
+
"pdf-lib": "^1.17.1",
|
|
54
|
+
"pdf-parse": "^2.3.0",
|
|
55
|
+
"pdfmake": "^0.2.20",
|
|
56
|
+
"zod-to-json-schema": "^3.24.6"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@jest/globals": "^30.2.0",
|
|
60
|
+
"@types/html-to-docx": "^1.8.0",
|
|
61
|
+
"@types/jest": "^30.0.0",
|
|
62
|
+
"@types/jsdom": "^27.0.0",
|
|
63
|
+
"@types/minimatch": "^5.1.2",
|
|
64
|
+
"@types/node": "^22",
|
|
65
|
+
"@types/pdf-parse": "^1.1.5",
|
|
66
|
+
"@types/pdfmake": "^0.2.12",
|
|
67
|
+
"jest": "^30.2.0",
|
|
68
|
+
"shx": "^0.3.4",
|
|
69
|
+
"ts-jest": "^29.4.5",
|
|
70
|
+
"ts-node": "^10.9.2",
|
|
71
|
+
"typescript": "^5.9.3"
|
|
72
|
+
}
|
|
73
|
+
}
|