Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# TOML cycle P refresh

Parent: cb75f49c9401e10f8bd37f4817cdef32b0a5cbe1, documentation PR #3681.
Source: f6db9cae8e8854c6df06087288a074d767f9787d by Hako.

Read-only git comparison from source parent to the current parent returned no changes in config-io.ts and the two affected client regression files. The 010 diff remains applicable. The shared parser admits both status and writers; no caller-specific exception or new option is required.

Implementation scope stays as 010. Main will cherry-pick the original commit and add the structure contract. An inherited independent reviewer audits the candidate; no local application tests or typecheck are permitted. Hosted CI supplies runtime verification; docs build may run in a fresh macmini-cf scratch checkout with no real credentials or service changes.
5 changes: 5 additions & 0 deletions docs-site/src/content/docs/fr/guides/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ l'application s'arrête et le signale au lieu d'écrire une valeur modifiée en
réussi. Le fichier concerné est indiqué et rien n'est déplacé sur le disque. Vous pouvez toujours modifier
ce fichier manuellement ; seule la réécriture automatique est refusée.

Les dates et heures TOML empêchent également la réécriture automatique : la fusion
les convertirait en chaînes entre guillemets, y compris dans les tableaux et les
tables en ligne. Les dates déjà écrites entre guillemets restent prises en charge.
Pour conserver une date typée sans guillemets, modifiez manuellement la configuration.

**Pi, Kimi Code, Gajae Code, MiniMax Code et l'intégration DSH gérée fonctionnent uniquement avec une adresse de
bouclage.** Les quatre premiers n'ont aucun champ de configuration pour l'en-tête `x-opencodex-api-key`
qu'exige une liaison hors bouclage. DSH possède une table d'en-têtes générique, mais rc.6 ne documente pas
Expand Down
5 changes: 5 additions & 0 deletions docs-site/src/content/docs/guides/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ changed value and calling it success. You will see the file named and nothing on
disk will have moved. Editing that file by hand still works; it is only our
automatic rewrite that declines.

TOML dates and times also refuse managed rewrites: the merge step would turn these
typed values into quoted strings. This includes values inside arrays and inline
tables. Quoted date strings remain supported; an unquoted date must be preserved
by editing the configuration manually.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

**Pi, Kimi Code, Gajae Code, MiniMax Code, Prime Agent and the managed DSH integration only work against a loopback bind.**
The first four have no config field for the `x-opencodex-api-key` header a non-loopback bind
requires. DSH has a generic headers map, but rc.6 does not document that dedicated admission
Expand Down
5 changes: 5 additions & 0 deletions docs-site/src/content/docs/tr/guides/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ değişen bir değer yazıp buna başarı demek yerine durur ve bunu söyler. Do
adlandırıldığını ve diskte hiçbir şeyin taşınmadığını görürsünüz. Bu dosyayı
elle düzenlemek hala çalışır; yalnızca otomatik yeniden yazmamız reddeder.

TOML tarih ve saat değerleri de otomatik yeniden yazmayı engeller: birleştirme adımı,
diziler ve satır içi tablolar dahil bu türlenmiş değerleri tırnaklı metne dönüştürür.
Zaten tırnak içinde yazılmış tarihler desteklenir. Tırnaksız tarih türünü korumak
için yapılandırmayı elle düzenleyin.

**Pi, Kimi Code, Gajae Code, MiniMax Code ve yönetilen DSH entegrasyonu yalnızca geri döngü (loopback) bağlantısına karşı
çalışır.** İlk dördünün yapılandırmasında geri döngü olmayan bir bağlantının gerektirdiği
`x-opencodex-api-key` başlığı için alan yoktur. DSH genel bir headers haritası sunar, ancak rc.6
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/zh-tw/guides/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ opencodex 從自己的環境讀取這些變數。如果你的 gateway 以 profil

**如果某個值無法忠實重寫,開關會拒絕執行。** 往返覆蓋這些格式在實務上會用到的值種類;當它做不到時——例如使用 `inf` 或 `nan` 的 TOML 檔案,我們可用的 parser 無法準確讀回——套用會停止並說明,而不是寫入被改動的值然後宣稱成功。你會看到檔案被指名,磁碟上沒有任何東西被移動。手動編輯那個檔案仍然有效;只有我們的自動重寫會拒絕。

TOML 日期與時間值也會阻止自動重寫:合併步驟會將這些帶有型別的值轉成加引號的字串,陣列和行內表格中的值也一樣。原本就加引號的日期字串仍受支援;若要保留不加引號的日期型別,請手動編輯設定。

**Pi、Kimi Code、Gajae Code、MiniMax Code 與受管理 DSH 整合只能對 loopback bind 運作。** 前四者的設定沒有非 loopback bind 所需的 `x-opencodex-api-key` header 欄位。DSH 雖然提供通用 headers map,但 rc.6 並未把這個專用准入 header 記錄為受支援的整合契約,因此受管理 writer 會選擇安全拒絕,而不自行猜測。請改用 SSH tunnel,或由本機 forwarder 加上該 header 後再以 loopback 存取。

**產生的 OMP 整合也刻意只支援 loopback。** OMP 確實支援 provider 層級的 headers,但這個最初的整合不會發出遠端 `x-opencodex-api-key` 憑證連線。手動的遠端 OMP 設定目前不在受管理的整合範圍內。
Expand Down
17 changes: 16 additions & 1 deletion src/integrations/config-io.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,22 @@ export function parseConfig(text: string | null, format: ConfigFormat): unknown
* evidence is gone.
*/
if (/(^|[\s,[=])[-+]?(?:inf|nan)(?=[\s,\]]|$)/mi.test(text)) return PARSE_FAILED;
return Bun.TOML.parse(text);
const document = Bun.TOML.parse(text);
// TOML date/time scalars are Temporal objects with toJSON methods.
// The merge layer JSON-clones documents, which silently turns these
// into strings. Refuse before either status or a writer can admit a
// lossy rewrite, including dates nested in arrays and inline tables.
const pending: unknown[] = [document];
while (pending.length > 0) {
const value = pending.pop();
if (value === null || typeof value !== "object") continue;
if (!Array.isArray(value)) {
const prototype = Object.getPrototypeOf(value);
if (prototype !== Object.prototype && prototype !== null) return PARSE_FAILED;
}
for (const child of Object.values(value)) pending.push(child);
}
return document;
}
}
} catch {
Expand Down
4 changes: 4 additions & 0 deletions structure/09_client-integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Status and mutation must use the same classifier. A special case added only to a
would be misleading because refresh or disable could still reject the same file; a special case
added only to a writer would let a mutation bypass the state users saw.

TOML temporal scalars cannot survive the JSON-cloned merge representation with their types
intact. The common parser refuses documents containing them before either status or mutation
proceeds, including nested arrays and inline tables. Quoted date strings remain supported.

## Fast model selectors

The serving proxy resolves `fastRowAvailable` on every management model row, including its
Expand Down
19 changes: 19 additions & 0 deletions tests/clients/integrations-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,25 @@ describe("classifier unit behavior", () => {
expect(parseConfig("{{{", "json")).toBe(PARSE_FAILED);
});

test("parseConfig refuses typed TOML dates before a JSON clone can turn them into strings", () => {
for (const literal of [
"2026-09-05T10:00:00Z",
"2026-09-05T10:00:00-07:00",
"2026-09-05T10:00:00.123456",
"2026-09-05",
"10:00:00.123456",
]) {
for (const text of [
`expires = ${literal}\n`,
`[user]\nexpires = ${literal}\n`,
`items = [{ expires = ${literal} }]\n`,
]) {
expect(parseConfig(text, "toml")).toBe(PARSE_FAILED);
}
expect(parseConfig(`expires = "${literal}"\n`, "toml")).toEqual({ expires: literal });
}
});

test("parseConfig refuses json number literals a rewrite would change", () => {
// Overflow to Infinity — a rewrite would bake in null.
expect(parseConfig("{\"a\": 1e999}", "json")).toBe(PARSE_FAILED);
Expand Down
18 changes: 18 additions & 0 deletions tests/clients/integrations-writer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ function reverseJsonObjectKeys(value: unknown): unknown {
}

describe("apply", () => {
test("refuses Kimi TOML date rewrites without changing the file or ownership store", () => {
const spec = INTEGRATION_CLIENTS.kimi;
mkdirSync(spec.detectDir(TEST_ENV, home), { recursive: true });
const configPath = spec.configPath(TEST_ENV, home);
mkdirSync(dirname(configPath), { recursive: true });
const original = "[user]\nexpires = 2026-09-05T10:00:00Z\n";
writeFileSync(configPath, original);
const request = input({ clientId: "kimi" });

expect(readIntegrationState(request).state).toBe("unsafe");
const result = applyIntegration(request);
expect(result.ok).toBe(false);
if (!result.ok) expect(result.reason).toBe("unsafe");
expect(readFileSync(configPath, "utf8")).toBe(original);
expect(store.listOperations()).toHaveLength(0);
expect(store.readRecords().kimi).toBeUndefined();
});

test("refuses a client that is not installed, and writes nothing", () => {
const result = applyIntegration(input());
expect(result.ok).toBe(false);
Expand Down
Loading