@imdeadpool/codex-account-switcher 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/package.json +6 -4
- package/scripts/postinstall-login-hook.cjs +90 -0
package/README.md
CHANGED
|
@@ -19,6 +19,14 @@ Codex stores your authentication session in a single `auth.json` file. This tool
|
|
|
19
19
|
npm i -g @imdeadpool/codex-account-switcher
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
During global install, the package asks for permission to add an optional shell hook
|
|
23
|
+
(`~/.bashrc` or `~/.zshrc`) that auto-runs a silent snapshot sync after successful
|
|
24
|
+
official `codex login`.
|
|
25
|
+
|
|
26
|
+
- Choose `y` to enable fully automatic login snapshot capture.
|
|
27
|
+
- Choose `n` (default) to skip.
|
|
28
|
+
- Set `CODEX_AUTH_SKIP_POSTINSTALL=1` to always suppress this prompt.
|
|
29
|
+
|
|
22
30
|
## Usage
|
|
23
31
|
|
|
24
32
|
```sh
|
|
@@ -37,7 +45,7 @@ codex-auth save <name>
|
|
|
37
45
|
# force overwrite a name even when it currently maps to a different email
|
|
38
46
|
codex-auth save <name> --force
|
|
39
47
|
|
|
40
|
-
# switch active account
|
|
48
|
+
# switch active account
|
|
41
49
|
codex-auth use <name>
|
|
42
50
|
|
|
43
51
|
# or pick interactively
|
|
@@ -80,7 +88,7 @@ codex-auth daemon --watch
|
|
|
80
88
|
|
|
81
89
|
- `codex-auth save <name> [--force]` – Validates `<name>`, ensures `auth.json` exists, then snapshots it to `~/.codex/accounts/<name>.json`. By default, it blocks overwriting a name when the existing snapshot email differs from current auth. If `name` is omitted, it first tries reusing the active snapshot name when identity matches; otherwise it infers one from auth email.
|
|
82
90
|
- `codex-auth login [name] [--device-auth] [--force]` – Runs `codex login` (optionally with device auth), waits for refreshed auth snapshot detection, then saves it. If `name` is omitted, it always infers one from auth email with unique-suffix handling for multi-workspace identities.
|
|
83
|
-
- `codex-auth use [name]` – Accepts a name or launches an interactive selector with the current account pre-selected.
|
|
91
|
+
- `codex-auth use [name]` – Accepts a name or launches an interactive selector with the current account pre-selected, writes `~/.codex/auth.json` as a regular file from the chosen snapshot, and records the active name.
|
|
84
92
|
- `codex-auth list [--details]` – Lists all saved snapshots alphabetically and marks the active one with `*`. `--details` adds per-snapshot mapping metadata (email, account id, user id, and usage metadata) for easier session/account troubleshooting.
|
|
85
93
|
- `codex-auth current` – Prints the active account name, or a friendly message if none is active.
|
|
86
94
|
- `codex-auth remove [query|--all]` – Removes snapshots interactively or by selector. If the active account is removed, the best remaining account is activated automatically.
|
|
@@ -113,5 +121,5 @@ Usage refresh is hybrid:
|
|
|
113
121
|
|
|
114
122
|
Notes:
|
|
115
123
|
|
|
116
|
-
- Works on macOS/Linux (
|
|
124
|
+
- Works on macOS/Linux/Windows (regular-file auth snapshot activation).
|
|
117
125
|
- Requires Node 18+.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@imdeadpool/codex-account-switcher",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "A command-line tool that lets you manage and switch between multiple Codex accounts instantly, no more constant logins and logouts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc -p tsconfig.json",
|
|
13
|
+
"postinstall": "node scripts/postinstall-login-hook.cjs",
|
|
13
14
|
"prepublishOnly": "npm run build",
|
|
14
15
|
"test": "npm run build && node --test dist/tests/**/*.test.js"
|
|
15
16
|
},
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
},
|
|
19
20
|
"files": [
|
|
20
21
|
"dist",
|
|
22
|
+
"scripts/postinstall-login-hook.cjs",
|
|
21
23
|
"README.md",
|
|
22
24
|
"LICENSE"
|
|
23
25
|
],
|
|
@@ -32,12 +34,12 @@
|
|
|
32
34
|
"preferGlobal": true,
|
|
33
35
|
"repository": {
|
|
34
36
|
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/
|
|
37
|
+
"url": "git+https://github.com/recodeecom/codex-account-switcher-cli.git"
|
|
36
38
|
},
|
|
37
39
|
"bugs": {
|
|
38
|
-
"url": "https://github.com/
|
|
40
|
+
"url": "https://github.com/recodeecom/codex-account-switcher-cli/issues"
|
|
39
41
|
},
|
|
40
|
-
"homepage": "https://github.com/
|
|
42
|
+
"homepage": "https://github.com/recodeecom/codex-account-switcher-cli#readme",
|
|
41
43
|
"author": "NagyVikt",
|
|
42
44
|
"dependencies": {
|
|
43
45
|
"@oclif/core": "^3.0.0",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("node:fs/promises");
|
|
4
|
+
const os = require("node:os");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const readline = require("node:readline/promises");
|
|
7
|
+
|
|
8
|
+
const MARK_START = "# >>> codex-auth-login-auto-snapshot >>>";
|
|
9
|
+
const MARK_END = "# <<< codex-auth-login-auto-snapshot <<<";
|
|
10
|
+
|
|
11
|
+
function isTruthy(value) {
|
|
12
|
+
return typeof value === "string" && /^(1|true|yes|on)$/i.test(value.trim());
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function targetShellRc() {
|
|
16
|
+
const shell = (process.env.SHELL || "").toLowerCase();
|
|
17
|
+
if (shell.includes("zsh")) return path.join(os.homedir(), ".zshrc");
|
|
18
|
+
return path.join(os.homedir(), ".bashrc");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function renderHookBlock() {
|
|
22
|
+
return [
|
|
23
|
+
MARK_START,
|
|
24
|
+
"# Auto-sync codex-auth snapshots after successful official `codex login`.",
|
|
25
|
+
"if ! typeset -f codex >/dev/null 2>&1; then",
|
|
26
|
+
" codex() {",
|
|
27
|
+
" command codex \"$@\"",
|
|
28
|
+
" local __codex_exit=$?",
|
|
29
|
+
" if [[ $__codex_exit -eq 0 ]]; then",
|
|
30
|
+
" local __first_non_flag=\"\"",
|
|
31
|
+
" local __arg",
|
|
32
|
+
" for __arg in \"$@\"; do",
|
|
33
|
+
" case \"$__arg\" in",
|
|
34
|
+
" --) break ;;",
|
|
35
|
+
" -*) ;;",
|
|
36
|
+
" *) __first_non_flag=\"$__arg\"; break ;;",
|
|
37
|
+
" esac",
|
|
38
|
+
" done",
|
|
39
|
+
" if [[ \"$__first_non_flag\" == \"login\" ]] && command -v codex-auth >/dev/null 2>&1; then",
|
|
40
|
+
" command codex-auth status >/dev/null 2>&1 || true",
|
|
41
|
+
" fi",
|
|
42
|
+
" fi",
|
|
43
|
+
" return $__codex_exit",
|
|
44
|
+
" }",
|
|
45
|
+
"fi",
|
|
46
|
+
MARK_END,
|
|
47
|
+
].join("\n");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function maybeInstallHook() {
|
|
51
|
+
if (process.env.npm_config_global !== "true") return;
|
|
52
|
+
if (isTruthy(process.env.CODEX_AUTH_SKIP_POSTINSTALL)) return;
|
|
53
|
+
if (isTruthy(process.env.CI)) return;
|
|
54
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
55
|
+
|
|
56
|
+
const rcPath = targetShellRc();
|
|
57
|
+
await fs.mkdir(path.dirname(rcPath), { recursive: true });
|
|
58
|
+
|
|
59
|
+
let rc = "";
|
|
60
|
+
try {
|
|
61
|
+
rc = await fs.readFile(rcPath, "utf8");
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error && error.code !== "ENOENT") throw error;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (rc.includes(MARK_START) && rc.includes(MARK_END)) return;
|
|
67
|
+
|
|
68
|
+
const rl = readline.createInterface({
|
|
69
|
+
input: process.stdin,
|
|
70
|
+
output: process.stdout,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const answer = await rl.question(
|
|
75
|
+
`Install optional codex login auto-snapshot hook in ${rcPath}? [y/N] `,
|
|
76
|
+
);
|
|
77
|
+
if (!/^(y|yes)$/i.test((answer || "").trim())) return;
|
|
78
|
+
} finally {
|
|
79
|
+
rl.close();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const next = `${rc.replace(/\s*$/, "")}\n\n${renderHookBlock()}\n`;
|
|
83
|
+
await fs.writeFile(rcPath, next, "utf8");
|
|
84
|
+
process.stdout.write(`\nInstalled shell hook in ${rcPath}. Restart terminal or run: source ${rcPath}\n`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
maybeInstallHook().catch((error) => {
|
|
88
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
89
|
+
process.stderr.write(`\n[codex-auth postinstall] Failed to install login hook: ${message}\n`);
|
|
90
|
+
});
|