@bjesuiter/codex-switcher 1.7.1 → 1.7.2
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 +2 -6
- package/cdx.mjs +92 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,15 +6,11 @@ Switch the coding-agents [pi](https://pi.dev/), [codex](https://developers.opena
|
|
|
6
6
|
|
|
7
7
|
## Latest Changes
|
|
8
8
|
|
|
9
|
-
### 1.7.
|
|
10
|
-
|
|
11
|
-
#### Features
|
|
12
|
-
|
|
13
|
-
- Add a release helper script (`scripts/wait-for-npm-latest.ts`) plus `bun run wait-npm-latest` to poll npm until the package `latest` tag matches the target version.
|
|
9
|
+
### 1.7.2
|
|
14
10
|
|
|
15
11
|
#### Fixes
|
|
16
12
|
|
|
17
|
-
-
|
|
13
|
+
- Improve device OAuth failure diagnostics during login/relogin. When device flow startup or polling fails, `cdx` now prints technical details (HTTP status, OAuth error code, and response/body snippets where available) instead of only showing a generic "not available right now" message.
|
|
18
14
|
|
|
19
15
|
see full changelog here: https://github.com/bjesuiter/codex-switcher/blob/main/CHANGELOG.md
|
|
20
16
|
|
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.7.
|
|
18
|
+
var version = "1.7.2";
|
|
19
19
|
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region lib/platform/path-resolver.ts
|
|
@@ -1032,6 +1032,10 @@ const createAuthorizationFlow = async () => {
|
|
|
1032
1032
|
url: url.toString()
|
|
1033
1033
|
};
|
|
1034
1034
|
};
|
|
1035
|
+
const truncateForLog = (value, maxLength = 300) => {
|
|
1036
|
+
if (value.length <= maxLength) return value;
|
|
1037
|
+
return `${value.slice(0, maxLength)}…`;
|
|
1038
|
+
};
|
|
1035
1039
|
const startDeviceAuthorizationFlow = async () => {
|
|
1036
1040
|
try {
|
|
1037
1041
|
const res = await fetch(DEVICE_CODE_URL, {
|
|
@@ -1042,19 +1046,53 @@ const startDeviceAuthorizationFlow = async () => {
|
|
|
1042
1046
|
scope: SCOPE
|
|
1043
1047
|
})
|
|
1044
1048
|
});
|
|
1045
|
-
if (!res.ok)
|
|
1049
|
+
if (!res.ok) {
|
|
1050
|
+
let oauthError;
|
|
1051
|
+
let responseBody;
|
|
1052
|
+
try {
|
|
1053
|
+
const json = await res.json();
|
|
1054
|
+
oauthError = json.error;
|
|
1055
|
+
responseBody = truncateForLog(JSON.stringify({
|
|
1056
|
+
...json.error ? { error: json.error } : {},
|
|
1057
|
+
...json.error_description ? { error_description: json.error_description } : {}
|
|
1058
|
+
}));
|
|
1059
|
+
} catch {
|
|
1060
|
+
try {
|
|
1061
|
+
responseBody = truncateForLog(await res.text());
|
|
1062
|
+
} catch {
|
|
1063
|
+
responseBody = void 0;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
return {
|
|
1067
|
+
type: "failed",
|
|
1068
|
+
error: `Device code request failed with HTTP ${res.status} ${res.statusText}`,
|
|
1069
|
+
status: res.status,
|
|
1070
|
+
...oauthError ? { oauthError } : {},
|
|
1071
|
+
...responseBody ? { responseBody } : {}
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1046
1074
|
const json = await res.json();
|
|
1047
|
-
if (!json?.device_code || !json?.user_code || !json?.verification_uri || typeof json?.expires_in !== "number") return
|
|
1075
|
+
if (!json?.device_code || !json?.user_code || !json?.verification_uri || typeof json?.expires_in !== "number") return {
|
|
1076
|
+
type: "failed",
|
|
1077
|
+
error: "Device code response is missing required fields.",
|
|
1078
|
+
responseBody: truncateForLog(JSON.stringify(json))
|
|
1079
|
+
};
|
|
1048
1080
|
return {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1081
|
+
type: "success",
|
|
1082
|
+
flow: {
|
|
1083
|
+
deviceCode: json.device_code,
|
|
1084
|
+
userCode: json.user_code,
|
|
1085
|
+
verificationUri: json.verification_uri,
|
|
1086
|
+
verificationUriComplete: json.verification_uri_complete,
|
|
1087
|
+
expiresIn: json.expires_in,
|
|
1088
|
+
interval: typeof json.interval === "number" && json.interval > 0 ? json.interval : 5
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
return {
|
|
1093
|
+
type: "failed",
|
|
1094
|
+
error: `Device code request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1055
1095
|
};
|
|
1056
|
-
} catch {
|
|
1057
|
-
return null;
|
|
1058
1096
|
}
|
|
1059
1097
|
};
|
|
1060
1098
|
const pollDeviceAuthorizationToken = async (deviceCode) => {
|
|
@@ -1070,7 +1108,11 @@ const pollDeviceAuthorizationToken = async (deviceCode) => {
|
|
|
1070
1108
|
});
|
|
1071
1109
|
if (res.ok) {
|
|
1072
1110
|
const json = await res.json();
|
|
1073
|
-
if (!json?.access_token || !json?.refresh_token || typeof json?.expires_in !== "number") return {
|
|
1111
|
+
if (!json?.access_token || !json?.refresh_token || typeof json?.expires_in !== "number") return {
|
|
1112
|
+
type: "failed",
|
|
1113
|
+
error: "Device token response is missing access_token/refresh_token/expires_in.",
|
|
1114
|
+
responseBody: truncateForLog(JSON.stringify(json))
|
|
1115
|
+
};
|
|
1074
1116
|
return {
|
|
1075
1117
|
type: "success",
|
|
1076
1118
|
access: json.access_token,
|
|
@@ -1081,11 +1123,23 @@ const pollDeviceAuthorizationToken = async (deviceCode) => {
|
|
|
1081
1123
|
}
|
|
1082
1124
|
let errorCode;
|
|
1083
1125
|
let interval;
|
|
1126
|
+
let responseBody;
|
|
1084
1127
|
try {
|
|
1085
1128
|
const json = await res.json();
|
|
1086
1129
|
errorCode = json.error;
|
|
1087
1130
|
interval = json.interval;
|
|
1088
|
-
|
|
1131
|
+
responseBody = truncateForLog(JSON.stringify({
|
|
1132
|
+
...json.error ? { error: json.error } : {},
|
|
1133
|
+
...json.error_description ? { error_description: json.error_description } : {},
|
|
1134
|
+
...typeof json.interval === "number" ? { interval: json.interval } : {}
|
|
1135
|
+
}));
|
|
1136
|
+
} catch {
|
|
1137
|
+
try {
|
|
1138
|
+
responseBody = truncateForLog(await res.text());
|
|
1139
|
+
} catch {
|
|
1140
|
+
responseBody = void 0;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1089
1143
|
if (errorCode === "authorization_pending") return {
|
|
1090
1144
|
type: "pending",
|
|
1091
1145
|
interval: typeof interval === "number" && interval > 0 ? interval : 5
|
|
@@ -1096,9 +1150,18 @@ const pollDeviceAuthorizationToken = async (deviceCode) => {
|
|
|
1096
1150
|
};
|
|
1097
1151
|
if (errorCode === "access_denied") return { type: "access_denied" };
|
|
1098
1152
|
if (errorCode === "expired_token") return { type: "expired" };
|
|
1099
|
-
return {
|
|
1100
|
-
|
|
1101
|
-
|
|
1153
|
+
return {
|
|
1154
|
+
type: "failed",
|
|
1155
|
+
error: `Device token polling failed with HTTP ${res.status} ${res.statusText}`,
|
|
1156
|
+
status: res.status,
|
|
1157
|
+
...errorCode ? { oauthError: errorCode } : {},
|
|
1158
|
+
...responseBody ? { responseBody } : {}
|
|
1159
|
+
};
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
return {
|
|
1162
|
+
type: "failed",
|
|
1163
|
+
error: `Device token polling request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1164
|
+
};
|
|
1102
1165
|
}
|
|
1103
1166
|
};
|
|
1104
1167
|
const exchangeAuthorizationCode = async (code, verifier) => {
|
|
@@ -1364,11 +1427,16 @@ const promptManualAuthorizationCode = async (authorizationUrl, expectedState) =>
|
|
|
1364
1427
|
return null;
|
|
1365
1428
|
};
|
|
1366
1429
|
const runDeviceOAuthFlow = async (useSpinner) => {
|
|
1367
|
-
const
|
|
1368
|
-
if (
|
|
1430
|
+
const deviceFlowResult = await startDeviceAuthorizationFlow();
|
|
1431
|
+
if (deviceFlowResult.type !== "success") {
|
|
1369
1432
|
p.log.error("Device OAuth flow is not available right now.");
|
|
1433
|
+
p.log.error(`Technical details: ${deviceFlowResult.error}`);
|
|
1434
|
+
if (typeof deviceFlowResult.status === "number") p.log.error(`HTTP status: ${deviceFlowResult.status}`);
|
|
1435
|
+
if (deviceFlowResult.oauthError) p.log.error(`OAuth error: ${deviceFlowResult.oauthError}`);
|
|
1436
|
+
if (deviceFlowResult.responseBody) p.log.error(`Response: ${deviceFlowResult.responseBody}`);
|
|
1370
1437
|
return null;
|
|
1371
1438
|
}
|
|
1439
|
+
const deviceFlow = deviceFlowResult.flow;
|
|
1372
1440
|
p.log.info("Using device OAuth flow.");
|
|
1373
1441
|
p.log.message(`Verification URL: ${deviceFlow.verificationUri}`);
|
|
1374
1442
|
p.log.message(`User code: ${deviceFlow.userCode}`);
|
|
@@ -1410,6 +1478,12 @@ const runDeviceOAuthFlow = async (useSpinner) => {
|
|
|
1410
1478
|
}
|
|
1411
1479
|
if (spinner) spinner.stop("Device authorization failed.");
|
|
1412
1480
|
else p.log.error("Device authorization failed.");
|
|
1481
|
+
if (pollResult.type === "failed") {
|
|
1482
|
+
if (pollResult.error) p.log.error(`Technical details: ${pollResult.error}`);
|
|
1483
|
+
if (typeof pollResult.status === "number") p.log.error(`HTTP status: ${pollResult.status}`);
|
|
1484
|
+
if (pollResult.oauthError) p.log.error(`OAuth error: ${pollResult.oauthError}`);
|
|
1485
|
+
if (pollResult.responseBody) p.log.error(`Response: ${pollResult.responseBody}`);
|
|
1486
|
+
}
|
|
1413
1487
|
return null;
|
|
1414
1488
|
}
|
|
1415
1489
|
if (spinner) spinner.stop("Device authorization timed out.");
|