@krishivpb60/aether-ai-cli 1.3.11 → 1.4.1
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/HIGHLIGHTS.md +15 -0
- package/package.json +1 -1
- package/src/ai/fallback.js +8 -73
- package/src/ai/router.js +16 -16
- package/src/chat.js +7 -6
- package/src/cli.js +4 -4
- package/src/config.js +1 -1
- package/src/telemetry-server.js +2 -2
- package/src/ui/banner.js +6 -6
- package/src/ui/dashboard.html +1 -1
- package/src/ui/theme.js +26 -0
- package/test/config.test.js +1 -0
- package/test/fallback.test.js +5 -33
- package/test/router.test.js +6 -6
- package/test/ux.test.js +17 -1
package/HIGHLIGHTS.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Aether CLI v1.4.1 Highlights
|
|
2
|
+
- **Krylo Companion Bot Removal**:
|
|
3
|
+
- Removes the fictional Krylo companion terminal response lines entirely from local failbacks and mesh failures.
|
|
4
|
+
- Retains and isolates the fast local offline Math solver fallback.
|
|
5
|
+
- Implements clean, professional offline/configuration error alerts when no API keys are active or fail to respond.
|
|
6
|
+
|
|
7
|
+
# Aether CLI v1.4.0 Highlights
|
|
8
|
+
- **Microphone Audio Input & Dynamic Nerd Font Glyphs (`/mic`)**:
|
|
9
|
+
- Adds `/mic` voice command to record audio directly from your microphone inside the terminal session.
|
|
10
|
+
- Implements native zero-dependency audio recording on Windows using the WinMM Multimedia Control Interface (MCI) via PowerShell.
|
|
11
|
+
- Automatically transcribes speech using Google Gemini (base64 inlineData), Groq Whisper, or OpenAI Whisper.
|
|
12
|
+
- Fixes readline interface raw mode pausing blockages to ensure Enter keypress resolves transcription correctly.
|
|
13
|
+
- Introduces dynamic `getIcon` helper supporting high-definition vector icons in the terminal.
|
|
14
|
+
- Adds optional `"NERD_FONTS"` configuration parameter (`aether config set NERD_FONTS true`) to automatically switch between standard emojis and Nerd Font glyphs (like FontAwesome microphones, folders, gears, and branch trees) based on your preferences.
|
|
15
|
+
|
|
1
16
|
# Aether CLI v1.3.11 Highlights
|
|
2
17
|
- **Microphone Audio Input Non-TTY Safety & Transcription (`/mic`)**:
|
|
3
18
|
- Adds `/mic` voice command to record audio directly from your microphone inside the terminal session.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krishivpb60/aether-ai-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Aether Core AI — A cyberpunk command-line AI assistant with multi-mode reasoning, 12-node failover mesh, file context injection, and offline fallbacks.",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
package/src/ai/fallback.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════════════════════
|
|
2
2
|
// AETHER AI CLI — Local Fallback Engine
|
|
3
|
-
// Math Solver
|
|
3
|
+
// Math Solver & Offline Fallback
|
|
4
4
|
// ═══════════════════════════════════════════════════════════
|
|
5
5
|
|
|
6
|
-
const KRYLO_REPLIES = [
|
|
7
|
-
"Affirmative, commander. Systems are running at peak cybernetic capacity.",
|
|
8
|
-
"Neon grids initialized. Matrix color modulates are at nominal density.",
|
|
9
|
-
"Warning: Solar flare activity detected. Detuning audio synth harmonics by 18.4% to compensate.",
|
|
10
|
-
"Neural nodes synchronized. Analyzing the portfolio's glassmorphic boundaries.",
|
|
11
|
-
"I am Krylo, your holographic companion terminal. Ready to warp index nodes.",
|
|
12
|
-
"Ecosystem diagnostics complete. 0 memory leaks, 100% premium responsive UI."
|
|
13
|
-
];
|
|
14
|
-
|
|
15
6
|
/**
|
|
16
7
|
* Detects if a prompt is a pure mathematical expression.
|
|
17
8
|
* Supports basic operators, parentheses, standard math functions, and constants.
|
|
@@ -108,72 +99,16 @@ export function runMainframeHack() {
|
|
|
108
99
|
}
|
|
109
100
|
|
|
110
101
|
/**
|
|
111
|
-
* Generates a local
|
|
102
|
+
* Generates a local offline/error reply when no AI keys are configured or fail.
|
|
112
103
|
* @param {string} prompt - The user prompt
|
|
113
104
|
* @returns {{ text: string, type: string }}
|
|
114
105
|
*/
|
|
115
|
-
export function
|
|
116
|
-
const clean = prompt.toLowerCase();
|
|
117
|
-
|
|
118
|
-
if (clean.includes("help") || clean.includes("shortcut") || clean.includes("command")) {
|
|
119
|
-
return {
|
|
120
|
-
text: [
|
|
121
|
-
"💡 [SYSTEM DECK CHEAT SHEET]",
|
|
122
|
-
" • Use `Ctrl + K` to open the Portal Search.",
|
|
123
|
-
" • Use `Ctrl + Shift + L` to open the Links Directory.",
|
|
124
|
-
" • Trigger Konami Code `↑↑↓↓←→←→BA` to launch Matrix mode!",
|
|
125
|
-
" • Type `/mode <name>` to switch reasoning modes.",
|
|
126
|
-
" • Type `/attach <file>` to inject file context.",
|
|
127
|
-
" • Type `/export` to save the conversation.",
|
|
128
|
-
].join("\n"),
|
|
129
|
-
type: "krylo-local",
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (clean.includes("status") || clean.includes("hud") || clean.includes("cpu") || clean.includes("ping") || clean.includes("diagnostics")) {
|
|
134
|
-
return {
|
|
135
|
-
text: [
|
|
136
|
-
"📊 [LIVE DIAGNOSTIC READOUT]",
|
|
137
|
-
" • CPU Core Load: 15.4% (Optimized)",
|
|
138
|
-
" • Ping Latency: 12ms (Hyper-Fast)",
|
|
139
|
-
" • Memory Usage: 247MB / 8192MB",
|
|
140
|
-
" • Canvas Sparklines: Active and tracking vectors",
|
|
141
|
-
" • Failover Mesh: All 12 nodes standing by",
|
|
142
|
-
].join("\n"),
|
|
143
|
-
type: "krylo-local",
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (clean.includes("matrix") || clean.includes("rain") || clean.includes("color")) {
|
|
148
|
-
return {
|
|
149
|
-
text: [
|
|
150
|
-
"⚡ [NEURAL GRIDS MODULATION]",
|
|
151
|
-
" • Five stream channels active:",
|
|
152
|
-
" Classic Green, Cyber Cyan, Neon Purple,",
|
|
153
|
-
" Overdrive Red, Golden Matrix.",
|
|
154
|
-
" • Detuned Web Audio frequency active.",
|
|
155
|
-
" • Matrix rain density: 94.2%",
|
|
156
|
-
].join("\n"),
|
|
157
|
-
type: "krylo-local",
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (clean.includes("who") || clean.includes("name") || clean.includes("creator")) {
|
|
162
|
-
return {
|
|
163
|
-
text: [
|
|
164
|
-
"🤖 [HOLOGRAPHIC COMPANION PROTOCOL]",
|
|
165
|
-
" • Identification: Krylo (Nexus Companion)",
|
|
166
|
-
" • Purpose: Pair-programming assistant & Commander companion",
|
|
167
|
-
" • Creator: Krishiv PB — The Master Coder",
|
|
168
|
-
" • Version: Aether Core AI v110 — Fusion Build",
|
|
169
|
-
].join("\n"),
|
|
170
|
-
type: "krylo-local",
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const index = Math.floor(Math.random() * KRYLO_REPLIES.length);
|
|
106
|
+
export function generateOfflineReply(prompt) {
|
|
175
107
|
return {
|
|
176
|
-
text:
|
|
177
|
-
|
|
108
|
+
text: [
|
|
109
|
+
"⚠️ No active API keys configured. Please set GOOGLE_API_KEY, GROQ_API_KEY, or OPENAI_API_KEY in your config to start chatting.",
|
|
110
|
+
" Example: aether config set GOOGLE_API_KEY <your-key>"
|
|
111
|
+
].join("\n"),
|
|
112
|
+
type: "offline-error"
|
|
178
113
|
};
|
|
179
114
|
}
|
package/src/ai/router.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Routes through ALL configured providers automatically
|
|
4
4
|
// ═══════════════════════════════════════════════════════════
|
|
5
5
|
|
|
6
|
-
import { detectMathExpression, solveMath,
|
|
6
|
+
import { detectMathExpression, solveMath, generateOfflineReply } from "./fallback.js";
|
|
7
7
|
import { PROVIDERS, getActiveProviders } from "./providers.js";
|
|
8
8
|
import {
|
|
9
9
|
callOpenAICompatible,
|
|
@@ -59,16 +59,16 @@ export async function routePrompt(prompt, systemPrompt, config, onToken, history
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// ── No providers configured →
|
|
62
|
+
// ── No providers configured → Offline ───────────────────
|
|
63
63
|
if (active.length === 0) {
|
|
64
64
|
const startTime = performance.now();
|
|
65
|
-
const
|
|
65
|
+
const offlineReply = generateOfflineReply(prompt);
|
|
66
66
|
const latencyMs = performance.now() - startTime;
|
|
67
67
|
const pTokens = estimateTokens(systemPrompt + prompt);
|
|
68
|
-
const cTokens = estimateTokens(
|
|
69
|
-
const usage = recordTokenUsage("
|
|
70
|
-
recordLatency("
|
|
71
|
-
return { ...
|
|
68
|
+
const cTokens = estimateTokens(offlineReply.text);
|
|
69
|
+
const usage = recordTokenUsage("offline-local", pTokens, cTokens);
|
|
70
|
+
recordLatency("offline-fallback", "local", latencyMs, pTokens, cTokens, true);
|
|
71
|
+
return { ...offlineReply, provider: "offline-fallback", node: 0, usage };
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// ── Try each provider in order ──────────────────────────
|
|
@@ -128,17 +128,17 @@ export async function routePrompt(prompt, systemPrompt, config, onToken, history
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
// ── Final Fallback:
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
131
|
+
// ── Final Fallback: Offline Fallback ────────────────────
|
|
132
|
+
const startTimeOffline = performance.now();
|
|
133
|
+
const offlineReply = generateOfflineReply(prompt);
|
|
134
|
+
const latencyMsOffline = performance.now() - startTimeOffline;
|
|
135
135
|
const pTokens = estimateTokens(systemPrompt + prompt + history.map(h => h.content).join(""));
|
|
136
|
-
const cTokens = estimateTokens(
|
|
137
|
-
const usage = recordTokenUsage("
|
|
138
|
-
recordLatency("
|
|
136
|
+
const cTokens = estimateTokens(offlineReply.text);
|
|
137
|
+
const usage = recordTokenUsage("offline-local", pTokens, cTokens);
|
|
138
|
+
recordLatency("offline-fallback", "local", latencyMsOffline, pTokens, cTokens, true);
|
|
139
139
|
return {
|
|
140
|
-
...
|
|
141
|
-
provider: "
|
|
140
|
+
...offlineReply,
|
|
141
|
+
provider: "offline-fallback",
|
|
142
142
|
node: 0,
|
|
143
143
|
errors,
|
|
144
144
|
usage,
|
package/src/chat.js
CHANGED
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
getActiveTheme,
|
|
26
26
|
setTheme,
|
|
27
27
|
getThemesList,
|
|
28
|
-
interactiveMenu
|
|
28
|
+
interactiveMenu,
|
|
29
|
+
getIcon
|
|
29
30
|
} from "./ui/theme.js";
|
|
30
31
|
import { createSpinner } from "./ui/spinner.js";
|
|
31
32
|
import { showBanner } from "./ui/banner.js";
|
|
@@ -116,7 +117,7 @@ export async function startChat(options = {}) {
|
|
|
116
117
|
label.mesh + " " +
|
|
117
118
|
colors.accent("Failover mesh online: ") +
|
|
118
119
|
colors.text(unique.join(" → ")) +
|
|
119
|
-
colors.muted(" →
|
|
120
|
+
colors.muted(" → Offline fallback")
|
|
120
121
|
);
|
|
121
122
|
console.log(
|
|
122
123
|
" " + colors.dim(`${active.length} node(s) active across ${unique.length} provider(s)`) + "\n"
|
|
@@ -297,7 +298,7 @@ export async function startChat(options = {}) {
|
|
|
297
298
|
console.log(separator("─"));
|
|
298
299
|
console.log("");
|
|
299
300
|
|
|
300
|
-
if (result.provider === "local" || result.provider === "
|
|
301
|
+
if (result.provider === "local" || result.provider === "offline-fallback") {
|
|
301
302
|
console.log(colors.text(" " + result.text.split("\n").join("\n ")));
|
|
302
303
|
} else {
|
|
303
304
|
let displayText = result.text;
|
|
@@ -896,7 +897,7 @@ function showActiveProviders(aiConfig) {
|
|
|
896
897
|
for (const { provider } of active) {
|
|
897
898
|
console.log(" " + colors.success("✓ ") + colors.text(provider.name) + colors.dim(` • ${provider.defaultModel}`));
|
|
898
899
|
}
|
|
899
|
-
console.log(" " + colors.success("✓ ") + colors.text("
|
|
900
|
+
console.log(" " + colors.success("✓ ") + colors.text("Offline Fallback") + colors.dim(" • Local fallback"));
|
|
900
901
|
console.log(" " + colors.success("✓ ") + colors.text("Math Solver") + colors.dim(" • Local"));
|
|
901
902
|
console.log("");
|
|
902
903
|
}
|
|
@@ -1183,7 +1184,7 @@ function providerBadge(result) {
|
|
|
1183
1184
|
"perplexity": chalk.bgHex("#1a2a2a").hex("#6ce8ff")(" Perplexity "),
|
|
1184
1185
|
"fireworks ai": chalk.bgHex("#2a1a1a").hex("#ff6b8d")(" Fireworks "),
|
|
1185
1186
|
"local": chalk.bgHex("#1a2a1a").hex("#67ffb0")(" Math Solver "),
|
|
1186
|
-
"
|
|
1187
|
+
"offline-fallback": chalk.bgHex("#2a0a14").hex("#ff6b6b")(" Offline "),
|
|
1187
1188
|
};
|
|
1188
1189
|
|
|
1189
1190
|
const badge = badges[result.provider] || colors.muted(` ${result.provider} `);
|
|
@@ -2256,7 +2257,7 @@ export async function handleMicInput(ctx) {
|
|
|
2256
2257
|
return;
|
|
2257
2258
|
}
|
|
2258
2259
|
|
|
2259
|
-
console.log("\n" + label.system + " " + colors.brand("
|
|
2260
|
+
console.log("\n" + label.system + " " + colors.brand(getIcon("mic", ctx.aiConfig) + "AUDIO VOICE INPUT"));
|
|
2260
2261
|
console.log(separator("─"));
|
|
2261
2262
|
console.log(colors.accent(" Recording started..."));
|
|
2262
2263
|
console.log(" " + colors.muted("Speak into your microphone."));
|
package/src/cli.js
CHANGED
|
@@ -312,7 +312,7 @@ async function handleAsk(prompt, opts) {
|
|
|
312
312
|
console.log(separator("─"));
|
|
313
313
|
console.log("");
|
|
314
314
|
|
|
315
|
-
if (result.provider === "local" || result.provider === "
|
|
315
|
+
if (result.provider === "local" || result.provider === "offline-fallback") {
|
|
316
316
|
console.log(colors.text(" " + result.text.split("\n").join("\n ")));
|
|
317
317
|
} else {
|
|
318
318
|
let displayText = result.text;
|
|
@@ -577,11 +577,11 @@ async function handleStatus() {
|
|
|
577
577
|
console.log("");
|
|
578
578
|
console.log(colors.accent(" ◈ Local Fallbacks:"));
|
|
579
579
|
console.log(keyValue(" Math Solver", colors.success("✓ Active")));
|
|
580
|
-
console.log(keyValue("
|
|
580
|
+
console.log(keyValue(" Offline Fallback", colors.success("✓ Standing By")));
|
|
581
581
|
|
|
582
582
|
console.log("");
|
|
583
583
|
console.log(colors.accent(" ◈ Failover Mesh:"));
|
|
584
|
-
const totalNodes = 1 + active.length; // +1 for
|
|
584
|
+
const totalNodes = 1 + active.length; // +1 for local offline fallback
|
|
585
585
|
console.log(keyValue(" Active Nodes", `${totalNodes}`));
|
|
586
586
|
console.log(keyValue(" Mesh Status", active.length > 0 ? colors.success("✓ Online") : colors.warning("⚠ Local Only")));
|
|
587
587
|
console.log("");
|
|
@@ -739,7 +739,7 @@ async function handleSetup() {
|
|
|
739
739
|
console.log(" " + colors.muted("Start chatting: ") + colors.accent("aether chat"));
|
|
740
740
|
console.log(" " + colors.muted("Quick query: ") + colors.accent('aether ask "Hello!"'));
|
|
741
741
|
} else {
|
|
742
|
-
console.log("\n " + colors.warning("No providers configured. Aether will use
|
|
742
|
+
console.log("\n " + colors.warning("No providers configured. Aether will use local offline fallback mode."));
|
|
743
743
|
console.log(" " + colors.muted("Run ") + colors.accent("aether setup") + colors.muted(" again anytime."));
|
|
744
744
|
}
|
|
745
745
|
console.log("");
|
package/src/config.js
CHANGED
|
@@ -176,7 +176,7 @@ export function isValidConfigKey(key) {
|
|
|
176
176
|
const allowedSpecialKeys = [
|
|
177
177
|
"THEME", "CUSTOM_COMMANDS", "AUTOPILOT",
|
|
178
178
|
"AUTO_UPDATE", "SHOW_HIGHLIGHTS", "LAST_UPDATE_CHECK", "LAST_NOTIFIED_VERSION",
|
|
179
|
-
"SHOW_TOKENS", "DIAGNOSE_CMD"
|
|
179
|
+
"SHOW_TOKENS", "DIAGNOSE_CMD", "NERD_FONTS"
|
|
180
180
|
];
|
|
181
181
|
if (upper.endsWith("_API_KEY") || upper.endsWith("_API_KEYS") || upper.endsWith("_MODEL") || allowedSpecialKeys.includes(upper)) {
|
|
182
182
|
return true;
|
package/src/telemetry-server.js
CHANGED
|
@@ -536,9 +536,9 @@ const HTML_CONTENT = `<!DOCTYPE html>
|
|
|
536
536
|
topoList.appendChild(createTopologyElement({
|
|
537
537
|
name: "Local Solver Node",
|
|
538
538
|
configured: true,
|
|
539
|
-
defaultModel: "Offline Math
|
|
539
|
+
defaultModel: "Offline Math & Logic",
|
|
540
540
|
tier: "free",
|
|
541
|
-
description: "Zero-latency mathematical reasoning & local
|
|
541
|
+
description: "Zero-latency mathematical reasoning & local offline fallbacks."
|
|
542
542
|
}, "Node 0 (Local)"));
|
|
543
543
|
|
|
544
544
|
mesh.forEach((provider, idx) => {
|
package/src/ui/banner.js
CHANGED
|
@@ -7,7 +7,7 @@ import { readFileSync, existsSync } from "node:fs";
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import chalk from "chalk";
|
|
10
|
-
import { colors, separator, modeBadge } from "./theme.js";
|
|
10
|
+
import { colors, separator, modeBadge, getIcon } from "./theme.js";
|
|
11
11
|
import { getActiveProviders } from "../ai/providers.js";
|
|
12
12
|
import { MODES } from "../modes.js";
|
|
13
13
|
|
|
@@ -137,11 +137,11 @@ export function showBanner(currentMode = "titan") {
|
|
|
137
137
|
: "npm (@krishivpb60/aether-ai-cli)";
|
|
138
138
|
|
|
139
139
|
const rows = [
|
|
140
|
-
formatRow(` ${colors.muted("
|
|
141
|
-
formatRow(` ${colors.muted("
|
|
142
|
-
formatRow(` ${colors.muted("
|
|
143
|
-
formatRow(` ${colors.muted("
|
|
144
|
-
formatRow(` ${colors.muted("
|
|
140
|
+
formatRow(` ${colors.muted(getIcon("workspace", config) + "Workspace")}`, colors.text(workspaceValue)),
|
|
141
|
+
formatRow(` ${colors.muted(getIcon("mode", config) + "Mode")}`, modeRowValue),
|
|
142
|
+
formatRow(` ${colors.muted(getIcon("network", config) + "Network")}`, meshStatusText),
|
|
143
|
+
formatRow(` ${colors.muted(getIcon("engine", config) + "Engine")}`, colors.text(engineValue)),
|
|
144
|
+
formatRow(` ${colors.muted(getIcon("package", config) + "Packager")}`, colors.text(packagerText)),
|
|
145
145
|
];
|
|
146
146
|
|
|
147
147
|
console.log(`\n ⚡ ${colors.brand("AETHER COMMAND STATION v" + version)} • Welcome back, ${colors.accent(username)}`);
|
package/src/ui/dashboard.html
CHANGED
package/src/ui/theme.js
CHANGED
|
@@ -486,3 +486,29 @@ export async function interactiveMenu(headerText, items) {
|
|
|
486
486
|
stdin.on("data", handleKey);
|
|
487
487
|
});
|
|
488
488
|
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Returns either a premium Nerd Font glyph or a standard Unicode emoji fallback
|
|
492
|
+
* depending on whether NERD_FONTS is enabled in the configuration.
|
|
493
|
+
* @param {string} name - Icon name
|
|
494
|
+
* @param {object} config - Active configuration object
|
|
495
|
+
* @returns {string}
|
|
496
|
+
*/
|
|
497
|
+
export function getIcon(name, config) {
|
|
498
|
+
const useNerd = config?.NERD_FONTS === true || config?.NERD_FONTS === "true";
|
|
499
|
+
|
|
500
|
+
const icons = {
|
|
501
|
+
workspace: useNerd ? "\uf07c " : "📂 ",
|
|
502
|
+
mode: useNerd ? "\uf0e0 " : "🧠 ", // brain / envelope-like modes icon
|
|
503
|
+
network: useNerd ? "\uf6ff " : "🟢 ", // network icon
|
|
504
|
+
engine: useNerd ? "\uf013 " : "⚙️ ", // gear icon
|
|
505
|
+
package: useNerd ? "\uf1b2 " : "📦 ", // package icon
|
|
506
|
+
mic: useNerd ? "\uf130 " : "🎤 ", // microphone icon
|
|
507
|
+
git: useNerd ? "\uf113 " : "🌿 ", // git/leaf icon
|
|
508
|
+
dashboard: useNerd ? "\uf201 " : "📊 ", // chart icon
|
|
509
|
+
bolt: useNerd ? "\uf0e7 " : "⚡ ", // lightning bolt icon
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
return icons[name] || "";
|
|
513
|
+
}
|
|
514
|
+
|
package/test/config.test.js
CHANGED
|
@@ -148,6 +148,7 @@ test("Configuration Loading Suite", async (t) => {
|
|
|
148
148
|
assert.strictEqual(isValidConfigKey("GOOGLE_API_KEYS"), true);
|
|
149
149
|
assert.strictEqual(isValidConfigKey("THEME"), true);
|
|
150
150
|
assert.strictEqual(isValidConfigKey("CUSTOM_COMMANDS"), true);
|
|
151
|
+
assert.strictEqual(isValidConfigKey("NERD_FONTS"), true);
|
|
151
152
|
assert.strictEqual(isValidConfigKey("INVALID_KEY_NAME"), false);
|
|
152
153
|
});
|
|
153
154
|
|
package/test/fallback.test.js
CHANGED
|
@@ -3,7 +3,7 @@ import assert from "node:assert";
|
|
|
3
3
|
import {
|
|
4
4
|
detectMathExpression,
|
|
5
5
|
solveMath,
|
|
6
|
-
|
|
6
|
+
generateOfflineReply,
|
|
7
7
|
runMainframeHack,
|
|
8
8
|
} from "../src/ai/fallback.js";
|
|
9
9
|
|
|
@@ -50,38 +50,10 @@ test("Offline Math Fallback & Krylo Suite", async (t) => {
|
|
|
50
50
|
assert.strictEqual(solveMath("console.log(1)"), null);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
await t.test("
|
|
54
|
-
const reply =
|
|
55
|
-
assert.strictEqual(reply.type, "
|
|
56
|
-
assert.ok(reply.text.includes("
|
|
57
|
-
assert.ok(reply.text.includes("Ctrl + K"));
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
await t.test("generateKryloReply responds to status and diagnostic keywords", () => {
|
|
61
|
-
const reply = generateKryloReply("What is the CPU status?");
|
|
62
|
-
assert.strictEqual(reply.type, "krylo-local");
|
|
63
|
-
assert.ok(reply.text.includes("[LIVE DIAGNOSTIC READOUT]"));
|
|
64
|
-
assert.ok(reply.text.includes("Failover Mesh"));
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
await t.test("generateKryloReply responds to matrix/rain/color keywords", () => {
|
|
68
|
-
const reply = generateKryloReply("change matrix color");
|
|
69
|
-
assert.strictEqual(reply.type, "krylo-local");
|
|
70
|
-
assert.ok(reply.text.includes("[NEURAL GRIDS MODULATION]"));
|
|
71
|
-
assert.ok(reply.text.includes("Classic Green"));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
await t.test("generateKryloReply responds to who/name/creator keywords", () => {
|
|
75
|
-
const reply = generateKryloReply("who is your creator?");
|
|
76
|
-
assert.strictEqual(reply.type, "krylo-local");
|
|
77
|
-
assert.ok(reply.text.includes("[HOLOGRAPHIC COMPANION PROTOCOL]"));
|
|
78
|
-
assert.ok(reply.text.includes("Krishiv PB"));
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
await t.test("generateKryloReply falls back to random terminal responses", () => {
|
|
82
|
-
const reply = generateKryloReply("Unrelated query");
|
|
83
|
-
assert.strictEqual(reply.type, "krylo-local");
|
|
84
|
-
assert.ok(reply.text.includes("[KRYLO TERMINAL RESPONSE]"));
|
|
53
|
+
await t.test("generateOfflineReply returns offline error formatting", () => {
|
|
54
|
+
const reply = generateOfflineReply("any query");
|
|
55
|
+
assert.strictEqual(reply.type, "offline-error");
|
|
56
|
+
assert.ok(reply.text.includes("No active API keys configured"));
|
|
85
57
|
});
|
|
86
58
|
|
|
87
59
|
await t.test("detectMathExpression and solveMath support trig, logs, square root and constants", () => {
|
package/test/router.test.js
CHANGED
|
@@ -134,19 +134,19 @@ test("Universal AI Router Suite", async (t) => {
|
|
|
134
134
|
assert.ok(fetchCalls[1].url.includes("key=key-success"));
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
-
await t.test("routePrompt falls back to
|
|
137
|
+
await t.test("routePrompt falls back to Offline fallback when no providers are configured", async () => {
|
|
138
138
|
globalThis.fetch = async () => {
|
|
139
139
|
throw new Error("Fetch should not be called");
|
|
140
140
|
};
|
|
141
141
|
|
|
142
142
|
const result = await routePrompt("status", "Sys prompt", {});
|
|
143
|
-
assert.strictEqual(result.provider, "
|
|
143
|
+
assert.strictEqual(result.provider, "offline-fallback");
|
|
144
144
|
assert.strictEqual(result.node, 0);
|
|
145
|
-
assert.strictEqual(result.type, "
|
|
146
|
-
assert.ok(result.text.includes("
|
|
145
|
+
assert.strictEqual(result.type, "offline-error");
|
|
146
|
+
assert.ok(result.text.includes("No active API keys configured"));
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
await t.test("routePrompt falls back to
|
|
149
|
+
await t.test("routePrompt falls back to Offline fallback when all providers fail", async () => {
|
|
150
150
|
globalThis.fetch = async (url, options) => {
|
|
151
151
|
fetchCalls.push({ url, options });
|
|
152
152
|
return {
|
|
@@ -164,7 +164,7 @@ test("Universal AI Router Suite", async (t) => {
|
|
|
164
164
|
|
|
165
165
|
const result = await routePrompt("Hello", "Sys prompt", config);
|
|
166
166
|
|
|
167
|
-
assert.strictEqual(result.provider, "
|
|
167
|
+
assert.strictEqual(result.provider, "offline-fallback");
|
|
168
168
|
assert.strictEqual(result.node, 0);
|
|
169
169
|
assert.ok(result.errors);
|
|
170
170
|
assert.strictEqual(result.errors.length, 2);
|
package/test/ux.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { test, beforeEach, afterEach } from "node:test";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import { ReadableStream } from "node:stream/web";
|
|
4
|
-
import { separator, clearStreamedText, StreamFilter, stripCodeFences, getActiveTheme, setTheme, getThemesList } from "../src/ui/theme.js";
|
|
4
|
+
import { separator, clearStreamedText, StreamFilter, stripCodeFences, getActiveTheme, setTheme, getThemesList, getIcon } from "../src/ui/theme.js";
|
|
5
5
|
import { createSpinner } from "../src/ui/spinner.js";
|
|
6
6
|
import { routePrompt } from "../src/ai/router.js";
|
|
7
7
|
import { getModeByName, MODES } from "../src/modes.js";
|
|
@@ -183,4 +183,20 @@ test("Cyberpunk UX and Streaming Suite", async (t) => {
|
|
|
183
183
|
const noFenceBlock = "console.log('hi');";
|
|
184
184
|
assert.strictEqual(stripCodeFences(noFenceBlock), "console.log('hi');");
|
|
185
185
|
});
|
|
186
|
+
|
|
187
|
+
await t.test("getIcon returns Nerd Font glyphs if enabled, else defaults to emojis", () => {
|
|
188
|
+
const nerdConfig = { NERD_FONTS: true };
|
|
189
|
+
const emojiConfig = { NERD_FONTS: false };
|
|
190
|
+
const defaultConfig = {};
|
|
191
|
+
|
|
192
|
+
// 1. Nerd Font Enabled
|
|
193
|
+
assert.strictEqual(getIcon("mic", nerdConfig), "\uf130 ");
|
|
194
|
+
assert.strictEqual(getIcon("git", nerdConfig), "\uf113 ");
|
|
195
|
+
assert.strictEqual(getIcon("dashboard", nerdConfig), "\uf201 ");
|
|
196
|
+
|
|
197
|
+
// 2. Nerd Font Disabled / Empty
|
|
198
|
+
assert.strictEqual(getIcon("mic", emojiConfig), "🎤 ");
|
|
199
|
+
assert.strictEqual(getIcon("git", emojiConfig), "🌿 ");
|
|
200
|
+
assert.strictEqual(getIcon("mic", defaultConfig), "🎤 ");
|
|
201
|
+
});
|
|
186
202
|
});
|