stella-coder 3.9.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.
@@ -0,0 +1,100 @@
1
+ import { bold, dim, italic, violet, blue, cyan, purple, gray, bgRgb, rgb } from "./theme.mjs"
2
+
3
+ const codeBg = bgRgb(30, 27, 55)
4
+ const codeFg = rgb(196, 181, 253)
5
+
6
+ // Inline markdown: **bold**, *italic*, `code`, [link](url)
7
+ export function renderInline(text) {
8
+ let out = text
9
+ out = out.replace(/\*\*([^*]+)\*\*/g, (_, s) => bold(violet(s)))
10
+ out = out.replace(/(^|[^*])\*([^*\n]+)\*/g, (_, p, s) => p + italic(s))
11
+ out = out.replace(/`([^`]+)`/g, (_, s) => codeBg(codeFg(` ${s} `)))
12
+ out = out.replace(/\[([^\]]+)\][(]([^)]+)[)]/g, (_, label, url) => blue(label) + dim(` (${url})`))
13
+ return out
14
+ }
15
+
16
+ // Block markdown renderer for a complete markdown string
17
+ export function renderMarkdown(md) {
18
+ const lines = md.split("\n")
19
+ const out = []
20
+ let inCode = false
21
+ let codeLang = ""
22
+ let codeBuf = []
23
+
24
+ for (const line of lines) {
25
+ const fence = line.match(/^\s*```(\w*)/)
26
+ if (fence) {
27
+ if (!inCode) {
28
+ inCode = true
29
+ codeLang = fence[1] || ""
30
+ codeBuf = []
31
+ } else {
32
+ inCode = false
33
+ const width = Math.max(...codeBuf.map((l) => l.length), codeLang.length + 2, 20)
34
+ out.push(purple(" ╭─") + dim(codeLang ? ` ${codeLang} ` : "") + purple("─".repeat(Math.max(width - codeLang.length, 2)) + "╮"))
35
+ for (const cl of codeBuf) {
36
+ out.push(purple(" │ ") + codeFg(cl) + " ".repeat(width - cl.length + 1) + purple("│"))
37
+ }
38
+ out.push(purple(" ╰" + "─".repeat(width + 3) + "╯"))
39
+ }
40
+ continue
41
+ }
42
+ if (inCode) {
43
+ codeBuf.push(line)
44
+ continue
45
+ }
46
+ const h = line.match(/^(#{1,4})\s+(.*)/)
47
+ if (h) {
48
+ out.push("")
49
+ out.push(bold(violet(h[2])))
50
+ continue
51
+ }
52
+ const li = line.match(/^(\s*)[-*]\s+(.*)/)
53
+ if (li) {
54
+ out.push(li[1] + violet("•") + " " + renderInline(li[2]))
55
+ continue
56
+ }
57
+ const ol = line.match(/^(\s*)(\d+)\.\s+(.*)/)
58
+ if (ol) {
59
+ out.push(ol[1] + blue(ol[2] + ".") + " " + renderInline(ol[3]))
60
+ continue
61
+ }
62
+ const bq = line.match(/^\s*>\s?(.*)/)
63
+ if (bq) {
64
+ out.push(purple(" ▎") + dim(renderInline(bq[1])))
65
+ continue
66
+ }
67
+ if (/^\s*(---|\*\*\*)\s*$/.test(line)) {
68
+ out.push(dim(" " + "─".repeat(40)))
69
+ continue
70
+ }
71
+ out.push(renderInline(line))
72
+ }
73
+ if (inCode && codeBuf.length) {
74
+ for (const cl of codeBuf) out.push(codeFg(" " + cl))
75
+ }
76
+ return out.join("\n")
77
+ }
78
+
79
+ // Streaming renderer: buffers text and flushes rendered lines as they complete
80
+ export function createStreamRenderer(write) {
81
+ let buf = ""
82
+ return {
83
+ push(delta) {
84
+ buf += delta
85
+ const idx = buf.lastIndexOf("\n")
86
+ if (idx === -1) return
87
+ // Don't flush while inside an unclosed code fence
88
+ const flushable = buf.slice(0, idx + 1)
89
+ const fences = (flushable.match(/```/g) || []).length
90
+ if (fences % 2 !== 0) return
91
+ write(renderMarkdown(flushable.replace(/\n$/, "")) + "\n")
92
+ buf = buf.slice(idx + 1)
93
+ },
94
+ flush() {
95
+ if (buf.trim()) write(renderMarkdown(buf) + "\n")
96
+ else if (buf) write(buf)
97
+ buf = ""
98
+ },
99
+ }
100
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "main": "index.mjs",
3
+ "output": "sea-prep.blob",
4
+ "disableExperimentalSEAWarning": true
5
+ }
@@ -0,0 +1,237 @@
1
+ import crypto from "node:crypto"
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+ import os from "node:os"
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 STELLA_HIDDEN = path.join(os.homedir(), ".stella", ".secure")
12
+ const KEY_FILE = path.join(STELLA_HIDDEN, "vault.dat")
13
+ const FINGERPRINT_FILE = path.join(STELLA_HIDDEN, ".fp")
14
+ const INTEGRITY_FILE = path.join(STELLA_HIDDEN, ".integrity")
15
+ const LOCKOUT_FILE = path.join(STELLA_HIDDEN, ".lockout")
16
+
17
+ const CLI_FILES = [
18
+ "stella-cli/index.mjs",
19
+ "stella-cli/tools.mjs",
20
+ "stella-cli/banner.mjs",
21
+ "stella-cli/theme.mjs",
22
+ "stella-cli/markdown.mjs",
23
+ "stella-cli/security.mjs",
24
+ ]
25
+
26
+ function ensureDir() {
27
+ fs.mkdirSync(STELLA_HIDDEN, { recursive: true })
28
+ try {
29
+ if (process.platform === "win32") {
30
+ execSync(`attrib +h "${STELLA_HIDDEN}"`, { stdio: "ignore" })
31
+ }
32
+ } catch {}
33
+ }
34
+
35
+ const PBKDF2_ITERATIONS = 100000
36
+ const SALT = "stella-vault-v3-2026"
37
+
38
+ function deriveKey(password) {
39
+ return crypto.pbkdf2Sync(password, SALT, PBKDF2_ITERATIONS, 32, "sha512")
40
+ }
41
+
42
+ function aesEncrypt(plaintext, password) {
43
+ const key = deriveKey(password)
44
+ const iv = crypto.randomBytes(12)
45
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv)
46
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()])
47
+ const authTag = cipher.getAuthTag()
48
+ const payload = Buffer.concat([iv, authTag, encrypted])
49
+ return payload.toString("base64")
50
+ }
51
+
52
+ function aesDecrypt(encryptedBase64, password) {
53
+ const key = deriveKey(password)
54
+ const payload = Buffer.from(encryptedBase64, "base64")
55
+ if (payload.length < 28) return null
56
+ const iv = payload.subarray(0, 12)
57
+ const authTag = payload.subarray(12, 28)
58
+ const encrypted = payload.subarray(28)
59
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv)
60
+ decipher.setAuthTag(authTag)
61
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()])
62
+ return decrypted.toString("utf8")
63
+ }
64
+
65
+ function xorEncrypt(data, key) {
66
+ return aesEncrypt(data, key)
67
+ }
68
+
69
+ function xorDecrypt(encrypted, key) {
70
+ try { return aesDecrypt(encrypted, key) } catch { return null }
71
+ }
72
+
73
+ function getHardwareFingerprint() {
74
+ const parts = []
75
+ try {
76
+ if (process.platform === "win32") {
77
+ const cpuId = execSync(
78
+ 'wmic cpu get ProcessorId /value 2>nul | findstr ProcessorId',
79
+ { encoding: "utf8", timeout: 5000 }
80
+ ).trim()
81
+ parts.push(cpuId)
82
+
83
+ const mbSerial = execSync(
84
+ 'wmic baseboard get SerialNumber /value 2>nul | findstr SerialNumber',
85
+ { encoding: "utf8", timeout: 5000 }
86
+ ).trim()
87
+ parts.push(mbSerial)
88
+
89
+ const biosSerial = execSync(
90
+ 'wmic bios get SerialNumber /value 2>nul | findstr SerialNumber',
91
+ { encoding: "utf8", timeout: 5000 }
92
+ ).trim()
93
+ parts.push(biosSerial)
94
+
95
+ const diskSerial = execSync(
96
+ 'wmic diskdrive get SerialNumber /value 2>nul | findstr SerialNumber',
97
+ { encoding: "utf8", timeout: 5000 }
98
+ ).trim()
99
+ parts.push(diskSerial)
100
+ } else if (process.platform === "darwin") {
101
+ const ioPlatformUuid = execSync(
102
+ "ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { print $3 }'",
103
+ { encoding: "utf8", timeout: 5000 }
104
+ ).trim()
105
+ parts.push(ioPlatformUuid)
106
+ } else {
107
+ const mid = execSync("cat /var/lib/dbus/machine-id 2>/dev/null || cat /etc/machine-id 2>/dev/null", {
108
+ encoding: "utf8",
109
+ timeout: 5000,
110
+ }).trim()
111
+ parts.push(mid)
112
+ }
113
+
114
+ const nets = os.networkInterfaces()
115
+ for (const name of Object.keys(nets)) {
116
+ for (const net of nets[name]) {
117
+ if (net.mac && net.mac !== "00:00:00:00:00:00") {
118
+ parts.push(net.mac)
119
+ }
120
+ }
121
+ }
122
+ } catch {}
123
+
124
+ if (parts.length === 0) {
125
+ parts.push(os.hostname())
126
+ parts.push(os.cpus()[0]?.model || "unknown")
127
+ parts.push(os.totalmem().toString())
128
+ }
129
+
130
+ return crypto.createHash("sha512").update(parts.join(":::")).digest("hex")
131
+ }
132
+
133
+ function getEncryptionKey() {
134
+ return crypto.createHash("sha256").update("stella-vault-portable-2026").digest("hex")
135
+ }
136
+
137
+ function computeCodeHash() {
138
+ const projectRoot = path.resolve(__dirname, "..")
139
+ let combined = ""
140
+ for (const file of CLI_FILES) {
141
+ const full = path.join(projectRoot, file)
142
+ try {
143
+ combined += fs.readFileSync(full, "utf8")
144
+ } catch {}
145
+ }
146
+ return crypto.createHash("sha256").update(combined).digest("hex")
147
+ }
148
+
149
+ export function verifyCodeIntegrity() {
150
+ ensureDir()
151
+ const currentHash = computeCodeHash()
152
+
153
+ if (fs.existsSync(INTEGRITY_FILE)) {
154
+ const saved = fs.readFileSync(INTEGRITY_FILE, "utf8").trim()
155
+ if (saved !== currentHash) {
156
+ return { ok: true, warning: "Код был изменён (ожидается при разработке)" }
157
+ }
158
+ } else {
159
+ fs.writeFileSync(INTEGRITY_FILE, currentHash, "utf8")
160
+ }
161
+
162
+ return { ok: true }
163
+ }
164
+
165
+ export function saveIntegrityHash() {
166
+ ensureDir()
167
+ fs.writeFileSync(INTEGRITY_FILE, computeCodeHash(), "utf8")
168
+ }
169
+
170
+ export function getApiKey() {
171
+ ensureDir()
172
+
173
+ if (!fs.existsSync(KEY_FILE)) return null
174
+
175
+ try {
176
+ const encrypted = fs.readFileSync(KEY_FILE, "utf8").trim()
177
+ const encKey = getEncryptionKey()
178
+ const apiKey = xorDecrypt(encrypted, encKey)
179
+
180
+ if (!apiKey || apiKey.length < 10) return null
181
+
182
+ return { apiKey }
183
+ } catch {
184
+ return null
185
+ }
186
+ }
187
+
188
+ export function saveApiKey(apiKey) {
189
+ ensureDir()
190
+
191
+ if (!apiKey || typeof apiKey !== "string" || apiKey.length < 10) {
192
+ return { ok: false, error: "Неверный формат API ключа" }
193
+ }
194
+
195
+ const encKey = getEncryptionKey()
196
+ const encrypted = xorEncrypt(apiKey, encKey)
197
+ fs.writeFileSync(KEY_FILE, encrypted, "utf8")
198
+
199
+ saveIntegrityHash()
200
+
201
+ try {
202
+ if (process.platform === "win32") {
203
+ execSync(`attrib +h +s "${KEY_FILE}"`, { stdio: "ignore" })
204
+ execSync(`attrib +h +s "${INTEGRITY_FILE}"`, { stdio: "ignore" })
205
+ }
206
+ } catch {}
207
+
208
+ return { ok: true }
209
+ }
210
+
211
+ export function deleteApiKey() {
212
+ ensureDir()
213
+ try {
214
+ if (fs.existsSync(KEY_FILE)) fs.unlinkSync(KEY_FILE)
215
+ if (fs.existsSync(FINGERPRINT_FILE)) fs.unlinkSync(FINGERPRINT_FILE)
216
+ if (fs.existsSync(INTEGRITY_FILE)) fs.unlinkSync(INTEGRITY_FILE)
217
+ return { ok: true }
218
+ } catch (e) {
219
+ return { ok: false, error: e.message }
220
+ }
221
+ }
222
+
223
+ export function isKeyBoundToThisMachine() {
224
+ const result = getApiKey()
225
+ if (!result || result.error) return false
226
+ return true
227
+ }
228
+
229
+ export function getHardwareInfo() {
230
+ const fp = getHardwareFingerprint()
231
+ return {
232
+ fingerprint: fp.substring(0, 16) + "...",
233
+ platform: process.platform,
234
+ hostname: os.hostname(),
235
+ cpu: os.cpus()[0]?.model || "unknown",
236
+ }
237
+ }
@@ -0,0 +1,89 @@
1
+ // Stella Coder 3.9 — purple/blue ANSI theme
2
+ const ESC = "\x1b["
3
+
4
+ export const reset = `${ESC}0m`
5
+ export const bold = (s) => `${ESC}1m${s}${reset}`
6
+ export const dim = (s) => `${ESC}2m${s}${reset}`
7
+ export const italic = (s) => `${ESC}3m${s}${reset}`
8
+ export const underline = (s) => `${ESC}4m${s}${reset}`
9
+ export const inverse = (s) => `${ESC}7m${s}${reset}`
10
+ export const strikethrough = (s) => `${ESC}9m${s}${reset}`
11
+
12
+ export const rgb = (r, g, b) => (s) => `${ESC}38;2;${r};${g};${b}m${s}${reset}`
13
+ export const bgRgb = (r, g, b) => (s) => `${ESC}48;2;${r};${g};${b}m${s}${reset}`
14
+
15
+ // Palette: violet -> blue
16
+ export const violet = rgb(167, 139, 250) // #a78bfa
17
+ export const purple = rgb(139, 92, 246) // #8b5cf6
18
+ export const indigo = rgb(129, 140, 248) // #818cf8
19
+ export const blue = rgb(96, 165, 250) // #60a5fa
20
+ export const cyan = rgb(103, 232, 249) // #67e8f9
21
+ export const green = rgb(74, 222, 128)
22
+ export const red = rgb(248, 113, 113)
23
+ export const yellow = rgb(250, 204, 21)
24
+ export const gray = rgb(148, 163, 184)
25
+ export const darkGray = rgb(100, 116, 139)
26
+ export const white = rgb(237, 233, 254)
27
+
28
+ // Gradient stops from violet to blue
29
+ const STOPS = [
30
+ [167, 139, 250],
31
+ [139, 92, 246],
32
+ [129, 140, 248],
33
+ [99, 102, 241],
34
+ [96, 165, 250],
35
+ ]
36
+
37
+ function lerp(a, b, t) {
38
+ return Math.round(a + (b - a) * t)
39
+ }
40
+
41
+ export function gradientLine(text) {
42
+ const chars = [...text]
43
+ const n = Math.max(chars.length - 1, 1)
44
+ let out = ""
45
+ for (let i = 0; i < chars.length; i++) {
46
+ const t = (i / n) * (STOPS.length - 1)
47
+ const idx = Math.min(Math.floor(t), STOPS.length - 2)
48
+ const f = t - idx
49
+ const [r1, g1, b1] = STOPS[idx]
50
+ const [r2, g2, b2] = STOPS[idx + 1]
51
+ out += `${ESC}38;2;${lerp(r1, r2, f)};${lerp(g1, g2, f)};${lerp(b1, b2, f)}m${chars[i]}`
52
+ }
53
+ return out + reset
54
+ }
55
+
56
+ export function gradient(text) {
57
+ return text
58
+ .split("\n")
59
+ .map((l) => gradientLine(l))
60
+ .join("\n")
61
+ }
62
+
63
+ // Rounded box drawing
64
+ export function box(lines, { color = purple, padding = 1, title = "" } = {}) {
65
+ const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, "")
66
+ const width = Math.max(...lines.map((l) => strip(l).length), strip(title).length + 2)
67
+ const pad = " ".repeat(padding)
68
+ const inner = width + padding * 2
69
+ const top = title
70
+ ? color("╭─ ") + bold(violet(title)) + color(" " + "─".repeat(Math.max(inner - strip(title).length - 3, 0)) + "╮")
71
+ : color("╭" + "─".repeat(inner) + "╮")
72
+ const body = lines.map((l) => color("│") + pad + l + " ".repeat(width - strip(l).length) + pad + color("│"))
73
+ const bottom = color("╰" + "─".repeat(inner) + "╯")
74
+ return [top, ...body, bottom].join("\n")
75
+ }
76
+
77
+ export const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
78
+ export const SPINNER_WORDS = [
79
+ "Thinking",
80
+ "Pondering",
81
+ "Vibing",
82
+ "Computing",
83
+ "Reasoning",
84
+ "Conjuring",
85
+ "Synthesizing",
86
+ "Brewing",
87
+ "Crafting",
88
+ "Weaving",
89
+ ]