refactor: removed markit converters test suite

- Removed markit-converters.test.ts from packages/coding-agent.
- Updated google-gemini-cli provider to ensure antigravity version during stream execution.
- Updated antigravity discovery options to use fetcher fallback.
This commit is contained in:
can1357
2026-08-14 00:04:34 +02:00
parent 834002adc5
commit 5dd0aca4b8
4 changed files with 8 additions and 226 deletions
@@ -8,6 +8,7 @@ import { scheduler } from "node:timers/promises";
import { type } from "@oh-my-pi/omptype";
import { calculateCost } from "@oh-my-pi/pi-catalog/models";
import {
ensureAntigravityVersion,
getAntigravityModelWireProfile,
getAntigravityUserAgent,
getGeminiCliHeaders,
@@ -587,6 +588,9 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
if (replacementPayload !== undefined) {
requestBody = replacementPayload as typeof requestBody;
}
// Fire-and-forget: the first request in a fresh process uses the pinned
// fallback version; later requests pick up the manifest-discovered one.
if (isAntigravity) void ensureAntigravityVersion(options?.fetch);
const headers = isAntigravity ? { "User-Agent": getAntigravityUserAgent() } : getGeminiCliHeaders(model.id);
const requestHeaders = {
@@ -162,7 +162,7 @@ export async function fetchAntigravityDiscoveryModels(
options: FetchAntigravityDiscoveryModelsOptions,
): Promise<ModelSpec<"google-gemini-cli">[] | null> {
if (options.userAgent === undefined) {
await ensureAntigravityVersion(fetch, options.signal);
await ensureAntigravityVersion(options.fetcher ?? fetch, options.signal);
}
const fetcher = discoveryFetch(options.fetcher);
@@ -90,6 +90,9 @@ export function ensureAntigravityVersion(fetcher: typeof fetch = fetch, signal?:
/** Antigravity `User-Agent` header value; rebuilt when the discovered version changes. */
export function getAntigravityUserAgent(): string {
const version = getAntigravityVersion();
// The backend does not validate `cl` (verified live: stale, zero, and absent
// cl all pass model gating on daily-cloudcode-pa; only the version gates).
// The update manifest carries no changelist, so the captured value stays.
const cl = process.env.PI_AI_ANTIGRAVITY_CL || "963137146";
const os = process.env.PI_AI_ANTIGRAVITY_OS || "darwin";
const arch = process.env.PI_AI_ANTIGRAVITY_ARCH || "arm64";
@@ -1,225 +0,0 @@
/**
* Runtime coverage for the in-house markit document engine (src/markit), which
* replaced the `markit-ai` package. Each format is generated in-memory via the
* shared zip util (src/utils/zip) — no external fixtures — and converted through
* the public wrapper (src/utils/markit), locking: docx text, xlsx tables, pptx
* slides, epub metadata+spine, shared HTML-table normalization, image
* extraction, the nested/relative zip path resolution regression surface, and
* the unsupported-format error contract.
*/
import { describe, expect, it } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { convertBufferWithMarkit, convertFileWithMarkit } from "@oh-my-pi/pi-coding-agent/utils/markit";
import { zip } from "@oh-my-pi/pi-coding-agent/utils/zip";
import { removeWithRetries } from "@oh-my-pi/pi-utils";
const enc = (s: string): Uint8Array => new TextEncoder().encode(s);
const WML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
const repoRoot = path.resolve(import.meta.dir, "..", "..", "..");
const cliEntry = path.join(repoRoot, "packages", "coding-agent", "src", "cli.ts");
function makeDocx(bodyXml: string): Uint8Array {
return zip({
"[Content_Types].xml": enc(
`<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/></Types>`,
),
"_rels/.rels": enc(
`<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>`,
),
"word/document.xml": enc(
`<?xml version="1.0"?><w:document xmlns:w="${WML}"><w:body>${bodyXml}</w:body></w:document>`,
),
});
}
describe("markit converters", () => {
it("converts docx paragraphs to markdown", async () => {
const docx = makeDocx(
`<w:p><w:r><w:t>First paragraph.</w:t></w:r></w:p><w:p><w:r><w:t>Second paragraph.</w:t></w:r></w:p>`,
);
const result = await convertBufferWithMarkit(docx, ".docx");
expect(result.ok).toBe(true);
expect(result.content).toBe("First paragraph.\n\nSecond paragraph.");
});
it("converts xlsx sheets to markdown tables", async () => {
const xlsx = zip({
"xl/workbook.xml": enc(
`<?xml version="1.0"?><workbook xmlns:r="r"><sheets><sheet name="People" sheetId="1" r:id="rId1"/></sheets></workbook>`,
),
"xl/_rels/workbook.xml.rels": enc(
`<?xml version="1.0"?><Relationships><Relationship Id="rId1" Target="worksheets/sheet1.xml"/></Relationships>`,
),
"xl/worksheets/sheet1.xml": enc(
`<?xml version="1.0"?><worksheet><sheetData><row><c t="inlineStr"><is><t>Name</t></is></c><c t="inlineStr"><is><t>Age</t></is></c></row><row><c t="inlineStr"><is><t>Alice</t></is></c><c><v>30</v></c></row></sheetData></worksheet>`,
),
});
const result = await convertBufferWithMarkit(xlsx, ".xlsx");
expect(result.ok).toBe(true);
expect(result.content).toContain("## People");
expect(result.content).toContain("| Name | Age |");
expect(result.content).toContain("| --- | --- |");
expect(result.content).toContain("| Alice | 30 |");
});
it("reads an xlsx worksheet through an absolute (/-prefixed) rel target", async () => {
const xlsx = zip({
"xl/workbook.xml": enc(
`<?xml version="1.0"?><workbook xmlns:r="r"><sheets><sheet name="S" sheetId="1" r:id="rId1"/></sheets></workbook>`,
),
"xl/_rels/workbook.xml.rels": enc(
`<?xml version="1.0"?><Relationships><Relationship Id="rId1" Target="/xl/worksheets/sheet1.xml"/></Relationships>`,
),
"xl/worksheets/sheet1.xml": enc(
`<?xml version="1.0"?><worksheet><sheetData><row><c t="inlineStr"><is><t>Header</t></is></c></row><row><c><v>42</v></c></row></sheetData></worksheet>`,
),
});
const result = await convertBufferWithMarkit(xlsx, ".xlsx");
expect(result.ok).toBe(true);
expect(result.content).toContain("| Header |");
expect(result.content).toContain("| 42 |");
});
it("converts pptx slides with a title heading and body text", async () => {
const pptx = zip({
"ppt/presentation.xml": enc(
`<?xml version="1.0"?><p:presentation xmlns:p="p" xmlns:r="r"><p:sldIdLst><p:sldId id="256" r:id="rId1"/></p:sldIdLst></p:presentation>`,
),
"ppt/_rels/presentation.xml.rels": enc(
`<?xml version="1.0"?><Relationships><Relationship Id="rId1" Target="slides/slide1.xml"/></Relationships>`,
),
"ppt/slides/slide1.xml": enc(
`<?xml version="1.0"?><p:sld xmlns:p="p" xmlns:a="a"><p:cSld><p:spTree><p:sp><p:txBody><a:p><a:r><a:t>The Title</a:t></a:r></a:p></p:txBody></p:sp><p:sp><p:txBody><a:p><a:r><a:t>Body line</a:t></a:r></a:p></p:txBody></p:sp></p:spTree></p:cSld></p:sld>`,
),
});
const result = await convertBufferWithMarkit(pptx, ".pptx");
expect(result.ok).toBe(true);
expect(result.content).toContain("# The Title");
expect(result.content).toContain("Body line");
});
it("extracts a pptx image through a ../media relative rel target into imageDir", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "markit-pptx-"));
try {
const pptx = zip({
"ppt/presentation.xml": enc(
`<?xml version="1.0"?><p:presentation xmlns:p="p" xmlns:r="r"><p:sldIdLst><p:sldId id="256" r:id="rId1"/></p:sldIdLst></p:presentation>`,
),
"ppt/_rels/presentation.xml.rels": enc(
`<?xml version="1.0"?><Relationships><Relationship Id="rId1" Target="slides/slide1.xml"/></Relationships>`,
),
"ppt/slides/slide1.xml": enc(
`<?xml version="1.0"?><p:sld xmlns:p="p" xmlns:a="a"><p:cSld><p:spTree><p:pic><p:nvPicPr><p:cNvPr name="Pic1"/></p:nvPicPr><p:blipFill><a:blip r:embed="rId2"/></p:blipFill></p:pic></p:spTree></p:cSld></p:sld>`,
),
"ppt/slides/_rels/slide1.xml.rels": enc(
`<?xml version="1.0"?><Relationships><Relationship Id="rId2" Target="../media/image1.png"/></Relationships>`,
),
"ppt/media/image1.png": new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]),
});
const pptxPath = path.join(dir, "deck.pptx");
const imageDir = path.join(dir, "imgs");
await Bun.write(pptxPath, pptx);
const result = await convertFileWithMarkit(pptxPath, undefined, { imageDir });
expect(result.ok).toBe(true);
const written = await fs.readdir(imageDir);
expect(written).toHaveLength(1);
expect(result.content).toContain(`](${path.join(imageDir, written[0]!)})`);
} finally {
await removeWithRetries(dir);
}
});
it("converts epub spine, normalizes HTML tables, and resolves a non-root OPF basePath", async () => {
const epub = zip({
"META-INF/container.xml": enc(
`<?xml version="1.0"?><container><rootfiles><rootfile full-path="OEBPS/content.opf"/></rootfiles></container>`,
),
"OEBPS/content.opf": enc(
`<?xml version="1.0"?><package><metadata xmlns:dc="dc"><dc:title>Nested Book</dc:title><dc:creator>Ada</dc:creator></metadata><manifest><item id="c1" href="text/ch1.xhtml"/></manifest><spine><itemref idref="c1"/></spine></package>`,
),
"OEBPS/text/ch1.xhtml": enc(
`<html><body><h2>Chapter One</h2><p>Body text.</p><table><tr><td>A</td><td>B</td></tr><tr><td>1</td><td>2</td></tr></table></body></html>`,
),
});
const result = await convertBufferWithMarkit(epub, ".epub");
expect(result.ok).toBe(true);
expect(result.content).toContain("**Title:** Nested Book");
expect(result.content).toContain("**Authors:** Ada");
expect(result.content).toContain("## Chapter One");
expect(result.content).toContain("Body text.");
// normalizeTablesHtml promotes the first row to a header so GFM renders a table.
expect(result.content).toContain("| A | B |");
expect(result.content).toContain("| --- | --- |");
});
it("reads PDF text after inline image binary data containing delimiter bytes", async () => {
const homeDir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-pdf-inline-home-"));
const homeRoot = path.parse(homeDir).root;
const homeDrive = homeRoot.endsWith(path.sep) ? homeRoot.slice(0, -1) : homeRoot;
const homePath = homeDir.slice(homeDrive.length) || path.sep;
try {
const pdfPath = path.join(
import.meta.dir,
"fixtures",
"pdf-inline-image-repro",
"bad-inline-image-delimiter.pdf",
);
const proc = Bun.spawn([process.execPath, cliEntry, "read", pdfPath], {
cwd: repoRoot,
stdout: "pipe",
stderr: "pipe",
env: {
...process.env,
APPDATA: path.join(homeDir, "AppData", "Roaming"),
HOME: homeDir,
HOMEDRIVE: homeDrive,
HOMEPATH: homePath,
LOCALAPPDATA: path.join(homeDir, "AppData", "Local"),
USERPROFILE: homeDir,
NO_COLOR: "1",
OMP_PROFILE: "",
PI_CODING_AGENT_DIR: path.join(homeDir, ".omp", "agent"),
PI_CONFIG_DIR: ".omp",
PI_NO_TITLE: "1",
PI_PROFILE: "",
XDG_CACHE_HOME: path.join(homeDir, ".cache"),
XDG_DATA_HOME: path.join(homeDir, ".local", "share"),
XDG_STATE_HOME: path.join(homeDir, ".local", "state"),
},
});
const stdout = new Response(proc.stdout).text();
const stderr = new Response(proc.stderr).text();
// The regression is a synchronous child-process spin; there is no in-process signal to await.
const outcome = await Promise.race([
proc.exited.then(exitCode => ({ type: "exit" as const, exitCode })),
Bun.sleep(5_000).then(() => ({ type: "timeout" as const })),
]);
if (outcome.type === "timeout") {
try {
proc.kill("SIGKILL");
} catch {
// already exited
}
await proc.exited;
throw new Error("read command timed out on inline image PDF");
}
const [out, err] = await Promise.all([stdout, stderr]);
expect(outcome.exitCode).toBe(0);
expect(err).toBe("");
expect(out).toContain("Inline image tokenizer repro issue");
expect(out).toContain("| Name | Qty |");
expect(out).toContain("| Wire | 12 |");
} finally {
await removeWithRetries(homeDir);
}
});
it("reports an unsupported format instead of emitting garbage", async () => {
const rtf = enc("{\\rtf1\\ansi binary-ish}");
const result = await convertBufferWithMarkit(rtf, ".rtf");
expect(result.ok).toBe(false);
expect(result.error).toContain("Unsupported format");
});
});