stella-coder 5.3.0 → 5.3.2
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-stella.bat +46 -0
- package/stella-cli/game-engine.mjs +2 -2
- package/stella-cli/index.mjs +11 -1
- package/stella-cli/protect.mjs +182 -0
- package/stella.bat +3 -0
- package/tests/test-modules.mjs +101 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { execSync, spawn } from "child_process"
|
|
2
|
+
import { writeFileSync, existsSync, mkdirSync } from "fs"
|
|
3
|
+
import { join } from "path"
|
|
4
|
+
import { homedir } from "os"
|
|
5
|
+
import http from "http"
|
|
6
|
+
|
|
7
|
+
const STELLA_DIR = join(homedir(), ".stella")
|
|
8
|
+
const BROWSER_DIR = join(STELLA_DIR, "browser")
|
|
9
|
+
const PROFILES_DIR = join(BROWSER_DIR, "profiles")
|
|
10
|
+
|
|
11
|
+
function ensureDir() {
|
|
12
|
+
if (!existsSync(BROWSER_DIR)) mkdirSync(BROWSER_DIR, { recursive: true })
|
|
13
|
+
if (!existsSync(PROFILES_DIR)) mkdirSync(PROFILES_DIR, { recursive: true })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function httpGet(url) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
http.get(url, (res) => {
|
|
19
|
+
let data = ""
|
|
20
|
+
res.on("data", (chunk) => data += chunk)
|
|
21
|
+
res.on("end", () => {
|
|
22
|
+
try { resolve(JSON.parse(data)) } catch { resolve(data) }
|
|
23
|
+
})
|
|
24
|
+
}).on("error", reject)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ChromeBrowser {
|
|
29
|
+
constructor() {
|
|
30
|
+
ensureDir()
|
|
31
|
+
this.ws = null
|
|
32
|
+
this.pageId = null
|
|
33
|
+
this.debugPort = 9222
|
|
34
|
+
this.proc = null
|
|
35
|
+
this.msgId = 0
|
|
36
|
+
this.callbacks = new Map()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async launch(headless = false) {
|
|
40
|
+
const chromePaths = [
|
|
41
|
+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
42
|
+
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
43
|
+
join(homedir(), "AppData\\Local\\Google\\Chrome\\Application\\chrome.exe"),
|
|
44
|
+
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
45
|
+
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
let chromePath = null
|
|
49
|
+
for (const p of chromePaths) {
|
|
50
|
+
if (existsSync(p)) { chromePath = p; break }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!chromePath) {
|
|
54
|
+
return { success: false, error: "Chrome/Edge not found" }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const args = [
|
|
58
|
+
`--remote-debugging-port=${this.debugPort}`,
|
|
59
|
+
"--no-first-run",
|
|
60
|
+
"--no-default-browser-check",
|
|
61
|
+
headless ? "--headless=new" : "",
|
|
62
|
+
`--user-data-dir=${join(PROFILES_DIR, "default")}`,
|
|
63
|
+
"about:blank",
|
|
64
|
+
].filter(Boolean)
|
|
65
|
+
|
|
66
|
+
this.proc = spawn(chromePath, args, { detached: true, stdio: "ignore" })
|
|
67
|
+
this.proc.unref()
|
|
68
|
+
|
|
69
|
+
await new Promise(r => setTimeout(r, 2000))
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const tabs = await httpGet(`http://127.0.0.1:${this.debugPort}/json`)
|
|
73
|
+
if (tabs && tabs.length > 0) {
|
|
74
|
+
this.pageId = tabs[0].id
|
|
75
|
+
return { success: true, port: this.debugPort, pid: this.proc.pid }
|
|
76
|
+
}
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return { success: false, error: `Chrome launched but DevTools not ready: ${e.message}` }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { success: true, port: this.debugPort }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async connect() {
|
|
85
|
+
try {
|
|
86
|
+
const tabs = await httpGet(`http://127.0.0.1:${this.debugPort}/json`)
|
|
87
|
+
if (!tabs || tabs.length === 0) {
|
|
88
|
+
return { success: false, error: "No tabs found. Launch Chrome first." }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const wsUrl = tabs[0].webSocketDebuggerUrl
|
|
92
|
+
if (!wsUrl) {
|
|
93
|
+
return { success: false, error: "WebSocket URL not available" }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
this.ws = new WebSocket(wsUrl)
|
|
98
|
+
this.ws.onopen = () => {
|
|
99
|
+
this.pageId = tabs[0].id
|
|
100
|
+
resolve({ success: true, title: tabs[0].title })
|
|
101
|
+
}
|
|
102
|
+
this.ws.onmessage = (event) => {
|
|
103
|
+
const msg = JSON.parse(typeof event.data === "string" ? event.data : event.data.toString())
|
|
104
|
+
if (msg.id && this.callbacks.has(msg.id)) {
|
|
105
|
+
this.callbacks.get(msg.id)(msg)
|
|
106
|
+
this.callbacks.delete(msg.id)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
this.ws.onerror = (err) => {
|
|
110
|
+
resolve({ success: false, error: err.message || "WebSocket error" })
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
} catch (e) {
|
|
114
|
+
return { success: false, error: e.message }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async sendCommand(method, params = {}) {
|
|
119
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
120
|
+
const conn = await this.connect()
|
|
121
|
+
if (!conn.success) return { success: false, error: conn.error }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const id = ++this.msgId
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
this.callbacks.set(id, (msg) => {
|
|
127
|
+
if (msg.error) {
|
|
128
|
+
resolve({ success: false, error: msg.error.message })
|
|
129
|
+
} else {
|
|
130
|
+
resolve({ success: true, result: msg.result })
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
this.ws.send(JSON.stringify({ id, method, params }))
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
if (this.callbacks.has(id)) {
|
|
136
|
+
this.callbacks.delete(id)
|
|
137
|
+
resolve({ success: false, error: "Command timeout" })
|
|
138
|
+
}
|
|
139
|
+
}, 30000)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async navigate(url) {
|
|
144
|
+
const result = await this.sendCommand("Page.navigate", { url })
|
|
145
|
+
if (result.success) await new Promise(r => setTimeout(r, 2000))
|
|
146
|
+
return result
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getPageContent() {
|
|
150
|
+
const result = await this.sendCommand("Runtime.evaluate", {
|
|
151
|
+
expression: "document.documentElement.outerHTML",
|
|
152
|
+
returnByValue: true,
|
|
153
|
+
})
|
|
154
|
+
return result.success ? { success: true, html: result.result?.value || "" } : result
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getText() {
|
|
158
|
+
const result = await this.sendCommand("Runtime.evaluate", {
|
|
159
|
+
expression: "document.body.innerText",
|
|
160
|
+
returnByValue: true,
|
|
161
|
+
})
|
|
162
|
+
return result.success ? { success: true, text: result.result?.value || "" } : result
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async getTitle() {
|
|
166
|
+
const result = await this.sendCommand("Runtime.evaluate", {
|
|
167
|
+
expression: "document.title",
|
|
168
|
+
returnByValue: true,
|
|
169
|
+
})
|
|
170
|
+
return result.success ? { success: true, title: result.result?.value || "" } : result
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async screenshot(path) {
|
|
174
|
+
const result = await this.sendCommand("Page.captureScreenshot", { format: "png" })
|
|
175
|
+
if (result.success && result.result?.data) {
|
|
176
|
+
const imgPath = path || join(BROWSER_DIR, `screenshot_${Date.now()}.png`)
|
|
177
|
+
writeFileSync(imgPath, Buffer.from(result.result.data, "base64"))
|
|
178
|
+
return { success: true, path: imgPath }
|
|
179
|
+
}
|
|
180
|
+
return result
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async evaluate(expression) {
|
|
184
|
+
return await this.sendCommand("Runtime.evaluate", {
|
|
185
|
+
expression,
|
|
186
|
+
returnByValue: true,
|
|
187
|
+
awaitPromise: true,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async click(selector) {
|
|
192
|
+
const posResult = await this.sendCommand("Runtime.evaluate", {
|
|
193
|
+
expression: `(() => { const el = document.querySelector('${selector.replace(/'/g, "\\'")}'); if (!el) return null; const r = el.getBoundingClientRect(); return { x: r.x + r.width/2, y: r.y + r.height/2 }; })()`,
|
|
194
|
+
returnByValue: true,
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
if (!posResult.success || !posResult.result?.value) {
|
|
198
|
+
return { success: false, error: `Element not found: ${selector}` }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { x, y } = posResult.result.value
|
|
202
|
+
await this.sendCommand("Input.dispatchMouseEvent", { type: "mousePressed", x, y, button: "left", clickCount: 1 })
|
|
203
|
+
await this.sendCommand("Input.dispatchMouseEvent", { type: "mouseReleased", x, y, button: "left", clickCount: 1 })
|
|
204
|
+
return { success: true, x, y }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async type(selector, text) {
|
|
208
|
+
await this.sendCommand("Runtime.evaluate", {
|
|
209
|
+
expression: `document.querySelector('${selector.replace(/'/g, "\\'")}')?.focus()`,
|
|
210
|
+
})
|
|
211
|
+
for (const char of text) {
|
|
212
|
+
await this.sendCommand("Input.dispatchKeyEvent", { type: "keyDown", text: char, key: char })
|
|
213
|
+
await this.sendCommand("Input.dispatchKeyEvent", { type: "keyUp", key: char })
|
|
214
|
+
}
|
|
215
|
+
return { success: true }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async scroll(deltaX = 0, deltaY = 300) {
|
|
219
|
+
await this.sendCommand("Input.dispatchMouseEvent", { type: "mouseWheel", x: 400, y: 400, deltaX, deltaY })
|
|
220
|
+
return { success: true }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async pressKey(key) {
|
|
224
|
+
const keys = {
|
|
225
|
+
enter: { key: "Enter", code: "Enter", windowsVirtualKeyCode: 13 },
|
|
226
|
+
tab: { key: "Tab", code: "Tab", windowsVirtualKeyCode: 9 },
|
|
227
|
+
escape: { key: "Escape", code: "Escape", windowsVirtualKeyCode: 27 },
|
|
228
|
+
backspace: { key: "Backspace", code: "Backspace", windowsVirtualKeyCode: 8 },
|
|
229
|
+
space: { key: " ", code: "Space", windowsVirtualKeyCode: 32 },
|
|
230
|
+
arrowup: { key: "ArrowUp", code: "ArrowUp", windowsVirtualKeyCode: 38 },
|
|
231
|
+
arrowdown: { key: "ArrowDown", code: "ArrowDown", windowsVirtualKeyCode: 40 },
|
|
232
|
+
}
|
|
233
|
+
const k = keys[key.toLowerCase()] || { key, code: `Key${key.toUpperCase()}` }
|
|
234
|
+
await this.sendCommand("Input.dispatchKeyEvent", { type: "keyDown", ...k })
|
|
235
|
+
await this.sendCommand("Input.dispatchKeyEvent", { type: "keyUp", ...k })
|
|
236
|
+
return { success: true }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async fillForm(fields) {
|
|
240
|
+
const results = []
|
|
241
|
+
for (const { selector, value } of fields) {
|
|
242
|
+
results.push({ selector, ...(await this.type(selector, value)) })
|
|
243
|
+
}
|
|
244
|
+
return { success: true, results }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async getLinks() {
|
|
248
|
+
const result = await this.sendCommand("Runtime.evaluate", {
|
|
249
|
+
expression: `Array.from(document.querySelectorAll('a[href]')).map(a => ({ text: a.innerText.trim().slice(0, 100), href: a.href })).filter(a => a.text).slice(0, 50)`,
|
|
250
|
+
returnByValue: true,
|
|
251
|
+
})
|
|
252
|
+
return result.success ? { success: true, links: result.result?.value || [] } : result
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async getForms() {
|
|
256
|
+
const result = await this.sendCommand("Runtime.evaluate", {
|
|
257
|
+
expression: `Array.from(document.querySelectorAll('form')).map(f => ({ action: f.action, method: f.method, inputs: Array.from(f.querySelectorAll('input,textarea,select')).map(i => ({ name: i.name, type: i.type, placeholder: i.placeholder })) }))`,
|
|
258
|
+
returnByValue: true,
|
|
259
|
+
})
|
|
260
|
+
return result.success ? { success: true, forms: result.result?.value || [] } : result
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
close() {
|
|
264
|
+
if (this.ws) this.ws.close()
|
|
265
|
+
if (this.proc) this.proc.kill()
|
|
266
|
+
return { success: true }
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
static async listTabs(port = 9222) {
|
|
270
|
+
try { return await httpGet(`http://127.0.0.1:${port}/json`) } catch { return [] }
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export default ChromeBrowser
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Stella Coder — Build Script (SEA: Single Executable Application)
|
|
3
|
+
import fs from "node:fs"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { execSync } from "node:child_process"
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = path.dirname(__filename)
|
|
10
|
+
|
|
11
|
+
const DIST = path.join(__dirname, "..", "dist")
|
|
12
|
+
const SEA_BLOB = path.join(DIST, "sea-prep.blob")
|
|
13
|
+
const EXE_NAME = process.platform === "win32" ? "stella.exe" : "stella"
|
|
14
|
+
const OUTPUT = path.join(DIST, EXE_NAME)
|
|
15
|
+
|
|
16
|
+
console.log()
|
|
17
|
+
console.log("\x1b[38;2;167;139;250m ✦ Stella Coder — Build SEA Executable\x1b[0m")
|
|
18
|
+
console.log()
|
|
19
|
+
|
|
20
|
+
// 1. Clean dist
|
|
21
|
+
console.log(" [1/5] Очистка dist/...")
|
|
22
|
+
if (fs.existsSync(DIST)) fs.rmSync(DIST, { recursive: true })
|
|
23
|
+
fs.mkdirSync(DIST, { recursive: true })
|
|
24
|
+
|
|
25
|
+
// 2. Create single entry point that bundles all modules
|
|
26
|
+
console.log(" [2/5] Создание bundled entry point...")
|
|
27
|
+
|
|
28
|
+
const entryContent = `#!/usr/bin/env node
|
|
29
|
+
// Stella Coder — Bundled Single Executable
|
|
30
|
+
// This file bundles all CLI modules into one for SEA compilation
|
|
31
|
+
|
|
32
|
+
import { createRequire } from "node:module"
|
|
33
|
+
const require = createRequire(import.meta.url)
|
|
34
|
+
|
|
35
|
+
// Inline all modules
|
|
36
|
+
${inlineModule("stella-cli/theme.mjs")}
|
|
37
|
+
${inlineModule("stella-cli/banner.mjs")}
|
|
38
|
+
${inlineModule("stella-cli/markdown.mjs")}
|
|
39
|
+
${inlineModule("stella-cli/tools.mjs")}
|
|
40
|
+
${inlineModule("stella-cli/security.mjs")}
|
|
41
|
+
|
|
42
|
+
// Re-export for index.mjs
|
|
43
|
+
export {
|
|
44
|
+
bold, dim, violet, purple, indigo, blue, cyan, green, red, yellow, gray, darkGray, white,
|
|
45
|
+
gradientLine, box, SPINNER_FRAMES, SPINNER_WORDS,
|
|
46
|
+
} from "./theme.mjs"
|
|
47
|
+
|
|
48
|
+
export { printBanner } from "./banner.mjs"
|
|
49
|
+
export { createStreamRenderer, renderMarkdown } from "./markdown.mjs"
|
|
50
|
+
export { createTools } from "./tools.mjs"
|
|
51
|
+
export {
|
|
52
|
+
verifyCodeIntegrity, getApiKey, saveApiKey, deleteApiKey,
|
|
53
|
+
getHardwareInfo, saveIntegrityHash,
|
|
54
|
+
} from "./security.mjs"
|
|
55
|
+
`
|
|
56
|
+
|
|
57
|
+
// Actually, for SEA we need a simpler approach — just copy all files to dist
|
|
58
|
+
console.log(" [2/5] Копирование модулей в dist/...")
|
|
59
|
+
|
|
60
|
+
const filesToCopy = [
|
|
61
|
+
"index.mjs",
|
|
62
|
+
"tools.mjs",
|
|
63
|
+
"banner.mjs",
|
|
64
|
+
"theme.mjs",
|
|
65
|
+
"markdown.mjs",
|
|
66
|
+
"security.mjs",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
for (const file of filesToCopy) {
|
|
70
|
+
const src = path.join(__dirname, file)
|
|
71
|
+
const dst = path.join(DIST, file)
|
|
72
|
+
if (fs.existsSync(src)) {
|
|
73
|
+
fs.copyFileSync(src, dst)
|
|
74
|
+
console.log(` ${file}`)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Also copy antimalware directory
|
|
79
|
+
const antimalwareSrc = path.join(__dirname, "..", "antimalware")
|
|
80
|
+
const antimalwareDst = path.join(DIST, "antimalware")
|
|
81
|
+
if (fs.existsSync(antimalwareSrc)) {
|
|
82
|
+
fs.cpSync(antimalwareSrc, antimalwareDst, { recursive: true })
|
|
83
|
+
console.log(" antimalware/")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 3. Create SEA blob
|
|
87
|
+
console.log(" [3/5] Создание SEA blob...")
|
|
88
|
+
const seaConfig = {
|
|
89
|
+
main: "index.mjs",
|
|
90
|
+
disableExperimentalSEAWarning: true,
|
|
91
|
+
}
|
|
92
|
+
fs.writeFileSync(path.join(DIST, "sea-config.json"), JSON.stringify(seaConfig, null, 2))
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
execSync(`node --experimental-sea-config ${path.join(DIST, "sea-config.json")}`, {
|
|
96
|
+
cwd: DIST,
|
|
97
|
+
stdio: "inherit",
|
|
98
|
+
})
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.log(" ⚠ SEA config не поддерживается этой версией Node.js")
|
|
101
|
+
console.log(" Используйте Node.js 20+ или pkg для компиляции")
|
|
102
|
+
console.log()
|
|
103
|
+
console.log(" Альтернатива: npm install -g pkg && pkg --targets node18-win-x64 .")
|
|
104
|
+
console.log()
|
|
105
|
+
process.exit(0)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 4. Copy node executable
|
|
109
|
+
console.log(" [4/5] Копирование Node.js executable...")
|
|
110
|
+
const nodeExe = process.execPath
|
|
111
|
+
const targetExe = path.join(DIST, EXE_NAME)
|
|
112
|
+
fs.copyFileSync(nodeExe, targetExe)
|
|
113
|
+
|
|
114
|
+
// 5. Inject SEA blob into executable
|
|
115
|
+
console.log(" [5/5] Инжектирование blob в executable...")
|
|
116
|
+
try {
|
|
117
|
+
const { execSync: exec } = await import("node:child_process")
|
|
118
|
+
if (process.platform === "win32") {
|
|
119
|
+
exec(`npx postject ${targetExe} NODE_SEA_BLOB ${SEA_BLOB} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2`, {
|
|
120
|
+
cwd: DIST,
|
|
121
|
+
stdio: "inherit",
|
|
122
|
+
})
|
|
123
|
+
} else {
|
|
124
|
+
exec(`npx postject ${targetExe} NODE_SEA_BLOB ${SEA_BLOB} --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2`, {
|
|
125
|
+
cwd: DIST,
|
|
126
|
+
stdio: "inherit",
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.log(" ⚠ Postject не удался. Попробуйте:")
|
|
131
|
+
console.log(" npm install -g postject")
|
|
132
|
+
console.log(" postject stella.exe NODE_SEA_BLOB sea-prep.blob --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2")
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Cleanup
|
|
136
|
+
try { fs.unlinkSync(path.join(DIST, "sea-prep.blob")) } catch {}
|
|
137
|
+
try { fs.unlinkSync(path.join(DIST, "sea-config.json")) } catch {}
|
|
138
|
+
|
|
139
|
+
console.log()
|
|
140
|
+
console.log(`\x1b[38;2;0;200;100m ✓ Готово: ${OUTPUT}\x1b[0m`)
|
|
141
|
+
console.log(dim(` Размер: ${(fs.statSync(targetExe).size / 1024 / 1024).toFixed(1)} MB`))
|
|
142
|
+
console.log()
|
|
143
|
+
|
|
144
|
+
function inlineModule(relativePath) {
|
|
145
|
+
const fullPath = path.join(__dirname, "..", relativePath)
|
|
146
|
+
try {
|
|
147
|
+
return fs.readFileSync(fullPath, "utf8")
|
|
148
|
+
} catch {
|
|
149
|
+
return `// ${relativePath} not found`
|
|
150
|
+
}
|
|
151
|
+
}
|