Merge PR #5406: fix(auth-broker): resolve nested auth.broker.url/token yaml keys (@roboomp)

# Conflicts:
#	packages/ai/src/auth-broker/discover.ts
This commit is contained in:
can1357
2026-07-14 18:33:01 +02:00
3 changed files with 88 additions and 3 deletions
+1
View File
@@ -172,6 +172,7 @@
- Fixed OAuth `launchUrl` advertisement for flows whose redirect never returns to the local callback server: custom-scheme redirects (e.g. GitLab Duo's `vscode://` URI, which `new URL` parses without complaint) and fixed non-loopback hosts no longer receive a `http://localhost:<port>/launch` copy target that misrepresents the callback endpoint and resolves nowhere for remote users.
- Codex load balancing: clear stale persisted and in-memory usage-limit blocks for an `openai-codex` account when a fresh live usage report shows it is allowed and below all limits, including broker-backed gateway snapshots, so traffic returns to recovered accounts instead of funneling to one sibling.
- Fixed Cursor `max_mode` requests to send discovered max-mode metadata on both model payload fields. ([#4797](https://github.com/can1357/oh-my-pi/issues/4797))
- Fixed `auth-broker` config discovery ignoring nested `auth.broker.url` / `auth.broker.token` YAML keys. `readConfigYaml` only read the literal flat dotted key, so standard nested YAML was silently dropped; it now resolves both nested and flat forms (nested wins). ([#4734](https://github.com/can1357/oh-my-pi/issues/4734))
## [16.3.11] - 2026-07-06
+22 -3
View File
@@ -72,6 +72,26 @@ interface ConfigSnapshot {
token?: string;
}
/**
* Resolve a dotted config key (e.g. `auth.broker.url`) against a parsed YAML
* record, accepting both nested form (`auth: { broker: { url } }`) and the
* legacy flat literal-dot key (`"auth.broker.url": ...`). Nested wins when both
* are present. Returns the value only when it is a string.
*/
function readDottedString(record: Record<string, unknown>, dottedKey: string): string | undefined {
let current: unknown = record;
for (const segment of dottedKey.split(".")) {
if (current === null || typeof current !== "object" || Array.isArray(current)) {
current = undefined;
break;
}
current = (current as Record<string, unknown>)[segment];
}
if (typeof current === "string") return current;
const flat = record[dottedKey];
return typeof flat === "string" ? flat : undefined;
}
async function readConfigYaml(agentDir: string): Promise<ConfigSnapshot> {
for (const filename of MAIN_CONFIG_FILENAMES) {
const configPath = path.join(agentDir, filename);
@@ -80,9 +100,8 @@ async function readConfigYaml(agentDir: string): Promise<ConfigSnapshot> {
const parsed = YAML.parse(raw);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
const record = parsed as Record<string, unknown>;
const url = typeof record["auth.broker.url"] === "string" ? (record["auth.broker.url"] as string) : undefined;
const token =
typeof record["auth.broker.token"] === "string" ? (record["auth.broker.token"] as string) : undefined;
const url = readDottedString(record, "auth.broker.url");
const token = readDottedString(record, "auth.broker.token");
return { url, token };
} catch (err) {
if (isEnoent(err)) continue;
@@ -0,0 +1,65 @@
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { resolveAuthBrokerConfig } from "@oh-my-pi/pi-ai/auth-broker";
import { removeWithRetries } from "../../utils/src/temp";
import { withEnv } from "./helpers";
const CLEAR_BROKER_ENV = {
OMP_AUTH_BROKER_URL: undefined,
OMP_AUTH_BROKER_TOKEN: undefined,
} as const;
describe("auth-broker config.yml key resolution", () => {
let agentDir = "";
beforeEach(async () => {
agentDir = await fs.mkdtemp(path.join(os.tmpdir(), "pi-ai-auth-broker-config-"));
});
afterEach(async () => {
if (agentDir) await removeWithRetries(agentDir);
agentDir = "";
});
async function writeConfig(yaml: string): Promise<void> {
await Bun.write(path.join(agentDir, "config.yml"), yaml);
}
test("nested YAML keys resolve broker url and token", async () => {
await writeConfig(
["auth:", " broker:", " url: https://broker.example", " token: nested-token", ""].join("\n"),
);
await withEnv(CLEAR_BROKER_ENV, async () => {
const config = await resolveAuthBrokerConfig({ agentDir });
expect(config).toEqual({ url: "https://broker.example", token: "nested-token" });
});
});
test("legacy flat dotted keys still resolve", async () => {
await writeConfig(['"auth.broker.url": https://flat.example', '"auth.broker.token": flat-token', ""].join("\n"));
await withEnv(CLEAR_BROKER_ENV, async () => {
const config = await resolveAuthBrokerConfig({ agentDir });
expect(config).toEqual({ url: "https://flat.example", token: "flat-token" });
});
});
test("nested value wins over the flat dotted key", async () => {
await writeConfig(
[
'"auth.broker.url": https://flat.example',
'"auth.broker.token": flat-token',
"auth:",
" broker:",
" url: https://nested.example",
" token: nested-token",
"",
].join("\n"),
);
await withEnv(CLEAR_BROKER_ENV, async () => {
const config = await resolveAuthBrokerConfig({ agentDir });
expect(config).toEqual({ url: "https://nested.example", token: "nested-token" });
});
});
});