windows-exe-decompiler-mcp-server 0.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 (190) hide show
  1. package/CODEX_INSTALLATION.md +69 -0
  2. package/COPILOT_INSTALLATION.md +77 -0
  3. package/LICENSE +21 -0
  4. package/README.md +314 -0
  5. package/bin/windows-exe-decompiler-mcp-server.js +3 -0
  6. package/dist/analysis-provenance.d.ts +184 -0
  7. package/dist/analysis-provenance.js +74 -0
  8. package/dist/analysis-task-runner.d.ts +31 -0
  9. package/dist/analysis-task-runner.js +160 -0
  10. package/dist/artifact-inventory.d.ts +23 -0
  11. package/dist/artifact-inventory.js +175 -0
  12. package/dist/cache-manager.d.ts +128 -0
  13. package/dist/cache-manager.js +454 -0
  14. package/dist/confidence-semantics.d.ts +66 -0
  15. package/dist/confidence-semantics.js +122 -0
  16. package/dist/config.d.ts +335 -0
  17. package/dist/config.js +193 -0
  18. package/dist/database.d.ts +227 -0
  19. package/dist/database.js +601 -0
  20. package/dist/decompiler-worker.d.ts +441 -0
  21. package/dist/decompiler-worker.js +1962 -0
  22. package/dist/dynamic-trace.d.ts +95 -0
  23. package/dist/dynamic-trace.js +629 -0
  24. package/dist/env-validator.d.ts +15 -0
  25. package/dist/env-validator.js +249 -0
  26. package/dist/error-handler.d.ts +28 -0
  27. package/dist/error-handler.example.d.ts +22 -0
  28. package/dist/error-handler.example.js +141 -0
  29. package/dist/error-handler.js +139 -0
  30. package/dist/ghidra-analysis-status.d.ts +49 -0
  31. package/dist/ghidra-analysis-status.js +178 -0
  32. package/dist/ghidra-config.d.ts +134 -0
  33. package/dist/ghidra-config.js +464 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.js +200 -0
  36. package/dist/job-queue.d.ts +169 -0
  37. package/dist/job-queue.js +407 -0
  38. package/dist/logger.d.ts +106 -0
  39. package/dist/logger.js +176 -0
  40. package/dist/policy-guard.d.ts +115 -0
  41. package/dist/policy-guard.js +243 -0
  42. package/dist/process-output.d.ts +15 -0
  43. package/dist/process-output.js +90 -0
  44. package/dist/prompts/function-explanation-review.d.ts +5 -0
  45. package/dist/prompts/function-explanation-review.js +64 -0
  46. package/dist/prompts/semantic-name-review.d.ts +5 -0
  47. package/dist/prompts/semantic-name-review.js +63 -0
  48. package/dist/runtime-correlation.d.ts +34 -0
  49. package/dist/runtime-correlation.js +279 -0
  50. package/dist/runtime-paths.d.ts +3 -0
  51. package/dist/runtime-paths.js +11 -0
  52. package/dist/selection-diff.d.ts +667 -0
  53. package/dist/selection-diff.js +53 -0
  54. package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
  55. package/dist/semantic-name-suggestion-artifacts.js +314 -0
  56. package/dist/server.d.ts +129 -0
  57. package/dist/server.js +578 -0
  58. package/dist/tools/artifact-read.d.ts +235 -0
  59. package/dist/tools/artifact-read.js +317 -0
  60. package/dist/tools/artifacts-diff.d.ts +728 -0
  61. package/dist/tools/artifacts-diff.js +304 -0
  62. package/dist/tools/artifacts-list.d.ts +515 -0
  63. package/dist/tools/artifacts-list.js +389 -0
  64. package/dist/tools/attack-map.d.ts +290 -0
  65. package/dist/tools/attack-map.js +519 -0
  66. package/dist/tools/cache-observability.d.ts +4 -0
  67. package/dist/tools/cache-observability.js +36 -0
  68. package/dist/tools/code-function-cfg.d.ts +50 -0
  69. package/dist/tools/code-function-cfg.js +102 -0
  70. package/dist/tools/code-function-decompile.d.ts +55 -0
  71. package/dist/tools/code-function-decompile.js +103 -0
  72. package/dist/tools/code-function-disassemble.d.ts +43 -0
  73. package/dist/tools/code-function-disassemble.js +185 -0
  74. package/dist/tools/code-function-explain-apply.d.ts +255 -0
  75. package/dist/tools/code-function-explain-apply.js +225 -0
  76. package/dist/tools/code-function-explain-prepare.d.ts +535 -0
  77. package/dist/tools/code-function-explain-prepare.js +276 -0
  78. package/dist/tools/code-function-explain-review.d.ts +397 -0
  79. package/dist/tools/code-function-explain-review.js +589 -0
  80. package/dist/tools/code-function-rename-apply.d.ts +248 -0
  81. package/dist/tools/code-function-rename-apply.js +220 -0
  82. package/dist/tools/code-function-rename-prepare.d.ts +506 -0
  83. package/dist/tools/code-function-rename-prepare.js +279 -0
  84. package/dist/tools/code-function-rename-review.d.ts +574 -0
  85. package/dist/tools/code-function-rename-review.js +761 -0
  86. package/dist/tools/code-functions-list.d.ts +37 -0
  87. package/dist/tools/code-functions-list.js +91 -0
  88. package/dist/tools/code-functions-rank.d.ts +34 -0
  89. package/dist/tools/code-functions-rank.js +90 -0
  90. package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
  91. package/dist/tools/code-functions-reconstruct.js +2807 -0
  92. package/dist/tools/code-functions-search.d.ts +39 -0
  93. package/dist/tools/code-functions-search.js +90 -0
  94. package/dist/tools/code-reconstruct-export.d.ts +1212 -0
  95. package/dist/tools/code-reconstruct-export.js +4002 -0
  96. package/dist/tools/code-reconstruct-plan.d.ts +274 -0
  97. package/dist/tools/code-reconstruct-plan.js +342 -0
  98. package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
  99. package/dist/tools/dotnet-metadata-extract.js +355 -0
  100. package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
  101. package/dist/tools/dotnet-reconstruct-export.js +1151 -0
  102. package/dist/tools/dotnet-types-list.d.ts +325 -0
  103. package/dist/tools/dotnet-types-list.js +201 -0
  104. package/dist/tools/dynamic-dependencies.d.ts +115 -0
  105. package/dist/tools/dynamic-dependencies.js +213 -0
  106. package/dist/tools/dynamic-memory-import.d.ts +10 -0
  107. package/dist/tools/dynamic-memory-import.js +567 -0
  108. package/dist/tools/dynamic-trace-import.d.ts +10 -0
  109. package/dist/tools/dynamic-trace-import.js +235 -0
  110. package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
  111. package/dist/tools/entrypoint-fallback-disasm.js +89 -0
  112. package/dist/tools/ghidra-analyze.d.ts +88 -0
  113. package/dist/tools/ghidra-analyze.js +208 -0
  114. package/dist/tools/ghidra-health.d.ts +37 -0
  115. package/dist/tools/ghidra-health.js +212 -0
  116. package/dist/tools/ioc-export.d.ts +209 -0
  117. package/dist/tools/ioc-export.js +542 -0
  118. package/dist/tools/packer-detect.d.ts +165 -0
  119. package/dist/tools/packer-detect.js +284 -0
  120. package/dist/tools/pe-exports-extract.d.ts +175 -0
  121. package/dist/tools/pe-exports-extract.js +253 -0
  122. package/dist/tools/pe-fingerprint.d.ts +234 -0
  123. package/dist/tools/pe-fingerprint.js +269 -0
  124. package/dist/tools/pe-imports-extract.d.ts +105 -0
  125. package/dist/tools/pe-imports-extract.js +245 -0
  126. package/dist/tools/report-generate.d.ts +157 -0
  127. package/dist/tools/report-generate.js +457 -0
  128. package/dist/tools/report-summarize.d.ts +2131 -0
  129. package/dist/tools/report-summarize.js +596 -0
  130. package/dist/tools/runtime-detect.d.ts +135 -0
  131. package/dist/tools/runtime-detect.js +247 -0
  132. package/dist/tools/sample-ingest.d.ts +94 -0
  133. package/dist/tools/sample-ingest.js +327 -0
  134. package/dist/tools/sample-profile-get.d.ts +183 -0
  135. package/dist/tools/sample-profile-get.js +121 -0
  136. package/dist/tools/sandbox-execute.d.ts +441 -0
  137. package/dist/tools/sandbox-execute.js +392 -0
  138. package/dist/tools/strings-extract.d.ts +375 -0
  139. package/dist/tools/strings-extract.js +314 -0
  140. package/dist/tools/strings-floss-decode.d.ts +143 -0
  141. package/dist/tools/strings-floss-decode.js +259 -0
  142. package/dist/tools/system-health.d.ts +434 -0
  143. package/dist/tools/system-health.js +446 -0
  144. package/dist/tools/task-cancel.d.ts +21 -0
  145. package/dist/tools/task-cancel.js +70 -0
  146. package/dist/tools/task-status.d.ts +27 -0
  147. package/dist/tools/task-status.js +106 -0
  148. package/dist/tools/task-sweep.d.ts +22 -0
  149. package/dist/tools/task-sweep.js +77 -0
  150. package/dist/tools/tool-help.d.ts +340 -0
  151. package/dist/tools/tool-help.js +261 -0
  152. package/dist/tools/yara-scan.d.ts +554 -0
  153. package/dist/tools/yara-scan.js +313 -0
  154. package/dist/types.d.ts +266 -0
  155. package/dist/types.js +41 -0
  156. package/dist/worker-pool.d.ts +204 -0
  157. package/dist/worker-pool.js +650 -0
  158. package/dist/workflows/deep-static.d.ts +104 -0
  159. package/dist/workflows/deep-static.js +276 -0
  160. package/dist/workflows/function-explanation-review.d.ts +655 -0
  161. package/dist/workflows/function-explanation-review.js +440 -0
  162. package/dist/workflows/reconstruct.d.ts +2053 -0
  163. package/dist/workflows/reconstruct.js +666 -0
  164. package/dist/workflows/semantic-name-review.d.ts +2418 -0
  165. package/dist/workflows/semantic-name-review.js +521 -0
  166. package/dist/workflows/triage.d.ts +659 -0
  167. package/dist/workflows/triage.js +1374 -0
  168. package/dist/workspace-manager.d.ts +150 -0
  169. package/dist/workspace-manager.js +411 -0
  170. package/ghidra_scripts/DecompileFunction.java +487 -0
  171. package/ghidra_scripts/DecompileFunction.py +150 -0
  172. package/ghidra_scripts/ExtractCFG.java +256 -0
  173. package/ghidra_scripts/ExtractCFG.py +233 -0
  174. package/ghidra_scripts/ExtractFunctions.java +442 -0
  175. package/ghidra_scripts/ExtractFunctions.py +101 -0
  176. package/ghidra_scripts/README.md +125 -0
  177. package/ghidra_scripts/SearchFunctionReferences.java +380 -0
  178. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
  179. package/helpers/DotNetMetadataProbe/Program.cs +566 -0
  180. package/install-to-codex.ps1 +178 -0
  181. package/install-to-copilot.ps1 +303 -0
  182. package/package.json +101 -0
  183. package/requirements.txt +9 -0
  184. package/workers/requirements-dynamic.txt +11 -0
  185. package/workers/requirements.txt +8 -0
  186. package/workers/speakeasy_compat.py +175 -0
  187. package/workers/static_worker.py +5183 -0
  188. package/workers/yara_rules/default.yar +33 -0
  189. package/workers/yara_rules/malware_families.yar +93 -0
  190. package/workers/yara_rules/packers.yar +80 -0
@@ -0,0 +1,327 @@
1
+ /**
2
+ * sample.ingest tool implementation
3
+ * Uploads and registers new samples to the system
4
+ * Requirements: 1.1, 1.2, 1.3, 1.4, 1.5
5
+ */
6
+ import { z } from 'zod';
7
+ import fs from 'fs';
8
+ import crypto from 'crypto';
9
+ import { withLogging, logError, logWarning } from '../logger.js';
10
+ // ============================================================================
11
+ // Constants
12
+ // ============================================================================
13
+ const MAX_SAMPLE_SIZE = 500 * 1024 * 1024; // 500MB
14
+ // ============================================================================
15
+ // Input/Output Schemas
16
+ // ============================================================================
17
+ /**
18
+ * Input schema for sample.ingest tool
19
+ * Requirements: 1.1
20
+ */
21
+ export const SampleIngestInputSchema = z
22
+ .object({
23
+ path: z
24
+ .string()
25
+ .trim()
26
+ .min(1)
27
+ .optional()
28
+ .describe('Preferred for local files. Pass an absolute local file path when the MCP client can access the file system.'),
29
+ bytes_b64: z
30
+ .string()
31
+ .trim()
32
+ .min(1)
33
+ .optional()
34
+ .describe('Fallback only. Use Base64 file bytes when the MCP client cannot access the local file path. Ignored when `path` is provided.'),
35
+ filename: z.string().optional().describe('Optional display/original filename'),
36
+ source: z.string().optional().describe('Optional source tag, e.g. upload/email/sandbox'),
37
+ })
38
+ .superRefine((value, ctx) => {
39
+ const hasPath = typeof value.path === 'string' && value.path.length > 0;
40
+ const hasBytes = typeof value.bytes_b64 === 'string' && value.bytes_b64.length > 0;
41
+ if (!hasPath && !hasBytes) {
42
+ ctx.addIssue({
43
+ code: z.ZodIssueCode.custom,
44
+ path: ['path'],
45
+ message: 'Provide either `path` (preferred for local files) or `bytes_b64` (fallback when the client cannot read the file path).',
46
+ });
47
+ }
48
+ })
49
+ .describe('Ingest a sample from a local file path or Base64 bytes. Prefer `path` whenever the MCP client can access the file directly.');
50
+ /**
51
+ * Output schema for sample.ingest tool
52
+ * Requirements: 1.5
53
+ */
54
+ export const SampleIngestOutputSchema = z.object({
55
+ ok: z.boolean(),
56
+ data: z.object({
57
+ sample_id: z.string(),
58
+ size: z.number(),
59
+ file_type: z.string().optional(),
60
+ existed: z.boolean().optional(),
61
+ }).optional(),
62
+ errors: z.array(z.string()).optional(),
63
+ });
64
+ // ============================================================================
65
+ // Tool Definition
66
+ // ============================================================================
67
+ /**
68
+ * Tool definition for sample.ingest
69
+ */
70
+ export const sampleIngestToolDefinition = {
71
+ name: 'sample.ingest',
72
+ description: 'Register a new sample from a local file path or Base64 bytes. Prefer `path` for local files; use `bytes_b64` only when the MCP client cannot read local disk.',
73
+ inputSchema: SampleIngestInputSchema,
74
+ outputSchema: SampleIngestOutputSchema,
75
+ };
76
+ // ============================================================================
77
+ // Helper Functions
78
+ // ============================================================================
79
+ /**
80
+ * Compute SHA256 hash of data
81
+ * Requirement: 1.1
82
+ */
83
+ function computeSHA256(data) {
84
+ return crypto.createHash('sha256').update(data).digest('hex');
85
+ }
86
+ /**
87
+ * Compute MD5 hash of data
88
+ * Requirement: 1.1
89
+ */
90
+ function computeMD5(data) {
91
+ return crypto.createHash('md5').update(data).digest('hex');
92
+ }
93
+ /**
94
+ * Detect file type from data
95
+ * Basic implementation - can be enhanced with magic number detection
96
+ */
97
+ function detectFileType(data) {
98
+ // Check for PE signature (MZ header)
99
+ if (data.length >= 2 && data[0] === 0x4D && data[1] === 0x5A) {
100
+ return 'PE';
101
+ }
102
+ // Check for ELF signature
103
+ if (data.length >= 4 &&
104
+ data[0] === 0x7F &&
105
+ data[1] === 0x45 &&
106
+ data[2] === 0x4C &&
107
+ data[3] === 0x46) {
108
+ return 'ELF';
109
+ }
110
+ return 'unknown';
111
+ }
112
+ // ============================================================================
113
+ // Tool Handler
114
+ // ============================================================================
115
+ /**
116
+ * Create sample.ingest tool handler
117
+ * Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6
118
+ */
119
+ export function createSampleIngestHandler(workspaceManager, database, policyGuard) {
120
+ return async (args) => {
121
+ const input = args;
122
+ return withLogging({
123
+ operation: 'sample.ingest',
124
+ toolName: 'sample.ingest',
125
+ source: input.source,
126
+ }, async () => {
127
+ try {
128
+ // 1. Read sample data
129
+ let data;
130
+ let originalFilename;
131
+ if (input.path) {
132
+ // Read from file path
133
+ if (!fs.existsSync(input.path)) {
134
+ logWarning('File not found', { path: input.path });
135
+ return {
136
+ ok: false,
137
+ errors: [`File not found: ${input.path}`],
138
+ };
139
+ }
140
+ data = fs.readFileSync(input.path);
141
+ // Extract just the filename, not the full path
142
+ const pathParts = input.path.replace(/\\/g, '/').split('/');
143
+ originalFilename = input.filename || pathParts[pathParts.length - 1] || 'sample.bin';
144
+ }
145
+ else if (input.bytes_b64) {
146
+ // Decode from Base64
147
+ // Validate Base64 format first
148
+ const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
149
+ if (!base64Regex.test(input.bytes_b64)) {
150
+ logWarning('Invalid Base64 encoding', { length: input.bytes_b64.length });
151
+ return {
152
+ ok: false,
153
+ errors: ['Invalid Base64 encoding: contains invalid characters'],
154
+ };
155
+ }
156
+ try {
157
+ data = Buffer.from(input.bytes_b64, 'base64');
158
+ // Verify the decoded data is not empty and makes sense
159
+ if (data.length === 0 && input.bytes_b64.length > 0) {
160
+ throw new Error('Base64 decoding resulted in empty buffer');
161
+ }
162
+ }
163
+ catch (error) {
164
+ logError(error, { operation: 'base64_decode' });
165
+ return {
166
+ ok: false,
167
+ errors: [`Invalid Base64 encoding: ${error.message}`],
168
+ };
169
+ }
170
+ originalFilename = input.filename || 'sample.bin';
171
+ }
172
+ else {
173
+ return {
174
+ ok: false,
175
+ errors: [
176
+ 'Missing input: provide `path` (preferred local file path) or `bytes_b64` (Base64 bytes fallback when the client cannot access the file path).',
177
+ ],
178
+ };
179
+ }
180
+ // 2. Check file size limit
181
+ // Requirement: 1.3
182
+ if (data.length > MAX_SAMPLE_SIZE) {
183
+ logWarning('Sample size exceeds limit', {
184
+ size: data.length,
185
+ maxSize: MAX_SAMPLE_SIZE,
186
+ });
187
+ return {
188
+ ok: false,
189
+ errors: [
190
+ `Sample size ${data.length} bytes exceeds maximum limit of ${MAX_SAMPLE_SIZE} bytes (500MB)`
191
+ ],
192
+ };
193
+ }
194
+ // 3. Compute hashes
195
+ // Requirement: 1.1
196
+ const sha256 = computeSHA256(data);
197
+ const md5 = computeMD5(data);
198
+ const sampleId = `sha256:${sha256}`;
199
+ // 4. Check if sample already exists
200
+ // Requirement: 1.2
201
+ const existingSample = database.findSampleBySha256(sha256);
202
+ if (existingSample) {
203
+ // Sample already exists, return existing sample_id
204
+ await policyGuard.auditLog({
205
+ timestamp: new Date().toISOString(),
206
+ operation: 'sample.ingest',
207
+ sampleId: existingSample.id,
208
+ decision: 'allow',
209
+ reason: 'Sample already exists (SHA256 match)',
210
+ metadata: {
211
+ size: data.length,
212
+ source: input.source || 'upload',
213
+ existed: true,
214
+ },
215
+ });
216
+ return {
217
+ ok: true,
218
+ data: {
219
+ sample_id: existingSample.id,
220
+ size: existingSample.size,
221
+ file_type: existingSample.file_type || undefined,
222
+ existed: true,
223
+ },
224
+ };
225
+ }
226
+ // 5. Create workspace
227
+ // Requirement: 1.4
228
+ const workspace = await workspaceManager.createWorkspace(sampleId);
229
+ // 6. Store sample file
230
+ // Requirement: 1.4
231
+ const samplePath = `${workspace.original}/${originalFilename}`;
232
+ fs.writeFileSync(samplePath, data);
233
+ // Mark file as non-executable (security measure)
234
+ // Requirement: 29.3
235
+ try {
236
+ fs.chmodSync(samplePath, 0o444); // Read-only
237
+ }
238
+ catch (error) {
239
+ // Ignore chmod errors on Windows
240
+ logWarning('Failed to set file permissions', {
241
+ path: samplePath,
242
+ error: error.message,
243
+ });
244
+ }
245
+ // 7. Detect file type
246
+ const fileType = detectFileType(data);
247
+ // 8. Insert into database
248
+ // Requirement: 1.5
249
+ const sample = {
250
+ id: sampleId,
251
+ sha256,
252
+ md5,
253
+ size: data.length,
254
+ file_type: fileType,
255
+ created_at: new Date().toISOString(),
256
+ source: input.source || 'upload',
257
+ };
258
+ try {
259
+ database.insertSample(sample);
260
+ }
261
+ catch (error) {
262
+ // Handle race condition: if another concurrent request already inserted this sample
263
+ if (error.code === 'SQLITE_CONSTRAINT_UNIQUE' || error.message?.includes('UNIQUE constraint')) {
264
+ // Sample was inserted by another concurrent request
265
+ // Query it again and return
266
+ const existingSample = database.findSampleBySha256(sha256);
267
+ if (existingSample) {
268
+ await policyGuard.auditLog({
269
+ timestamp: new Date().toISOString(),
270
+ operation: 'sample.ingest',
271
+ sampleId: existingSample.id,
272
+ decision: 'allow',
273
+ reason: 'Sample already exists (concurrent insert race condition)',
274
+ metadata: {
275
+ size: data.length,
276
+ source: input.source || 'upload',
277
+ existed: true,
278
+ },
279
+ });
280
+ return {
281
+ ok: true,
282
+ data: {
283
+ sample_id: existingSample.id,
284
+ size: existingSample.size,
285
+ file_type: existingSample.file_type || undefined,
286
+ existed: true,
287
+ },
288
+ };
289
+ }
290
+ }
291
+ // Re-throw if it's a different error
292
+ throw error;
293
+ }
294
+ // 9. Audit log
295
+ // Requirement: 1.6
296
+ await policyGuard.auditLog({
297
+ timestamp: new Date().toISOString(),
298
+ operation: 'sample.ingest',
299
+ sampleId: sample.id,
300
+ decision: 'allow',
301
+ metadata: {
302
+ size: data.length,
303
+ source: sample.source,
304
+ file_type: fileType,
305
+ },
306
+ });
307
+ // 10. Return result
308
+ return {
309
+ ok: true,
310
+ data: {
311
+ sample_id: sampleId,
312
+ size: data.length,
313
+ file_type: fileType,
314
+ },
315
+ };
316
+ }
317
+ catch (error) {
318
+ logError(error, { operation: 'sample.ingest' });
319
+ return {
320
+ ok: false,
321
+ errors: [error.message],
322
+ };
323
+ }
324
+ });
325
+ };
326
+ }
327
+ //# sourceMappingURL=sample-ingest.js.map
@@ -0,0 +1,183 @@
1
+ /**
2
+ * sample.profile.get tool implementation
3
+ * Retrieves sample profile including basic information and completed analyses
4
+ * Requirements: Data Model
5
+ */
6
+ import { z } from 'zod';
7
+ import type { ToolDefinition, ToolArgs, WorkerResult } from '../types.js';
8
+ import type { DatabaseManager } from '../database.js';
9
+ /**
10
+ * Input schema for sample.profile.get tool
11
+ */
12
+ export declare const SampleProfileGetInputSchema: z.ZodObject<{
13
+ sample_id: z.ZodString;
14
+ stale_running_ms: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ sample_id: string;
17
+ stale_running_ms?: number | null | undefined;
18
+ }, {
19
+ sample_id: string;
20
+ stale_running_ms?: number | null | undefined;
21
+ }>;
22
+ export type SampleProfileGetInput = z.infer<typeof SampleProfileGetInputSchema>;
23
+ /**
24
+ * Output schema for sample.profile.get tool
25
+ */
26
+ export declare const SampleProfileGetOutputSchema: z.ZodObject<{
27
+ ok: z.ZodBoolean;
28
+ data: z.ZodOptional<z.ZodObject<{
29
+ sample: z.ZodObject<{
30
+ id: z.ZodString;
31
+ sha256: z.ZodString;
32
+ md5: z.ZodString;
33
+ size: z.ZodNumber;
34
+ file_type: z.ZodOptional<z.ZodString>;
35
+ created_at: z.ZodString;
36
+ source: z.ZodString;
37
+ }, "strip", z.ZodTypeAny, {
38
+ id: string;
39
+ size: number;
40
+ sha256: string;
41
+ created_at: string;
42
+ source: string;
43
+ md5: string;
44
+ file_type?: string | undefined;
45
+ }, {
46
+ id: string;
47
+ size: number;
48
+ sha256: string;
49
+ created_at: string;
50
+ source: string;
51
+ md5: string;
52
+ file_type?: string | undefined;
53
+ }>;
54
+ analyses: z.ZodArray<z.ZodObject<{
55
+ id: z.ZodString;
56
+ stage: z.ZodString;
57
+ backend: z.ZodString;
58
+ status: z.ZodString;
59
+ started_at: z.ZodOptional<z.ZodString>;
60
+ finished_at: z.ZodOptional<z.ZodString>;
61
+ output_json: z.ZodOptional<z.ZodString>;
62
+ metrics_json: z.ZodOptional<z.ZodString>;
63
+ }, "strip", z.ZodTypeAny, {
64
+ status: string;
65
+ id: string;
66
+ stage: string;
67
+ backend: string;
68
+ started_at?: string | undefined;
69
+ finished_at?: string | undefined;
70
+ output_json?: string | undefined;
71
+ metrics_json?: string | undefined;
72
+ }, {
73
+ status: string;
74
+ id: string;
75
+ stage: string;
76
+ backend: string;
77
+ started_at?: string | undefined;
78
+ finished_at?: string | undefined;
79
+ output_json?: string | undefined;
80
+ metrics_json?: string | undefined;
81
+ }>, "many">;
82
+ }, "strip", z.ZodTypeAny, {
83
+ sample: {
84
+ id: string;
85
+ size: number;
86
+ sha256: string;
87
+ created_at: string;
88
+ source: string;
89
+ md5: string;
90
+ file_type?: string | undefined;
91
+ };
92
+ analyses: {
93
+ status: string;
94
+ id: string;
95
+ stage: string;
96
+ backend: string;
97
+ started_at?: string | undefined;
98
+ finished_at?: string | undefined;
99
+ output_json?: string | undefined;
100
+ metrics_json?: string | undefined;
101
+ }[];
102
+ }, {
103
+ sample: {
104
+ id: string;
105
+ size: number;
106
+ sha256: string;
107
+ created_at: string;
108
+ source: string;
109
+ md5: string;
110
+ file_type?: string | undefined;
111
+ };
112
+ analyses: {
113
+ status: string;
114
+ id: string;
115
+ stage: string;
116
+ backend: string;
117
+ started_at?: string | undefined;
118
+ finished_at?: string | undefined;
119
+ output_json?: string | undefined;
120
+ metrics_json?: string | undefined;
121
+ }[];
122
+ }>>;
123
+ errors: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
124
+ }, "strip", z.ZodTypeAny, {
125
+ ok: boolean;
126
+ data?: {
127
+ sample: {
128
+ id: string;
129
+ size: number;
130
+ sha256: string;
131
+ created_at: string;
132
+ source: string;
133
+ md5: string;
134
+ file_type?: string | undefined;
135
+ };
136
+ analyses: {
137
+ status: string;
138
+ id: string;
139
+ stage: string;
140
+ backend: string;
141
+ started_at?: string | undefined;
142
+ finished_at?: string | undefined;
143
+ output_json?: string | undefined;
144
+ metrics_json?: string | undefined;
145
+ }[];
146
+ } | undefined;
147
+ errors?: string[] | undefined;
148
+ }, {
149
+ ok: boolean;
150
+ data?: {
151
+ sample: {
152
+ id: string;
153
+ size: number;
154
+ sha256: string;
155
+ created_at: string;
156
+ source: string;
157
+ md5: string;
158
+ file_type?: string | undefined;
159
+ };
160
+ analyses: {
161
+ status: string;
162
+ id: string;
163
+ stage: string;
164
+ backend: string;
165
+ started_at?: string | undefined;
166
+ finished_at?: string | undefined;
167
+ output_json?: string | undefined;
168
+ metrics_json?: string | undefined;
169
+ }[];
170
+ } | undefined;
171
+ errors?: string[] | undefined;
172
+ }>;
173
+ export type SampleProfileGetOutput = z.infer<typeof SampleProfileGetOutputSchema>;
174
+ /**
175
+ * Tool definition for sample.profile.get
176
+ */
177
+ export declare const sampleProfileGetToolDefinition: ToolDefinition;
178
+ /**
179
+ * Create sample.profile.get tool handler
180
+ * Requirements: Data Model
181
+ */
182
+ export declare function createSampleProfileGetHandler(database: DatabaseManager): (args: ToolArgs) => Promise<WorkerResult>;
183
+ //# sourceMappingURL=sample-profile-get.d.ts.map
@@ -0,0 +1,121 @@
1
+ /**
2
+ * sample.profile.get tool implementation
3
+ * Retrieves sample profile including basic information and completed analyses
4
+ * Requirements: Data Model
5
+ */
6
+ import { z } from 'zod';
7
+ // ============================================================================
8
+ // Input/Output Schemas
9
+ // ============================================================================
10
+ /**
11
+ * Input schema for sample.profile.get tool
12
+ */
13
+ export const SampleProfileGetInputSchema = z.object({
14
+ sample_id: z.string().describe('Sample ID (format: sha256:<hex>)'),
15
+ stale_running_ms: z
16
+ .number()
17
+ .int()
18
+ .min(1000)
19
+ .nullable()
20
+ .optional()
21
+ .describe('Optional stale-analysis reap threshold in milliseconds. Omit or null to disable auto-reaping.'),
22
+ });
23
+ /**
24
+ * Output schema for sample.profile.get tool
25
+ */
26
+ export const SampleProfileGetOutputSchema = z.object({
27
+ ok: z.boolean(),
28
+ data: z.object({
29
+ sample: z.object({
30
+ id: z.string(),
31
+ sha256: z.string(),
32
+ md5: z.string(),
33
+ size: z.number(),
34
+ file_type: z.string().optional(),
35
+ created_at: z.string(),
36
+ source: z.string(),
37
+ }),
38
+ analyses: z.array(z.object({
39
+ id: z.string(),
40
+ stage: z.string(),
41
+ backend: z.string(),
42
+ status: z.string(),
43
+ started_at: z.string().optional(),
44
+ finished_at: z.string().optional(),
45
+ output_json: z.string().optional(),
46
+ metrics_json: z.string().optional(),
47
+ })),
48
+ }).optional(),
49
+ errors: z.array(z.string()).optional(),
50
+ });
51
+ // ============================================================================
52
+ // Tool Definition
53
+ // ============================================================================
54
+ /**
55
+ * Tool definition for sample.profile.get
56
+ */
57
+ export const sampleProfileGetToolDefinition = {
58
+ name: 'sample.profile.get',
59
+ description: '查询样本基础信息和已完成的分析',
60
+ inputSchema: SampleProfileGetInputSchema,
61
+ outputSchema: SampleProfileGetOutputSchema,
62
+ };
63
+ // ============================================================================
64
+ // Tool Handler
65
+ // ============================================================================
66
+ /**
67
+ * Create sample.profile.get tool handler
68
+ * Requirements: Data Model
69
+ */
70
+ export function createSampleProfileGetHandler(database) {
71
+ return async (args) => {
72
+ try {
73
+ const input = SampleProfileGetInputSchema.parse(args);
74
+ // 1. Query sample from database
75
+ const sample = database.findSample(input.sample_id);
76
+ if (!sample) {
77
+ return {
78
+ ok: false,
79
+ errors: [`Sample not found: ${input.sample_id}`],
80
+ };
81
+ }
82
+ // 2. Query analyses for this sample
83
+ if (typeof input.stale_running_ms === 'number') {
84
+ database.reapStaleAnalyses(input.stale_running_ms, input.sample_id);
85
+ }
86
+ const analyses = database.findAnalysesBySample(input.sample_id);
87
+ // 3. Return profile data
88
+ return {
89
+ ok: true,
90
+ data: {
91
+ sample: {
92
+ id: sample.id,
93
+ sha256: sample.sha256,
94
+ md5: sample.md5,
95
+ size: sample.size,
96
+ file_type: sample.file_type || undefined,
97
+ created_at: sample.created_at,
98
+ source: sample.source,
99
+ },
100
+ analyses: analyses.map(analysis => ({
101
+ id: analysis.id,
102
+ stage: analysis.stage,
103
+ backend: analysis.backend,
104
+ status: analysis.status,
105
+ started_at: analysis.started_at || undefined,
106
+ finished_at: analysis.finished_at || undefined,
107
+ output_json: analysis.output_json || undefined,
108
+ metrics_json: analysis.metrics_json || undefined,
109
+ })),
110
+ },
111
+ };
112
+ }
113
+ catch (error) {
114
+ return {
115
+ ok: false,
116
+ errors: [error.message],
117
+ };
118
+ }
119
+ };
120
+ }
121
+ //# sourceMappingURL=sample-profile-get.js.map