@bjesuiter/codex-switcher 1.8.0 → 1.8.1
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 +3 -10
- package/cdx.mjs +89 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,19 +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.
|
|
10
|
-
|
|
11
|
-
#### Features
|
|
12
|
-
|
|
13
|
-
- Added manual OAuth URL clipboard assist for login/relogin fallback flow, including an opt-in copy prompt and non-blocking behavior.
|
|
14
|
-
- Added cross-platform clipboard copy strategies for auth URLs (local clipboard commands + OSC52 terminal fallback).
|
|
15
|
-
- Added tmux/screen OSC52 framing support and fallback copy-command hints when auto-copy is unavailable.
|
|
9
|
+
### 1.8.1
|
|
16
10
|
|
|
17
11
|
#### Fixes
|
|
18
12
|
|
|
19
|
-
-
|
|
20
|
-
- Linux secure-store
|
|
21
|
-
- Added Mosh-specific clipboard heuristics/warnings so OSC52 copy reports are less misleading when clipboard updates are unreliable.
|
|
13
|
+
- Added platform-native secure-store write/read/delete probes to `cdx doctor` on Linux, macOS, and Windows, so runtime secure-store failures are detected directly instead of only reporting adapter capability.
|
|
14
|
+
- Linux secure-store error handling now treats Secret Service `no result found` responses as missing-entry cases and classifies generic `Couldn't access platform secure storage` failures as unavailable-store errors with actionable guidance.
|
|
22
15
|
|
|
23
16
|
see full changelog here: https://github.com/bjesuiter/codex-switcher/blob/main/CHANGELOG.md
|
|
24
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.
|
|
18
|
+
var version = "1.8.1";
|
|
19
19
|
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region lib/platform/path-resolver.ts
|
|
@@ -663,13 +663,15 @@ const MISSING_ENTRY_MARKERS = [
|
|
|
663
663
|
"no matching entry found in secure storage",
|
|
664
664
|
"password not found",
|
|
665
665
|
"no stored credentials found",
|
|
666
|
-
"credential not found"
|
|
666
|
+
"credential not found",
|
|
667
|
+
"no result found"
|
|
667
668
|
];
|
|
668
669
|
const STORE_UNAVAILABLE_MARKERS = [
|
|
669
670
|
"unable to initialize linux secure-store backend",
|
|
670
671
|
"no keyring backend could be initialized",
|
|
671
672
|
"native keyring module not available",
|
|
672
673
|
"secret service operation failed",
|
|
674
|
+
"couldn't access platform secure storage",
|
|
673
675
|
"dbus",
|
|
674
676
|
"d-bus",
|
|
675
677
|
"org.freedesktop.secrets",
|
|
@@ -1061,14 +1063,16 @@ const windowsCrossKeychainPayloadExists = async (accountId) => withWindowsBacken
|
|
|
1061
1063
|
//#endregion
|
|
1062
1064
|
//#region lib/secrets/store.ts
|
|
1063
1065
|
const MISSING_SECRET_STORE_ERROR_MARKERS = [
|
|
1064
|
-
"
|
|
1065
|
-
"
|
|
1066
|
-
"
|
|
1067
|
-
"no matching entry found in secure storage"
|
|
1066
|
+
"no stored credentials found",
|
|
1067
|
+
"no keychain payload found",
|
|
1068
|
+
"password not found",
|
|
1069
|
+
"no matching entry found in secure storage",
|
|
1070
|
+
"no result found"
|
|
1068
1071
|
];
|
|
1069
1072
|
const isMissingSecretStoreEntryError = (error) => {
|
|
1070
1073
|
if (!(error instanceof Error)) return false;
|
|
1071
|
-
|
|
1074
|
+
const message = error.message.toLowerCase();
|
|
1075
|
+
return MISSING_SECRET_STORE_ERROR_MARKERS.some((marker) => message.includes(marker));
|
|
1072
1076
|
};
|
|
1073
1077
|
const createMissingSecretStoreEntryError = (accountId) => /* @__PURE__ */ new Error(`No stored credentials found for account ${accountId}.`);
|
|
1074
1078
|
const CACHED_ADAPTER_SYMBOL = Symbol.for("cdx.secretStore.cachedAdapter");
|
|
@@ -2711,6 +2715,56 @@ const getKeychainDecryptAccessByServiceAsync = async (services) => {
|
|
|
2711
2715
|
return parseKeychainDecryptAccessFromDump(dumpResult.output, dedupedServices);
|
|
2712
2716
|
};
|
|
2713
2717
|
|
|
2718
|
+
//#endregion
|
|
2719
|
+
//#region lib/secrets/probe.ts
|
|
2720
|
+
const toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
2721
|
+
const createProbePayload = (accountId, now) => ({
|
|
2722
|
+
refresh: `probe-refresh-${accountId}`,
|
|
2723
|
+
access: `probe-access-${accountId}`,
|
|
2724
|
+
expires: now + 6e4,
|
|
2725
|
+
accountId
|
|
2726
|
+
});
|
|
2727
|
+
const payloadMatches = (expected, actual) => expected.accountId === actual.accountId && expected.refresh === actual.refresh && expected.access === actual.access && expected.expires === actual.expires;
|
|
2728
|
+
const runSecretStoreWriteReadProbe = async (secretStore, options = {}) => {
|
|
2729
|
+
const probeAccountId = options.probeAccountId ?? `cdx-doctor-probe-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2730
|
+
const payload = createProbePayload(probeAccountId, options.now ?? Date.now());
|
|
2731
|
+
let saveSucceeded = false;
|
|
2732
|
+
let result = { ok: true };
|
|
2733
|
+
try {
|
|
2734
|
+
await secretStore.save(probeAccountId, payload);
|
|
2735
|
+
saveSucceeded = true;
|
|
2736
|
+
} catch (error) {
|
|
2737
|
+
result = {
|
|
2738
|
+
ok: false,
|
|
2739
|
+
stage: "save",
|
|
2740
|
+
error: toError(error)
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
if (result.ok) try {
|
|
2744
|
+
if (!payloadMatches(payload, await secretStore.load(probeAccountId))) result = {
|
|
2745
|
+
ok: false,
|
|
2746
|
+
stage: "verify",
|
|
2747
|
+
error: /* @__PURE__ */ new Error("Secure-store probe loaded payload does not match the saved payload.")
|
|
2748
|
+
};
|
|
2749
|
+
} catch (error) {
|
|
2750
|
+
result = {
|
|
2751
|
+
ok: false,
|
|
2752
|
+
stage: "load",
|
|
2753
|
+
error: toError(error)
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
if (saveSucceeded) try {
|
|
2757
|
+
await secretStore.delete(probeAccountId);
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
if (result.ok) result = {
|
|
2760
|
+
ok: false,
|
|
2761
|
+
stage: "delete",
|
|
2762
|
+
error: toError(error)
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
return result;
|
|
2766
|
+
};
|
|
2767
|
+
|
|
2714
2768
|
//#endregion
|
|
2715
2769
|
//#region lib/commands/doctor.ts
|
|
2716
2770
|
const hasRuntimeTrustedApp = (trustedApplications, runtimeExecutablePath) => {
|
|
@@ -2720,6 +2774,23 @@ const hasRuntimeTrustedApp = (trustedApplications, runtimeExecutablePath) => {
|
|
|
2720
2774
|
return path.basename(trustedApp).toLowerCase() === runtimeBaseName;
|
|
2721
2775
|
});
|
|
2722
2776
|
};
|
|
2777
|
+
const getSecretStoreProbeHeading = (platform) => {
|
|
2778
|
+
if (platform === "linux") return "Linux secure-store probe";
|
|
2779
|
+
if (platform === "darwin") return "macOS secure-store probe";
|
|
2780
|
+
if (platform === "win32") return "Windows secure-store probe";
|
|
2781
|
+
return null;
|
|
2782
|
+
};
|
|
2783
|
+
const createProbeAdapterForCurrentPlatform = () => {
|
|
2784
|
+
const currentAdapter = getSecretStoreAdapter();
|
|
2785
|
+
if (process.platform === "darwin" && currentAdapter.id === "macos-legacy-keychain") return createSecretStoreAdapterFromSelection("legacy-keychain", "darwin");
|
|
2786
|
+
return createRuntimeSecretStoreAdapter(process.platform);
|
|
2787
|
+
};
|
|
2788
|
+
const getSecretStoreProbeGuidance = (platform) => {
|
|
2789
|
+
if (platform === "linux") return "Suggested fix: ensure Secret Service is running/unlocked (for example gnome-keyring + secret-tool), then retry login.";
|
|
2790
|
+
if (platform === "darwin") return "Suggested fix: ensure Keychain Access is unlocked and allows this runtime/toolchain to store/read passwords, then retry login.";
|
|
2791
|
+
if (platform === "win32") return "Suggested fix: ensure Windows Credential Manager is available for this user session, then retry login.";
|
|
2792
|
+
return null;
|
|
2793
|
+
};
|
|
2723
2794
|
const registerDoctorCommand = (program) => {
|
|
2724
2795
|
program.command("doctor").description("Show auth file paths and runtime capabilities").option("--check-keychain-acl", "Run keychain trusted-app/ACL checks on macOS (can be slow)").action(async (options) => {
|
|
2725
2796
|
try {
|
|
@@ -2770,6 +2841,17 @@ const registerDoctorCommand = (program) => {
|
|
|
2770
2841
|
process.stdout.write(` Summary: ${okCount}/${status.accounts.length} configured account(s) passed secure-store load checks.\n`);
|
|
2771
2842
|
}
|
|
2772
2843
|
}
|
|
2844
|
+
const probeHeading = getSecretStoreProbeHeading(process.platform);
|
|
2845
|
+
if (probeHeading) {
|
|
2846
|
+
process.stdout.write(`\n${probeHeading}:\n`);
|
|
2847
|
+
const probeResult = await runSecretStoreWriteReadProbe(createProbeAdapterForCurrentPlatform());
|
|
2848
|
+
if (probeResult.ok) process.stdout.write(" write/read/delete probe: OK\n");
|
|
2849
|
+
else {
|
|
2850
|
+
process.stdout.write(` ⚠ ${probeResult.stage} failed: ${probeResult.error.message}\n`);
|
|
2851
|
+
const guidance = getSecretStoreProbeGuidance(process.platform);
|
|
2852
|
+
if (guidance) process.stdout.write(` ${guidance}\n`);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2773
2855
|
if (process.platform === "darwin" && !options.checkKeychainAcl) {
|
|
2774
2856
|
process.stdout.write(" ┌─ Optional keychain ACL check\n");
|
|
2775
2857
|
process.stdout.write(" │ Run: cdx doctor --check-keychain-acl\n");
|