@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 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 (symlinks on macOS/Linux; copies on Windows)
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. Copies on Windows, creates a symlink elsewhere, and records the active name.
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 (symlink) and Windows (copy).
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.6",
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/NagyVikt/codex-account-switcher.git"
37
+ "url": "git+https://github.com/recodeecom/codex-account-switcher-cli.git"
36
38
  },
37
39
  "bugs": {
38
- "url": "https://github.com/NagyVikt/codex-account-switcher/issues"
40
+ "url": "https://github.com/recodeecom/codex-account-switcher-cli/issues"
39
41
  },
40
- "homepage": "https://github.com/NagyVikt/codex-account-switcher#readme",
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
+ });