stella-coder 3.9.2 → 4.0.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 (235) hide show
  1. package/.mcp.json +11 -0
  2. package/.opencode/skills/banner-design/SKILL.md +196 -0
  3. package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +118 -0
  4. package/.opencode/skills/brand/SKILL.md +97 -0
  5. package/.opencode/skills/brand/references/approval-checklist.md +169 -0
  6. package/.opencode/skills/brand/references/asset-organization.md +157 -0
  7. package/.opencode/skills/brand/references/brand-guideline-template.md +140 -0
  8. package/.opencode/skills/brand/references/color-palette-management.md +186 -0
  9. package/.opencode/skills/brand/references/consistency-checklist.md +94 -0
  10. package/.opencode/skills/brand/references/logo-usage-rules.md +185 -0
  11. package/.opencode/skills/brand/references/messaging-framework.md +85 -0
  12. package/.opencode/skills/brand/references/typography-specifications.md +214 -0
  13. package/.opencode/skills/brand/references/update.md +118 -0
  14. package/.opencode/skills/brand/references/visual-identity.md +96 -0
  15. package/.opencode/skills/brand/references/voice-framework.md +88 -0
  16. package/.opencode/skills/brand/scripts/extract-colors.cjs +341 -0
  17. package/.opencode/skills/brand/scripts/inject-brand-context.cjs +349 -0
  18. package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +248 -0
  19. package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +52 -0
  20. package/.opencode/skills/brand/scripts/validate-asset.cjs +387 -0
  21. package/.opencode/skills/brand/templates/brand-guidelines-starter.md +275 -0
  22. package/.opencode/skills/design/SKILL.md +313 -0
  23. package/.opencode/skills/design/data/cip/deliverables.csv +51 -0
  24. package/.opencode/skills/design/data/cip/industries.csv +21 -0
  25. package/.opencode/skills/design/data/cip/mockup-contexts.csv +21 -0
  26. package/.opencode/skills/design/data/cip/styles.csv +21 -0
  27. package/.opencode/skills/design/data/icon/styles.csv +16 -0
  28. package/.opencode/skills/design/data/logo/colors.csv +56 -0
  29. package/.opencode/skills/design/data/logo/industries.csv +56 -0
  30. package/.opencode/skills/design/data/logo/styles.csv +56 -0
  31. package/.opencode/skills/design/references/banner-sizes-and-styles.md +118 -0
  32. package/.opencode/skills/design/references/cip-deliverable-guide.md +95 -0
  33. package/.opencode/skills/design/references/cip-design.md +121 -0
  34. package/.opencode/skills/design/references/cip-prompt-engineering.md +84 -0
  35. package/.opencode/skills/design/references/cip-style-guide.md +68 -0
  36. package/.opencode/skills/design/references/design-routing.md +207 -0
  37. package/.opencode/skills/design/references/icon-design.md +122 -0
  38. package/.opencode/skills/design/references/logo-color-psychology.md +101 -0
  39. package/.opencode/skills/design/references/logo-design.md +92 -0
  40. package/.opencode/skills/design/references/logo-prompt-engineering.md +158 -0
  41. package/.opencode/skills/design/references/logo-style-guide.md +109 -0
  42. package/.opencode/skills/design/references/slides-copywriting-formulas.md +84 -0
  43. package/.opencode/skills/design/references/slides-create.md +4 -0
  44. package/.opencode/skills/design/references/slides-html-template.md +295 -0
  45. package/.opencode/skills/design/references/slides-layout-patterns.md +137 -0
  46. package/.opencode/skills/design/references/slides-strategies.md +94 -0
  47. package/.opencode/skills/design/references/slides.md +42 -0
  48. package/.opencode/skills/design/references/social-photos-design.md +329 -0
  49. package/.opencode/skills/design/scripts/cip/core.py +215 -0
  50. package/.opencode/skills/design/scripts/cip/generate.py +484 -0
  51. package/.opencode/skills/design/scripts/cip/render-html.py +424 -0
  52. package/.opencode/skills/design/scripts/cip/search.py +127 -0
  53. package/.opencode/skills/design/scripts/icon/generate.py +487 -0
  54. package/.opencode/skills/design/scripts/logo/core.py +175 -0
  55. package/.opencode/skills/design/scripts/logo/generate.py +362 -0
  56. package/.opencode/skills/design/scripts/logo/search.py +114 -0
  57. package/.opencode/skills/design-system/SKILL.md +244 -0
  58. package/.opencode/skills/design-system/data/slide-backgrounds.csv +11 -0
  59. package/.opencode/skills/design-system/data/slide-charts.csv +26 -0
  60. package/.opencode/skills/design-system/data/slide-color-logic.csv +14 -0
  61. package/.opencode/skills/design-system/data/slide-copy.csv +26 -0
  62. package/.opencode/skills/design-system/data/slide-layout-logic.csv +16 -0
  63. package/.opencode/skills/design-system/data/slide-layouts.csv +26 -0
  64. package/.opencode/skills/design-system/data/slide-strategies.csv +16 -0
  65. package/.opencode/skills/design-system/data/slide-typography.csv +15 -0
  66. package/.opencode/skills/design-system/references/component-specs.md +236 -0
  67. package/.opencode/skills/design-system/references/component-tokens.md +214 -0
  68. package/.opencode/skills/design-system/references/primitive-tokens.md +203 -0
  69. package/.opencode/skills/design-system/references/semantic-tokens.md +215 -0
  70. package/.opencode/skills/design-system/references/states-and-variants.md +241 -0
  71. package/.opencode/skills/design-system/references/tailwind-integration.md +251 -0
  72. package/.opencode/skills/design-system/references/token-architecture.md +224 -0
  73. package/.opencode/skills/design-system/scripts/embed-tokens.cjs +99 -0
  74. package/.opencode/skills/design-system/scripts/fetch-background.py +317 -0
  75. package/.opencode/skills/design-system/scripts/generate-slide.py +770 -0
  76. package/.opencode/skills/design-system/scripts/generate-tokens.cjs +205 -0
  77. package/.opencode/skills/design-system/scripts/html-token-validator.py +327 -0
  78. package/.opencode/skills/design-system/scripts/search-slides.py +218 -0
  79. package/.opencode/skills/design-system/scripts/slide-token-validator.py +35 -0
  80. package/.opencode/skills/design-system/scripts/slide_search_core.py +453 -0
  81. package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +48 -0
  82. package/.opencode/skills/design-system/scripts/validate-tokens.cjs +246 -0
  83. package/.opencode/skills/design-system/templates/design-tokens-starter.json +143 -0
  84. package/.opencode/skills/slides/SKILL.md +40 -0
  85. package/.opencode/skills/slides/references/copywriting-formulas.md +84 -0
  86. package/.opencode/skills/slides/references/create.md +4 -0
  87. package/.opencode/skills/slides/references/html-template.md +295 -0
  88. package/.opencode/skills/slides/references/layout-patterns.md +137 -0
  89. package/.opencode/skills/slides/references/slide-strategies.md +94 -0
  90. package/.opencode/skills/ui-styling/LICENSE.txt +202 -0
  91. package/.opencode/skills/ui-styling/SKILL.md +324 -0
  92. package/.opencode/skills/ui-styling/references/canvas-design-system.md +320 -0
  93. package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +471 -0
  94. package/.opencode/skills/ui-styling/references/shadcn-components.md +424 -0
  95. package/.opencode/skills/ui-styling/references/shadcn-theming.md +373 -0
  96. package/.opencode/skills/ui-styling/references/tailwind-customization.md +483 -0
  97. package/.opencode/skills/ui-styling/references/tailwind-responsive.md +382 -0
  98. package/.opencode/skills/ui-styling/references/tailwind-utilities.md +455 -0
  99. package/.opencode/skills/ui-styling/scripts/requirements.txt +17 -0
  100. package/.opencode/skills/ui-styling/scripts/shadcn_add.py +308 -0
  101. package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +473 -0
  102. package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +1 -0
  103. package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +3 -0
  104. package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +266 -0
  105. package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +394 -0
  106. package/.opencode/skills/ui-ux-pro-max/SKILL.md +396 -0
  107. package/.opencode/skills/ui-ux-pro-max/data/_sync_all.py +414 -0
  108. package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +31 -0
  109. package/.opencode/skills/ui-ux-pro-max/data/charts.csv +26 -0
  110. package/.opencode/skills/ui-ux-pro-max/data/colors.csv +193 -0
  111. package/.opencode/skills/ui-ux-pro-max/data/design.csv +1776 -0
  112. package/.opencode/skills/ui-ux-pro-max/data/draft.csv +1779 -0
  113. package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +1924 -0
  114. package/.opencode/skills/ui-ux-pro-max/data/icons.csv +106 -0
  115. package/.opencode/skills/ui-ux-pro-max/data/landing.csv +35 -0
  116. package/.opencode/skills/ui-ux-pro-max/data/motion.csv +17 -0
  117. package/.opencode/skills/ui-ux-pro-max/data/products.csv +193 -0
  118. package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  119. package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +51 -0
  120. package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  121. package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +57 -0
  122. package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  123. package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  124. package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +76 -0
  125. package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  126. package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
  127. package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  128. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +71 -0
  129. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  130. package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  131. package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  132. package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  133. package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  134. package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  135. package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
  136. package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +60 -0
  137. package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +56 -0
  138. package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  139. package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +60 -0
  140. package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +57 -0
  141. package/.opencode/skills/ui-ux-pro-max/data/styles.csv +85 -0
  142. package/.opencode/skills/ui-ux-pro-max/data/typography.csv +75 -0
  143. package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
  144. package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  145. package/.opencode/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
  146. package/.opencode/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
  147. package/.opencode/skills/ui-ux-pro-max/scripts/core.py +274 -0
  148. package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +1329 -0
  149. package/.opencode/skills/ui-ux-pro-max/scripts/search.py +127 -0
  150. package/README.md +5 -0
  151. package/STELLA.md +1 -1
  152. package/dist/antimalware/database.mjs +871 -0
  153. package/dist/antimalware/index.mjs +8 -0
  154. package/dist/antimalware/scanner.mjs +591 -0
  155. package/dist/antimalware/ui.mjs +570 -0
  156. package/dist/banner.mjs +46 -0
  157. package/dist/index.mjs +3073 -0
  158. package/dist/markdown.mjs +100 -0
  159. package/dist/sea-config.json +4 -0
  160. package/dist/security.mjs +237 -0
  161. package/dist/theme.mjs +89 -0
  162. package/dist/tools.mjs +3145 -0
  163. package/install-stella.bat +44 -0
  164. package/package.json +4 -2
  165. package/presentation-advanced-features-demo/README.md +27 -0
  166. package/presentation-advanced-features-demo/index.html +371 -0
  167. package/presentation-advanced-features-demo/print.html +81 -0
  168. package/presentation-advanced-features-demo/slide-01.html +98 -0
  169. package/presentation-advanced-features-demo/slide-02.html +101 -0
  170. package/presentation-advanced-features-demo/slide-03.html +100 -0
  171. package/presentation-advanced-features-demo/slide-04.html +100 -0
  172. package/presentation-advanced-features-demo/slide-05.html +100 -0
  173. package/presentation-advanced-features-demo/slide-06.html +64 -0
  174. package/presentation-advanced-features-demo/slide-07.html +101 -0
  175. package/presentation-advanced-features-demo/slide-08.html +91 -0
  176. package/presentation-stella-coder---ai-coding-agent/README.md +27 -0
  177. package/presentation-stella-coder---ai-coding-agent/index.html +168 -0
  178. package/presentation-stella-coder---ai-coding-agent/slide-01.html +98 -0
  179. package/presentation-stella-coder---ai-coding-agent/slide-02.html +101 -0
  180. package/presentation-stella-coder---ai-coding-agent/slide-03.html +100 -0
  181. package/presentation-stella-coder---ai-coding-agent/slide-04.html +100 -0
  182. package/presentation-stella-coder---ai-coding-agent/slide-05.html +100 -0
  183. package/presentation-stella-coder---ai-coding-agent/slide-06.html +100 -0
  184. package/presentation-stella-coder---ai-coding-agent/slide-07.html +100 -0
  185. package/presentation-stella-coder---ai-coding-agent/slide-08.html +64 -0
  186. package/presentation-stella-coder---ai-coding-agent/slide-09.html +101 -0
  187. package/presentation-stella-coder---ai-coding-agent/slide-10.html +91 -0
  188. package/presentation-stella-coder-demo/README.md +27 -0
  189. package/presentation-stella-coder-demo/index.html +371 -0
  190. package/presentation-stella-coder-demo/slide-01.html +98 -0
  191. package/presentation-stella-coder-demo/slide-02.html +101 -0
  192. package/presentation-stella-coder-demo/slide-03.html +100 -0
  193. package/presentation-stella-coder-demo/slide-04.html +100 -0
  194. package/presentation-stella-coder-demo/slide-05.html +100 -0
  195. package/presentation-stella-coder-demo/slide-06.html +64 -0
  196. package/presentation-stella-coder-demo/slide-07.html +101 -0
  197. package/presentation-stella-coder-demo/slide-08.html +91 -0
  198. package/presentation-stella-coder-features/README.md +27 -0
  199. package/presentation-stella-coder-features/index.html +364 -0
  200. package/presentation-stella-coder-features/print.html +77 -0
  201. package/presentation-stella-coder-features/slide-01.html +98 -0
  202. package/presentation-stella-coder-features/slide-02.html +101 -0
  203. package/presentation-stella-coder-features/slide-03.html +100 -0
  204. package/presentation-stella-coder-features/slide-04.html +100 -0
  205. package/presentation-stella-coder-features/slide-05.html +64 -0
  206. package/presentation-stella-coder-features/slide-06.html +101 -0
  207. package/presentation-stella-coder-features/slide-07.html +91 -0
  208. package/presentation-test-presentation/README.md +27 -0
  209. package/presentation-test-presentation/index.html +350 -0
  210. package/presentation-test-presentation/print.html +69 -0
  211. package/presentation-test-presentation/slide-01.html +98 -0
  212. package/presentation-test-presentation/slide-02.html +101 -0
  213. package/presentation-test-presentation/slide-03.html +64 -0
  214. package/presentation-test-presentation/slide-04.html +101 -0
  215. package/presentation-test-presentation/slide-05.html +91 -0
  216. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/README.md +27 -0
  217. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/index.html +159 -0
  218. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-01.html +98 -0
  219. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-02.html +101 -0
  220. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-03.html +100 -0
  221. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-04.html +100 -0
  222. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-05.html +64 -0
  223. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-06.html +101 -0
  224. package/presentation-/321/202/320/265/321/201/321/202/320/276/320/262/320/260/321/217-/320/277/321/200/320/265/320/267/320/265/320/275/321/202/320/260/321/206/320/270/321/217/slide-07.html +91 -0
  225. package/stella-cli/coding-brain.mjs +753 -0
  226. package/stella-cli/index.mjs +931 -15
  227. package/stella-cli/mcp.mjs +296 -0
  228. package/stella-cli/presentations.mjs +1106 -0
  229. package/stella-cli/subagents.mjs +142 -0
  230. package/stella-cli/telegram-bot.mjs +824 -0
  231. package/stella-cli/tg-bg.bat +13 -0
  232. package/stella-cli/tg-server.bat +15 -0
  233. package/stella-cli/tg-server.mjs +116 -0
  234. package/stella-cli/theme.mjs +6 -4
  235. package/stella-coder-3.9.2.tgz +0 -0
@@ -0,0 +1,8 @@
1
+ import { startInteractive } from "./ui.mjs"
2
+
3
+ // ═══════════════════════════════════════════════════════════════
4
+ // STELLAR ANTIVIRUS — Entry Point
5
+ // Full system scanner with Kaspersky-like UI
6
+ // ═══════════════════════════════════════════════════════════════
7
+
8
+ startInteractive()
@@ -0,0 +1,591 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import { execSync } from "node:child_process"
4
+ import { checkFileForMalware, computeFileHash, SKIP_DIRS, QUICK_SCAN_PATHS, QUARANTINE_DIR, isExcluded } from "./database.mjs"
5
+
6
+ // ═══════════════════════════════════════════════════════════════
7
+ // STELLAR ANTIVIRUS — Full Scanner Engine
8
+ // ═══════════════════════════════════════════════════════════════
9
+
10
+ const SCAN_EXTENSIONS = new Set([
11
+ ".js", ".mjs", ".cjs", ".ts", ".tsx", ".jsx",
12
+ ".py", ".rb", ".php", ".pl", ".sh", ".bash",
13
+ ".bat", ".cmd", ".ps1", ".psm1", ".psd1",
14
+ ".exe", ".dll", ".sys", ".com", ".scr", ".pif",
15
+ ".vbs", ".vbe", ".jse", ".wsf", ".wsh",
16
+ ".hta", ".cpl", ".msi", ".msp", ".mst",
17
+ ".doc", ".docm", ".xls", ".xlsm", ".ppt", ".pptm",
18
+ ".pdf", ".rtf",
19
+ ".jar", ".class", ".war",
20
+ ".reg", ".inf",
21
+ ])
22
+
23
+ // Skip large files (over 10MB for speed)
24
+ const MAX_FILE_SIZE = 10 * 1024 * 1024
25
+
26
+ // Known safe directories to skip
27
+ const SAFE_DIRS = new Set([
28
+ "Microsoft", "dotnet", "NuGet", "pip", "npm", "node-gyp",
29
+ "VisualStudio", "VS", "Windows Kits", "Windows Defender",
30
+ "Package Cache", "Installer", "apps", "packages",
31
+ "Windows SDK", "WindowsAppCertificationKit", "WindowsDesktopExtensionSDK",
32
+ "WindowsIoTExtensionSDK", "WindowsMobileExtensionSDK", "WindowsTeamExtensionSDK",
33
+ "Universal CRT", "WinRT Intellisense", "Kits", "WPT",
34
+ ])
35
+
36
+ // Skip files from known safe publishers
37
+ const SAFE_FILE_PATTERNS = [
38
+ /^vs_/, /^Microsoft\.VisualStudio/, /^dump64/, /^msdia/, /^msvcp/,
39
+ /^vcruntime/, /^KernelTrace/, /^feedback/, /^Dia2Lib/, /^envdte/,
40
+ /^OSExtensions/, /^TraceRelogger/, /^msalruntime/, /^websocket-sharp/,
41
+ /^System\./, /^Microsoft\.Diagnostics/, /^Microsoft\.Identity/,
42
+ /^node-gyp/, /^Setup$/, /^setup\.exe$/, /^InstallCleanup/,
43
+ /^VSInstallerElevationService/, /^vswhere/, /^vs_installer/,
44
+ /^vs_installershell/, /^Microsoft\.VisualStudio\.Setup\./,
45
+ /^D3DCompiler/, /^PresentationNative/, /^wpfgfx/,
46
+ /^capCut/, /^ChromeSetup/, /^Claude/, /^Codex/, /^Docker/,
47
+ /^BlueStacks/, /^VC_redist/,
48
+ ]
49
+
50
+ // Safe path patterns (substring match)
51
+ const SAFE_PATH_PATTERNS = [
52
+ "\\Microsoft\\", "\\dotnet\\", "\\NuGet\\", "\\pip\\", "\\npm\\",
53
+ "\\node-gyp\\", "\\Visual Studio\\", "\\Windows Kits\\",
54
+ "\\Package Cache\\", "\\Installer\\", "\\Windows SDK\\",
55
+ "\\AppData\\Local\\Temp\\", "\\AppData\\Local\\Microsoft\\",
56
+ "\\AppData\\Roaming\\npm\\", "\\AppData\\Roaming\\pip\\",
57
+ ]
58
+
59
+ // ═══════════════════════════════════════════════════════════════
60
+ // EXCLUSIONS
61
+ // ═══════════════════════════════════════════════════════════════
62
+
63
+ let exclusions = []
64
+
65
+ export function loadExclusions(filepath) {
66
+ try {
67
+ if (fs.existsSync(filepath)) {
68
+ exclusions = fs.readFileSync(filepath, "utf8")
69
+ .split("\n")
70
+ .map(l => l.trim())
71
+ .filter(l => l && !l.startsWith("#"))
72
+ }
73
+ } catch {}
74
+ }
75
+
76
+ export function addExclusion(pattern) {
77
+ if (!exclusions.includes(pattern)) {
78
+ exclusions.push(pattern)
79
+ }
80
+ }
81
+
82
+ export function removeExclusion(pattern) {
83
+ exclusions = exclusions.filter(e => e !== pattern)
84
+ }
85
+
86
+ export function getExclusions() {
87
+ return [...exclusions]
88
+ }
89
+
90
+ function isExcludedPath(filepath) {
91
+ const normalized = filepath.replace(/\\/g, "/").toLowerCase()
92
+ for (const exc of exclusions) {
93
+ const pattern = exc.replace(/\\/g, "/").toLowerCase()
94
+ if (normalized.includes(pattern)) return true
95
+ }
96
+ return false
97
+ }
98
+
99
+ // ═══════════════════════════════════════════════════════════════
100
+ // FILE SCANNER
101
+ // ═══════════════════════════════════════════════════════════════
102
+
103
+ export function scanFile(filepath, options = {}) {
104
+ const { verbose = false, onFile = null } = options
105
+
106
+ try {
107
+ if (!fs.existsSync(filepath)) return { filepath, error: "Файл не найден" }
108
+
109
+ const stat = fs.statSync(filepath)
110
+ if (!stat.isFile()) return { filepath, error: "Не является файлом" }
111
+
112
+ if (stat.size > MAX_FILE_SIZE) return { filepath, error: `Файл слишком большой (${(stat.size / 1024 / 1024).toFixed(1)} MB)` }
113
+
114
+ if (isExcludedPath(filepath)) return { filepath, excluded: true }
115
+
116
+ // Skip known safe files
117
+ const basename = path.basename(filepath)
118
+ if (SAFE_FILE_PATTERNS.some(p => p.test(basename))) return { filepath, excluded: true }
119
+
120
+ // Skip safe path patterns
121
+ if (SAFE_PATH_PATTERNS.some(p => filepath.includes(p))) return { filepath, excluded: true }
122
+
123
+ if (onFile) onFile(filepath, stat.size)
124
+
125
+ const content = fs.readFileSync(filepath)
126
+ const result = checkFileForMalware(content, filepath)
127
+ result.size = stat.size
128
+ result.modified = stat.mtime
129
+ result.hash = computeFileHash(content)
130
+
131
+ return result
132
+ } catch (e) {
133
+ return { filepath, error: e.message }
134
+ }
135
+ }
136
+
137
+ // ═══════════════════════════════════════════════════════════════
138
+ // FULL SYSTEM SCAN
139
+ // ═══════════════════════════════════════════════════════════════
140
+
141
+ export function fullSystemScan(options = {}) {
142
+ const { verbose = false, onFile = null, onProgress = null, maxDepth = 20 } = options
143
+ const results = []
144
+ let fileCount = 0
145
+
146
+ function walk(currentPath, depth) {
147
+ if (depth > maxDepth) return
148
+
149
+ let entries
150
+ try {
151
+ entries = fs.readdirSync(currentPath, { withFileTypes: true })
152
+ } catch { return }
153
+
154
+ for (const entry of entries) {
155
+ if (SKIP_DIRS.has(entry.name)) continue
156
+ if (SAFE_DIRS.has(entry.name)) continue
157
+ if (entry.name.startsWith(".") && entry.name !== ".env" && entry.name !== ".config") continue
158
+
159
+ const fullPath = path.join(currentPath, entry.name)
160
+
161
+ if (entry.isDirectory()) {
162
+ walk(fullPath, depth + 1)
163
+ } else if (entry.isFile()) {
164
+ const ext = path.extname(entry.name).toLowerCase()
165
+ if (SCAN_EXTENSIONS.has(ext) || !ext) {
166
+ fileCount++
167
+ if (onProgress) onProgress(fileCount, fullPath)
168
+
169
+ const result = scanFile(fullPath, { verbose, onFile })
170
+ results.push(result)
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ // Scan all drives on Windows
177
+ const drives = ["C:", "D:", "E:", "F:"]
178
+ for (const drive of drives) {
179
+ const drivePath = drive + "\\"
180
+ if (fs.existsSync(drivePath)) {
181
+ walk(drivePath, 0)
182
+ }
183
+ }
184
+
185
+ return { results, totalFiles: fileCount, scanType: "full" }
186
+ }
187
+
188
+ // ═══════════════════════════════════════════════════════════════
189
+ // QUICK SCAN
190
+ // ═══════════════════════════════════════════════════════════════
191
+
192
+ export function quickScan(options = {}) {
193
+ const { verbose = false, onFile = null, onProgress = null, maxFiles = 2000 } = options
194
+ const results = []
195
+ let fileCount = 0
196
+
197
+ const username = process.env.USERNAME || process.env.USER || "user"
198
+
199
+ const scanPaths = QUICK_SCAN_PATHS.map(p =>
200
+ p.replace(/%USERNAME%/g, username)
201
+ )
202
+
203
+ for (const scanPath of scanPaths) {
204
+ if (!fs.existsSync(scanPath)) continue
205
+ if (fileCount >= maxFiles) break
206
+
207
+ function walk(currentPath, depth) {
208
+ if (depth > 5 || fileCount >= maxFiles) return
209
+
210
+ let entries
211
+ try {
212
+ entries = fs.readdirSync(currentPath, { withFileTypes: true })
213
+ } catch { return }
214
+
215
+ for (const entry of entries) {
216
+ if (SKIP_DIRS.has(entry.name)) continue
217
+ if (SAFE_DIRS.has(entry.name)) continue
218
+ if (fileCount >= maxFiles) break
219
+
220
+ const fullPath = path.join(currentPath, entry.name)
221
+
222
+ if (entry.isDirectory()) {
223
+ walk(fullPath, depth + 1)
224
+ } else if (entry.isFile()) {
225
+ const ext = path.extname(entry.name).toLowerCase()
226
+ if (SCAN_EXTENSIONS.has(ext) || !ext) {
227
+ fileCount++
228
+ if (onProgress) onProgress(fileCount, fullPath)
229
+
230
+ const result = scanFile(fullPath, { verbose, onFile })
231
+ results.push(result)
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ walk(scanPath, 0)
238
+ }
239
+
240
+ return { results, totalFiles: fileCount, scanType: "quick" }
241
+ }
242
+
243
+ // ═══════════════════════════════════════════════════════════════
244
+ // CUSTOM PATH SCAN
245
+ // ═══════════════════════════════════════════════════════════════
246
+
247
+ export function scanPath(dirPath, options = {}) {
248
+ const { verbose = false, onFile = null, onProgress = null, maxDepth = 10 } = options
249
+ const results = []
250
+ let fileCount = 0
251
+
252
+ function walk(currentPath, depth) {
253
+ if (depth > maxDepth) return
254
+
255
+ let entries
256
+ try {
257
+ entries = fs.readdirSync(currentPath, { withFileTypes: true })
258
+ } catch { return }
259
+
260
+ for (const entry of entries) {
261
+ if (SKIP_DIRS.has(entry.name)) continue
262
+ if (entry.name.startsWith(".")) continue
263
+
264
+ const fullPath = path.join(currentPath, entry.name)
265
+
266
+ if (entry.isDirectory()) {
267
+ walk(fullPath, depth + 1)
268
+ } else if (entry.isFile()) {
269
+ const ext = path.extname(entry.name).toLowerCase()
270
+ if (SCAN_EXTENSIONS.has(ext) || !ext) {
271
+ fileCount++
272
+ if (onProgress) onProgress(fileCount, fullPath)
273
+
274
+ const result = scanFile(fullPath, { verbose, onFile })
275
+ results.push(result)
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ if (fs.existsSync(dirPath)) {
282
+ const stat = fs.statSync(dirPath)
283
+ if (stat.isFile()) {
284
+ results.push(scanFile(dirPath, { verbose, onFile }))
285
+ fileCount = 1
286
+ } else {
287
+ walk(dirPath, 0)
288
+ }
289
+ }
290
+
291
+ return { results, totalFiles: fileCount, scanType: "custom" }
292
+ }
293
+
294
+ // ═══════════════════════════════════════════════════════════════
295
+ // BOOT SECTOR / MBR SCANNER
296
+ // ═══════════════════════════════════════════════════════════════
297
+
298
+ export function scanBootSector(drive = "C:") {
299
+ try {
300
+ const devicePath = `\\\\.\\${drive}`
301
+ const fd = fs.openSync(devicePath, "r")
302
+ const buf = Buffer.alloc(512)
303
+ fs.readSync(fd, buf, 0, 512, 0)
304
+ fs.closeSync(fd)
305
+
306
+ const result = checkFileForMalware(buf, `${drive}\\MBR`)
307
+ result.scanType = "boot-sector"
308
+ return result
309
+ } catch (e) {
310
+ return { filepath: `${drive}\\MBR`, error: `Не удалось прочитать MBR: ${e.message}` }
311
+ }
312
+ }
313
+
314
+ // ═══════════════════════════════════════════════════════════════
315
+ // MEMORY PROCESS SCANNER
316
+ // ═══════════════════════════════════════════════════════════════
317
+
318
+ export function scanRunningProcesses() {
319
+ const results = []
320
+
321
+ // Known safe Windows processes
322
+ const SAFE_PROCESSES = new Set([
323
+ "WMIRegistrationService.exe", "WmiPrvSE.exe", "WmiApSrv.exe",
324
+ "svchost.exe", "services.exe", "lsass.exe", "wininit.exe",
325
+ "csrss.exe", "smss.exe", "winlogon.exe", "dwm.exe",
326
+ "explorer.exe", "taskhostw.exe", "conhost.exe",
327
+ "RuntimeBroker.exe", "ShellExperienceHost.exe", "StartMenuExperienceHost.exe",
328
+ "SearchUI.exe", "SearchIndexer.exe", "SearchProtocolHost.exe",
329
+ "SiHost.exe", "fontdrvhost.exe", "dllhost.exe",
330
+ "spoolsv.exe", "SearchFilterHost.exe", "WUDFHost.exe",
331
+ "msdtc.exe", "WerFault.exe", "WerMgr.exe",
332
+ "audiodg.exe", "mmsysse.exe", "ctfmon.exe",
333
+ "notepad.exe", "calc.exe", "mspaint.exe",
334
+ "chrome.exe", "firefox.exe", "msedge.exe", "opera.exe",
335
+ "Code.exe", "node.exe", "pnpm.exe", "npm.exe",
336
+ "ollama.exe", "ollama_llama_server.exe",
337
+ "powershell.exe", "pwsh.exe", "cmd.exe",
338
+ "WindowsTerminal.exe", "wt.exe",
339
+ "SecurityHealthService.exe", "SecurityHealthSystray.exe",
340
+ "MpCmdRun.exe", "MsMpEng.exe",
341
+ "NisSrv.exe", "WdNisSvc.exe",
342
+ "ApplicationFrameHost.exe", "SystemSettings.exe",
343
+ "TextInputHost.exe", "CompPkgSrv.exe",
344
+ "backgroundTaskHost.exe", "hctool.exe",
345
+ "KasperskyLab.exe", "avp.exe", "avpui.exe",
346
+ "OneDrive.exe", "onedrive.exe",
347
+ "Teams.exe", "Slack.exe", "Discord.exe",
348
+ "Spotify.exe", "Steam.exe", "EpicGamesLauncher.exe",
349
+ "vmwaretray.exe", "vmwareuser.exe", "vmtoolsd.exe",
350
+ "vboxservice.exe", "vboxtray.exe",
351
+ "Docker Desktop.exe", "com.docker.backend.exe",
352
+ "zoom.exe", "Zoom.exe",
353
+ ])
354
+
355
+ try {
356
+ const output = execSync("tasklist /FO CSV /NH", { encoding: "utf8", timeout: 10000 })
357
+ const lines = output.split("\n").filter(l => l.trim())
358
+
359
+ for (const line of lines) {
360
+ const match = line.match(/"([^"]+)","(\d+)","([^"]+)"/)
361
+ if (match) {
362
+ const [, name, pid, session] = match
363
+ const isSafe = SAFE_PROCESSES.has(name)
364
+ const suspicious = !isSafe && /\b(mimikatz|meterpreter|cobaltstrike|inject|hook|keylog|rat|trojan|backdoor|rootkit|empire|covenant|sliver|brute ratel)\b/i.test(name)
365
+ results.push({
366
+ name,
367
+ pid: parseInt(pid),
368
+ memory: session,
369
+ suspicious,
370
+ severity: suspicious ? "critical" : "clean",
371
+ })
372
+ }
373
+ }
374
+ } catch {}
375
+ return results
376
+ }
377
+
378
+ // ═══════════════════════════════════════════════════════════════
379
+ // QUARANTINE SYSTEM
380
+ // ═══════════════════════════════════════════════════════════════
381
+
382
+ export function ensureQuarantine() {
383
+ if (!fs.existsSync(QUARANTINE_DIR)) {
384
+ fs.mkdirSync(QUARANTINE_DIR, { recursive: true })
385
+ }
386
+ }
387
+
388
+ export function quarantineFile(filepath) {
389
+ ensureQuarantine()
390
+ const filename = path.basename(filepath)
391
+ const hash = computeFileHash(fs.readFileSync(filepath))
392
+ const quarantinePath = path.join(QUARANTINE_DIR, `${hash}_${filename}.quarantine`)
393
+ const metaPath = quarantinePath + ".meta"
394
+
395
+ const metadata = {
396
+ originalPath: filepath,
397
+ hash,
398
+ timestamp: new Date().toISOString(),
399
+ size: fs.statSync(filepath).size,
400
+ }
401
+
402
+ fs.copyFileSync(filepath, quarantinePath)
403
+ fs.writeFileSync(metaPath, JSON.stringify(metadata, null, 2))
404
+ fs.unlinkSync(filepath)
405
+
406
+ return { quarantined: quarantinePath, metadata }
407
+ }
408
+
409
+ export function restoreFromQuarantine(quarantinePath) {
410
+ const metaPath = quarantinePath + ".meta"
411
+ if (!fs.existsSync(metaPath)) return { error: "Метаданные не найдены" }
412
+
413
+ const metadata = JSON.parse(fs.readFileSync(metaPath, "utf8"))
414
+ const restoreDir = path.dirname(metadata.originalPath)
415
+
416
+ if (!fs.existsSync(restoreDir)) {
417
+ fs.mkdirSync(restoreDir, { recursive: true })
418
+ }
419
+
420
+ fs.copyFileSync(quarantinePath, metadata.originalPath)
421
+ fs.unlinkSync(quarantinePath)
422
+ fs.unlinkSync(metaPath)
423
+
424
+ return { restored: metadata.originalPath }
425
+ }
426
+
427
+ export function listQuarantine() {
428
+ ensureQuarantine()
429
+ const files = fs.readdirSync(QUARANTINE_DIR).filter(f => f.endsWith(".quarantine"))
430
+ return files.map(f => {
431
+ const metaPath = path.join(QUARANTINE_DIR, f + ".meta")
432
+ try {
433
+ const meta = JSON.parse(fs.readFileSync(metaPath, "utf8"))
434
+ return { file: f, ...meta }
435
+ } catch {
436
+ return { file: f, originalPath: "unknown" }
437
+ }
438
+ })
439
+ }
440
+
441
+ // ═══════════════════════════════════════════════════════════════
442
+ // REAL-TIME FILE MONITOR
443
+ // ═══════════════════════════════════════════════════════════════
444
+
445
+ let monitorActive = false
446
+ let monitorInterval = null
447
+ let fileHashes = new Map()
448
+
449
+ export function startRealTimeMonitor(dirPath, options = {}) {
450
+ const { interval = 5000, onThreat = null, onFileChange = null } = options
451
+
452
+ if (monitorActive) return { error: "Мониторинг уже запущен" }
453
+
454
+ monitorActive = true
455
+ const initialHashes = new Map()
456
+
457
+ // Build initial hash map
458
+ function buildHashMap(currentPath, depth) {
459
+ if (depth > 5) return
460
+ try {
461
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true })
462
+ for (const entry of entries) {
463
+ if (SKIP_DIRS.has(entry.name)) continue
464
+ const fullPath = path.join(currentPath, entry.name)
465
+ if (entry.isDirectory()) {
466
+ buildHashMap(fullPath, depth + 1)
467
+ } else if (entry.isFile()) {
468
+ try {
469
+ const stat = fs.statSync(fullPath)
470
+ initialHashes.set(fullPath, { size: stat.size, mtime: stat.mtimeMs })
471
+ } catch {}
472
+ }
473
+ }
474
+ } catch {}
475
+ }
476
+
477
+ buildHashMap(dirPath, 0)
478
+ fileHashes = initialHashes
479
+
480
+ // Monitor loop
481
+ monitorInterval = setInterval(() => {
482
+ if (!monitorActive) return
483
+
484
+ const currentFiles = new Map()
485
+
486
+ function scanDir(currentPath, depth) {
487
+ if (depth > 5) return
488
+ try {
489
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true })
490
+ for (const entry of entries) {
491
+ if (SKIP_DIRS.has(entry.name)) continue
492
+ const fullPath = path.join(currentPath, entry.name)
493
+ if (entry.isDirectory()) {
494
+ scanDir(fullPath, depth + 1)
495
+ } else if (entry.isFile()) {
496
+ try {
497
+ const stat = fs.statSync(fullPath)
498
+ currentFiles.set(fullPath, { size: stat.size, mtime: stat.mtimeMs })
499
+ } catch {}
500
+ }
501
+ }
502
+ } catch {}
503
+ }
504
+
505
+ scanDir(dirPath, 0)
506
+
507
+ // Check for new/modified files
508
+ for (const [filepath, info] of currentFiles) {
509
+ const prev = fileHashes.get(filepath)
510
+ if (!prev || prev.mtime !== info.mtime || prev.size !== info.size) {
511
+ if (onFileChange) onFileChange(filepath, prev ? "modified" : "new")
512
+
513
+ // Scan the file
514
+ const result = scanFile(filepath)
515
+ if (!result.clean && onThreat) {
516
+ onThreat(result)
517
+ }
518
+ }
519
+ }
520
+
521
+ // Check for deleted files
522
+ for (const [filepath] of fileHashes) {
523
+ if (!currentFiles.has(filepath)) {
524
+ if (onFileChange) onFileChange(filepath, "deleted")
525
+ }
526
+ }
527
+
528
+ fileHashes = currentFiles
529
+ }, interval)
530
+
531
+ return { status: "started", dirPath, interval }
532
+ }
533
+
534
+ export function stopRealTimeMonitor() {
535
+ monitorActive = false
536
+ if (monitorInterval) {
537
+ clearInterval(monitorInterval)
538
+ monitorInterval = null
539
+ }
540
+ fileHashes.clear()
541
+ return { status: "stopped" }
542
+ }
543
+
544
+ export function getMonitorStatus() {
545
+ return { active: monitorActive, trackedFiles: fileHashes.size }
546
+ }
547
+
548
+ // ═══════════════════════════════════════════════════════════════
549
+ // SCAN REPORT
550
+ // ═══════════════════════════════════════════════════════════════
551
+
552
+ export function generateReport(results, scanType = "full") {
553
+ const threats = results.filter(r => !r.clean && !r.error)
554
+ const warnings = results.filter(r => r.clean && r.warnings && r.warnings.length > 0)
555
+ const clean = results.filter(r => r.clean && (!r.warnings || r.warnings.length === 0))
556
+ const errors = results.filter(r => r.error)
557
+
558
+ const report = {
559
+ scanType,
560
+ timestamp: new Date().toISOString(),
561
+ summary: {
562
+ totalFiles: results.length,
563
+ threats: threats.length,
564
+ warnings: warnings.length,
565
+ clean: clean.length,
566
+ errors: errors.length,
567
+ },
568
+ threats: threats.map(r => ({
569
+ filepath: r.filepath,
570
+ score: r.score,
571
+ detections: [...r.threats, ...r.warnings],
572
+ })),
573
+ warnings: warnings.map(r => ({
574
+ filepath: r.filepath,
575
+ detections: r.warnings,
576
+ })),
577
+ }
578
+
579
+ return report
580
+ }
581
+
582
+ export function saveReport(report, filepath) {
583
+ fs.writeFileSync(filepath, JSON.stringify(report, null, 2))
584
+ return filepath
585
+ }
586
+
587
+ // ═══════════════════════════════════════════════════════════════
588
+ // LEGACY COMPAT
589
+ // ═══════════════════════════════════════════════════════════════
590
+
591
+ export { scanPath as scanDirectory }