@ouro.bot/cli 0.1.0-alpha.644 → 0.1.0-alpha.645
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/changelog.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.645",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Rescue openai-codex refresh when the vault refresh token has rotated by retrying with the local Codex login, and classify sign-in-required refresh failures as human-required."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
4
10
|
{
|
|
5
11
|
"version": "0.1.0-alpha.644",
|
|
6
12
|
"changes": [
|
|
@@ -129,6 +129,29 @@ function parseRefreshFailure(body) {
|
|
|
129
129
|
}
|
|
130
130
|
return body.trim() || "refresh endpoint returned an empty error body";
|
|
131
131
|
}
|
|
132
|
+
function readLocalCodexAuthTokens(homeDir) {
|
|
133
|
+
const authPath = path.join(homeDir, ".codex", "auth.json");
|
|
134
|
+
let raw;
|
|
135
|
+
try {
|
|
136
|
+
raw = fs.readFileSync(authPath, "utf8");
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return { status: "missing" };
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(raw);
|
|
143
|
+
if (!parsed.tokens || typeof parsed.tokens !== "object")
|
|
144
|
+
return { status: "invalid" };
|
|
145
|
+
const accessToken = typeof parsed.tokens.access_token === "string" ? parsed.tokens.access_token.trim() : "";
|
|
146
|
+
const refreshToken = typeof parsed.tokens.refresh_token === "string" ? parsed.tokens.refresh_token.trim() : "";
|
|
147
|
+
if (!accessToken || !refreshToken)
|
|
148
|
+
return { status: "invalid" };
|
|
149
|
+
return { status: "ready", tokens: { accessToken, refreshToken } };
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return { status: "invalid" };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
132
155
|
async function updateLocalCodexAuthIfUnchanged(input) {
|
|
133
156
|
const authPath = path.join(input.homeDir, ".codex", "auth.json");
|
|
134
157
|
let raw;
|
|
@@ -190,6 +213,14 @@ async function requestOpenAICodexTokenRefresh(input) {
|
|
|
190
213
|
return { ok: false, detail: "refresh endpoint returned no access_token" };
|
|
191
214
|
return { ok: true, accessToken, refreshToken };
|
|
192
215
|
}
|
|
216
|
+
function refreshFailureRequiresHuman(refresh) {
|
|
217
|
+
const detail = refresh.detail.toLowerCase();
|
|
218
|
+
return refresh.status === 401
|
|
219
|
+
|| detail.includes("signing in again")
|
|
220
|
+
|| detail.includes("already been used")
|
|
221
|
+
|| detail.includes("invalid_grant")
|
|
222
|
+
|| detail.includes("refresh token expired");
|
|
223
|
+
}
|
|
193
224
|
async function refreshOpenAICodexProviderCredentials(agentName, options = {}) {
|
|
194
225
|
const now = options.now ?? new Date();
|
|
195
226
|
const readRecord = options.readRecord ?? provider_credentials_1.readProviderCredentialRecord;
|
|
@@ -212,25 +243,52 @@ async function refreshOpenAICodexProviderCredentials(agentName, options = {}) {
|
|
|
212
243
|
}
|
|
213
244
|
const oldAccessToken = recordCredentialString(record, "oauthAccessToken");
|
|
214
245
|
const oldRefreshToken = recordCredentialString(record, "refreshToken");
|
|
215
|
-
|
|
246
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
247
|
+
const fallbackLocalAuth = oldRefreshToken ? undefined : readLocalCodexAuthTokens(homeDir);
|
|
248
|
+
let refreshSource = oldRefreshToken
|
|
249
|
+
? { source: "vault", accessToken: oldAccessToken, refreshToken: oldRefreshToken }
|
|
250
|
+
: fallbackLocalAuth?.status === "ready"
|
|
251
|
+
? { source: "local-codex-auth", accessToken: fallbackLocalAuth.tokens.accessToken, refreshToken: fallbackLocalAuth.tokens.refreshToken }
|
|
252
|
+
: undefined;
|
|
253
|
+
if (!refreshSource) {
|
|
216
254
|
return {
|
|
217
255
|
ok: false,
|
|
218
256
|
actor: "human-required",
|
|
219
|
-
message: `openai-codex has no saved refresh token for ${agentName}. Run '${authCommand(agentName)}'.`,
|
|
257
|
+
message: `openai-codex has no saved refresh token for ${agentName} and no usable local Codex login to import. Run '${authCommand(agentName)}'.`,
|
|
220
258
|
};
|
|
221
259
|
}
|
|
222
260
|
(0, runtime_1.emitNervesEvent)({
|
|
223
261
|
component: "engine",
|
|
224
262
|
event: "engine.openai_codex_token_refresh_start",
|
|
225
263
|
message: "refreshing openai-codex OAuth token",
|
|
226
|
-
meta: { agentName, reason: options.reason ?? "unspecified" },
|
|
264
|
+
meta: { agentName, reason: options.reason ?? "unspecified", source: refreshSource.source },
|
|
227
265
|
});
|
|
228
|
-
|
|
229
|
-
refreshToken:
|
|
266
|
+
let refresh = await requestOpenAICodexTokenRefresh({
|
|
267
|
+
refreshToken: refreshSource.refreshToken,
|
|
230
268
|
fetchImpl: options.fetchImpl ?? fetch,
|
|
231
269
|
});
|
|
270
|
+
if (!refresh.ok && oldRefreshToken) {
|
|
271
|
+
const retryLocalAuth = readLocalCodexAuthTokens(homeDir);
|
|
272
|
+
if (retryLocalAuth.status === "ready" && retryLocalAuth.tokens.refreshToken !== oldRefreshToken) {
|
|
273
|
+
(0, runtime_1.emitNervesEvent)({
|
|
274
|
+
component: "engine",
|
|
275
|
+
event: "engine.openai_codex_token_refresh_local_rescue",
|
|
276
|
+
message: "retrying openai-codex OAuth refresh with local Codex auth tokens",
|
|
277
|
+
meta: { agentName, reason: options.reason ?? "unspecified", status: refresh.status ?? "none", detail: refresh.detail },
|
|
278
|
+
});
|
|
279
|
+
refreshSource = {
|
|
280
|
+
source: "local-codex-auth",
|
|
281
|
+
accessToken: retryLocalAuth.tokens.accessToken,
|
|
282
|
+
refreshToken: retryLocalAuth.tokens.refreshToken,
|
|
283
|
+
};
|
|
284
|
+
refresh = await requestOpenAICodexTokenRefresh({
|
|
285
|
+
refreshToken: refreshSource.refreshToken,
|
|
286
|
+
fetchImpl: options.fetchImpl ?? fetch,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
232
290
|
if (!refresh.ok) {
|
|
233
|
-
const actor = refresh
|
|
291
|
+
const actor = refreshFailureRequiresHuman(refresh) ? "human-required" : "agent-runnable";
|
|
234
292
|
(0, runtime_1.emitNervesEvent)({
|
|
235
293
|
level: actor === "human-required" ? "warn" : "error",
|
|
236
294
|
component: "engine",
|
|
@@ -240,6 +298,7 @@ async function refreshOpenAICodexProviderCredentials(agentName, options = {}) {
|
|
|
240
298
|
agentName,
|
|
241
299
|
reason: options.reason ?? "unspecified",
|
|
242
300
|
actor,
|
|
301
|
+
source: refreshSource.source,
|
|
243
302
|
...(refresh.status ? { status: refresh.status } : {}),
|
|
244
303
|
detail: refresh.detail,
|
|
245
304
|
},
|
|
@@ -266,10 +325,10 @@ async function refreshOpenAICodexProviderCredentials(agentName, options = {}) {
|
|
|
266
325
|
provenance: { source: record.provenance.source },
|
|
267
326
|
now,
|
|
268
327
|
});
|
|
269
|
-
const
|
|
270
|
-
homeDir
|
|
271
|
-
oldAccessToken,
|
|
272
|
-
oldRefreshToken,
|
|
328
|
+
const localAuthSync = await updateLocalCodexAuthIfUnchanged({
|
|
329
|
+
homeDir,
|
|
330
|
+
oldAccessToken: refreshSource.accessToken,
|
|
331
|
+
oldRefreshToken: refreshSource.refreshToken,
|
|
273
332
|
newAccessToken: refresh.accessToken,
|
|
274
333
|
newRefreshToken: refresh.refreshToken,
|
|
275
334
|
now,
|
|
@@ -282,7 +341,8 @@ async function refreshOpenAICodexProviderCredentials(agentName, options = {}) {
|
|
|
282
341
|
agentName,
|
|
283
342
|
reason: options.reason ?? "unspecified",
|
|
284
343
|
credentialRevision: updated.revision,
|
|
285
|
-
|
|
344
|
+
source: refreshSource.source,
|
|
345
|
+
localCodexAuth: localAuthSync,
|
|
286
346
|
},
|
|
287
347
|
});
|
|
288
348
|
return { ok: true, refreshed: true, record: updated };
|