@krishivpb60/aether-ai-cli 1.4.0 → 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 +6 -0
- package/package.json +1 -1
- package/src/ai/fallback.js +8 -73
- package/src/ai/router.js +16 -16
- package/src/chat.js +4 -4
- package/src/cli.js +4 -4
- package/src/telemetry-server.js +2 -2
- package/src/ui/dashboard.html +1 -1
- package/test/fallback.test.js +5 -33
- package/test/router.test.js +6 -6
package/HIGHLIGHTS.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
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
|
+
|
|
1
7
|
# Aether CLI v1.4.0 Highlights
|
|
2
8
|
- **Microphone Audio Input & Dynamic Nerd Font Glyphs (`/mic`)**:
|
|
3
9
|
- 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.4.
|
|
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
|
@@ -117,7 +117,7 @@ export async function startChat(options = {}) {
|
|
|
117
117
|
label.mesh + " " +
|
|
118
118
|
colors.accent("Failover mesh online: ") +
|
|
119
119
|
colors.text(unique.join(" → ")) +
|
|
120
|
-
colors.muted(" →
|
|
120
|
+
colors.muted(" → Offline fallback")
|
|
121
121
|
);
|
|
122
122
|
console.log(
|
|
123
123
|
" " + colors.dim(`${active.length} node(s) active across ${unique.length} provider(s)`) + "\n"
|
|
@@ -298,7 +298,7 @@ export async function startChat(options = {}) {
|
|
|
298
298
|
console.log(separator("─"));
|
|
299
299
|
console.log("");
|
|
300
300
|
|
|
301
|
-
if (result.provider === "local" || result.provider === "
|
|
301
|
+
if (result.provider === "local" || result.provider === "offline-fallback") {
|
|
302
302
|
console.log(colors.text(" " + result.text.split("\n").join("\n ")));
|
|
303
303
|
} else {
|
|
304
304
|
let displayText = result.text;
|
|
@@ -897,7 +897,7 @@ function showActiveProviders(aiConfig) {
|
|
|
897
897
|
for (const { provider } of active) {
|
|
898
898
|
console.log(" " + colors.success("✓ ") + colors.text(provider.name) + colors.dim(` • ${provider.defaultModel}`));
|
|
899
899
|
}
|
|
900
|
-
console.log(" " + colors.success("✓ ") + colors.text("
|
|
900
|
+
console.log(" " + colors.success("✓ ") + colors.text("Offline Fallback") + colors.dim(" • Local fallback"));
|
|
901
901
|
console.log(" " + colors.success("✓ ") + colors.text("Math Solver") + colors.dim(" • Local"));
|
|
902
902
|
console.log("");
|
|
903
903
|
}
|
|
@@ -1184,7 +1184,7 @@ function providerBadge(result) {
|
|
|
1184
1184
|
"perplexity": chalk.bgHex("#1a2a2a").hex("#6ce8ff")(" Perplexity "),
|
|
1185
1185
|
"fireworks ai": chalk.bgHex("#2a1a1a").hex("#ff6b8d")(" Fireworks "),
|
|
1186
1186
|
"local": chalk.bgHex("#1a2a1a").hex("#67ffb0")(" Math Solver "),
|
|
1187
|
-
"
|
|
1187
|
+
"offline-fallback": chalk.bgHex("#2a0a14").hex("#ff6b6b")(" Offline "),
|
|
1188
1188
|
};
|
|
1189
1189
|
|
|
1190
1190
|
const badge = badges[result.provider] || colors.muted(` ${result.provider} `);
|
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/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/dashboard.html
CHANGED
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);
|