Skip to content
Closed
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
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.

**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
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