stella-coder 5.3.1 → 5.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMMANDS.md +47 -0
- package/install-av.bat +17 -0
- package/install-stella-pkg.bat +21 -0
- package/oneline-av.ps1 +7 -0
- package/oneline-stella.ps1 +2 -0
- package/package.json +1 -1
- package/releases/stella-antivirus/database.mjs +871 -0
- package/releases/stella-antivirus/index.mjs +8 -0
- package/releases/stella-antivirus/install-av.bat +12 -0
- package/releases/stella-antivirus/scanner.mjs +591 -0
- package/releases/stella-antivirus/ui.mjs +570 -0
- package/releases/stella-antivirus.zip +0 -0
- package/releases/stella-coder/README.md +67 -0
- package/releases/stella-coder/adb.mjs +200 -0
- package/releases/stella-coder/autonomous-agent.mjs +550 -0
- package/releases/stella-coder/banner.mjs +46 -0
- package/releases/stella-coder/browser-control.mjs +274 -0
- package/releases/stella-coder/build.mjs +151 -0
- package/releases/stella-coder/charts.mjs +411 -0
- package/releases/stella-coder/coding-brain.mjs +753 -0
- package/releases/stella-coder/game-engine.mjs +708 -0
- package/releases/stella-coder/gdrive-backup.mjs +338 -0
- package/releases/stella-coder/git-api.mjs +407 -0
- package/releases/stella-coder/gmail.mjs +415 -0
- package/releases/stella-coder/home-assistant.mjs +168 -0
- package/releases/stella-coder/index.mjs +5810 -0
- package/releases/stella-coder/install-stella.bat +12 -0
- package/releases/stella-coder/markdown.mjs +100 -0
- package/releases/stella-coder/mcp.mjs +296 -0
- package/releases/stella-coder/package.json +67 -0
- package/releases/stella-coder/presentations.mjs +1106 -0
- package/releases/stella-coder/protect.mjs +182 -0
- package/releases/stella-coder/screen-monitor.mjs +334 -0
- package/releases/stella-coder/sea-config.json +5 -0
- package/releases/stella-coder/security.mjs +237 -0
- package/releases/stella-coder/subagents.mjs +142 -0
- package/releases/stella-coder/telegram-bot.mjs +824 -0
- package/releases/stella-coder/tg-server.mjs +116 -0
- package/releases/stella-coder/theme.mjs +91 -0
- package/releases/stella-coder/tools.mjs +3143 -0
- package/releases/stella-coder/web-parser.mjs +229 -0
- package/releases/stella-coder/yandex-maps.mjs +426 -0
- package/releases/stella-coder.zip +0 -0
- package/run-antivirus.bat +4 -0
- package/setup-antivirus.bat +64 -0
- package/setup-full.bat +65 -0
- package/setup-full.ps1 +84 -0
- package/setup-stella.bat +46 -0
- package/stella-cli/index.mjs +12 -2
- package/stella-cli/protect.mjs +182 -0
- package/stella.bat +3 -0
- package/tests/test-modules.mjs +101 -0
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import readline from "node:readline"
|
|
2
|
+
import { fullSystemScan, quickScan, scanPath, scanFile, scanRunningProcesses, scanBootSector, startRealTimeMonitor, stopRealTimeMonitor, getMonitorStatus, quarantineFile, listQuarantine, restoreFromQuarantine, loadExclusions, addExclusion, removeExclusion, getExclusions, generateReport, saveReport } from "./scanner.mjs"
|
|
3
|
+
import { QUARANTINE_DIR } from "./database.mjs"
|
|
4
|
+
import fs from "node:fs"
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
|
|
7
|
+
// ═══════════════════════════════════════════════════════════════
|
|
8
|
+
// STELLAR ANTIVIRUS — Kaspersky-style UI
|
|
9
|
+
// ═══════════════════════════════════════════════════════════════
|
|
10
|
+
|
|
11
|
+
const COLORS = {
|
|
12
|
+
reset: "\x1b[0m",
|
|
13
|
+
bold: "\x1b[1m",
|
|
14
|
+
dim: "\x1b[2m",
|
|
15
|
+
italic: "\x1b[3m",
|
|
16
|
+
underline: "\x1b[4m",
|
|
17
|
+
|
|
18
|
+
red: "\x1b[31m",
|
|
19
|
+
green: "\x1b[32m",
|
|
20
|
+
yellow: "\x1b[33m",
|
|
21
|
+
blue: "\x1b[34m",
|
|
22
|
+
magenta: "\x1b[35m",
|
|
23
|
+
cyan: "\x1b[36m",
|
|
24
|
+
white: "\x1b[37m",
|
|
25
|
+
gray: "\x1b[90m",
|
|
26
|
+
|
|
27
|
+
bgRed: "\x1b[41m",
|
|
28
|
+
bgGreen: "\x1b[42m",
|
|
29
|
+
bgYellow: "\x1b[43m",
|
|
30
|
+
bgBlue: "\x1b[44m",
|
|
31
|
+
bgMagenta: "\x1b[45m",
|
|
32
|
+
bgCyan: "\x1b[46m",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function c(color, text) {
|
|
36
|
+
return `${COLORS[color]}${text}${COLORS.reset}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function bold(text) { return c("bold", text) }
|
|
40
|
+
function dim(text) { return c("dim", text) }
|
|
41
|
+
function red(text) { return c("red", text) }
|
|
42
|
+
function green(text) { return c("green", text) }
|
|
43
|
+
function yellow(text) { return c("yellow", text) }
|
|
44
|
+
function cyan(text) { return c("cyan", text) }
|
|
45
|
+
function magenta(text) { return c("magenta", text) }
|
|
46
|
+
|
|
47
|
+
function box(title, lines, color = [0, 200, 255]) {
|
|
48
|
+
const [r, g, b] = color
|
|
49
|
+
const ansiColor = `\x1b[38;2;${r};${g};${b}m`
|
|
50
|
+
const reset = "\x1b[0m"
|
|
51
|
+
const maxLen = Math.max(title.length + 4, ...lines.map(l => l.length))
|
|
52
|
+
const width = maxLen + 4
|
|
53
|
+
|
|
54
|
+
const top = `${ansiColor}╭${"─".repeat(width)}╮${reset}`
|
|
55
|
+
const bottom = `${ansiColor}╰${"─".repeat(width)}╯${reset}`
|
|
56
|
+
const titleLine = `${ansiColor}│${reset} ${ansiColor}${COLORS.bold}${title}${reset}${" ".repeat(width - title.length - 2)}${ansiColor}│${reset}`
|
|
57
|
+
|
|
58
|
+
const contentLines = lines.map(l =>
|
|
59
|
+
`${ansiColor}│${reset} ${l}${" ".repeat(Math.max(0, width - l.length - 2))}${ansiColor}│${reset}`
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return [top, titleLine, `${ansiColor}│${reset}${" ".repeat(width)}${ansiColor}│${reset}`, ...contentLines, bottom].join("\n")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function progressLine(current, total, width = 40) {
|
|
66
|
+
const pct = Math.floor((current / total) * 100)
|
|
67
|
+
const filled = Math.floor((current / total) * width)
|
|
68
|
+
const empty = width - filled
|
|
69
|
+
const bar = "█".repeat(filled) + "░".repeat(empty)
|
|
70
|
+
return ` ${cyan(bar)} ${pct}% (${current}/${total})`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function statusIcon(severity) {
|
|
74
|
+
switch (severity) {
|
|
75
|
+
case "critical": return red("●")
|
|
76
|
+
case "high": return yellow("●")
|
|
77
|
+
case "medium": return cyan("●")
|
|
78
|
+
case "low": return dim("●")
|
|
79
|
+
default: return green("●")
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function severityColor(severity) {
|
|
84
|
+
switch (severity) {
|
|
85
|
+
case "critical": return red
|
|
86
|
+
case "high": return yellow
|
|
87
|
+
case "medium": return cyan
|
|
88
|
+
default: return dim
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ═══════════════════════════════════════════════════════════════
|
|
93
|
+
// BANNER
|
|
94
|
+
// ═══════════════════════════════════════════════════════════════
|
|
95
|
+
|
|
96
|
+
function printBanner() {
|
|
97
|
+
const gradient = [
|
|
98
|
+
[167, 139, 250], [165, 136, 250], [163, 133, 250], [161, 130, 249],
|
|
99
|
+
[159, 127, 249], [157, 124, 249], [155, 121, 248], [153, 118, 248],
|
|
100
|
+
[151, 115, 248], [149, 112, 248], [147, 109, 247], [145, 106, 247],
|
|
101
|
+
[143, 103, 247], [141, 100, 247], [139, 97, 246], [137, 94, 246],
|
|
102
|
+
[135, 97, 246], [133, 100, 246], [131, 103, 247], [129, 106, 247],
|
|
103
|
+
[127, 109, 247], [125, 112, 247], [123, 115, 246], [121, 118, 246],
|
|
104
|
+
[119, 121, 246], [117, 124, 245], [115, 127, 245], [113, 130, 245],
|
|
105
|
+
[111, 133, 245], [109, 136, 244], [107, 139, 244], [105, 142, 243],
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
const logo = [
|
|
109
|
+
" ███████╗████████╗██████╗ ███████╗██████╗ ███╗ ███╗",
|
|
110
|
+
" ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██╔══██╗████╗ ████║",
|
|
111
|
+
" ███████╗ ██║ ██████╔╝█████╗ ██████╔╝██╔████╔██║",
|
|
112
|
+
" ╚════██║ ██║ ██╔══██╗██╔══╝ ██╔══██╗██║╚██╔╝██║",
|
|
113
|
+
" ███████║ ██║ ██║ ██║███████╗██║ ██║██║ ╚═╝ ██║",
|
|
114
|
+
" ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝",
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
console.log()
|
|
118
|
+
for (const line of logo) {
|
|
119
|
+
let colored = ""
|
|
120
|
+
for (let i = 0; i < line.length; i++) {
|
|
121
|
+
const colorIdx = Math.floor((i / line.length) * gradient.length)
|
|
122
|
+
const [r, g, b] = gradient[Math.min(colorIdx, gradient.length - 1)]
|
|
123
|
+
colored += `\x1b[38;2;${r};${g};${b}m${line[i]}`
|
|
124
|
+
}
|
|
125
|
+
console.log(colored + "\x1b[0m")
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
console.log()
|
|
129
|
+
console.log(` ${magenta("✦")} ${bold("Stellar Antivirus")}${dim(" · powered by ")}${cyan("Stella")} ${dim("Security Engine")}`)
|
|
130
|
+
console.log()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function printStatusBanner(status, detail = "") {
|
|
134
|
+
const [r, g, b] = status === "safe" ? [0, 200, 100] : status === "warning" ? [255, 200, 0] : [255, 80, 80]
|
|
135
|
+
const ansi = `\x1b[38;2;${r};${g};${b}m`
|
|
136
|
+
const icon = status === "safe" ? "✓" : status === "warning" ? "⚠" : "✗"
|
|
137
|
+
|
|
138
|
+
console.log()
|
|
139
|
+
console.log(`${ansi} ╔══════════════════════════════════════════════════════════╗${COLORS.reset}`)
|
|
140
|
+
console.log(`${ansi} ║ ${icon} ${bold(detail || (status === "safe" ? "СИСТЕМА ЗАЩИЩЕНА" : "ОБНАРУЖЕНЫ УГРОЗЫ"))}${" ".repeat(Math.max(0, 52 - (detail || "").length))}║${COLORS.reset}`)
|
|
141
|
+
console.log(`${ansi} ╚══════════════════════════════════════════════════════════╝${COLORS.reset}`)
|
|
142
|
+
console.log()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ═══════════════════════════════════════════════════════════════
|
|
146
|
+
// SCAN RESULTS DISPLAY
|
|
147
|
+
// ═══════════════════════════════════════════════════════════════
|
|
148
|
+
|
|
149
|
+
function displayScanResults(results, scanType = "full") {
|
|
150
|
+
const filtered = results.filter(r => !r.excluded)
|
|
151
|
+
const excludedCount = results.filter(r => r.excluded).length
|
|
152
|
+
const threats = filtered.filter(r => !r.clean && !r.error)
|
|
153
|
+
const warnings = filtered.filter(r => r.clean && r.warnings && r.warnings.length > 0)
|
|
154
|
+
const clean = filtered.filter(r => r.clean && (!r.warnings || r.warnings.length === 0))
|
|
155
|
+
const errors = filtered.filter(r => r.error)
|
|
156
|
+
|
|
157
|
+
const scanTypeNames = {
|
|
158
|
+
full: "ПОЛНОЕ СКАНИРОВАНИЕ",
|
|
159
|
+
quick: "БЫСТРОЕ СКАНИРОВАНИЕ",
|
|
160
|
+
custom: "СКАНИРОВАНИЕ КАТАЛОГА",
|
|
161
|
+
"boot-sector": "СКАНИРОВАНИЕ MBR",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log()
|
|
165
|
+
console.log(box(scanTypeNames[scanType] || "СКАНИРОВАНИЕ", [
|
|
166
|
+
`Файлов: ${results.length}`,
|
|
167
|
+
`Угрозы: ${threats.length > 0 ? red(String(threats.length)) : green(String(threats.length))}`,
|
|
168
|
+
`Предупрежд.: ${warnings.length > 0 ? yellow(String(warnings.length)) : green(String(warnings.length))}`,
|
|
169
|
+
`Чисто: ${green(String(clean.length))}`,
|
|
170
|
+
`Исключено: ${dim(String(excludedCount))}`,
|
|
171
|
+
`Ошибки: ${errors.length > 0 ? red(String(errors.length)) : green(String(errors.length))}`,
|
|
172
|
+
], threats.length > 0 ? [255, 80, 80] : [0, 200, 100]))
|
|
173
|
+
|
|
174
|
+
if (threats.length > 0) {
|
|
175
|
+
console.log(`\n ${red(bold("══════ УГРОЗЫ ══════"))}`)
|
|
176
|
+
console.log()
|
|
177
|
+
for (const t of threats) {
|
|
178
|
+
console.log(` ${red("●")} ${bold(path.basename(t.filepath))}`)
|
|
179
|
+
console.log(` ${dim(t.filepath)}`)
|
|
180
|
+
if (t.threats && t.threats.length > 0) {
|
|
181
|
+
for (const d of t.threats) {
|
|
182
|
+
const color = severityColor(d.severity)
|
|
183
|
+
console.log(` ${color("▸")} ${d.message}`)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
console.log()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (warnings.length > 0) {
|
|
191
|
+
console.log(`\n ${yellow(bold("══════ ПРЕДУПРЕЖДЕНИЯ ══════"))}`)
|
|
192
|
+
console.log()
|
|
193
|
+
for (const w of warnings.slice(0, 20)) {
|
|
194
|
+
console.log(` ${yellow("●")} ${bold(path.basename(w.filepath))}`)
|
|
195
|
+
for (const d of w.warnings) {
|
|
196
|
+
const color = severityColor(d.severity)
|
|
197
|
+
console.log(` ${color("▸")} ${d.message}`)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (warnings.length > 20) {
|
|
201
|
+
console.log(` ${dim(`... и ещё ${warnings.length - 20} предупреждений`)}`)
|
|
202
|
+
}
|
|
203
|
+
console.log()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (threats.length === 0 && warnings.length === 0) {
|
|
207
|
+
printStatusBanner("safe")
|
|
208
|
+
} else {
|
|
209
|
+
printStatusBanner("danger")
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ═══════════════════════════════════════════════════════════════
|
|
214
|
+
// MAIN MENU
|
|
215
|
+
// ═══════════════════════════════════════════════════════════════
|
|
216
|
+
|
|
217
|
+
function printMenu() {
|
|
218
|
+
console.log(box("✦ Stellar AV — Команды", [
|
|
219
|
+
"",
|
|
220
|
+
` ${cyan("1")} │ ${bold("Полное сканирование")} Сканировать весь компьютер (C:\\, D:\\, ...)`,
|
|
221
|
+
` ${cyan("2")} │ ${bold("Быстрое сканирование")} Temp, Downloads, Desktop, Startup`,
|
|
222
|
+
` ${cyan("3")} │ ${bold("Сканировать файл")} Проверить один файл`,
|
|
223
|
+
` ${cyan("4")} │ ${bold("Сканировать папку")} Проверить директорию`,
|
|
224
|
+
` ${cyan("5")} │ ${bold("Сканировать MBR")} Проверить загрузочный сектор`,
|
|
225
|
+
` ${cyan("6")} │ ${bold("Процессы")} Проверить запущенные процессы`,
|
|
226
|
+
` ${cyan("7")} │ ${bold("Мониторинг 24/7")} Реальное время — следить за файлами`,
|
|
227
|
+
` ${cyan("8")} │ ${bold("Карантин")} Удалённые угрозы`,
|
|
228
|
+
` ${cyan("9")} │ ${bold("Исключения")} Список исключений`,
|
|
229
|
+
` ${cyan("10")}│ ${bold("Отчёт")} Сохранить отчёт в JSON`,
|
|
230
|
+
"",
|
|
231
|
+
` ${cyan("/exit")} │ ${dim("Выход")}`,
|
|
232
|
+
], [0, 200, 255]))
|
|
233
|
+
console.log()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ═══════════════════════════════════════════════════════════════
|
|
237
|
+
// INTERACTIVE MODE
|
|
238
|
+
// ═══════════════════════════════════════════════════════════════
|
|
239
|
+
|
|
240
|
+
export function startInteractive() {
|
|
241
|
+
const rl = readline.createInterface({
|
|
242
|
+
input: process.stdin,
|
|
243
|
+
output: process.stdout,
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
printBanner()
|
|
247
|
+
printStatusBanner("safe", "Stellar Antivirus запущен — готов к сканированию")
|
|
248
|
+
printMenu()
|
|
249
|
+
|
|
250
|
+
const ask = () => {
|
|
251
|
+
rl.question(`${magenta("❯")} `, async (input) => {
|
|
252
|
+
const cmd = input.trim().toLowerCase()
|
|
253
|
+
|
|
254
|
+
switch (cmd) {
|
|
255
|
+
case "1":
|
|
256
|
+
case "full":
|
|
257
|
+
case "полное": {
|
|
258
|
+
console.log()
|
|
259
|
+
console.log(` ${cyan("▶")} ${bold("Полное сканирование системы...")}`)
|
|
260
|
+
console.log(` ${dim("Сканирование всех дисков C:\\, D:\\, ...")}`)
|
|
261
|
+
console.log()
|
|
262
|
+
|
|
263
|
+
let lastUpdate = Date.now()
|
|
264
|
+
const { results, totalFiles } = fullSystemScan({
|
|
265
|
+
onProgress: (count, file) => {
|
|
266
|
+
if (Date.now() - lastUpdate > 100) {
|
|
267
|
+
process.stdout.write(`\r ${progressLine(count, Math.max(count, 500))} ${dim(path.basename(file).substring(0, 30))}`)
|
|
268
|
+
lastUpdate = Date.now()
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r")
|
|
274
|
+
displayScanResults(results, "full")
|
|
275
|
+
break
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
case "2":
|
|
279
|
+
case "quick":
|
|
280
|
+
case "быстрое": {
|
|
281
|
+
console.log()
|
|
282
|
+
console.log(` ${cyan("▶")} ${bold("Быстрое сканирование...")}`)
|
|
283
|
+
console.log(` ${dim("Temp, Downloads, Desktop, Startup, AppData")}`)
|
|
284
|
+
console.log()
|
|
285
|
+
|
|
286
|
+
let lastUpdate = Date.now()
|
|
287
|
+
const { results } = quickScan({
|
|
288
|
+
onProgress: (count, file) => {
|
|
289
|
+
if (Date.now() - lastUpdate > 100) {
|
|
290
|
+
process.stdout.write(`\r ${progressLine(count, Math.max(count, 200))} ${dim(path.basename(file).substring(0, 30))}`)
|
|
291
|
+
lastUpdate = Date.now()
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r")
|
|
297
|
+
displayScanResults(results, "quick")
|
|
298
|
+
break
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
case "3":
|
|
302
|
+
case "file":
|
|
303
|
+
case "файл": {
|
|
304
|
+
rl.question(` Введите путь к файлу: `, (filepath) => {
|
|
305
|
+
if (!filepath.trim()) { ask(); return }
|
|
306
|
+
console.log()
|
|
307
|
+
const result = scanFile(filepath.trim())
|
|
308
|
+
if (result.error) {
|
|
309
|
+
console.log(` ${red("✗")} ${result.error}`)
|
|
310
|
+
} else {
|
|
311
|
+
displayScanResults([result], "custom")
|
|
312
|
+
}
|
|
313
|
+
ask()
|
|
314
|
+
})
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
case "4":
|
|
319
|
+
case "folder":
|
|
320
|
+
case "папка": {
|
|
321
|
+
rl.question(` Введите путь к папке: `, (dirpath) => {
|
|
322
|
+
if (!dirpath.trim()) { ask(); return }
|
|
323
|
+
console.log()
|
|
324
|
+
console.log(` ${cyan("▶")} ${bold("Сканирование:")} ${dirpath}`)
|
|
325
|
+
console.log()
|
|
326
|
+
|
|
327
|
+
let lastUpdate = Date.now()
|
|
328
|
+
const { results } = scanPath(dirpath.trim(), {
|
|
329
|
+
onProgress: (count, file) => {
|
|
330
|
+
if (Date.now() - lastUpdate > 100) {
|
|
331
|
+
process.stdout.write(`\r ${progressLine(count, Math.max(count, 100))} ${dim(path.basename(file).substring(0, 30))}`)
|
|
332
|
+
lastUpdate = Date.now()
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
process.stdout.write("\r" + " ".repeat(80) + "\r")
|
|
338
|
+
displayScanResults(results, "custom")
|
|
339
|
+
ask()
|
|
340
|
+
})
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
case "5":
|
|
345
|
+
case "mbr":
|
|
346
|
+
case "загрузчик": {
|
|
347
|
+
console.log()
|
|
348
|
+
console.log(` ${cyan("▶")} ${bold("Сканирование MBR (Master Boot Record)...")}`)
|
|
349
|
+
console.log()
|
|
350
|
+
|
|
351
|
+
const drives = ["C:", "D:", "E:"]
|
|
352
|
+
for (const drive of drives) {
|
|
353
|
+
if (fs.existsSync(drive + "\\")) {
|
|
354
|
+
console.log(` Проверка ${drive}\\ ...`)
|
|
355
|
+
const result = scanBootSector(drive)
|
|
356
|
+
if (result.error) {
|
|
357
|
+
console.log(` ${yellow("⚠")} ${result.error}`)
|
|
358
|
+
} else {
|
|
359
|
+
if (!result.clean) {
|
|
360
|
+
console.log(` ${red("✗")} ${drive} MBR: ОБНАРУЖЕНА УГРОЗА!`)
|
|
361
|
+
for (const t of result.threats) console.log(` ${red("▸")} ${t.message}`)
|
|
362
|
+
} else {
|
|
363
|
+
console.log(` ${green("✓")} ${drive} MBR: чисто`)
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
console.log()
|
|
369
|
+
break
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
case "6":
|
|
373
|
+
case "processes":
|
|
374
|
+
case "процессы": {
|
|
375
|
+
console.log()
|
|
376
|
+
console.log(` ${cyan("▶")} ${bold("Проверка запущенных процессов...")}`)
|
|
377
|
+
console.log()
|
|
378
|
+
|
|
379
|
+
const procs = scanRunningProcesses()
|
|
380
|
+
if (procs.length === 0) {
|
|
381
|
+
console.log(` ${yellow("⚠")} Не удалось получить список процессов`)
|
|
382
|
+
} else {
|
|
383
|
+
const susp = procs.filter(p => p.suspicious)
|
|
384
|
+
const normal = procs.filter(p => !p.suspicious)
|
|
385
|
+
|
|
386
|
+
console.log(` Всего процессов: ${procs.length}`)
|
|
387
|
+
console.log(` ${green("Чистых:")} ${normal.length}`)
|
|
388
|
+
if (susp.length > 0) {
|
|
389
|
+
console.log(` ${red("Подозрительных:")} ${susp.length}`)
|
|
390
|
+
console.log()
|
|
391
|
+
console.log(` ${red(bold("══════ ПОДОЗРИТЕЛЬНЫЕ ══════"))}`)
|
|
392
|
+
for (const p of susp) {
|
|
393
|
+
console.log(` ${red("●")} ${bold(p.name)} (PID: ${p.pid}, Память: ${p.memory})`)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
console.log()
|
|
398
|
+
break
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
case "7":
|
|
402
|
+
case "monitor":
|
|
403
|
+
case "мониторинг": {
|
|
404
|
+
const status = getMonitorStatus()
|
|
405
|
+
if (status.active) {
|
|
406
|
+
console.log()
|
|
407
|
+
console.log(` ${green("✓")} Мониторинг активен`)
|
|
408
|
+
console.log(` ${dim("Отслеживаемых файлов:")} ${status.trackedFiles}`)
|
|
409
|
+
console.log()
|
|
410
|
+
rl.question(` Остановить мониторинг? (y/n): `, (ans) => {
|
|
411
|
+
if (ans.toLowerCase() === "y" || ans.toLowerCase() === "да") {
|
|
412
|
+
stopRealTimeMonitor()
|
|
413
|
+
console.log(` ${green("✓")} Мониторинг остановлен`)
|
|
414
|
+
}
|
|
415
|
+
ask()
|
|
416
|
+
})
|
|
417
|
+
return
|
|
418
|
+
} else {
|
|
419
|
+
rl.question(` Путь для мониторинга (Enter = текущая папка): `, (dirpath) => {
|
|
420
|
+
const dir = dirpath.trim() || "."
|
|
421
|
+
console.log()
|
|
422
|
+
console.log(` ${cyan("▶")} ${bold("Запуск мониторинга:")} ${dir}`)
|
|
423
|
+
console.log(` ${dim("Проверка каждые 5 секунд...")}`)
|
|
424
|
+
console.log(` ${dim("Нажмите Ctrl+C для остановки")}`)
|
|
425
|
+
console.log()
|
|
426
|
+
|
|
427
|
+
startRealTimeMonitor(dir, {
|
|
428
|
+
interval: 5000,
|
|
429
|
+
onThreat: (result) => {
|
|
430
|
+
console.log()
|
|
431
|
+
console.log(` ${red("⚠ ОБНАРУЖЕНА УГРОЗА!")}`)
|
|
432
|
+
console.log(` ${bold(path.basename(result.filepath))}`)
|
|
433
|
+
for (const t of result.threats) {
|
|
434
|
+
console.log(` ${red("▸")} ${t.message}`)
|
|
435
|
+
}
|
|
436
|
+
console.log()
|
|
437
|
+
},
|
|
438
|
+
onFileChange: (file, type) => {
|
|
439
|
+
const icon = type === "new" ? green("+") : type === "modified" ? yellow("~") : red("-")
|
|
440
|
+
process.stdout.write(`\r ${icon} ${dim(path.basename(file).substring(0, 50))}`)
|
|
441
|
+
},
|
|
442
|
+
})
|
|
443
|
+
console.log()
|
|
444
|
+
ask()
|
|
445
|
+
})
|
|
446
|
+
return
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
case "8":
|
|
451
|
+
case "quarantine":
|
|
452
|
+
case "карантин": {
|
|
453
|
+
const items = listQuarantine()
|
|
454
|
+
console.log()
|
|
455
|
+
if (items.length === 0) {
|
|
456
|
+
console.log(` ${green("✓")} Карантин пуст`)
|
|
457
|
+
} else {
|
|
458
|
+
console.log(box("Карантин", [`Файлов: ${items.length}`], [255, 200, 0]))
|
|
459
|
+
console.log()
|
|
460
|
+
for (const item of items) {
|
|
461
|
+
console.log(` ${yellow("●")} ${bold(item.originalPath)}`)
|
|
462
|
+
console.log(` ${dim("Дата:")} ${item.timestamp}`)
|
|
463
|
+
console.log()
|
|
464
|
+
}
|
|
465
|
+
rl.question(` Восстановить файл? (y/n): `, (ans) => {
|
|
466
|
+
if (ans.toLowerCase() === "y") {
|
|
467
|
+
rl.question(` Номер файла (1-${items.length}): `, (num) => {
|
|
468
|
+
const idx = parseInt(num) - 1
|
|
469
|
+
if (idx >= 0 && idx < items.length) {
|
|
470
|
+
const quarantinePath = path.join(QUARANTINE_DIR, items[idx].file)
|
|
471
|
+
const result = restoreFromQuarantine(quarantinePath)
|
|
472
|
+
if (result.error) {
|
|
473
|
+
console.log(` ${red("✗")} ${result.error}`)
|
|
474
|
+
} else {
|
|
475
|
+
console.log(` ${green("✓")} Восстановлено: ${result.restored}`)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
ask()
|
|
479
|
+
})
|
|
480
|
+
return
|
|
481
|
+
}
|
|
482
|
+
ask()
|
|
483
|
+
})
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
break
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
case "9":
|
|
490
|
+
case "exclusions":
|
|
491
|
+
case "исключения": {
|
|
492
|
+
const excs = getExclusions()
|
|
493
|
+
console.log()
|
|
494
|
+
console.log(box("Исключения", [`Правил: ${excs.length}`], [0, 200, 255]))
|
|
495
|
+
console.log()
|
|
496
|
+
for (const exc of excs) {
|
|
497
|
+
console.log(` ${cyan("●")} ${exc}`)
|
|
498
|
+
}
|
|
499
|
+
console.log()
|
|
500
|
+
rl.question(` Добавить исключение (Enter = пропустить): `, (pattern) => {
|
|
501
|
+
if (pattern.trim()) {
|
|
502
|
+
addExclusion(pattern.trim())
|
|
503
|
+
console.log(` ${green("✓")} Добавлено: ${pattern.trim()}`)
|
|
504
|
+
}
|
|
505
|
+
ask()
|
|
506
|
+
})
|
|
507
|
+
return
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
case "10":
|
|
511
|
+
case "report":
|
|
512
|
+
case "отчёт": {
|
|
513
|
+
console.log()
|
|
514
|
+
rl.question(` Тип отчёта (full/quick): `, (type) => {
|
|
515
|
+
console.log(` ${cyan("▶")} ${bold("Создание отчёта...")}`)
|
|
516
|
+
console.log()
|
|
517
|
+
|
|
518
|
+
const scanFunc = type === "quick" ? quickScan : fullSystemScan
|
|
519
|
+
const { results } = scanFunc()
|
|
520
|
+
const report = generateReport(results, type || "full")
|
|
521
|
+
|
|
522
|
+
const filename = `stellar-report-${new Date().toISOString().slice(0, 10)}.json`
|
|
523
|
+
saveReport(report, filename)
|
|
524
|
+
console.log(` ${green("✓")} Отчёт сохранён: ${filename}`)
|
|
525
|
+
console.log()
|
|
526
|
+
ask()
|
|
527
|
+
})
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
case "/exit":
|
|
532
|
+
case "exit":
|
|
533
|
+
case "выход": {
|
|
534
|
+
console.log()
|
|
535
|
+
console.log(` ${magenta("✦")} ${dim("Stellar Antivirus — до встречи!")}`)
|
|
536
|
+
console.log()
|
|
537
|
+
rl.close()
|
|
538
|
+
return
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
case "/help":
|
|
542
|
+
case "help":
|
|
543
|
+
case "помощь": {
|
|
544
|
+
printMenu()
|
|
545
|
+
break
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
default: {
|
|
549
|
+
console.log(` ${yellow("⚠")} Неизвестная команда. Введите ${cyan("/help")} для справки.`)
|
|
550
|
+
console.log()
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
ask()
|
|
555
|
+
})
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
ask()
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// ═══════════════════════════════════════════════════════════════
|
|
562
|
+
// EXPORTS
|
|
563
|
+
// ═══════════════════════════════════════════════════════════════
|
|
564
|
+
|
|
565
|
+
export { printBanner, displayScanResults, box, statusIcon, severityColor }
|
|
566
|
+
|
|
567
|
+
// Run if executed directly
|
|
568
|
+
if (process.argv[1] && process.argv[1].includes("ui.mjs")) {
|
|
569
|
+
startInteractive()
|
|
570
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Stella Coder 5.0
|
|
2
|
+
|
|
3
|
+
> AI coding agent with Telegram bot, huge context, TDD, Git ecosystem, and more — all in your terminal.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **AI Agent** — Read, create, edit files; run shell commands; search code
|
|
8
|
+
- **100+ commands** — `/help`, `/model`, `/plan`, `/commit`, `/exec`, `/open`, `/tv`, etc.
|
|
9
|
+
- **20+ AI models** — Free defaults (MiMo, DeepSeek), plus GPT, Claude, Gemini
|
|
10
|
+
- **Telegram Bot** — Control your computer remotely from Telegram
|
|
11
|
+
- **Coding Brain** — Huge context (200K tokens), auto-load SPEC/CLAUDE.md
|
|
12
|
+
- **TDD** — Auto-write tests, run linters, fix code
|
|
13
|
+
- **Git Ecosystem** — Branches, PRs, merge conflicts resolution
|
|
14
|
+
- **Presentations** — Create beautiful HTML presentations
|
|
15
|
+
- **Computer control** — Screenshot, volume, brightness, WiFi, notifications
|
|
16
|
+
- **Server management** — SSH, Docker, PM2, ports, firewall
|
|
17
|
+
- **Smart home** — Sony TV, HDMI-CEC, Yeelight, Chromecast, Wake-on-LAN
|
|
18
|
+
- **Office automation** — PowerPoint, Word, Excel via COM
|
|
19
|
+
- **Antivirus** — Built-in scanner with 100+ signatures
|
|
20
|
+
|
|
21
|
+
## Quick Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g stella-coder
|
|
25
|
+
stella
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
stella # Interactive REPL
|
|
32
|
+
stella -p "fix bug" # One-shot command
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Telegram Bot
|
|
36
|
+
|
|
37
|
+
1. Start bot: `/tg`
|
|
38
|
+
2. Generate code: `/tg-code`
|
|
39
|
+
3. Share code with friends
|
|
40
|
+
4. Friends enter code in @stella_CLI_bot
|
|
41
|
+
5. They can now control your computer!
|
|
42
|
+
|
|
43
|
+
## Key Commands
|
|
44
|
+
|
|
45
|
+
| Command | Description |
|
|
46
|
+
|---------|-------------|
|
|
47
|
+
| `/help` | Show all commands |
|
|
48
|
+
| `/brain` | Project diagnostics |
|
|
49
|
+
| `/tdd <file>` | Auto-write tests |
|
|
50
|
+
| `/fix-all` | Lint + format + types + tests |
|
|
51
|
+
| `/tg` | Start Telegram bot |
|
|
52
|
+
| `/tg-code` | Generate access code |
|
|
53
|
+
| `/presentation <topic>` | Create presentation |
|
|
54
|
+
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
First run creates `~/.stella/config.json` with your API key and preferences.
|
|
58
|
+
|
|
59
|
+
**Free models work without API key** — just install [Ollama](https://ollama.com/download).
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
**Stella Coder 5.0** · powered by codex alex
|