From c43bc0bf66a4e245a0cafa40def0d0207bcbd73c Mon Sep 17 00:00:00 2001 From: hhang <169027046+xu91102@users.noreply.github.com> Date: Sun, 18 Oct 2026 02:23:39 +0800 Subject: [PATCH] fix(clients): preserve large TOML documents without argument spreads Fixes #4856 --- ...ientConfigurationDocument.capacity.test.ts | 56 ++++++++++++++ .../ClientConfigurationDocument.ts | 10 ++- .../tomlConfigurationCapacityCli.test.ts | 76 +++++++++++++++++++ 3 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 src/application/ClientConfigurationDocument.capacity.test.ts create mode 100644 tests/acceptance/setup/tomlConfigurationCapacityCli.test.ts diff --git a/src/application/ClientConfigurationDocument.capacity.test.ts b/src/application/ClientConfigurationDocument.capacity.test.ts new file mode 100644 index 000000000..e8670bdd0 --- /dev/null +++ b/src/application/ClientConfigurationDocument.capacity.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from "vitest"; + +import { + parseClientConfiguration, + serializeClientConfiguration, + withClientServers, +} from "./ClientConfigurationDocument.js"; + +const registration = { command: "rea", args: ["mcp"] }; + +describe("TOML configuration source capacity", () => { + it("retains a large multiline foreign value inside an inline server table", () => { + const note = `first\n${"x\n".repeat(500_000)}last`; + const original = `mcp_servers = { other = {command="other",env={NOTE="""${note}"""}}}\n`; + const parsed = parseClientConfiguration(original, "toml"); + const desired = withClientServers(parsed, { + ...parsed.servers, + rea: registration, + }); + const configured = serializeClientConfiguration(desired, "toml", original); + expect(configured).toContain(`NOTE="""${note}"""`); + expect(parseClientConfiguration(configured, "toml").document).toEqual( + desired, + ); + const removed = serializeClientConfiguration( + parsed.document, + "toml", + configured, + ); + expect(removed).toContain(`NOTE="""${note}"""`); + expect(parseClientConfiguration(removed, "toml").document).toEqual( + parsed.document, + ); + }); + + it("removes only REA from a large multiline Grok disabled-server list", () => { + const otherNames = '"other",\n'.repeat(500_000); + const original = + `disabled_mcp_servers = [\n"rea",\n${otherNames}]\n` + + '[mcp_servers.other]\ncommand = "other"\n'; + const parsed = parseClientConfiguration(original, "grok"); + const desired = withClientServers(parsed, { + ...parsed.servers, + rea: registration, + }); + const configured = serializeClientConfiguration(desired, "grok", original); + const result = parseClientConfiguration(configured, "grok"); + expect(result.document.disabled_mcp_servers).toEqual( + Array(500_000).fill("other"), + ); + expect(result.servers).toEqual({ + other: { command: "other" }, + rea: registration, + }); + }); +}); diff --git a/src/application/ClientConfigurationDocument.ts b/src/application/ClientConfigurationDocument.ts index bd4169019..2fb2ee35f 100644 --- a/src/application/ClientConfigurationDocument.ts +++ b/src/application/ClientConfigurationDocument.ts @@ -967,8 +967,12 @@ const upsertTomlServerSection = ( const discardPending = (): void => { pending.length = 0; }; + // Document line counts must not become engine-limited argument counts. + const appendLines = (lines: readonly string[]): void => { + for (const line of lines) kept.push(line); + }; const flushPending = (): void => { - kept.push(...pending); + appendLines(pending); pending.length = 0; }; const lineIndexAt = (offset: number): number => { @@ -1039,7 +1043,7 @@ const upsertTomlServerSection = ( : undefined; if (disabledLines !== undefined) { flushPending(); - kept.push(...disabledLines); + appendLines(disabledLines); lineIndex = endLine + 1; mode = "none"; continue; @@ -1064,7 +1068,7 @@ const upsertTomlServerSection = ( edited === undefined ? text.slice(lineStart, statementEnd) : `${prefix}${edited}${suffix}`; - kept.push(...replacement.split("\n")); + appendLines(replacement.split("\n")); inlineServerTable = true; } else discardPending(); } else { diff --git a/tests/acceptance/setup/tomlConfigurationCapacityCli.test.ts b/tests/acceptance/setup/tomlConfigurationCapacityCli.test.ts new file mode 100644 index 000000000..e49b0cd56 --- /dev/null +++ b/tests/acceptance/setup/tomlConfigurationCapacityCli.test.ts @@ -0,0 +1,76 @@ +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { parse as parseToml } from "smol-toml"; +import { expect } from "vitest"; + +import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js"; +import { cliTest } from "../../support/cli/cliFixture.js"; + +for (const scenario of [ + { client: "codex", path: ".codex/config.toml", line: "\n" }, + { client: "grok_build", path: ".grok/config.toml", line: "# retained\n" }, +] as const) + cliTest( + `${scenario.client} setup and uninstall preserve large valid TOML documents`, + async ({ cli }) => { + const home = await createTestTempDirectory("rea-toml-capacity-"); + const config = join(home, scenario.path); + const original = + 'model = "local-control"\n' + + scenario.line.repeat(140_000) + + '[mcp_servers.other]\ncommand = "other-local-control"\n'; + await mkdir(dirname(config), { recursive: true }); + await writeFile(config, original); + const environment = { + HOME: home, + USERPROFILE: home, + CODEX_HOME: join(home, ".codex"), + GROK_HOME: join(home, ".grok"), + XDG_CONFIG_HOME: join(home, ".config"), + TMPDIR: home, + }; + const setupArguments = [ + "setup", + "--client", + scenario.client, + "--skill=false", + "--json", + ]; + const run = (arguments_: readonly string[]) => + cli.run({ arguments: arguments_, cwd: home, environment }); + + const preview = await run([...setupArguments, "--dry-run"]); + expect(preview.exitCode, preview.stdout).toBe(0); + expect(preview.json).toMatchObject({ status: "planned" }); + expect(await readFile(config, "utf8")).toBe(original); + + const applied = await run([...setupArguments, "--yes"]); + expect(applied.exitCode, applied.stdout).toBe(0); + expect(applied.json).toMatchObject({ + status: "ready", + clients: { [scenario.client]: { status: "configured" } }, + }); + expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original); + const configured = await readFile(config, "utf8"); + expect(configured.startsWith(original.trimEnd())).toBe(true); + expect(parseToml(configured)).toMatchObject({ + mcp_servers: { other: { command: "other-local-control" }, rea: {} }, + }); + + const repeated = await run([...setupArguments, "--yes"]); + expect(repeated.exitCode, repeated.stdout).toBe(0); + expect(repeated.json).toMatchObject({ + status: "ready", + appliedActions: [], + }); + expect(await readFile(config, "utf8")).toBe(configured); + + const removed = await run(["uninstall", "--json"]); + expect(removed.exitCode, removed.stdout).toBe(0); + expect(removed.json).toMatchObject({ status: "complete" }); + const remaining = await readFile(config, "utf8"); + expect(remaining.startsWith(original.trimEnd())).toBe(true); + expect(parseToml(remaining)).toEqual(parseToml(original)); + expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original); + }, + );