mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
refactor(tui): remove dead exports (#107241)
This commit is contained in:
committed by
GitHub
parent
5a7184e3e3
commit
e91d0fc8b7
@@ -864,18 +864,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
|
||||
"src/tasks/task-registry.ts: setTaskRegistryControlRuntimeForTests",
|
||||
"src/tasks/task-registry.ts: setTaskRegistryDeliveryRuntimeForTests",
|
||||
"src/tasks/task-retention.ts: resolveTaskRetentionMs",
|
||||
"src/tui/components/filterable-select-list.ts: FilterableSelectListTheme",
|
||||
"src/tui/gateway-chat.ts: resolveBoundGatewayConnection",
|
||||
"src/tui/gateway-chat.ts: resolveGatewayConnection",
|
||||
"src/tui/theme/theme.ts: lightMode",
|
||||
"src/tui/theme/theme.ts: lightPalette",
|
||||
"src/tui/theme/theme.ts: palette",
|
||||
"src/tui/tui-busy-notice.ts: TUI_AGENT_BUSY_MESSAGE",
|
||||
"src/tui/tui-last-session.ts: isHeartbeatLikeTuiSession",
|
||||
"src/tui/tui-last-session.ts: resolveTuiLastSessionStatePath",
|
||||
"src/tui/tui-plugin-approvals.ts: parseTuiPluginApproval",
|
||||
"src/tui/tui-task-suggestions.ts: parseTuiTaskSuggestion",
|
||||
"src/tui/tui-waiting.ts: pickWaitingPhrase",
|
||||
"src/wizard/clack-navigation-prompts.ts: formatNavigationFooter",
|
||||
"src/wizard/i18n/index.ts: listWizardI18nKeys",
|
||||
"src/wizard/i18n/index.ts: resolveWizardLocale",
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Filterable select list tests cover keyboard filtering and cursor behavior.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
FilterableSelectList,
|
||||
type FilterableSelectItem,
|
||||
type FilterableSelectListTheme,
|
||||
} from "./filterable-select-list.js";
|
||||
import { FilterableSelectList, type FilterableSelectItem } from "./filterable-select-list.js";
|
||||
|
||||
type FilterableSelectListTheme = ConstructorParameters<typeof FilterableSelectList>[2];
|
||||
|
||||
const mockTheme: FilterableSelectListTheme = {
|
||||
selectedPrefix: (t) => `[${t}]`,
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface FilterableSelectItem extends SelectItem {
|
||||
searchTextLower?: string;
|
||||
}
|
||||
|
||||
export interface FilterableSelectListTheme extends SelectListTheme {
|
||||
interface FilterableSelectListTheme extends SelectListTheme {
|
||||
filterLabel: (text: string) => string;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,16 @@ vi.mock("../infra/gateway-lock.js", () => ({
|
||||
readActiveGatewayLockPort: readActiveGatewayLockPortMock,
|
||||
}));
|
||||
|
||||
const { GatewayChatClient, resolveBoundGatewayConnection, resolveGatewayConnection } =
|
||||
await import("./gateway-chat.js");
|
||||
const { GatewayChatClient } = await import("./gateway-chat.js");
|
||||
const { GatewayClientRequestError } = await import("../gateway/client.js");
|
||||
|
||||
const resolveBoundGatewayConnection = (
|
||||
opts: Parameters<typeof GatewayChatClient.connectBound>[0],
|
||||
) => GatewayChatClient.connectBound(opts).connection;
|
||||
|
||||
const resolveGatewayConnection = async (opts: Parameters<typeof GatewayChatClient.connect>[0]) =>
|
||||
(await GatewayChatClient.connect(opts)).connection;
|
||||
|
||||
async function fileExists(filePath: string): Promise<boolean> {
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
|
||||
@@ -394,7 +394,7 @@ export class GatewayChatClient implements TuiBackend {
|
||||
* deliberately ignores global config and Gateway env overrides, including
|
||||
* credentials, while still applying the normal remote URL safety policy.
|
||||
*/
|
||||
export function resolveBoundGatewayConnection(
|
||||
function resolveBoundGatewayConnection(
|
||||
opts: GatewayConnectionOptions & { config: OpenClawConfig; url: string },
|
||||
): ResolvedGatewayConnection {
|
||||
const url = buildGatewayConnectionDetails({
|
||||
@@ -413,7 +413,7 @@ export function resolveBoundGatewayConnection(
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveGatewayConnection(
|
||||
async function resolveGatewayConnection(
|
||||
opts: GatewayConnectionOptions,
|
||||
): Promise<ResolvedGatewayConnection> {
|
||||
const config = getRuntimeConfig();
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
// TUI theme tests cover theme defaults and environment-driven variants.
|
||||
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import chalk from "chalk";
|
||||
import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
const originalChalkLevel = chalk.level;
|
||||
chalk.level = 3;
|
||||
|
||||
const { markdownTheme, searchableSelectListTheme, selectListTheme, theme } =
|
||||
await import("./theme.js");
|
||||
@@ -13,6 +17,10 @@ const stripAnsi = (str: string) =>
|
||||
let themeImportCase = 0;
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
afterAll(() => {
|
||||
chalk.level = originalChalkLevel;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
});
|
||||
@@ -22,6 +30,48 @@ type ThemeEnvOverrides = {
|
||||
COLORFGBG?: string | undefined;
|
||||
};
|
||||
|
||||
type ThemeModule = typeof import("./theme.js");
|
||||
const ansiRgbPattern = new RegExp(
|
||||
`${String.fromCharCode(27)}\\[(38|48);2;(\\d+);(\\d+);(\\d+)m`,
|
||||
"u",
|
||||
);
|
||||
|
||||
function colorFromStyle(style: (text: string) => string, layer: 38 | 48): string {
|
||||
const match = style("x").match(ansiRgbPattern);
|
||||
if (!match || Number(match[1]) !== layer) {
|
||||
throw new Error(`expected ${layer === 38 ? "foreground" : "background"} RGB style`);
|
||||
}
|
||||
return `#${match
|
||||
.slice(2, 5)
|
||||
.map((channel) => Number(channel).toString(16).padStart(2, "0"))
|
||||
.join("")}`.toUpperCase();
|
||||
}
|
||||
|
||||
function readActivePalette(mod: ThemeModule) {
|
||||
return {
|
||||
text: colorFromStyle(mod.theme.fg, 38),
|
||||
dim: colorFromStyle(mod.theme.dim, 38),
|
||||
accent: colorFromStyle(mod.theme.accent, 38),
|
||||
accentSoft: colorFromStyle(mod.theme.accentSoft, 38),
|
||||
border: colorFromStyle(mod.theme.border, 38),
|
||||
userBg: colorFromStyle(mod.theme.userBg, 48),
|
||||
userText: colorFromStyle(mod.theme.userText, 38),
|
||||
systemText: colorFromStyle(mod.theme.system, 38),
|
||||
toolPendingBg: colorFromStyle(mod.theme.toolPendingBg, 48),
|
||||
toolSuccessBg: colorFromStyle(mod.theme.toolSuccessBg, 48),
|
||||
toolErrorBg: colorFromStyle(mod.theme.toolErrorBg, 48),
|
||||
toolTitle: colorFromStyle(mod.theme.toolTitle, 38),
|
||||
toolOutput: colorFromStyle(mod.theme.toolOutput, 38),
|
||||
quote: colorFromStyle(mod.markdownTheme.quote, 38),
|
||||
quoteBorder: colorFromStyle(mod.markdownTheme.quoteBorder, 38),
|
||||
code: colorFromStyle(mod.markdownTheme.code, 38),
|
||||
codeBorder: colorFromStyle(mod.markdownTheme.codeBlockBorder, 38),
|
||||
link: colorFromStyle(mod.markdownTheme.link, 38),
|
||||
error: colorFromStyle(mod.theme.error, 38),
|
||||
success: colorFromStyle(mod.theme.success, 38),
|
||||
};
|
||||
}
|
||||
|
||||
async function importThemeWithEnv(env: ThemeEnvOverrides) {
|
||||
if (Object.hasOwn(env, "OPENCLAW_THEME")) {
|
||||
if (env.OPENCLAW_THEME === undefined) {
|
||||
@@ -37,10 +87,16 @@ async function importThemeWithEnv(env: ThemeEnvOverrides) {
|
||||
process.env.COLORFGBG = env.COLORFGBG;
|
||||
}
|
||||
}
|
||||
return importFreshModule<typeof import("./theme.js")>(
|
||||
const mod = await importFreshModule<ThemeModule>(
|
||||
import.meta.url,
|
||||
`./theme.js?env=${++themeImportCase}`,
|
||||
);
|
||||
const lightPalette = readActivePalette(mod);
|
||||
return {
|
||||
...mod,
|
||||
lightMode: lightPalette.text === "#1E1E1E",
|
||||
lightPalette,
|
||||
};
|
||||
}
|
||||
|
||||
function relativeLuminance(hex: string): number {
|
||||
@@ -235,7 +291,7 @@ describe("light palette accessibility", () => {
|
||||
pending: mod.lightPalette.toolPendingBg,
|
||||
success: mod.lightPalette.toolSuccessBg,
|
||||
error: mod.lightPalette.toolErrorBg,
|
||||
code: mod.lightPalette.codeBlock,
|
||||
code: "#FFFFFF",
|
||||
};
|
||||
|
||||
const textPairs = [
|
||||
|
||||
@@ -85,8 +85,7 @@ function isLightBackground(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Whether the terminal has a light background. Exported for testing only. */
|
||||
export const lightMode = isLightBackground();
|
||||
const lightMode = isLightBackground();
|
||||
|
||||
const darkPalette = {
|
||||
text: "#E8E3D5",
|
||||
@@ -105,14 +104,13 @@ const darkPalette = {
|
||||
quote: "#8CC8FF",
|
||||
quoteBorder: "#3B4D6B",
|
||||
code: "#F0C987",
|
||||
codeBlock: "#1E232A",
|
||||
codeBorder: "#343A45",
|
||||
link: "#7DD3A5",
|
||||
error: "#F97066",
|
||||
success: "#7DD3A5",
|
||||
} as const;
|
||||
|
||||
export const lightPalette = {
|
||||
const lightPalette = {
|
||||
text: "#1E1E1E",
|
||||
dim: "#5B6472",
|
||||
accent: "#B45309",
|
||||
@@ -129,14 +127,13 @@ export const lightPalette = {
|
||||
quote: "#1D4ED8",
|
||||
quoteBorder: "#2563EB",
|
||||
code: "#92400E",
|
||||
codeBlock: "#F9FAFB",
|
||||
codeBorder: "#92400E",
|
||||
link: "#047857",
|
||||
error: "#DC2626",
|
||||
success: "#047857",
|
||||
} as const;
|
||||
|
||||
export const palette = lightMode ? lightPalette : darkPalette;
|
||||
const palette = lightMode ? lightPalette : darkPalette;
|
||||
|
||||
const fg = (hex: string) => (text: string) => chalk.hex(hex)(text);
|
||||
const bg = (hex: string) => (text: string) => chalk.bgHex(hex)(text);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { normalizeTestText } from "../../test/helpers/normalize-text.js";
|
||||
import { ChatLog } from "./components/chat-log.js";
|
||||
import { addBlockedChatSubmitNotice, TUI_AGENT_BUSY_MESSAGE } from "./tui-busy-notice.js";
|
||||
import { addBlockedChatSubmitNotice } from "./tui-busy-notice.js";
|
||||
|
||||
describe("addBlockedChatSubmitNotice", () => {
|
||||
it("coalesces repeated busy submit notices", () => {
|
||||
@@ -13,6 +13,8 @@ describe("addBlockedChatSubmitNotice", () => {
|
||||
|
||||
const rendered = normalizeTestText(chatLog.render(120).join("\n"));
|
||||
expect(chatLog.children.length).toBe(1);
|
||||
expect(rendered).toContain(`${TUI_AGENT_BUSY_MESSAGE} x3`);
|
||||
expect(rendered).toContain(
|
||||
"agent is busy — press Esc to abort before sending a new message x3",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { ChatLog } from "./components/chat-log.js";
|
||||
|
||||
export const TUI_AGENT_BUSY_MESSAGE =
|
||||
"agent is busy — press Esc to abort before sending a new message";
|
||||
const TUI_AGENT_BUSY_MESSAGE = "agent is busy — press Esc to abort before sending a new message";
|
||||
|
||||
export function addBlockedChatSubmitNotice(chatLog: Pick<ChatLog, "addSystem">) {
|
||||
chatLog.addSystem(TUI_AGENT_BUSY_MESSAGE, { coalesceConsecutive: true });
|
||||
|
||||
@@ -2,14 +2,11 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildTuiLastSessionScopeKey,
|
||||
isHeartbeatLikeTuiSession,
|
||||
readTuiLastSessionKey,
|
||||
resolveRememberedTuiSessionKey,
|
||||
resolveTuiLastSessionStatePath,
|
||||
writeTuiLastSessionKey,
|
||||
} from "./tui-last-session.js";
|
||||
|
||||
@@ -41,7 +38,7 @@ describe("tui last session state", () => {
|
||||
});
|
||||
|
||||
await expect(readTuiLastSessionKey({ scopeKey, stateDir })).resolves.toBe("agent:main:tui-123");
|
||||
const raw = await fs.readFile(resolveTuiLastSessionStatePath(stateDir), "utf8");
|
||||
const raw = await fs.readFile(path.join(stateDir, "tui", "last-session.json"), "utf8");
|
||||
expect(raw).not.toContain("127.0.0.1");
|
||||
});
|
||||
|
||||
@@ -108,9 +105,6 @@ describe("tui last session state", () => {
|
||||
{ key: "agent:main:tui-123" },
|
||||
];
|
||||
|
||||
expect(
|
||||
isHeartbeatLikeTuiSession(expectDefined(sessions[0], "sessions[0] test invariant")),
|
||||
).toBe(true);
|
||||
expect(
|
||||
resolveRememberedTuiSessionKey({
|
||||
rememberedKey: "agent:main:main",
|
||||
|
||||
@@ -16,7 +16,7 @@ type LastSessionRecord = {
|
||||
type LastSessionStore = Record<string, LastSessionRecord>;
|
||||
|
||||
/** Resolves the private state file for remembered TUI sessions. */
|
||||
export function resolveTuiLastSessionStatePath(stateDir = resolveStateDir()): string {
|
||||
function resolveTuiLastSessionStatePath(stateDir = resolveStateDir()): string {
|
||||
return path.join(stateDir, "tui", "last-session.json");
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ function isHeartbeatSessionKey(sessionKey: string): boolean {
|
||||
}
|
||||
|
||||
/** Detects heartbeat/system sessions that should not become the remembered human session. */
|
||||
export function isHeartbeatLikeTuiSession(session: TuiSessionList["sessions"][number]): boolean {
|
||||
function isHeartbeatLikeTuiSession(session: TuiSessionList["sessions"][number]): boolean {
|
||||
if (isHeartbeatSessionKey(session.key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ import type { Component, OverlayHandle, SelectItem } from "@earendil-works/pi-tu
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { stripAnsi } from "../../packages/terminal-core/src/ansi.js";
|
||||
import {
|
||||
createTuiPluginApprovalController,
|
||||
parseTuiPluginApproval,
|
||||
} from "./tui-plugin-approvals.js";
|
||||
import { createTuiPluginApprovalController } from "./tui-plugin-approvals.js";
|
||||
|
||||
type TestSelector = Component & {
|
||||
items: SelectItem[];
|
||||
@@ -121,9 +118,12 @@ function createHarness() {
|
||||
}
|
||||
|
||||
describe("TUI plugin approvals", () => {
|
||||
it("parses the pending plugin approval gateway shape", () => {
|
||||
expect(parseTuiPluginApproval(approvalPayload())).toEqual(approvalPayload());
|
||||
expect(parseTuiPluginApproval({ id: "plugin:missing-request" })).toBeNull();
|
||||
it("ignores malformed plugin approval gateway payloads", () => {
|
||||
const harness = createHarness();
|
||||
harness.controller.handleEvent("plugin.approval.requested", {
|
||||
id: "plugin:missing-request",
|
||||
});
|
||||
expect(harness.openOverlay).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows workspace skill approvals for the active session and resolves the selection", async () => {
|
||||
|
||||
@@ -153,7 +153,7 @@ function parseSeverity(value: unknown): TuiPluginApproval["request"]["severity"]
|
||||
}
|
||||
|
||||
/** Parses the gateway event/list shape used for pending plugin approvals. */
|
||||
export function parseTuiPluginApproval(payload: unknown): TuiPluginApproval | null {
|
||||
function parseTuiPluginApproval(payload: unknown): TuiPluginApproval | null {
|
||||
if (!isRecord(payload) || !isRecord(payload.request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ import type { Component, OverlayHandle, SelectItem } from "@earendil-works/pi-tu
|
||||
import { expectDefined } from "@openclaw/normalization-core";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { stripAnsi } from "../../packages/terminal-core/src/ansi.js";
|
||||
import {
|
||||
createTuiTaskSuggestionController,
|
||||
parseTuiTaskSuggestion,
|
||||
} from "./tui-task-suggestions.js";
|
||||
import { createTuiTaskSuggestionController } from "./tui-task-suggestions.js";
|
||||
|
||||
type TestSelector = Component & {
|
||||
items: SelectItem[];
|
||||
@@ -115,9 +112,13 @@ function createHarness() {
|
||||
}
|
||||
|
||||
describe("TUI task suggestions", () => {
|
||||
it("parses the Gateway suggestion shape", () => {
|
||||
expect(parseTuiTaskSuggestion(suggestionPayload())).toEqual(suggestionPayload());
|
||||
expect(parseTuiTaskSuggestion({ id: "task_missing_fields" })).toBeNull();
|
||||
it("ignores malformed Gateway suggestion payloads", () => {
|
||||
const harness = createHarness();
|
||||
harness.controller.handleEvent("task.suggestion", {
|
||||
action: "created",
|
||||
suggestion: { id: "task_missing_fields" },
|
||||
});
|
||||
expect(harness.openOverlay).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an active-session suggestion and starts it after confirmation", async () => {
|
||||
|
||||
@@ -69,7 +69,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
}
|
||||
|
||||
/** Parses the task suggestion shape carried by Gateway list and event payloads. */
|
||||
export function parseTuiTaskSuggestion(value: unknown): TaskSuggestion | null {
|
||||
function parseTuiTaskSuggestion(value: unknown): TaskSuggestion | null {
|
||||
if (!isRecord(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Verifies TUI waiting indicators and elapsed-time rendering.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildWaitingStatusMessage, pickWaitingPhrase } from "./tui-waiting.js";
|
||||
import { buildWaitingStatusMessage } from "./tui-waiting.js";
|
||||
|
||||
const theme = {
|
||||
dim: (s: string) => `<d>${s}</d>`,
|
||||
@@ -9,13 +9,21 @@ const theme = {
|
||||
} satisfies Parameters<typeof buildWaitingStatusMessage>[0]["theme"];
|
||||
|
||||
describe("tui-waiting", () => {
|
||||
it("pickWaitingPhrase rotates every 10 ticks", () => {
|
||||
it("rotates the rendered waiting phrase every 10 ticks", () => {
|
||||
const phrases = ["a", "b", "c"];
|
||||
expect(pickWaitingPhrase(0, phrases)).toBe("a");
|
||||
expect(pickWaitingPhrase(9, phrases)).toBe("a");
|
||||
expect(pickWaitingPhrase(10, phrases)).toBe("b");
|
||||
expect(pickWaitingPhrase(20, phrases)).toBe("c");
|
||||
expect(pickWaitingPhrase(30, phrases)).toBe("a");
|
||||
const renderPhrase = (tick: number) =>
|
||||
buildWaitingStatusMessage({
|
||||
theme,
|
||||
tick,
|
||||
elapsed: "3s",
|
||||
connectionStatus: "connected",
|
||||
phrases,
|
||||
}).replace(/<[^>]+>/g, "");
|
||||
expect(renderPhrase(0)).toMatch(/^a…/);
|
||||
expect(renderPhrase(9)).toMatch(/^a…/);
|
||||
expect(renderPhrase(10)).toMatch(/^b…/);
|
||||
expect(renderPhrase(20)).toMatch(/^c…/);
|
||||
expect(renderPhrase(30)).toMatch(/^a…/);
|
||||
});
|
||||
|
||||
it("buildWaitingStatusMessage includes shimmer markup and metadata", () => {
|
||||
|
||||
@@ -20,7 +20,7 @@ export const defaultWaitingPhrases = [
|
||||
];
|
||||
|
||||
/** Picks a stable phrase for a timer tick. */
|
||||
export function pickWaitingPhrase(tick: number, phrases = defaultWaitingPhrases) {
|
||||
function pickWaitingPhrase(tick: number, phrases = defaultWaitingPhrases) {
|
||||
const idx = Math.floor(tick / 10) % phrases.length;
|
||||
return phrases[idx] ?? phrases[0] ?? "waiting";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user