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.
- package/(/320/270/320/274/321/217 +0 -0
- package/.env.example +4 -0
- package/README.md +72 -0
- package/STELLA.md +6 -0
- package/antimalware/database.mjs +871 -0
- package/antimalware/index.mjs +8 -0
- package/antimalware/scanner.mjs +591 -0
- package/antimalware/ui.mjs +570 -0
- package/components.json +21 -0
- package/create_cats_pptx.py +121 -0
- package/eslint.config.mjs +16 -0
- package/hello.py +2 -0
- package/install.bat +35 -0
- package/next-env.d.ts +6 -0
- package/next.config.mjs +11 -0
- package/package.json +58 -0
- package/pnpm-workspace.yaml +7 -0
- package/postcss.config.mjs +8 -0
- package/publish.mjs +52 -0
- package/stella-cli/banner.mjs +46 -0
- package/stella-cli/build.mjs +151 -0
- package/stella-cli/index.mjs +3073 -0
- package/stella-cli/markdown.mjs +100 -0
- package/stella-cli/sea-config.json +5 -0
- package/stella-cli/security.mjs +237 -0
- package/stella-cli/theme.mjs +89 -0
- package/stella-cli/tools.mjs +3145 -0
- package/tsconfig.json +41 -0
|
@@ -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,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
|
+
]
|