@krishivpb60/aether-ai-cli 1.1.9 → 1.3.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/.agents/AGENTS.md +15 -0
- package/.agents/orchestrator/BRIEFING.md +76 -0
- package/.agents/orchestrator/ORIGINAL_REQUEST.md +13 -0
- package/.agents/orchestrator/context.md +27 -0
- package/.agents/orchestrator/handoff.md +28 -0
- package/.agents/orchestrator/plan.md +30 -0
- package/.agents/orchestrator/progress.md +30 -0
- package/.agents/sentinel/BRIEFING.md +30 -0
- package/.agents/sentinel/handoff.md +19 -0
- package/.agents/teamwork_preview_auditor/BRIEFING.md +51 -0
- package/.agents/teamwork_preview_auditor/ORIGINAL_REQUEST.md +22 -0
- package/.agents/teamwork_preview_auditor/android-cli_SKILL.md +203 -0
- package/.agents/teamwork_preview_auditor/github_SKILL.md +58 -0
- package/.agents/teamwork_preview_auditor/handoff.md +80 -0
- package/.agents/teamwork_preview_auditor/progress.md +12 -0
- package/.agents/teamwork_preview_explorer_git_ci_ux/BRIEFING.md +50 -0
- package/.agents/teamwork_preview_explorer_git_ci_ux/ORIGINAL_REQUEST.md +12 -0
- package/.agents/teamwork_preview_explorer_git_ci_ux/handoff.md +170 -0
- package/.agents/teamwork_preview_explorer_git_ci_ux/progress.md +9 -0
- package/.agents/teamwork_preview_worker_git_ci/BRIEFING.md +60 -0
- package/.agents/teamwork_preview_worker_git_ci/ORIGINAL_REQUEST.md +18 -0
- package/.agents/teamwork_preview_worker_git_ci/handoff.md +122 -0
- package/.agents/teamwork_preview_worker_git_ci/progress.md +14 -0
- package/.agents/teamwork_preview_worker_git_ci/skills/android-cli_SKILL.md +63 -0
- package/.agents/teamwork_preview_worker_git_ci/skills/github_SKILL.md +54 -0
- package/.agents/teamwork_preview_worker_git_commit/BRIEFING.md +43 -0
- package/.agents/teamwork_preview_worker_git_commit/ORIGINAL_REQUEST.md +15 -0
- package/.agents/teamwork_preview_worker_git_commit/handoff.md +89 -0
- package/.agents/teamwork_preview_worker_git_commit/progress.md +12 -0
- package/.agents/teamwork_preview_worker_test_suite/BRIEFING.md +57 -0
- package/.agents/teamwork_preview_worker_test_suite/ORIGINAL_REQUEST.md +21 -0
- package/.agents/teamwork_preview_worker_test_suite/handoff.md +62 -0
- package/.agents/teamwork_preview_worker_test_suite/progress.md +12 -0
- package/.agents/teamwork_preview_worker_ux_upgrades/BRIEFING.md +56 -0
- package/.agents/teamwork_preview_worker_ux_upgrades/ORIGINAL_REQUEST.md +35 -0
- package/.agents/teamwork_preview_worker_ux_upgrades/handoff.md +53 -0
- package/.agents/teamwork_preview_worker_ux_upgrades/progress.md +12 -0
- package/.agents/teamwork_preview_worker_verification/BRIEFING.md +49 -0
- package/.agents/teamwork_preview_worker_verification/ORIGINAL_REQUEST.md +18 -0
- package/.agents/teamwork_preview_worker_verification/handoff.md +187 -0
- package/.agents/teamwork_preview_worker_verification/progress.md +16 -0
- package/HIGHLIGHTS.md +16 -0
- package/aether_pip/cli.py +1 -0
- package/package.json +1 -1
- package/src/ai/router.js +18 -3
- package/src/ai/tokens.js +101 -0
- package/src/chat.js +64 -3
- package/src/config.js +6 -1
- package/src/modes.js +7 -36
- package/src/updater.js +186 -0
- package/test/tokens.test.js +89 -0
- package/test/updater.test.js +90 -0
- package/test/ux.test.js +11 -7
package/src/updater.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════
|
|
2
|
+
// AETHER AI CLI — Automated Update & Highlights Engine
|
|
3
|
+
// Checks NPM registry, updates packages, and renders release details.
|
|
4
|
+
// ═══════════════════════════════════════════════════════════
|
|
5
|
+
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { exec } from "node:child_process";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
import { getConfigValue, setConfigValue } from "./config.js";
|
|
12
|
+
import { colors, label, separator } from "./ui/theme.js";
|
|
13
|
+
import { createSpinner } from "./ui/spinner.js";
|
|
14
|
+
|
|
15
|
+
const execAsync = promisify(exec);
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = dirname(__filename);
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8"));
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns true if version 'latest' is newer than version 'current'.
|
|
23
|
+
* Supports standard semver format x.y.z.
|
|
24
|
+
* @param {string} latest
|
|
25
|
+
* @param {string} current
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
export function isNewerVersion(latest, current) {
|
|
29
|
+
const l = latest.split(".").map((num) => parseInt(num, 10) || 0);
|
|
30
|
+
const c = current.split(".").map((num) => parseInt(num, 10) || 0);
|
|
31
|
+
for (let i = 0; i < 3; i++) {
|
|
32
|
+
if (l[i] > c[i]) return true;
|
|
33
|
+
if (l[i] < c[i]) return false;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Fetches and displays release highlights from the remote repository.
|
|
40
|
+
* @param {string} version
|
|
41
|
+
*/
|
|
42
|
+
export async function showReleaseHighlights(version) {
|
|
43
|
+
try {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timeoutId = setTimeout(() => controller.abort(), 2000);
|
|
46
|
+
|
|
47
|
+
const res = await fetch("https://raw.githubusercontent.com/Krylo-60/aether-ai-cli/main/HIGHLIGHTS.md", {
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
});
|
|
50
|
+
clearTimeout(timeoutId);
|
|
51
|
+
|
|
52
|
+
if (!res.ok) return;
|
|
53
|
+
const text = await res.text();
|
|
54
|
+
|
|
55
|
+
console.log("\n" + separator("━"));
|
|
56
|
+
console.log(colors.accent.bold(` ★ AETHER AI CLI v${version} RELEASE HIGHLIGHTS ★`));
|
|
57
|
+
console.log(separator("─"));
|
|
58
|
+
|
|
59
|
+
const lines = text.split("\n");
|
|
60
|
+
let inReleaseHeader = false;
|
|
61
|
+
let printedAny = false;
|
|
62
|
+
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
const trimmed = line.trim();
|
|
65
|
+
if (trimmed.startsWith("#")) {
|
|
66
|
+
// Matches the version header like "# Aether CLI v1.1.9 Highlights" or similar
|
|
67
|
+
if (trimmed.toLowerCase().includes(`v${version}`)) {
|
|
68
|
+
inReleaseHeader = true;
|
|
69
|
+
} else {
|
|
70
|
+
inReleaseHeader = false;
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (inReleaseHeader && trimmed) {
|
|
76
|
+
if (trimmed.startsWith("-") || trimmed.startsWith("*")) {
|
|
77
|
+
console.log(colors.brand(" " + trimmed));
|
|
78
|
+
printedAny = true;
|
|
79
|
+
} else {
|
|
80
|
+
console.log(colors.text(" " + trimmed));
|
|
81
|
+
printedAny = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!printedAny) {
|
|
87
|
+
// Fallback if no specific version section was found in HIGHLIGHTS.md
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
const trimmed = line.trim();
|
|
90
|
+
if (trimmed.startsWith("#")) continue;
|
|
91
|
+
if (trimmed.startsWith("-") || trimmed.startsWith("*")) {
|
|
92
|
+
console.log(colors.brand(" " + trimmed));
|
|
93
|
+
} else if (trimmed) {
|
|
94
|
+
console.log(colors.text(" " + trimmed));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(separator("━") + "\n");
|
|
100
|
+
} catch {
|
|
101
|
+
// Fail silently (offline or repo down)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Checks for updates and runs the automatic updater if configured.
|
|
107
|
+
*/
|
|
108
|
+
export async function checkForUpdates() {
|
|
109
|
+
const autoUpdate = (await getConfigValue("AUTO_UPDATE")) !== "false";
|
|
110
|
+
const showHighlights = (await getConfigValue("SHOW_HIGHLIGHTS")) !== "false";
|
|
111
|
+
const lastCheck = parseInt(await getConfigValue("LAST_UPDATE_CHECK") || "0", 10);
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
const currentVersion = pkg.version;
|
|
114
|
+
|
|
115
|
+
// Run update check at most once every 24 hours (86,400,000 ms)
|
|
116
|
+
const checkInterval = 24 * 60 * 60 * 1000;
|
|
117
|
+
if (now - lastCheck < checkInterval) {
|
|
118
|
+
// Show highlights if we just updated and haven't shown highlights for this version
|
|
119
|
+
const lastNotified = await getConfigValue("LAST_NOTIFIED_VERSION") || "";
|
|
120
|
+
if (showHighlights && lastNotified !== currentVersion) {
|
|
121
|
+
await showReleaseHighlights(currentVersion);
|
|
122
|
+
await setConfigValue("LAST_NOTIFIED_VERSION", currentVersion);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Update check timestamp immediately
|
|
128
|
+
await setConfigValue("LAST_UPDATE_CHECK", now.toString());
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const controller = new AbortController();
|
|
132
|
+
const timeoutId = setTimeout(() => controller.abort(), 2000);
|
|
133
|
+
|
|
134
|
+
const res = await fetch("https://registry.npmjs.org/@krishivpb60/aether-ai-cli/latest", {
|
|
135
|
+
signal: controller.signal,
|
|
136
|
+
});
|
|
137
|
+
clearTimeout(timeoutId);
|
|
138
|
+
|
|
139
|
+
if (!res.ok) return;
|
|
140
|
+
const data = await res.json();
|
|
141
|
+
const latestVersion = data.version;
|
|
142
|
+
|
|
143
|
+
if (isNewerVersion(latestVersion, currentVersion)) {
|
|
144
|
+
if (autoUpdate) {
|
|
145
|
+
console.log("\n" + label.system + " " + colors.brand(`⚡ New version detected! Auto-updating from v${currentVersion} to v${latestVersion}...`));
|
|
146
|
+
|
|
147
|
+
const isPip = process.env.AETHER_PACKAGER === "pip";
|
|
148
|
+
const updateCmd = isPip
|
|
149
|
+
? "pip install --upgrade aether-ai-agent-cli"
|
|
150
|
+
: "npm install -g @krishivpb60/aether-ai-cli";
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const spinner = createSpinner("Installing update").start();
|
|
154
|
+
await execAsync(updateCmd);
|
|
155
|
+
spinner.succeed("Update complete!");
|
|
156
|
+
|
|
157
|
+
console.log(label.system + " " + colors.success(`✓ Successfully updated to v${latestVersion}.`));
|
|
158
|
+
|
|
159
|
+
if (showHighlights) {
|
|
160
|
+
await showReleaseHighlights(latestVersion);
|
|
161
|
+
}
|
|
162
|
+
await setConfigValue("LAST_NOTIFIED_VERSION", latestVersion);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
console.log(label.system + " " + colors.warning(`⚠ Auto-update failed: ${err.message}`));
|
|
165
|
+
console.log(label.system + " " + colors.muted(`Please run manually: ${updateCmd}`));
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
console.log("\n" + label.system + " " + colors.warning(`⚡ A new version (v${latestVersion}) is available!`));
|
|
169
|
+
const isPip = process.env.AETHER_PACKAGER === "pip";
|
|
170
|
+
const updateCmd = isPip
|
|
171
|
+
? "pip install -U aether-ai-agent-cli"
|
|
172
|
+
: "npm install -g @krishivpb60/aether-ai-cli";
|
|
173
|
+
console.log(label.system + " " + colors.muted(`To update, run: ${updateCmd}`));
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
// Already on latest version, check if we need to show highlights
|
|
177
|
+
const lastNotified = await getConfigValue("LAST_NOTIFIED_VERSION") || "";
|
|
178
|
+
if (showHighlights && lastNotified !== currentVersion) {
|
|
179
|
+
await showReleaseHighlights(currentVersion);
|
|
180
|
+
await setConfigValue("LAST_NOTIFIED_VERSION", currentVersion);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} catch {
|
|
184
|
+
// Fail silently (offline or registry down)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import {
|
|
4
|
+
estimateTokens,
|
|
5
|
+
getSessionTokenStats,
|
|
6
|
+
resetSessionTokenStats,
|
|
7
|
+
recordTokenUsage,
|
|
8
|
+
getBreakdownByModel
|
|
9
|
+
} from "../src/ai/tokens.js";
|
|
10
|
+
|
|
11
|
+
test("Token Estimation & Usage Telemetry Suite", async (t) => {
|
|
12
|
+
await t.test("estimateTokens uses standard character heuristics", () => {
|
|
13
|
+
assert.strictEqual(estimateTokens(""), 0);
|
|
14
|
+
assert.strictEqual(estimateTokens(null), 0);
|
|
15
|
+
// 8 characters should be 2 tokens (8 / 4)
|
|
16
|
+
assert.strictEqual(estimateTokens("12345678"), 2);
|
|
17
|
+
// 9 characters should be 3 tokens (Math.ceil(9 / 4))
|
|
18
|
+
assert.strictEqual(estimateTokens("123456789"), 3);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await t.test("recordTokenUsage updates session stats and breakdowns", () => {
|
|
22
|
+
resetSessionTokenStats();
|
|
23
|
+
|
|
24
|
+
// 1. Initial State
|
|
25
|
+
let stats = getSessionTokenStats();
|
|
26
|
+
assert.strictEqual(stats.prompt, 0);
|
|
27
|
+
assert.strictEqual(stats.completion, 0);
|
|
28
|
+
assert.strictEqual(stats.total, 0);
|
|
29
|
+
assert.strictEqual(stats.exchanges, 0);
|
|
30
|
+
|
|
31
|
+
// 2. Record first usage
|
|
32
|
+
const result1 = recordTokenUsage("gpt-4o", 100, 50);
|
|
33
|
+
assert.deepStrictEqual(result1, { promptTokens: 100, completionTokens: 50, totalTokens: 150 });
|
|
34
|
+
|
|
35
|
+
stats = getSessionTokenStats();
|
|
36
|
+
assert.strictEqual(stats.prompt, 100);
|
|
37
|
+
assert.strictEqual(stats.completion, 50);
|
|
38
|
+
assert.strictEqual(stats.total, 150);
|
|
39
|
+
assert.strictEqual(stats.exchanges, 1);
|
|
40
|
+
|
|
41
|
+
let breakdown = getBreakdownByModel();
|
|
42
|
+
assert.ok(breakdown["gpt-4o"]);
|
|
43
|
+
assert.strictEqual(breakdown["gpt-4o"].prompt, 100);
|
|
44
|
+
assert.strictEqual(breakdown["gpt-4o"].completion, 50);
|
|
45
|
+
assert.strictEqual(breakdown["gpt-4o"].total, 150);
|
|
46
|
+
assert.strictEqual(breakdown["gpt-4o"].exchanges, 1);
|
|
47
|
+
|
|
48
|
+
// 3. Record second usage for same model
|
|
49
|
+
recordTokenUsage("gpt-4o", 200, 100);
|
|
50
|
+
stats = getSessionTokenStats();
|
|
51
|
+
assert.strictEqual(stats.prompt, 300);
|
|
52
|
+
assert.strictEqual(stats.completion, 150);
|
|
53
|
+
assert.strictEqual(stats.total, 450);
|
|
54
|
+
assert.strictEqual(stats.exchanges, 2);
|
|
55
|
+
|
|
56
|
+
breakdown = getBreakdownByModel();
|
|
57
|
+
assert.strictEqual(breakdown["gpt-4o"].prompt, 300);
|
|
58
|
+
assert.strictEqual(breakdown["gpt-4o"].completion, 150);
|
|
59
|
+
assert.strictEqual(breakdown["gpt-4o"].total, 450);
|
|
60
|
+
assert.strictEqual(breakdown["gpt-4o"].exchanges, 2);
|
|
61
|
+
|
|
62
|
+
// 4. Record usage for another model
|
|
63
|
+
recordTokenUsage("gemini-2.5-flash", 50, 25);
|
|
64
|
+
stats = getSessionTokenStats();
|
|
65
|
+
assert.strictEqual(stats.prompt, 350);
|
|
66
|
+
assert.strictEqual(stats.completion, 175);
|
|
67
|
+
assert.strictEqual(stats.total, 525);
|
|
68
|
+
assert.strictEqual(stats.exchanges, 3);
|
|
69
|
+
|
|
70
|
+
breakdown = getBreakdownByModel();
|
|
71
|
+
assert.ok(breakdown["gemini-2.5-flash"]);
|
|
72
|
+
assert.strictEqual(breakdown["gemini-2.5-flash"].prompt, 50);
|
|
73
|
+
assert.strictEqual(breakdown["gemini-2.5-flash"].completion, 25);
|
|
74
|
+
assert.strictEqual(breakdown["gemini-2.5-flash"].total, 75);
|
|
75
|
+
assert.strictEqual(breakdown["gemini-2.5-flash"].exchanges, 1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await t.test("resetSessionTokenStats clears stats and breakdowns", () => {
|
|
79
|
+
resetSessionTokenStats();
|
|
80
|
+
const stats = getSessionTokenStats();
|
|
81
|
+
assert.strictEqual(stats.prompt, 0);
|
|
82
|
+
assert.strictEqual(stats.completion, 0);
|
|
83
|
+
assert.strictEqual(stats.total, 0);
|
|
84
|
+
assert.strictEqual(stats.exchanges, 0);
|
|
85
|
+
|
|
86
|
+
const breakdown = getBreakdownByModel();
|
|
87
|
+
assert.strictEqual(Object.keys(breakdown).length, 0);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
4
|
+
import { test, before, after, beforeEach, afterEach } from "node:test";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
7
|
+
|
|
8
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
9
|
+
|
|
10
|
+
// Redirect homedir before importing config and updater to point to a temporary test folder
|
|
11
|
+
const tempHome = join(process.cwd(), "temp-test-home-updater");
|
|
12
|
+
process.env.USERPROFILE = tempHome;
|
|
13
|
+
process.env.HOME = tempHome;
|
|
14
|
+
|
|
15
|
+
const { resetConfig, setConfigValue, getConfigValue } = await import("../src/config.js");
|
|
16
|
+
const { isNewerVersion, checkForUpdates, showReleaseHighlights } = await import("../src/updater.js");
|
|
17
|
+
|
|
18
|
+
const originalFetch = globalThis.fetch;
|
|
19
|
+
|
|
20
|
+
test("Auto-Updater & Highlights Suite", async (t) => {
|
|
21
|
+
let fetchCalls = [];
|
|
22
|
+
|
|
23
|
+
before(async () => {
|
|
24
|
+
await mkdir(tempHome, { recursive: true });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
after(async () => {
|
|
28
|
+
await rm(tempHome, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
fetchCalls = [];
|
|
33
|
+
await resetConfig();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
globalThis.fetch = originalFetch;
|
|
38
|
+
await resetConfig();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await t.test("isNewerVersion compares semver versions correctly", () => {
|
|
42
|
+
assert.strictEqual(isNewerVersion("1.1.9", "1.1.8"), true);
|
|
43
|
+
assert.strictEqual(isNewerVersion("1.2.0", "1.1.9"), true);
|
|
44
|
+
assert.strictEqual(isNewerVersion("2.0.0", "1.9.9"), true);
|
|
45
|
+
assert.strictEqual(isNewerVersion("1.1.9", "1.1.9"), false);
|
|
46
|
+
assert.strictEqual(isNewerVersion("1.1.8", "1.1.9"), false);
|
|
47
|
+
assert.strictEqual(isNewerVersion("1.0.0", "2.0.0"), false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
await t.test("checkForUpdates respects 24h throttling", async () => {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
// Set last check to 1 hour ago
|
|
53
|
+
await setConfigValue("LAST_UPDATE_CHECK", (now - 60 * 60 * 1000).toString());
|
|
54
|
+
// Set last notified version to current version so highlights are not triggered
|
|
55
|
+
await setConfigValue("LAST_NOTIFIED_VERSION", pkg.version);
|
|
56
|
+
|
|
57
|
+
let fetchCalled = false;
|
|
58
|
+
globalThis.fetch = async () => {
|
|
59
|
+
fetchCalled = true;
|
|
60
|
+
return { ok: false };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
await checkForUpdates();
|
|
64
|
+
// Should NOT call fetch because it hasn't been 24 hours
|
|
65
|
+
assert.strictEqual(fetchCalled, false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await t.test("checkForUpdates triggers check if 24h passed", async () => {
|
|
69
|
+
const now = Date.now();
|
|
70
|
+
// Set last check to 25 hours ago
|
|
71
|
+
await setConfigValue("LAST_UPDATE_CHECK", (now - 25 * 60 * 60 * 1000).toString());
|
|
72
|
+
|
|
73
|
+
let fetchCalled = false;
|
|
74
|
+
globalThis.fetch = async (url) => {
|
|
75
|
+
fetchCalled = true;
|
|
76
|
+
assert.ok(url.includes("registry.npmjs.org"));
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
json: async () => ({ version: "1.1.9" })
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
await checkForUpdates();
|
|
84
|
+
assert.strictEqual(fetchCalled, true);
|
|
85
|
+
|
|
86
|
+
// Check that LAST_UPDATE_CHECK was updated to a recent timestamp
|
|
87
|
+
const updatedCheck = parseInt(await getConfigValue("LAST_UPDATE_CHECK") || "0", 10);
|
|
88
|
+
assert.ok(updatedCheck > now - 10000);
|
|
89
|
+
});
|
|
90
|
+
});
|
package/test/ux.test.js
CHANGED
|
@@ -128,23 +128,27 @@ test("Cyberpunk UX and Streaming Suite", async (t) => {
|
|
|
128
128
|
setTheme("cyberpunk");
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
await t.test("Reasoning modes should be loaded correctly including codex and cloude-code", () => {
|
|
131
|
+
await t.test("Reasoning modes should be loaded correctly including codex and cloude-code redirection", () => {
|
|
132
132
|
const synthesis = getModeByName("synthesis");
|
|
133
133
|
assert.strictEqual(synthesis.name, "synthesis");
|
|
134
134
|
|
|
135
|
+
const titan = getModeByName("titan");
|
|
136
|
+
assert.strictEqual(titan.name, "titan");
|
|
137
|
+
assert.ok(titan.systemPrompt.includes("fuses the absolute best capabilities of OpenAI Codex"));
|
|
138
|
+
assert.ok(titan.systemPrompt.includes("Claude Code"));
|
|
139
|
+
|
|
140
|
+
// Deprecated codex and cloude-code modes should redirect to titan
|
|
135
141
|
const codex = getModeByName("codex");
|
|
136
|
-
assert.strictEqual(codex.name, "
|
|
137
|
-
assert.ok(codex.systemPrompt.includes("OpenAI Codex mode"));
|
|
142
|
+
assert.strictEqual(codex.name, "titan");
|
|
138
143
|
|
|
139
144
|
const cloudeCode = getModeByName("cloude-code");
|
|
140
|
-
assert.strictEqual(cloudeCode.name, "
|
|
141
|
-
assert.ok(cloudeCode.systemPrompt.includes("Claude Code mode"));
|
|
145
|
+
assert.strictEqual(cloudeCode.name, "titan");
|
|
142
146
|
|
|
143
147
|
const claudeCode = getModeByName("claude-code");
|
|
144
|
-
assert.strictEqual(claudeCode.name, "
|
|
148
|
+
assert.strictEqual(claudeCode.name, "titan");
|
|
145
149
|
|
|
146
150
|
const caseCheck = getModeByName(" CoDeX ");
|
|
147
|
-
assert.strictEqual(caseCheck.name, "
|
|
151
|
+
assert.strictEqual(caseCheck.name, "titan");
|
|
148
152
|
|
|
149
153
|
const unknown = getModeByName("nonexistent-mode");
|
|
150
154
|
assert.strictEqual(unknown, null);
|