@bjesuiter/codex-switcher 1.8.2 → 1.8.3

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.
Files changed (3) hide show
  1. package/README.md +4 -7
  2. package/cdx.mjs +231 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,15 +6,12 @@ Switch the coding-agents [pi](https://pi.dev/), [codex](https://developers.opena
6
6
 
7
7
  ## Latest Changes
8
8
 
9
- ### 1.8.2
9
+ ### 1.8.3
10
10
 
11
- #### Features
11
+ #### Improvements
12
12
 
13
- - Added `update-self` command aliases: `self-update`, `update`, and `updte`.
14
-
15
- #### Fixes
16
-
17
- - Added an interactive Linux troubleshooting checklist in `cdx doctor` when the secure-store probe fails, guiding sequential checks for `gnome-keyring`, `secret-tool`, and running `gnome-keyring-daemon`.
13
+ - `cdx update-self` now prints the detected installed version directly after a successful update, so you don't need to run `cdx version` manually.
14
+ - Linux `cdx doctor` guided checks now offer interactive recovery when `gnome-keyring-daemon` is not running: start now, or (when detectable as disabled) enable autostart and start now.
18
15
 
19
16
  see full changelog here: https://github.com/bjesuiter/codex-switcher/blob/main/CHANGELOG.md
20
17
 
package/cdx.mjs CHANGED
@@ -15,7 +15,7 @@ import { generatePKCE } from "@openauthjs/openauth/pkce";
15
15
  import http from "node:http";
16
16
 
17
17
  //#region package.json
18
- var version = "1.8.2";
18
+ var version = "1.8.3";
19
19
 
20
20
  //#endregion
21
21
  //#region lib/platform/path-resolver.ts
@@ -2839,22 +2839,201 @@ const checkGnomeKeyringRunning = async () => {
2839
2839
  details: extractCommandFailureDetails(psFallback) ?? "No gnome-keyring-daemon process found."
2840
2840
  };
2841
2841
  };
2842
+ const parseSystemctlEnabledState = (output) => {
2843
+ const normalized = output.trim().toLowerCase();
2844
+ if ([
2845
+ "enabled",
2846
+ "enabled-runtime",
2847
+ "static",
2848
+ "indirect",
2849
+ "generated"
2850
+ ].includes(normalized)) return "enabled";
2851
+ if ([
2852
+ "disabled",
2853
+ "masked",
2854
+ "not-found",
2855
+ "linked",
2856
+ "linked-runtime"
2857
+ ].includes(normalized)) return "disabled";
2858
+ return "unknown";
2859
+ };
2860
+ const getLinuxGnomeKeyringAutoStartStatus = async () => {
2861
+ if (!await isCommandAvailable("systemctl")) return {
2862
+ state: "unknown",
2863
+ details: "systemctl is not available; autostart detection depends on your desktop/session config."
2864
+ };
2865
+ const units = ["gnome-keyring-daemon.socket", "gnome-keyring-daemon.service"];
2866
+ let sawDisabled = false;
2867
+ const details = [];
2868
+ for (const unit of units) {
2869
+ const result = await runCommandCapture("systemctl", [
2870
+ "--user",
2871
+ "is-enabled",
2872
+ unit
2873
+ ]);
2874
+ const state = parseSystemctlEnabledState(result.stdout || result.stderr);
2875
+ if (state === "enabled") return {
2876
+ state: "enabled",
2877
+ details: `${unit} is enabled (${result.stdout || "enabled"}).`
2878
+ };
2879
+ if (state === "disabled") {
2880
+ sawDisabled = true;
2881
+ details.push(`${unit}: ${result.stdout || result.stderr || "disabled"}`);
2882
+ continue;
2883
+ }
2884
+ const maybeDetail = extractCommandFailureDetails(result);
2885
+ if (maybeDetail) details.push(`${unit}: ${maybeDetail}`);
2886
+ }
2887
+ if (sawDisabled) return {
2888
+ state: "disabled",
2889
+ details: details.join("; ")
2890
+ };
2891
+ return {
2892
+ state: "unknown",
2893
+ details: details.join("; ") || "Unable to determine gnome-keyring autostart state."
2894
+ };
2895
+ };
2896
+ const applyKeyringEnvAssignments = (raw) => {
2897
+ const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
2898
+ for (const line of lines) {
2899
+ const match = line.match(/^([A-Z0-9_]+)=(.*);?$/);
2900
+ if (!match) continue;
2901
+ const key = match[1];
2902
+ const value = match[2].replace(/;$/, "");
2903
+ if (key) process.env[key] = value;
2904
+ }
2905
+ };
2906
+ const startGnomeKeyringNow = async () => {
2907
+ const directStart = await runCommandCapture("gnome-keyring-daemon", ["--start", "--components=secrets"]);
2908
+ if (directStart.ok) {
2909
+ applyKeyringEnvAssignments(directStart.stdout);
2910
+ const runningCheck = await checkGnomeKeyringRunning();
2911
+ if (runningCheck.ok) return { ok: true };
2912
+ return {
2913
+ ok: false,
2914
+ details: runningCheck.details ?? "gnome-keyring-daemon start command succeeded, but process was not detected afterwards."
2915
+ };
2916
+ }
2917
+ if (await isCommandAvailable("systemctl")) {
2918
+ const serviceStart = await runCommandCapture("systemctl", [
2919
+ "--user",
2920
+ "start",
2921
+ "gnome-keyring-daemon.service"
2922
+ ]);
2923
+ if (serviceStart.ok) {
2924
+ const runningCheck = await checkGnomeKeyringRunning();
2925
+ if (runningCheck.ok) return { ok: true };
2926
+ return {
2927
+ ok: false,
2928
+ details: runningCheck.details ?? "systemctl start succeeded, but gnome-keyring-daemon was not detected afterwards."
2929
+ };
2930
+ }
2931
+ return {
2932
+ ok: false,
2933
+ details: extractCommandFailureDetails(directStart) ?? extractCommandFailureDetails(serviceStart) ?? "Failed to start gnome-keyring-daemon."
2934
+ };
2935
+ }
2936
+ return {
2937
+ ok: false,
2938
+ details: extractCommandFailureDetails(directStart) ?? "Failed to start gnome-keyring-daemon."
2939
+ };
2940
+ };
2941
+ const enableGnomeKeyringAutoStart = async () => {
2942
+ if (!await isCommandAvailable("systemctl")) return {
2943
+ ok: false,
2944
+ details: "systemctl is not available, so cdx cannot automatically enable startup in this session manager."
2945
+ };
2946
+ const units = ["gnome-keyring-daemon.socket", "gnome-keyring-daemon.service"];
2947
+ const failures = [];
2948
+ for (const unit of units) {
2949
+ const result = await runCommandCapture("systemctl", [
2950
+ "--user",
2951
+ "enable",
2952
+ unit
2953
+ ]);
2954
+ if (result.ok) return {
2955
+ ok: true,
2956
+ details: `${unit} enabled.`
2957
+ };
2958
+ const detail = extractCommandFailureDetails(result);
2959
+ failures.push(`${unit}: ${detail ?? "enable failed"}`);
2960
+ }
2961
+ return {
2962
+ ok: false,
2963
+ details: failures.join("; ")
2964
+ };
2965
+ };
2966
+ const maybeOfferToStartGnomeKeyring = async () => {
2967
+ const autoStartStatus = await getLinuxGnomeKeyringAutoStartStatus();
2968
+ if (autoStartStatus.state === "enabled") {
2969
+ const shouldStartNow = await p.confirm({
2970
+ message: "gnome-keyring autostart appears enabled, but it is not running right now. Start it now?",
2971
+ initialValue: true
2972
+ });
2973
+ if (p.isCancel(shouldStartNow) || !shouldStartNow) return false;
2974
+ const startResult = await startGnomeKeyringNow();
2975
+ if (!startResult.ok) {
2976
+ process.stdout.write(` failed to start gnome-keyring-daemon: ${startResult.details ?? "unknown error"}\n`);
2977
+ return false;
2978
+ }
2979
+ process.stdout.write(" started gnome-keyring-daemon for this session.\n");
2980
+ return true;
2981
+ }
2982
+ if (autoStartStatus.details) process.stdout.write(` autostart check: ${autoStartStatus.details}\n`);
2983
+ const action = await p.select({
2984
+ message: autoStartStatus.state === "disabled" ? "gnome-keyring autostart seems disabled. What should cdx do?" : "Could not confirm gnome-keyring autostart. What should cdx do?",
2985
+ options: [
2986
+ {
2987
+ value: "start-now",
2988
+ label: "Start now only"
2989
+ },
2990
+ {
2991
+ value: "enable-and-start",
2992
+ label: "Enable on system start and start now"
2993
+ },
2994
+ {
2995
+ value: "skip",
2996
+ label: "Skip"
2997
+ }
2998
+ ],
2999
+ initialValue: "start-now"
3000
+ });
3001
+ if (p.isCancel(action) || action === "skip") return false;
3002
+ if (action === "enable-and-start") {
3003
+ const enableResult = await enableGnomeKeyringAutoStart();
3004
+ if (!enableResult.ok) {
3005
+ process.stdout.write(` failed to enable autostart: ${enableResult.details ?? "unknown error"}\n`);
3006
+ return false;
3007
+ }
3008
+ process.stdout.write(` autostart enabled${enableResult.details ? ` (${enableResult.details})` : ""}.\n`);
3009
+ }
3010
+ const startResult = await startGnomeKeyringNow();
3011
+ if (!startResult.ok) {
3012
+ process.stdout.write(` failed to start gnome-keyring-daemon: ${startResult.details ?? "unknown error"}\n`);
3013
+ return false;
3014
+ }
3015
+ process.stdout.write(" started gnome-keyring-daemon for this session.\n");
3016
+ return true;
3017
+ };
2842
3018
  const runLinuxSecretStoreChecklist = async () => {
2843
3019
  const gnomeKeyringInstalled = await isCommandAvailable("gnome-keyring-daemon");
2844
3020
  const secretToolInstalled = await isCommandAvailable("secret-tool");
2845
3021
  const gnomeKeyringRunning = await checkGnomeKeyringRunning();
2846
3022
  return [
2847
3023
  {
3024
+ id: "gnome-keyring-installed",
2848
3025
  question: "Is gnome-keyring installed?",
2849
3026
  ok: gnomeKeyringInstalled,
2850
3027
  hint: "Install the `gnome-keyring` package, then log out/in (or restart your session)."
2851
3028
  },
2852
3029
  {
3030
+ id: "secret-tool-installed",
2853
3031
  question: "Is secret-tool installed?",
2854
3032
  ok: secretToolInstalled,
2855
3033
  hint: "Install the package that provides `secret-tool` (often `libsecret-tools`)."
2856
3034
  },
2857
3035
  {
3036
+ id: "gnome-keyring-running",
2858
3037
  question: "Is gnome-keyring running?",
2859
3038
  ok: gnomeKeyringRunning.ok,
2860
3039
  details: gnomeKeyringRunning.details,
@@ -2888,6 +3067,15 @@ const maybeRunLinuxSecretStoreChecklist = async () => {
2888
3067
  process.stdout.write(` ${i + 1}/3 ${item.question} no\n`);
2889
3068
  if (item.details) process.stdout.write(` details: ${item.details}\n`);
2890
3069
  if (item.hint) process.stdout.write(` hint: ${item.hint}\n`);
3070
+ if (item.id === "gnome-keyring-running") {
3071
+ if (await maybeOfferToStartGnomeKeyring()) {
3072
+ const runningNow = await checkGnomeKeyringRunning();
3073
+ if (runningNow.ok) {
3074
+ passed += 1;
3075
+ process.stdout.write(" re-check: gnome-keyring-daemon is now running.\n");
3076
+ } else process.stdout.write(` re-check still failing: ${runningNow.details ?? "process not detected"}\n`);
3077
+ }
3078
+ }
2891
3079
  }
2892
3080
  process.stdout.write(` Guided checklist summary: ${passed}/${checklist.length} checks passed.\n`);
2893
3081
  };
@@ -3629,6 +3817,46 @@ const executeUpdate = async (command, args) => {
3629
3817
  });
3630
3818
  });
3631
3819
  };
3820
+ const executeCapture = async (command, args) => await new Promise((resolve) => {
3821
+ const child = spawn(command, args, { stdio: [
3822
+ "ignore",
3823
+ "pipe",
3824
+ "pipe"
3825
+ ] });
3826
+ let stdout = "";
3827
+ child.stdout?.on("data", (chunk) => {
3828
+ stdout += chunk.toString();
3829
+ });
3830
+ child.once("error", () => {
3831
+ resolve({
3832
+ ok: false,
3833
+ output: ""
3834
+ });
3835
+ });
3836
+ child.once("close", (code) => {
3837
+ resolve({
3838
+ ok: code === 0,
3839
+ output: stdout.trim()
3840
+ });
3841
+ });
3842
+ });
3843
+ const getInstalledCdxVersion = async () => {
3844
+ const attempts = [{
3845
+ command: "cdx",
3846
+ args: ["--version"]
3847
+ }];
3848
+ if (process.argv[0] && process.argv[1]) attempts.push({
3849
+ command: process.argv[0],
3850
+ args: [process.argv[1], "--version"]
3851
+ });
3852
+ for (const attempt of attempts) {
3853
+ const result = await executeCapture(attempt.command, attempt.args);
3854
+ if (!result.ok || !result.output) continue;
3855
+ const version = result.output.split(/\r?\n/).at(-1)?.trim();
3856
+ if (version) return version;
3857
+ }
3858
+ return null;
3859
+ };
3632
3860
  const registerUpdateSelfCommand = (program) => {
3633
3861
  program.command("update-self").aliases([
3634
3862
  "self-update",
@@ -3703,8 +3931,9 @@ const registerUpdateSelfCommand = (program) => {
3703
3931
  }
3704
3932
  }
3705
3933
  await executeUpdate(command.command, command.args);
3934
+ const installedVersion = await getInstalledCdxVersion();
3706
3935
  process.stdout.write("Update completed.\n");
3707
- process.stdout.write("Run `cdx version` to verify the installed version.\n");
3936
+ process.stdout.write(`Installed version: ${installedVersion ?? "unknown"}\n`);
3708
3937
  } catch (error) {
3709
3938
  exitWithCommandError(error);
3710
3939
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bjesuiter/codex-switcher",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "type": "module",
5
5
  "description": "CLI tool to switch between multiple OpenAI accounts for OpenCode",
6
6
  "bin": {