Skip to content

Commit 51c1250

Browse files
committed
Merge remote-tracking branch 'origin/refactor/pr5-package-subpath-exports-latest' into release/mainbound-20260323
2 parents 78e64a0 + f335beb commit 51c1250

7 files changed

Lines changed: 165 additions & 27 deletions

File tree

docs/reference/public-api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Stable APIs are covered by semver compatibility guarantees and must remain backw
1616
- `OpenAIOAuthPlugin`
1717
- `OpenAIAuthPlugin`
1818
- default export (alias of `OpenAIOAuthPlugin`)
19+
- Supported package subpath entrypoints:
20+
- `codex-multi-auth/auth`
21+
- `codex-multi-auth/storage`
22+
- `codex-multi-auth/config`
23+
- `codex-multi-auth/request`
24+
- `codex-multi-auth/cli`
1925
- CLI surface:
2026
- `codex auth ...` command family
2127
- documented flags and aliases in `reference/commands.md`

lib/auth/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./auth.js";

lib/codex-cli/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from "./observability.js";
2+
export * from "./state.js";
3+
export * from "./sync.js";
4+
export * from "./writer.js";

lib/request/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from "./failure-policy.js";
2+
export * from "./fetch-helpers.js";
3+
export * from "./rate-limit-backoff.js";
4+
export * from "./request-transformer.js";

package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,39 @@
44
"description": "Multi-account OAuth manager and codex auth wrapper for the official @openai/codex CLI, with switching, health checks, and recovery tools",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",
7+
"exports": {
8+
".": {
9+
"types": "./dist/index.d.ts",
10+
"import": "./dist/index.js",
11+
"default": "./dist/index.js"
12+
},
13+
"./auth": {
14+
"types": "./dist/lib/auth/index.d.ts",
15+
"import": "./dist/lib/auth/index.js",
16+
"default": "./dist/lib/auth/index.js"
17+
},
18+
"./storage": {
19+
"types": "./dist/lib/storage.d.ts",
20+
"import": "./dist/lib/storage.js",
21+
"default": "./dist/lib/storage.js"
22+
},
23+
"./config": {
24+
"types": "./dist/lib/config.d.ts",
25+
"import": "./dist/lib/config.js",
26+
"default": "./dist/lib/config.js"
27+
},
28+
"./request": {
29+
"types": "./dist/lib/request/index.d.ts",
30+
"import": "./dist/lib/request/index.js",
31+
"default": "./dist/lib/request/index.js"
32+
},
33+
"./cli": {
34+
"types": "./dist/lib/codex-cli/index.d.ts",
35+
"import": "./dist/lib/codex-cli/index.js",
36+
"default": "./dist/lib/codex-cli/index.js"
37+
},
38+
"./package.json": "./package.json"
39+
},
740
"type": "module",
841
"license": "MIT",
942
"author": "ndycode",

test/documentation.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ describe("Documentation Integrity", () => {
233233
expect(publicApi).toContain("tier c");
234234
expect(publicApi).toContain("options-object");
235235
expect(publicApi).toContain("semver");
236+
expect(publicApi).toContain("codex-multi-auth/auth");
237+
expect(publicApi).toContain("codex-multi-auth/storage");
238+
expect(publicApi).toContain("codex-multi-auth/config");
239+
expect(publicApi).toContain("codex-multi-auth/request");
240+
expect(publicApi).toContain("codex-multi-auth/cli");
236241

237242
expect(errorContracts).toContain("exit codes");
238243
expect(errorContracts).toContain("json mode contract");

test/public-api-contract.test.ts

Lines changed: 112 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { describe, expect, it, vi } from "vitest";
2-
import {
3-
HealthScoreTracker,
4-
TokenBucketTracker,
5-
exponentialBackoff,
6-
selectHybridAccount,
7-
} from "../lib/rotation.js";
82
import { getTopCandidates } from "../lib/parallel-probe.js";
93
import { createCodexHeaders } from "../lib/request/fetch-helpers.js";
104
import {
115
clearRateLimitBackoffState,
126
getRateLimitBackoffWithReason,
137
} from "../lib/request/rate-limit-backoff.js";
148
import { transformRequestBody } from "../lib/request/request-transformer.js";
9+
import {
10+
exponentialBackoff,
11+
HealthScoreTracker,
12+
selectHybridAccount,
13+
TokenBucketTracker,
14+
} from "../lib/rotation.js";
1515
import type { RequestBody } from "../lib/types.js";
16+
import pkg from "../package.json" with { type: "json" };
1617

1718
describe("public api contract", () => {
1819
it("keeps root plugin exports aligned", async () => {
@@ -26,37 +27,108 @@ describe("public api contract", () => {
2627
const rotation = await import("../lib/rotation.js");
2728
const parallelProbe = await import("../lib/parallel-probe.js");
2829
const fetchHelpers = await import("../lib/request/fetch-helpers.js");
29-
const rateLimitBackoff = await import("../lib/request/rate-limit-backoff.js");
30-
const requestTransformer = await import("../lib/request/request-transformer.js");
31-
const required: ReadonlyArray<
32-
readonly [string, Record<string, unknown>]
33-
> = [
34-
["selectHybridAccount", rotation],
35-
["exponentialBackoff", rotation],
36-
["getTopCandidates", parallelProbe],
37-
["createCodexHeaders", fetchHelpers],
38-
["getRateLimitBackoffWithReason", rateLimitBackoff],
39-
["transformRequestBody", requestTransformer],
40-
];
30+
const rateLimitBackoff = await import(
31+
"../lib/request/rate-limit-backoff.js"
32+
);
33+
const requestTransformer = await import(
34+
"../lib/request/request-transformer.js"
35+
);
36+
const required: ReadonlyArray<readonly [string, Record<string, unknown>]> =
37+
[
38+
["selectHybridAccount", rotation],
39+
["exponentialBackoff", rotation],
40+
["getTopCandidates", parallelProbe],
41+
["createCodexHeaders", fetchHelpers],
42+
["getRateLimitBackoffWithReason", rateLimitBackoff],
43+
["transformRequestBody", requestTransformer],
44+
];
4145
for (const [name, mod] of required) {
4246
expect(name in mod, `missing export: ${name}`).toBe(true);
4347
expect(typeof mod[name], `${name} should be a function`).toBe("function");
4448
}
4549
});
4650

51+
it("declares the supported package subpath exports", async () => {
52+
expect(pkg.exports).toEqual({
53+
".": {
54+
types: "./dist/index.d.ts",
55+
import: "./dist/index.js",
56+
default: "./dist/index.js",
57+
},
58+
"./auth": {
59+
types: "./dist/lib/auth/index.d.ts",
60+
import: "./dist/lib/auth/index.js",
61+
default: "./dist/lib/auth/index.js",
62+
},
63+
"./storage": {
64+
types: "./dist/lib/storage.d.ts",
65+
import: "./dist/lib/storage.js",
66+
default: "./dist/lib/storage.js",
67+
},
68+
"./config": {
69+
types: "./dist/lib/config.d.ts",
70+
import: "./dist/lib/config.js",
71+
default: "./dist/lib/config.js",
72+
},
73+
"./request": {
74+
types: "./dist/lib/request/index.d.ts",
75+
import: "./dist/lib/request/index.js",
76+
default: "./dist/lib/request/index.js",
77+
},
78+
"./cli": {
79+
types: "./dist/lib/codex-cli/index.d.ts",
80+
import: "./dist/lib/codex-cli/index.js",
81+
default: "./dist/lib/codex-cli/index.js",
82+
},
83+
"./package.json": "./package.json",
84+
});
85+
});
86+
87+
it("keeps the supported subpath entry barrels aligned", async () => {
88+
const auth = await import("../lib/auth/index.js");
89+
const storage = await import("../lib/storage.js");
90+
const config = await import("../lib/config.js");
91+
const request = await import("../lib/request/index.js");
92+
const cli = await import("../lib/codex-cli/index.js");
93+
94+
expect(typeof auth.exchangeAuthorizationCode).toBe("function");
95+
expect(typeof storage.loadAccounts).toBe("function");
96+
expect(typeof config.loadPluginConfig).toBe("function");
97+
expect(typeof request.createCodexHeaders).toBe("function");
98+
expect(typeof request.transformRequestBody).toBe("function");
99+
expect("handleResponse" in request).toBe(false);
100+
expect("withStreamFailover" in request).toBe(false);
101+
expect(typeof cli.loadCodexCliState).toBe("function");
102+
});
103+
47104
it("keeps positional and options-object overload behavior aligned", async () => {
48105
const healthTracker = new HealthScoreTracker();
49106
const tokenTracker = new TokenBucketTracker();
50-
const accounts = [{ index: 0, isAvailable: true, lastUsed: 1_709_280_000_000 }];
107+
const accounts = [
108+
{ index: 0, isAvailable: true, lastUsed: 1_709_280_000_000 },
109+
];
51110

52-
const selectedPositional = selectHybridAccount(accounts, healthTracker, tokenTracker);
53-
const selectedNamed = selectHybridAccount({ accounts, healthTracker, tokenTracker });
111+
const selectedPositional = selectHybridAccount(
112+
accounts,
113+
healthTracker,
114+
tokenTracker,
115+
);
116+
const selectedNamed = selectHybridAccount({
117+
accounts,
118+
healthTracker,
119+
tokenTracker,
120+
});
54121
expect(selectedNamed?.index).toBe(selectedPositional?.index);
55122

56123
const randomSpy = vi.spyOn(Math, "random").mockReturnValue(0.5);
57124
try {
58125
const backoffPositional = exponentialBackoff(3, 1000, 60000, 0);
59-
const backoffNamed = exponentialBackoff({ attempt: 3, baseMs: 1000, maxMs: 60000, jitterFactor: 0 });
126+
const backoffNamed = exponentialBackoff({
127+
attempt: 3,
128+
baseMs: 1000,
129+
maxMs: 60000,
130+
jitterFactor: 0,
131+
});
60132
expect(backoffNamed).toBe(backoffPositional);
61133
} finally {
62134
randomSpy.mockRestore();
@@ -82,7 +154,9 @@ describe("public api contract", () => {
82154
1,
83155
);
84156
const topNamed = getTopCandidates({
85-
accountManager: manager as unknown as Parameters<typeof getTopCandidates>[0],
157+
accountManager: manager as unknown as Parameters<
158+
typeof getTopCandidates
159+
>[0],
86160
modelFamily: "codex",
87161
model: null,
88162
maxCandidates: 1,
@@ -99,11 +173,22 @@ describe("public api contract", () => {
99173
accessToken: "token",
100174
opts: { model: "gpt-5", promptCacheKey: "session-compat" },
101175
});
102-
expect(headersNamed.get("Authorization")).toBe(headersPositional.get("Authorization"));
103-
expect(headersNamed.get("conversation_id")).toBe(headersPositional.get("conversation_id"));
104-
expect(headersNamed.get("session_id")).toBe(headersPositional.get("session_id"));
176+
expect(headersNamed.get("Authorization")).toBe(
177+
headersPositional.get("Authorization"),
178+
);
179+
expect(headersNamed.get("conversation_id")).toBe(
180+
headersPositional.get("conversation_id"),
181+
);
182+
expect(headersNamed.get("session_id")).toBe(
183+
headersPositional.get("session_id"),
184+
);
105185

106-
const ratePositional = getRateLimitBackoffWithReason(1, "compat", 1000, "tokens");
186+
const ratePositional = getRateLimitBackoffWithReason(
187+
1,
188+
"compat",
189+
1000,
190+
"tokens",
191+
);
107192
clearRateLimitBackoffState();
108193
const rateNamed = getRateLimitBackoffWithReason({
109194
accountIndex: 1,

0 commit comments

Comments
 (0)