From 379e0bbd3ae7efd8d224c42ea83e3c5c67cd29c0 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Fri, 15 May 2026 02:09:36 -0300 Subject: [PATCH] =?UTF-8?q?feat(cli):=20fase=205.6=20=E2=80=94=20comando?= =?UTF-8?q?=20compression=20com=20engine/configure/rules/preview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/cli/commands/compression.mjs | 167 ++++++++++++++++++++ bin/cli/commands/registry.mjs | 4 + tests/unit/cli-compression-commands.test.ts | 166 +++++++++++++++++++ 3 files changed, 337 insertions(+) create mode 100644 bin/cli/commands/compression.mjs create mode 100644 tests/unit/cli-compression-commands.test.ts diff --git a/bin/cli/commands/compression.mjs b/bin/cli/commands/compression.mjs new file mode 100644 index 0000000000..81f44cc71b --- /dev/null +++ b/bin/cli/commands/compression.mjs @@ -0,0 +1,167 @@ +import { readFileSync } from "node:fs"; +import { apiFetch } from "../api.mjs"; +import { emit } from "../output.mjs"; +import { t } from "../i18n.mjs"; + +const VALID_ENGINES = ["caveman", "rtk", "hybrid", "none"]; + +async function mcpCall(name, args) { + const res = await apiFetch("/api/mcp/tools/call", { + method: "POST", + body: { name, arguments: args }, + }); + if (!res.ok) { + process.stderr.write(`Error: ${res.status}\n`); + process.exit(1); + } + return res.json(); +} + +async function confirm(q) { + return new Promise((resolve) => { + process.stdout.write(`${q} (yes/no) `); + process.stdin.setEncoding("utf8"); + process.stdin.once("data", (c) => resolve(c.toString().trim().toLowerCase().startsWith("y"))); + }); +} + +export async function runCompressionStatus(opts, cmd) { + const data = await mcpCall("omniroute_compression_status", {}); + emit(data, cmd.optsWithGlobals()); +} + +export async function runCompressionConfigure(opts, cmd) { + const config = {}; + if (opts.engine) config.engine = opts.engine; + if (opts.cavemanAggressiveness !== undefined) + config.caveman = { aggressiveness: opts.cavemanAggressiveness }; + if (opts.rtkBudget !== undefined) config.rtk = { tokenBudget: opts.rtkBudget }; + if (opts.languagePack) config.languagePack = opts.languagePack; + const data = await mcpCall("omniroute_compression_configure", config); + emit(data, cmd.optsWithGlobals()); +} + +export async function runCompressionEngineSet(name, opts, cmd) { + if (!VALID_ENGINES.includes(name)) { + process.stderr.write(`Unknown engine: ${name}. Valid: ${VALID_ENGINES.join(", ")}\n`); + process.exit(2); + } + await mcpCall("omniroute_set_compression_engine", { engine: name }); + process.stdout.write(`Engine: ${name}\n`); +} + +export async function runCompressionPreview(opts, cmd) { + const body = JSON.parse(readFileSync(opts.file, "utf8")); + const res = await apiFetch("/api/compression/preview", { method: "POST", body }); + if (!res.ok) { + process.stderr.write(`Error: ${res.status}\n`); + process.exit(1); + } + const data = await res.json(); + emit(data, cmd.optsWithGlobals()); + if (cmd.optsWithGlobals().output !== "json") { + process.stderr.write( + `\nOriginal: ${data.beforeTokens ?? "?"} tok → After: ${data.afterTokens ?? "?"} tok (${data.savingsPct ?? "?"}%)\n` + ); + } +} + +export function registerCompression(program) { + const cmp = program.command("compression").description(t("compression.description")); + + cmp + .command("status") + .description(t("compression.status.description")) + .action(runCompressionStatus); + + cmp + .command("configure") + .description(t("compression.configure.description")) + .option("--engine ", t("compression.configure.engine")) + .option("--caveman-aggressiveness ", t("compression.configure.caveman_agg"), parseFloat) + .option("--rtk-budget ", t("compression.configure.rtk_budget"), parseInt) + .option("--language-pack

", t("compression.configure.language_pack")) + .action(runCompressionConfigure); + + const engine = cmp.command("engine").description(t("compression.engine.description")); + engine.command("set ").action(runCompressionEngineSet); + engine.command("get").action(async (opts, cmd) => { + const data = await mcpCall("omniroute_compression_status", {}); + process.stdout.write(`${data.engine ?? "(default)"}\n`); + }); + + const combos = cmp.command("combos").description(t("compression.combos.description")); + combos.command("list").action(async (opts, cmd) => { + const data = await mcpCall("omniroute_list_compression_combos", {}); + emit(data.combos ?? data, cmd.optsWithGlobals()); + }); + combos + .command("stats") + .option("--period

", null, "7d") + .action(async (opts, cmd) => { + const data = await mcpCall("omniroute_compression_combo_stats", { + period: opts.period ?? "7d", + }); + emit(data, cmd.optsWithGlobals()); + }); + + const rules = cmp.command("rules").description(t("compression.rules.description")); + rules.command("list").action(async (opts, cmd) => { + const res = await apiFetch("/api/compression/rules"); + if (!res.ok) { + process.stderr.write(`Error: ${res.status}\n`); + process.exit(1); + } + emit(await res.json(), cmd.optsWithGlobals()); + }); + rules + .command("add") + .requiredOption("--pattern

", t("compression.rules.add.pattern")) + .requiredOption("--action ", t("compression.rules.add.action")) + .option("--replacement ") + .action(async (opts, cmd) => { + const body = { pattern: opts.pattern, action: opts.action }; + if (opts.replacement) body.replacement = opts.replacement; + const res = await apiFetch("/api/compression/rules", { method: "POST", body }); + if (!res.ok) { + process.stderr.write(`Error: ${res.status}\n`); + process.exit(1); + } + emit(await res.json(), cmd.optsWithGlobals()); + }); + rules + .command("remove ") + .option("--yes") + .action(async (id, opts, cmd) => { + if (!opts.yes) { + const ok = await confirm(`Remove rule ${id}?`); + if (!ok) return; + } + const res = await apiFetch(`/api/compression/rules?id=${encodeURIComponent(id)}`, { + method: "DELETE", + }); + if (!res.ok) { + process.stderr.write(`Error: ${res.status}\n`); + process.exit(1); + } + process.stdout.write("Removed\n"); + }); + + cmp + .command("language-packs") + .description(t("compression.language_packs.description")) + .action(async (opts, cmd) => { + const res = await apiFetch("/api/compression/language-packs"); + if (!res.ok) { + process.stderr.write(`Error: ${res.status}\n`); + process.exit(1); + } + emit(await res.json(), cmd.optsWithGlobals()); + }); + + cmp + .command("preview") + .description(t("compression.preview.description")) + .requiredOption("--file ", t("compression.preview.file")) + .action(runCompressionPreview); +} diff --git a/bin/cli/commands/registry.mjs b/bin/cli/commands/registry.mjs index b50417235e..62469c3d2e 100644 --- a/bin/cli/commands/registry.mjs +++ b/bin/cli/commands/registry.mjs @@ -5,6 +5,8 @@ import { registerOAuth } from "./oauth.mjs"; import { registerCloud } from "./cloud.mjs"; import { registerEval } from "./eval.mjs"; import { registerWebhooks } from "./webhooks.mjs"; +import { registerPolicy } from "./policy.mjs"; +import { registerCompression } from "./compression.mjs"; import { registerChat } from "./chat.mjs"; import { registerStream } from "./stream.mjs"; import { registerSimulate } from "./simulate.mjs"; @@ -44,6 +46,8 @@ export function registerCommands(program) { registerCloud(program); registerEval(program); registerWebhooks(program); + registerPolicy(program); + registerCompression(program); registerChat(program); registerStream(program); registerSimulate(program); diff --git a/tests/unit/cli-compression-commands.test.ts b/tests/unit/cli-compression-commands.test.ts new file mode 100644 index 0000000000..e5c80b0ef1 --- /dev/null +++ b/tests/unit/cli-compression-commands.test.ts @@ -0,0 +1,166 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +function makeResp(data: unknown, status = 200) { + const obj = { + ok: status < 400, + status, + exitCode: status < 400 ? 0 : 1, + json: () => Promise.resolve(data), + text: () => Promise.resolve(JSON.stringify(data)), + headers: new Headers(), + }; + obj.json = obj.json.bind(obj); + obj.text = obj.text.bind(obj); + return obj; +} + +async function captureStdout(fn: () => Promise): Promise { + const chunks: string[] = []; + const orig = process.stdout.write.bind(process.stdout); + process.stdout.write = (c: string | Uint8Array) => { + chunks.push(typeof c === "string" ? c : c.toString()); + return true; + }; + try { + await fn(); + } finally { + process.stdout.write = orig; + } + return chunks.join(""); +} + +function makeCmd(output = "json") { + return { optsWithGlobals: () => ({ output, quiet: output !== "table" }) }; +} + +test("compression status chama omniroute_compression_status via mcp", async () => { + let capturedBody: any = null; + const origFetch = globalThis.fetch; + globalThis.fetch = ((_url: string, opts: any) => { + if (opts?.body) capturedBody = JSON.parse(opts.body); + return Promise.resolve(makeResp({ engine: "caveman", enabled: true })); + }) as any; + + const { runCompressionStatus } = await import("../../bin/cli/commands/compression.mjs"); + await captureStdout(() => runCompressionStatus({}, makeCmd() as any)); + + globalThis.fetch = origFetch; + assert.equal(capturedBody.name, "omniroute_compression_status"); +}); + +test("compression configure envia configuração via mcp", async () => { + let capturedBody: any = null; + const origFetch = globalThis.fetch; + globalThis.fetch = ((_url: string, opts: any) => { + if (opts?.body) capturedBody = JSON.parse(opts.body); + return Promise.resolve(makeResp({ success: true })); + }) as any; + + const { runCompressionConfigure } = await import("../../bin/cli/commands/compression.mjs"); + await captureStdout(() => + runCompressionConfigure({ engine: "caveman", cavemanAggressiveness: 0.8 }, makeCmd() as any) + ); + + globalThis.fetch = origFetch; + assert.equal(capturedBody.name, "omniroute_compression_configure"); + assert.equal(capturedBody.arguments.engine, "caveman"); + assert.ok(capturedBody.arguments.caveman?.aggressiveness === 0.8); +}); + +test("compression engine set chama omniroute_set_compression_engine", async () => { + let capturedBody: any = null; + const origFetch = globalThis.fetch; + globalThis.fetch = ((_url: string, opts: any) => { + if (opts?.body) capturedBody = JSON.parse(opts.body); + return Promise.resolve(makeResp({ success: true })); + }) as any; + + const out = await captureStdout(async () => { + const { runCompressionEngineSet } = await import("../../bin/cli/commands/compression.mjs"); + await runCompressionEngineSet("rtk", {}, makeCmd() as any); + }); + + globalThis.fetch = origFetch; + assert.equal(capturedBody.name, "omniroute_set_compression_engine"); + assert.equal(capturedBody.arguments.engine, "rtk"); + assert.ok(out.includes("rtk")); +}); + +test("compression engine set rejeita engine inválido", async () => { + const origExit = process.exit; + let exitCode: number | undefined; + process.exit = ((code: number) => { + exitCode = code; + throw new Error("exit"); + }) as any; + + try { + const { runCompressionEngineSet } = await import("../../bin/cli/commands/compression.mjs"); + await runCompressionEngineSet("invalid_engine", {}, makeCmd() as any); + } catch { + // expected + } + + process.exit = origExit; + assert.equal(exitCode, 2); +}); + +test("compression rules list busca /api/compression/rules", async () => { + let capturedUrl = ""; + const origFetch = globalThis.fetch; + globalThis.fetch = ((url: string) => { + capturedUrl = url; + return Promise.resolve( + makeResp([{ id: "rule-1", pattern: "system_prompt:.*", action: "drop" }]) + ); + }) as any; + + await (globalThis.fetch as any)("/api/compression/rules"); + + globalThis.fetch = origFetch; + assert.ok(capturedUrl.includes("/api/compression/rules")); +}); + +test("compression rules add envia pattern e action", async () => { + let capturedBody: any = null; + let capturedUrl = ""; + const origFetch = globalThis.fetch; + globalThis.fetch = ((url: string, opts: any) => { + capturedUrl = url; + if (opts?.body) capturedBody = JSON.parse(opts.body); + return Promise.resolve(makeResp({ id: "rule-2", pattern: ".*debug.*", action: "drop" })); + }) as any; + + await (globalThis.fetch as any)("/api/compression/rules", { + method: "POST", + body: JSON.stringify({ pattern: ".*debug.*", action: "drop" }), + }); + + globalThis.fetch = origFetch; + assert.ok(capturedUrl.includes("/api/compression/rules")); + assert.equal(capturedBody.pattern, ".*debug.*"); + assert.equal(capturedBody.action, "drop"); +}); + +test("compression language-packs busca endpoint correto", async () => { + let capturedUrl = ""; + const origFetch = globalThis.fetch; + globalThis.fetch = ((url: string) => { + capturedUrl = url; + return Promise.resolve(makeResp([{ id: "pt-BR", name: "Portuguese" }])); + }) as any; + + await (globalThis.fetch as any)("/api/compression/language-packs"); + + globalThis.fetch = origFetch; + assert.ok(capturedUrl.includes("/api/compression/language-packs")); +}); + +test("compression.mjs pode ser importado sem erro", async () => { + const mod = await import("../../bin/cli/commands/compression.mjs"); + assert.equal(typeof mod.registerCompression, "function"); + assert.equal(typeof mod.runCompressionStatus, "function"); + assert.equal(typeof mod.runCompressionEngineSet, "function"); + assert.equal(typeof mod.runCompressionPreview, "function"); +});