Skip to content
Open
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
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ npm i -g codex-multi-auth

### Option C: Verify wiring

`codex --version` confirms the official Codex CLI is reachable. `codex-multi-auth --version` confirms the installed wrapper package version.

```bash
codex --version
codex-multi-auth --version
codex auth status
```

Expand Down Expand Up @@ -291,9 +294,9 @@ codex auth doctor --json

## Release Notes

- Current stable: [docs/releases/v1.2.0.md](docs/releases/v1.2.0.md)
- Previous stable: [docs/releases/v1.1.10.md](docs/releases/v1.1.10.md)
- Earlier stable: [docs/releases/v0.1.9.md](docs/releases/v0.1.9.md)
- Current stable: [docs/releases/v1.1.10.md](docs/releases/v1.1.10.md)
- Previous stable: [docs/releases/v0.1.9.md](docs/releases/v0.1.9.md)
- Earlier stable: [docs/releases/v0.1.8.md](docs/releases/v0.1.8.md)
- Archived prerelease: [docs/releases/v0.1.0-beta.0.md](docs/releases/v0.1.0-beta.0.md)

## License
Expand Down
8 changes: 4 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ Public documentation for `codex-multi-auth`.
| [troubleshooting.md](troubleshooting.md) | Recovery playbooks for install, login, switching, and stale state |
| [privacy.md](privacy.md) | Data handling and local storage behavior |
| [upgrade.md](upgrade.md) | Migration from legacy package and path history |
| [releases/v1.2.0.md](releases/v1.2.0.md) | Stable release notes |
| [releases/v1.1.10.md](releases/v1.1.10.md) | Previous stable release notes |
| [releases/v0.1.9.md](releases/v0.1.9.md) | Earlier stable release notes |
| [releases/v1.1.10.md](releases/v1.1.10.md) | Stable release notes |
| [releases/v0.1.9.md](releases/v0.1.9.md) | Previous stable release notes |
| [releases/v0.1.8.md](releases/v0.1.8.md) | Earlier stable release notes |
| [releases/v0.1.7.md](releases/v0.1.7.md) | Archived stable release notes |
| [releases/v0.1.6.md](releases/v0.1.6.md) | Archived stable release notes |
| [releases/v0.1.5.md](releases/v0.1.5.md) | Archived stable release notes |
Expand All @@ -45,7 +45,7 @@ Public documentation for `codex-multi-auth`.
| [reference/storage-paths.md](reference/storage-paths.md) | Canonical and compatibility storage paths |
| [reference/public-api.md](reference/public-api.md) | Public API stability and semver contract |
| [reference/error-contracts.md](reference/error-contracts.md) | CLI, JSON, and helper error semantics |
| [releases/v1.2.0.md](releases/v1.2.0.md) | Current stable release notes |
| [releases/v1.1.10.md](releases/v1.1.10.md) | Current stable release notes |
| [releases/v0.1.0-beta.0.md](releases/v0.1.0-beta.0.md) | Archived prerelease reference |
| [User Guides release notes](#user-guides) | Stable, previous, and archived release notes |
| [releases/legacy-pre-0.1-history.md](releases/legacy-pre-0.1-history.md) | Archived pre-0.1 changelog history |
Expand Down
6 changes: 5 additions & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ npm uninstall -g @ndycode/codex-multi-auth
npm i -g codex-multi-auth
```

Verify that the wrapper is active:
Verify both installed surfaces:

- `codex --version` checks the official `@openai/codex` CLI that the wrapper forwards to.
- `codex-multi-auth --version` checks the installed wrapper package version.

```bash
codex --version
codex-multi-auth --version
codex auth status
```

Expand Down
2 changes: 2 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Compatibility aliases are supported:
## Compatibility and Non-TTY Behavior

- `codex` remains the primary wrapper entrypoint. It routes `codex auth ...` and the compatibility aliases to the multi-auth runtime, and forwards every other command to the official `@openai/codex` CLI.
- `codex --version` reports the official `@openai/codex` CLI version.
- `codex-multi-auth --version` and `codex-multi-auth -v` report the installed wrapper package version.
- In non-TTY or host-managed sessions, including `CODEX_TUI=1`, `CODEX_DESKTOP=1`, `TERM_PROGRAM=codex`, or `ELECTRON_RUN_AS_NODE=1`, auth flows degrade to deterministic text behavior.
- The non-TTY fallback keeps `codex auth login` predictable: it defaults to add-account mode, skips the extra "add another account" prompt, and auto-picks the default workspace selection when a follow-up choice is needed.
- `codex auth login --manual` keeps the login flow usable in browser-restricted shells by printing the OAuth URL and accepting manual callback input instead of trying to open a browser.
Expand Down
2 changes: 2 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Check which `codex` executable is running:
```bash
where codex
codex --version
codex-multi-auth --version
codex auth status
codex multi auth status
npm ls -g codex-multi-auth
Expand Down Expand Up @@ -118,6 +119,7 @@ Attach these outputs when opening a bug report:
- `codex auth report --json`
- `codex auth doctor --json`
- `codex --version`
- `codex-multi-auth --version`
- `npm ls -g codex-multi-auth`
- the failing command and full terminal output

Expand Down
3 changes: 2 additions & 1 deletion docs/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ npm uninstall -g @ndycode/codex-multi-auth
npm i -g codex-multi-auth
```

1. Verify routing and status:
1. Verify routing and wrapper status:

```bash
codex --version
codex-multi-auth --version
codex auth status
```

Expand Down
42 changes: 32 additions & 10 deletions scripts/codex-multi-auth.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
#!/usr/bin/env node

import { createRequire } from "node:module";
import { runCodexMultiAuthCli } from "../dist/lib/codex-manager.js";

try {
const versionFlags = new Set(["--version", "-v"]);

function resolveCliVersion() {
const require = createRequire(import.meta.url);
const pkg = require("../package.json");
const version = typeof pkg?.version === "string" ? pkg.version.trim() : "";
if (version.length > 0) {
process.env.CODEX_MULTI_AUTH_CLI_VERSION = version;
try {
const pkg = require("../package.json");
const version = typeof pkg?.version === "string" ? pkg.version.trim() : "";
if (version.length > 0) {
return version;
}
} catch {
// Best effort only.
}
} catch {
// Best effort only.
return "";
}

const args = process.argv.slice(2);
const version = resolveCliVersion();

if (version.length > 0) {
process.env.CODEX_MULTI_AUTH_CLI_VERSION = version;
}

const exitCode = await runCodexMultiAuthCli(process.argv.slice(2));
process.exitCode = Number.isInteger(exitCode) ? exitCode : 1;
if (args.length === 1 && versionFlags.has(args[0])) {
if (version.length > 0) {
process.stdout.write(`${version}\n`);
process.exitCode = 0;
} else {
process.stderr.write("codex-multi-auth version is unavailable.\n");
process.exitCode = 1;
}
} else {
const { runCodexMultiAuthCli } = await import("../dist/lib/codex-manager.js");
const exitCode = await runCodexMultiAuthCli(args);
process.exitCode = Number.isInteger(exitCode) ? exitCode : 1;
}
81 changes: 77 additions & 4 deletions test/codex-multi-auth-bin-wrapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import { sleep } from "../lib/utils.js";
const createdDirs: string[] = [];
const testFileDir = dirname(fileURLToPath(import.meta.url));
const repoRootDir = join(testFileDir, "..");
const passthroughEnvKeys = ["HOME", "PATH", "SystemRoot", "TEMP", "TMP", "USERPROFILE"] as const;

function isRetriableFsError(error: unknown): boolean {
if (!error || typeof error !== "object" || !("code" in error)) {
return false;
}
const { code } = error as { code?: unknown };
return code === "EBUSY" || code === "EPERM";
return code === "EBUSY" || code === "EPERM" || code === "ENOTEMPTY";
}

async function removeDirectoryWithRetry(dir: string): Promise<void> {
Expand All @@ -42,22 +43,36 @@ function createWrapperFixture(): string {
createdDirs.push(fixtureRoot);
const scriptDir = join(fixtureRoot, "scripts");
mkdirSync(scriptDir, { recursive: true });
writeFileSync(
join(fixtureRoot, "package.json"),
JSON.stringify({ type: "module", version: "9.8.7" }, null, "\t"),
"utf8",
);
copyFileSync(
join(repoRootDir, "scripts", "codex-multi-auth.js"),
join(scriptDir, "codex-multi-auth.js"),
);
return fixtureRoot;
}

function createChildEnv(): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = {};
for (const key of passthroughEnvKeys) {
const value = process.env[key];
if (typeof value === "string" && value.length > 0) {
env[key] = value;
}
}
return env;
}

function runWrapper(fixtureRoot: string, args: string[] = []) {
return spawnSync(
process.execPath,
[join(fixtureRoot, "scripts", "codex-multi-auth.js"), ...args],
{
encoding: "utf8",
env: {
...process.env,
},
env: createChildEnv(),
},
);
}
Expand All @@ -69,6 +84,64 @@ afterEach(async () => {
});

describe("codex-multi-auth bin wrapper", () => {
it("prints package version for --version without loading the runtime", () => {
const fixtureRoot = createWrapperFixture();
const result = runWrapper(fixtureRoot, ["--version"]);

expect(result.status).toBe(0);
expect(result.stdout).toBe("9.8.7\n");
expect(result.stderr).toBe("");
});

it("prints package version for -v without loading the runtime", () => {
const fixtureRoot = createWrapperFixture();
const result = runWrapper(fixtureRoot, ["-v"]);

expect(result.status).toBe(0);
expect(result.stdout).toBe("9.8.7\n");
expect(result.stderr).toBe("");
});

it("prints a clear error when the wrapper version cannot be resolved", () => {
const fixtureRoot = createWrapperFixture();
writeFileSync(
join(fixtureRoot, "package.json"),
JSON.stringify({ type: "module" }, null, "\t"),
"utf8",
);

const result = runWrapper(fixtureRoot, ["--version"]);

expect(result.status).toBe(1);
expect(result.stdout).toBe("");
expect(result.stderr).toContain("codex-multi-auth version is unavailable.");
});

it.each([
["--version", "extra"],
["-v", "extra"],
])("passes multi-argument version flags through to the runtime: %s", (flag, extraArg) => {
const fixtureRoot = createWrapperFixture();
const distLibDir = join(fixtureRoot, "dist", "lib");
mkdirSync(distLibDir, { recursive: true });
writeFileSync(
join(distLibDir, "codex-manager.js"),
[
"export async function runCodexMultiAuthCli(args) {",
`\tif (!Array.isArray(args) || args[0] !== ${JSON.stringify(flag)} || args[1] !== ${JSON.stringify(extraArg)}) throw new Error("bad args");`,
"\treturn 6;",
"}",
].join("\n"),
"utf8",
);

const result = runWrapper(fixtureRoot, [flag, extraArg]);

expect(result.status).toBe(6);
expect(result.stdout).toBe("");
expect(result.stderr).toBe("");
});

it("propagates integer exit codes", () => {
const fixtureRoot = createWrapperFixture();
const distLibDir = join(fixtureRoot, "dist", "lib");
Expand Down