winhelm-mcp 1.1.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 (125) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +646 -0
  4. package/dist/config/config-manager.d.ts +50 -0
  5. package/dist/config/config-manager.d.ts.map +1 -0
  6. package/dist/config/config-manager.js +300 -0
  7. package/dist/config/config-manager.js.map +1 -0
  8. package/dist/config/default-config.d.ts +4 -0
  9. package/dist/config/default-config.d.ts.map +1 -0
  10. package/dist/config/default-config.js +48 -0
  11. package/dist/config/default-config.js.map +1 -0
  12. package/dist/config/profiles.d.ts +16 -0
  13. package/dist/config/profiles.d.ts.map +1 -0
  14. package/dist/config/profiles.js +208 -0
  15. package/dist/config/profiles.js.map +1 -0
  16. package/dist/engine/filesystem.d.ts +52 -0
  17. package/dist/engine/filesystem.d.ts.map +1 -0
  18. package/dist/engine/filesystem.js +499 -0
  19. package/dist/engine/filesystem.js.map +1 -0
  20. package/dist/engine/http-client.d.ts +10 -0
  21. package/dist/engine/http-client.d.ts.map +1 -0
  22. package/dist/engine/http-client.js +93 -0
  23. package/dist/engine/http-client.js.map +1 -0
  24. package/dist/engine/pdf-generator.d.ts +15 -0
  25. package/dist/engine/pdf-generator.d.ts.map +1 -0
  26. package/dist/engine/pdf-generator.js +325 -0
  27. package/dist/engine/pdf-generator.js.map +1 -0
  28. package/dist/engine/powershell-runner.d.ts +23 -0
  29. package/dist/engine/powershell-runner.d.ts.map +1 -0
  30. package/dist/engine/powershell-runner.js +148 -0
  31. package/dist/engine/powershell-runner.js.map +1 -0
  32. package/dist/engine/ripgrep.d.ts +20 -0
  33. package/dist/engine/ripgrep.d.ts.map +1 -0
  34. package/dist/engine/ripgrep.js +173 -0
  35. package/dist/engine/ripgrep.js.map +1 -0
  36. package/dist/engine/system.d.ts +67 -0
  37. package/dist/engine/system.d.ts.map +1 -0
  38. package/dist/engine/system.js +558 -0
  39. package/dist/engine/system.js.map +1 -0
  40. package/dist/engine/task-manager.d.ts +66 -0
  41. package/dist/engine/task-manager.d.ts.map +1 -0
  42. package/dist/engine/task-manager.js +203 -0
  43. package/dist/engine/task-manager.js.map +1 -0
  44. package/dist/gateway/dashboard-html.d.ts +7 -0
  45. package/dist/gateway/dashboard-html.d.ts.map +1 -0
  46. package/dist/gateway/dashboard-html.js +624 -0
  47. package/dist/gateway/dashboard-html.js.map +1 -0
  48. package/dist/gateway/preview-html.d.ts +5 -0
  49. package/dist/gateway/preview-html.d.ts.map +1 -0
  50. package/dist/gateway/preview-html.js +351 -0
  51. package/dist/gateway/preview-html.js.map +1 -0
  52. package/dist/gateway/rate-limiter.d.ts +8 -0
  53. package/dist/gateway/rate-limiter.d.ts.map +1 -0
  54. package/dist/gateway/rate-limiter.js +51 -0
  55. package/dist/gateway/rate-limiter.js.map +1 -0
  56. package/dist/gateway/server.d.ts +18 -0
  57. package/dist/gateway/server.d.ts.map +1 -0
  58. package/dist/gateway/server.js +337 -0
  59. package/dist/gateway/server.js.map +1 -0
  60. package/dist/gateway/sse-gateway.d.ts +43 -0
  61. package/dist/gateway/sse-gateway.d.ts.map +1 -0
  62. package/dist/gateway/sse-gateway.js +161 -0
  63. package/dist/gateway/sse-gateway.js.map +1 -0
  64. package/dist/gateway/streamable-gateway.d.ts +39 -0
  65. package/dist/gateway/streamable-gateway.d.ts.map +1 -0
  66. package/dist/gateway/streamable-gateway.js +176 -0
  67. package/dist/gateway/streamable-gateway.js.map +1 -0
  68. package/dist/index.d.ts +5 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +164 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/tools/desktop-tools.d.ts +3 -0
  73. package/dist/tools/desktop-tools.d.ts.map +1 -0
  74. package/dist/tools/desktop-tools.js +280 -0
  75. package/dist/tools/desktop-tools.js.map +1 -0
  76. package/dist/tools/file-tools.d.ts +3 -0
  77. package/dist/tools/file-tools.d.ts.map +1 -0
  78. package/dist/tools/file-tools.js +433 -0
  79. package/dist/tools/file-tools.js.map +1 -0
  80. package/dist/tools/index.d.ts +12 -0
  81. package/dist/tools/index.d.ts.map +1 -0
  82. package/dist/tools/index.js +16 -0
  83. package/dist/tools/index.js.map +1 -0
  84. package/dist/tools/network-tools.d.ts +3 -0
  85. package/dist/tools/network-tools.d.ts.map +1 -0
  86. package/dist/tools/network-tools.js +108 -0
  87. package/dist/tools/network-tools.js.map +1 -0
  88. package/dist/tools/registry.d.ts +50 -0
  89. package/dist/tools/registry.d.ts.map +1 -0
  90. package/dist/tools/registry.js +54 -0
  91. package/dist/tools/registry.js.map +1 -0
  92. package/dist/tools/terminal-tools.d.ts +3 -0
  93. package/dist/tools/terminal-tools.d.ts.map +1 -0
  94. package/dist/tools/terminal-tools.js +152 -0
  95. package/dist/tools/terminal-tools.js.map +1 -0
  96. package/dist/tools/tool-wrapper.d.ts +7 -0
  97. package/dist/tools/tool-wrapper.d.ts.map +1 -0
  98. package/dist/tools/tool-wrapper.js +47 -0
  99. package/dist/tools/tool-wrapper.js.map +1 -0
  100. package/dist/types/index.d.ts +261 -0
  101. package/dist/types/index.d.ts.map +1 -0
  102. package/dist/types/index.js +2 -0
  103. package/dist/types/index.js.map +1 -0
  104. package/dist/utils/file-logger.d.ts +24 -0
  105. package/dist/utils/file-logger.d.ts.map +1 -0
  106. package/dist/utils/file-logger.js +117 -0
  107. package/dist/utils/file-logger.js.map +1 -0
  108. package/dist/utils/logger.d.ts +51 -0
  109. package/dist/utils/logger.d.ts.map +1 -0
  110. package/dist/utils/logger.js +189 -0
  111. package/dist/utils/logger.js.map +1 -0
  112. package/dist/utils/sanitizer.d.ts +13 -0
  113. package/dist/utils/sanitizer.d.ts.map +1 -0
  114. package/dist/utils/sanitizer.js +60 -0
  115. package/dist/utils/sanitizer.js.map +1 -0
  116. package/docs/EXAMPLES.md +171 -0
  117. package/docs/PROFILES.md +423 -0
  118. package/docs/SECURITY.md +190 -0
  119. package/docs/TOOLS.md +366 -0
  120. package/manifest.json +74 -0
  121. package/package.json +89 -0
  122. package/scripts/build-exe.ps1 +37 -0
  123. package/scripts/manage-service.ps1 +112 -0
  124. package/server.json +23 -0
  125. package/winhelm.config.example.json +30 -0
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Fast HTTP health check ping for local/remote servers
3
+ */
4
+ export async function pingEndpoint(url, timeoutMs = 5000, expectedStatus) {
5
+ const start = Date.now();
6
+ try {
7
+ const res = await fetch(url, {
8
+ method: "GET",
9
+ signal: AbortSignal.timeout(timeoutMs),
10
+ });
11
+ const latencyMs = Date.now() - start;
12
+ const isExpected = expectedStatus ? res.status === expectedStatus : res.ok;
13
+ return {
14
+ ok: isExpected,
15
+ status: res.status,
16
+ statusText: res.statusText,
17
+ latencyMs,
18
+ url,
19
+ };
20
+ }
21
+ catch (err) {
22
+ return {
23
+ ok: false,
24
+ status: 0,
25
+ statusText: err?.message || "Connection failed or timed out",
26
+ latencyMs: Date.now() - start,
27
+ url,
28
+ };
29
+ }
30
+ }
31
+ /**
32
+ * Execute HTTP request to test APIs, microservices, and web endpoints
33
+ */
34
+ export async function executeHttpRequest(options) {
35
+ const { url, method = "GET", headers = {}, body, timeoutMs = 15000 } = options;
36
+ const start = Date.now();
37
+ const fetchOptions = {
38
+ method: method.toUpperCase(),
39
+ headers: { ...headers },
40
+ signal: AbortSignal.timeout(timeoutMs),
41
+ };
42
+ if (body && !["GET", "HEAD"].includes(method.toUpperCase())) {
43
+ fetchOptions.body = body;
44
+ if (!fetchOptions.headers || !fetchOptions.headers["Content-Type"]) {
45
+ // Auto-detect JSON
46
+ try {
47
+ JSON.parse(body);
48
+ fetchOptions.headers["Content-Type"] = "application/json";
49
+ }
50
+ catch {
51
+ fetchOptions.headers["Content-Type"] = "text/plain";
52
+ }
53
+ }
54
+ }
55
+ try {
56
+ const res = await fetch(url, fetchOptions);
57
+ const durationMs = Date.now() - start;
58
+ const resHeaders = {};
59
+ res.headers.forEach((v, k) => {
60
+ resHeaders[k] = v;
61
+ });
62
+ const text = await res.text();
63
+ let isJson = false;
64
+ let json = undefined;
65
+ try {
66
+ json = JSON.parse(text);
67
+ isJson = true;
68
+ }
69
+ catch {
70
+ // plain text
71
+ }
72
+ return {
73
+ status: res.status,
74
+ statusText: res.statusText,
75
+ durationMs,
76
+ headers: resHeaders,
77
+ body: text.length > 50000 ? text.slice(0, 50000) + "\n...[truncated]" : text,
78
+ isJson,
79
+ json,
80
+ };
81
+ }
82
+ catch (err) {
83
+ return {
84
+ status: 0,
85
+ statusText: err?.message || "HTTP request failed",
86
+ durationMs: Date.now() - start,
87
+ headers: {},
88
+ body: "",
89
+ isJson: false,
90
+ };
91
+ }
92
+ }
93
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/engine/http-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { PdfGenerateOptions, PdfGenerateResult } from "../types/index.js";
2
+ /**
3
+ * Locate system Chromium browser (Edge or Chrome)
4
+ */
5
+ export declare function findChromiumPath(): string | null;
6
+ /**
7
+ * Lightweight, robust Markdown to HTML converter with print styling
8
+ */
9
+ export declare function markdownToHtml(md: string, title?: string): string;
10
+ export declare function sanitizeHref(url: string): string;
11
+ /**
12
+ * Generate a PDF from a Markdown, HTML, or Text file using native Chromium headless
13
+ */
14
+ export declare function generatePdf(options: PdfGenerateOptions): Promise<PdfGenerateResult>;
15
+ //# sourceMappingURL=pdf-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-generator.d.ts","sourceRoot":"","sources":["../../src/engine/pdf-generator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI/E;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAiBhD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAa,GAAG,MAAM,CA2LrE;AAWD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiBhD;AAaD;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6EzF"}
@@ -0,0 +1,325 @@
1
+ import { execFile } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import { existsSync, statSync } from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import { promisify } from "node:util";
7
+ import { ConfigManager } from "../config/config-manager.js";
8
+ const execFileAsync = promisify(execFile);
9
+ /**
10
+ * Locate system Chromium browser (Edge or Chrome)
11
+ */
12
+ export function findChromiumPath() {
13
+ const localAppData = process.env.LOCALAPPDATA || "";
14
+ const candidates = [
15
+ "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
16
+ "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
17
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
18
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
19
+ path.join(localAppData, "Google\\Chrome\\Application\\chrome.exe"),
20
+ path.join(localAppData, "Microsoft\\Edge\\Application\\msedge.exe"),
21
+ ];
22
+ for (const p of candidates) {
23
+ if (existsSync(p)) {
24
+ return p;
25
+ }
26
+ }
27
+ return null;
28
+ }
29
+ /**
30
+ * Lightweight, robust Markdown to HTML converter with print styling
31
+ */
32
+ export function markdownToHtml(md, title = "Document") {
33
+ // Convert Markdown blocks
34
+ const lines = md.split(/\r?\n/);
35
+ const htmlParts = [];
36
+ let inCodeBlock = false;
37
+ let codeBlockLang = "";
38
+ let codeBuffer = [];
39
+ let inList = false;
40
+ let inTable = false;
41
+ for (let i = 0; i < lines.length; i++) {
42
+ const line = lines[i];
43
+ // Code blocks
44
+ if (line.trim().startsWith("```")) {
45
+ if (inCodeBlock) {
46
+ const safeLang = escapeHtml(codeBlockLang.replace(/[^\w\-]/g, ""));
47
+ htmlParts.push(`<pre class="code-block ${safeLang}"><code>${escapeHtml(codeBuffer.join("\n"))}</code></pre>`);
48
+ codeBuffer = [];
49
+ inCodeBlock = false;
50
+ }
51
+ else {
52
+ inCodeBlock = true;
53
+ codeBlockLang = line.trim().slice(3).trim();
54
+ }
55
+ continue;
56
+ }
57
+ if (inCodeBlock) {
58
+ codeBuffer.push(line);
59
+ continue;
60
+ }
61
+ // Tables
62
+ if (line.trim().startsWith("|") && line.trim().endsWith("|")) {
63
+ if (!inTable) {
64
+ inTable = true;
65
+ htmlParts.push("<table>");
66
+ }
67
+ // Check if delimiter row
68
+ if (/^\|[\s\-:|]+\|$/.test(line.trim())) {
69
+ continue;
70
+ }
71
+ const cells = line
72
+ .trim()
73
+ .slice(1, -1)
74
+ .split("|")
75
+ .map((c) => c.trim());
76
+ const isHeader = i > 0 && lines[i + 1] && /^\|[\s\-:|]+\|$/.test(lines[i + 1].trim());
77
+ const tag = isHeader ? "th" : "td";
78
+ htmlParts.push(`<tr>${cells.map((c) => `<${tag}>${formatInline(c)}</${tag}>`).join("")}</tr>`);
79
+ continue;
80
+ }
81
+ else if (inTable) {
82
+ inTable = false;
83
+ htmlParts.push("</table>");
84
+ }
85
+ // Lists
86
+ if (/^\s*[\*\-]\s+(.*)$/.test(line)) {
87
+ if (!inList) {
88
+ inList = true;
89
+ htmlParts.push("<ul>");
90
+ }
91
+ const itemText = line.replace(/^\s*[\*\-]\s+/, "");
92
+ htmlParts.push(`<li>${formatInline(itemText)}</li>`);
93
+ continue;
94
+ }
95
+ else if (inList) {
96
+ inList = false;
97
+ htmlParts.push("</ul>");
98
+ }
99
+ // Headings
100
+ if (/^######\s+(.*)$/.test(line)) {
101
+ htmlParts.push(`<h6>${formatInline(line.replace(/^######\s+/, ""))}</h6>`);
102
+ }
103
+ else if (/^#####\s+(.*)$/.test(line)) {
104
+ htmlParts.push(`<h5>${formatInline(line.replace(/^#####\s+/, ""))}</h5>`);
105
+ }
106
+ else if (/^####\s+(.*)$/.test(line)) {
107
+ htmlParts.push(`<h4>${formatInline(line.replace(/^####\s+/, ""))}</h4>`);
108
+ }
109
+ else if (/^###\s+(.*)$/.test(line)) {
110
+ htmlParts.push(`<h3>${formatInline(line.replace(/^###\s+/, ""))}</h3>`);
111
+ }
112
+ else if (/^##\s+(.*)$/.test(line)) {
113
+ htmlParts.push(`<h2>${formatInline(line.replace(/^##\s+/, ""))}</h2>`);
114
+ }
115
+ else if (/^#\s+(.*)$/.test(line)) {
116
+ htmlParts.push(`<h1>${formatInline(line.replace(/^#\s+/, ""))}</h1>`);
117
+ }
118
+ else if (/^>\s*(.*)$/.test(line)) {
119
+ htmlParts.push(`<blockquote>${formatInline(line.replace(/^>\s*/, ""))}</blockquote>`);
120
+ }
121
+ else if (/^---|\*\*\*|___$/.test(line.trim())) {
122
+ htmlParts.push("<hr />");
123
+ }
124
+ else if (line.trim().length > 0) {
125
+ htmlParts.push(`<p>${formatInline(line)}</p>`);
126
+ }
127
+ }
128
+ if (inList)
129
+ htmlParts.push("</ul>");
130
+ if (inTable)
131
+ htmlParts.push("</table>");
132
+ return `<!DOCTYPE html>
133
+ <html lang="en">
134
+ <head>
135
+ <meta charset="UTF-8">
136
+ <title>${escapeHtml(title)}</title>
137
+ <style>
138
+ @page {
139
+ margin: 15mm 20mm;
140
+ size: A4 portrait;
141
+ }
142
+ body {
143
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
144
+ font-size: 11pt;
145
+ line-height: 1.6;
146
+ color: #24292f;
147
+ background: #ffffff;
148
+ padding: 0;
149
+ margin: 0;
150
+ }
151
+ h1, h2, h3, h4, h5, h6 {
152
+ color: #1f2328;
153
+ margin-top: 1.4em;
154
+ margin-bottom: 0.6em;
155
+ font-weight: 600;
156
+ page-break-after: avoid;
157
+ }
158
+ h1 { font-size: 20pt; border-bottom: 1.5px solid #d0d7de; padding-bottom: 0.3em; }
159
+ h2 { font-size: 15pt; border-bottom: 1px solid #eaeef2; padding-bottom: 0.2em; }
160
+ h3 { font-size: 13pt; }
161
+ p, ul, ol { margin-top: 0; margin-bottom: 1em; }
162
+ ul { padding-left: 2em; }
163
+ li { margin-bottom: 0.25em; }
164
+ code {
165
+ font-family: "Cascadia Code", Consolas, "Courier New", monospace;
166
+ font-size: 9.5pt;
167
+ background-color: #f6f8fa;
168
+ padding: 0.2em 0.4em;
169
+ border-radius: 4px;
170
+ border: 1px solid #e1e4e8;
171
+ }
172
+ pre.code-block {
173
+ background-color: #f6f8fa;
174
+ border: 1px solid #d0d7de;
175
+ border-radius: 6px;
176
+ padding: 14px;
177
+ overflow-x: auto;
178
+ font-size: 9pt;
179
+ line-height: 1.45;
180
+ page-break-inside: avoid;
181
+ }
182
+ pre.code-block code {
183
+ background: none;
184
+ border: none;
185
+ padding: 0;
186
+ }
187
+ table {
188
+ border-collapse: collapse;
189
+ width: 100%;
190
+ margin: 1em 0;
191
+ font-size: 10pt;
192
+ page-break-inside: avoid;
193
+ }
194
+ th, td {
195
+ border: 1px solid #d0d7de;
196
+ padding: 8px 12px;
197
+ text-align: left;
198
+ }
199
+ th {
200
+ background-color: #f6f8fa;
201
+ font-weight: 600;
202
+ }
203
+ blockquote {
204
+ border-left: 4px solid #0969da;
205
+ margin: 1em 0;
206
+ padding: 0.5em 1em;
207
+ color: #57606a;
208
+ background-color: #f6f8fa;
209
+ }
210
+ hr {
211
+ border: 0;
212
+ height: 1px;
213
+ background: #d0d7de;
214
+ margin: 1.5em 0;
215
+ }
216
+ </style>
217
+ </head>
218
+ <body>
219
+ ${htmlParts.join("\n")}
220
+ </body>
221
+ </html>`;
222
+ }
223
+ function escapeHtml(str) {
224
+ return str
225
+ .replace(/&/g, "&amp;")
226
+ .replace(/</g, "&lt;")
227
+ .replace(/>/g, "&gt;")
228
+ .replace(/"/g, "&quot;")
229
+ .replace(/'/g, "&#039;");
230
+ }
231
+ export function sanitizeHref(url) {
232
+ const clean = url.trim().replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
233
+ const normalized = clean.replace(/&#x?[0-9a-f]+;?/gi, (match) => {
234
+ try {
235
+ if (match.startsWith("&#x") || match.startsWith("&#X")) {
236
+ return String.fromCharCode(parseInt(match.slice(3), 16));
237
+ }
238
+ else if (match.startsWith("&#")) {
239
+ return String.fromCharCode(parseInt(match.slice(2), 10));
240
+ }
241
+ }
242
+ catch { }
243
+ return match;
244
+ });
245
+ if (/^(?:javascript|vbscript|data)\s*:/i.test(normalized.trim())) {
246
+ return "#";
247
+ }
248
+ return clean;
249
+ }
250
+ function formatInline(str) {
251
+ return escapeHtml(str)
252
+ .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
253
+ .replace(/\*(.*?)\*/g, "<em>$1</em>")
254
+ .replace(/~~(.*?)~~/g, "<del>$1</del>")
255
+ .replace(/`([^`]+)`/g, "<code>$1</code>")
256
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text, href) => {
257
+ return `<a href="${sanitizeHref(href)}">${text}</a>`;
258
+ });
259
+ }
260
+ /**
261
+ * Generate a PDF from a Markdown, HTML, or Text file using native Chromium headless
262
+ */
263
+ export async function generatePdf(options) {
264
+ const configManager = ConfigManager.getInstance();
265
+ if (configManager.isReadOnly()) {
266
+ throw new Error("Operation blocked: Server is running in read-only mode.");
267
+ }
268
+ const resolvedSource = path.resolve(options.sourcePath);
269
+ const resolvedPdf = path.resolve(options.pdfPath);
270
+ if (!existsSync(resolvedSource)) {
271
+ throw new Error(`Source file not found: ${options.sourcePath}`);
272
+ }
273
+ const dstCheck = configManager.isPathAllowed(resolvedPdf);
274
+ if (!dstCheck.allowed) {
275
+ throw new Error(dstCheck.reason);
276
+ }
277
+ const browserPath = findChromiumPath();
278
+ if (!browserPath) {
279
+ throw new Error("No Chromium-based browser (Microsoft Edge or Google Chrome) found on this Windows system.");
280
+ }
281
+ // Ensure output directory exists
282
+ await fs.mkdir(path.dirname(resolvedPdf), { recursive: true });
283
+ const ext = path.extname(resolvedSource).toLowerCase();
284
+ let htmlPath = resolvedSource;
285
+ let tempHtmlPath = null;
286
+ if (ext === ".md" || ext === ".markdown" || ext === ".txt") {
287
+ const rawContent = await fs.readFile(resolvedSource, "utf-8");
288
+ const docTitle = options.title || path.basename(resolvedSource, ext);
289
+ const htmlContent = markdownToHtml(rawContent, docTitle);
290
+ tempHtmlPath = path.join(os.tmpdir(), `wincommander_pdf_${Date.now()}_${Math.random().toString(36).slice(2)}.html`);
291
+ await fs.writeFile(tempHtmlPath, htmlContent, "utf-8");
292
+ htmlPath = tempHtmlPath;
293
+ }
294
+ const args = [
295
+ "--headless",
296
+ "--disable-gpu",
297
+ "--no-pdf-header-footer",
298
+ "--run-all-compositor-stages-before-draw",
299
+ `--print-to-pdf=${resolvedPdf}`,
300
+ htmlPath,
301
+ ];
302
+ try {
303
+ await execFileAsync(browserPath, args, {
304
+ timeout: 30000,
305
+ windowsHide: true,
306
+ });
307
+ if (!existsSync(resolvedPdf)) {
308
+ throw new Error(`PDF generation completed but target file was not created at ${resolvedPdf}`);
309
+ }
310
+ const stat = statSync(resolvedPdf);
311
+ return {
312
+ success: true,
313
+ pdfPath: resolvedPdf,
314
+ sourcePath: resolvedSource,
315
+ sizeBytes: stat.size,
316
+ browserUsed: path.basename(browserPath),
317
+ };
318
+ }
319
+ finally {
320
+ if (tempHtmlPath && existsSync(tempHtmlPath)) {
321
+ await fs.unlink(tempHtmlPath).catch(() => { });
322
+ }
323
+ }
324
+ }
325
+ //# sourceMappingURL=pdf-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-generator.js","sourceRoot":"","sources":["../../src/engine/pdf-generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { ExecutionResult } from "../types/index.js";
2
+ /**
3
+ * Check whether PowerShell 7+ (pwsh.exe) is installed on the host system.
4
+ * Cached in memory after the initial check to eliminate recurring spawn overhead.
5
+ */
6
+ export declare function isPwshAvailable(): boolean;
7
+ /**
8
+ * Reset or set pwsh availability cache (useful for testing and mocking)
9
+ */
10
+ export declare function setPwshAvailableCache(val: boolean | null): void;
11
+ /**
12
+ * Select preferred PowerShell executable: pwsh.exe if available and preferred, else powershell.exe
13
+ */
14
+ export declare function getPowerShellExecutable(preferPwsh?: boolean): string;
15
+ export interface PowerShellOptions {
16
+ cwd?: string;
17
+ timeoutMs?: number;
18
+ }
19
+ /**
20
+ * Execute a PowerShell command with UTF-8 encoding, security guard, and timeout protection
21
+ */
22
+ export declare function runPowerShell(command: string, options?: PowerShellOptions): Promise<ExecutionResult>;
23
+ //# sourceMappingURL=powershell-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"powershell-runner.d.ts","sourceRoot":"","sources":["../../src/engine/powershell-runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMzD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAezC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAE/D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,UAAO,GAAG,MAAM,CAKjE;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,CAmH1B"}
@@ -0,0 +1,148 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import path from "node:path";
4
+ import { ConfigManager } from "../config/config-manager.js";
5
+ import { logger } from "../utils/logger.js";
6
+ let cachedPwshAvailable = null;
7
+ /**
8
+ * Check whether PowerShell 7+ (pwsh.exe) is installed on the host system.
9
+ * Cached in memory after the initial check to eliminate recurring spawn overhead.
10
+ */
11
+ export function isPwshAvailable() {
12
+ if (cachedPwshAvailable !== null) {
13
+ return cachedPwshAvailable;
14
+ }
15
+ try {
16
+ const res = spawnSync("pwsh.exe", ["-NoProfile", "-Version"], {
17
+ windowsHide: true,
18
+ stdio: "ignore",
19
+ timeout: 2000,
20
+ });
21
+ cachedPwshAvailable = res.status === 0;
22
+ }
23
+ catch {
24
+ cachedPwshAvailable = false;
25
+ }
26
+ return cachedPwshAvailable;
27
+ }
28
+ /**
29
+ * Reset or set pwsh availability cache (useful for testing and mocking)
30
+ */
31
+ export function setPwshAvailableCache(val) {
32
+ cachedPwshAvailable = val;
33
+ }
34
+ /**
35
+ * Select preferred PowerShell executable: pwsh.exe if available and preferred, else powershell.exe
36
+ */
37
+ export function getPowerShellExecutable(preferPwsh = true) {
38
+ if (preferPwsh && isPwshAvailable()) {
39
+ return "pwsh.exe";
40
+ }
41
+ return "powershell.exe";
42
+ }
43
+ /**
44
+ * Execute a PowerShell command with UTF-8 encoding, security guard, and timeout protection
45
+ */
46
+ export function runPowerShell(command, options = {}) {
47
+ const configManager = ConfigManager.getInstance();
48
+ const config = configManager.getConfig();
49
+ // 1. Security Check
50
+ const check = configManager.isCommandAllowed(command);
51
+ if (!check.allowed) {
52
+ const reason = check.reason || "Command blocked by security policy";
53
+ logger.security(reason, command);
54
+ return Promise.reject(new Error(reason));
55
+ }
56
+ // 2. Validate CWD against allowedDirectories
57
+ let cwd;
58
+ if (options.cwd) {
59
+ const cwdCheck = configManager.isPathAllowed(options.cwd);
60
+ if (!cwdCheck.allowed && config.allowSystemExecution === false) {
61
+ const reason = cwdCheck.reason || `Working directory "${options.cwd}" is not permitted`;
62
+ logger.security(reason, command);
63
+ return Promise.reject(new Error(reason));
64
+ }
65
+ cwd = options.cwd;
66
+ }
67
+ else {
68
+ const currentCwd = process.cwd();
69
+ const currentCheck = configManager.isPathAllowed(currentCwd);
70
+ if (currentCheck.allowed) {
71
+ cwd = currentCwd;
72
+ }
73
+ else {
74
+ const allowed = config.allowedDirectories;
75
+ if (allowed && allowed.length > 0) {
76
+ let first = allowed[0].trim();
77
+ if (/^[a-zA-Z]:?[\\/]?$/.test(first)) {
78
+ first = first[0].toUpperCase() + ":\\";
79
+ }
80
+ cwd = path.resolve(first);
81
+ }
82
+ else {
83
+ cwd = currentCwd;
84
+ }
85
+ }
86
+ }
87
+ const timeoutMs = options.timeoutMs || config.defaultTimeoutMs || 60000;
88
+ const startTime = Date.now();
89
+ const executionId = randomUUID();
90
+ return new Promise((resolve, reject) => {
91
+ // UTF-8 setup preamble for Windows PowerShell to prevent garbled Unicode / Thai characters
92
+ const utf8Setup = "[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(); " +
93
+ "[Console]::InputEncoding = [System.Text.UTF8Encoding]::new(); " +
94
+ "$OutputEncoding = [System.Text.UTF8Encoding]::new(); ";
95
+ const fullCommand = utf8Setup + command;
96
+ const preferPwsh = config.preferPwsh !== false;
97
+ const executable = getPowerShellExecutable(preferPwsh);
98
+ const child = spawn(executable, ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", fullCommand], {
99
+ cwd,
100
+ windowsHide: true,
101
+ env: { ...process.env, PYTHONIOENCODING: "utf-8" },
102
+ });
103
+ let stdout = "";
104
+ let stderr = "";
105
+ let timedOut = false;
106
+ const timer = setTimeout(() => {
107
+ timedOut = true;
108
+ try {
109
+ child.kill("SIGKILL");
110
+ }
111
+ catch {
112
+ // Process might already be dead
113
+ }
114
+ }, timeoutMs);
115
+ child.stdout.on("data", (chunk) => {
116
+ stdout += chunk.toString("utf-8");
117
+ });
118
+ child.stderr.on("data", (chunk) => {
119
+ stderr += chunk.toString("utf-8");
120
+ });
121
+ child.on("close", (code) => {
122
+ clearTimeout(timer);
123
+ const exitCode = code ?? (timedOut ? -1 : 0);
124
+ const durationMs = Date.now() - startTime;
125
+ // Log execution for auditing
126
+ configManager.logExecution({
127
+ id: executionId,
128
+ timestamp: new Date().toISOString(),
129
+ command,
130
+ cwd,
131
+ exitCode,
132
+ durationMs,
133
+ timedOut,
134
+ });
135
+ resolve({
136
+ stdout: stdout.trim(),
137
+ stderr: stderr.trim(),
138
+ exitCode,
139
+ timedOut,
140
+ });
141
+ });
142
+ child.on("error", (err) => {
143
+ clearTimeout(timer);
144
+ reject(err);
145
+ });
146
+ });
147
+ }
148
+ //# sourceMappingURL=powershell-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"powershell-runner.js","sourceRoot":"","sources":["../../src/engine/powershell-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { RipgrepSearchResult } from "../types/index.js";
2
+ /**
3
+ * Locate ripgrep executable (rg.exe) with multi-path resolution
4
+ */
5
+ export declare function findRipgrepPath(): string | null;
6
+ export interface RipgrepOptions {
7
+ query: string;
8
+ searchPath?: string;
9
+ filePattern?: string;
10
+ caseSensitive?: boolean;
11
+ isRegex?: boolean;
12
+ page?: number;
13
+ pageSize?: number;
14
+ contextLines?: number;
15
+ }
16
+ /**
17
+ * Execute paginated high-speed codebase search using ripgrep with fallback
18
+ */
19
+ export declare function searchCodebase(options: RipgrepOptions): Promise<RipgrepSearchResult>;
20
+ //# sourceMappingURL=ripgrep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ripgrep.d.ts","sourceRoot":"","sources":["../../src/engine/ripgrep.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAM3E;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAoC/C;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,mBAAmB,CAAC,CA4I9B"}