@ouro.bot/cli 0.1.0-alpha.546 → 0.1.0-alpha.548
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,19 @@
|
|
|
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.548",
|
|
6
|
+
"changes": [
|
|
7
|
+
"OpenAI Codex auth now reuses an existing fresh local Codex login before starting browser login, letting `ouro auth --provider openai-codex` repair stale vault copies without a human OAuth round-trip when the local Codex CLI is already logged in.",
|
|
8
|
+
"Codex provider credentials now retain refresh metadata (`refreshToken` and `expiresAt`) alongside the access token, setting up proactive non-browser refresh handling for future hardening."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"version": "0.1.0-alpha.547",
|
|
13
|
+
"changes": [
|
|
14
|
+
"Version-managed macOS daemon LaunchAgents now point at the stable `~/.ouro-cli/CurrentVersion` entry path instead of a concrete installed version directory, so future runtime updates do not leave launchd holding stale daemon argv."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
"version": "0.1.0-alpha.546",
|
|
6
19
|
"changes": [
|
|
@@ -52,6 +52,7 @@ const provider_credentials_1 = require("../provider-credentials");
|
|
|
52
52
|
const vault_unlock_1 = require("../../repertoire/vault-unlock");
|
|
53
53
|
const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
|
|
54
54
|
const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
|
|
55
|
+
const CODEX_LOCAL_TOKEN_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
55
56
|
function assertPersistentProviderCredentialsAllowed(agentName) {
|
|
56
57
|
if (agentName === "SerpentGuide") {
|
|
57
58
|
throw new Error("SerpentGuide uses provider credentials in memory during hatch bootstrap; persistent SerpentGuide auth is not supported.");
|
|
@@ -171,18 +172,64 @@ function writeAgentModel(agentName, facing, modelName, deps = {}) {
|
|
|
171
172
|
});
|
|
172
173
|
return { configPath, provider, previousModel };
|
|
173
174
|
}
|
|
174
|
-
function
|
|
175
|
+
function decodeJwtPayload(token) {
|
|
176
|
+
const parts = token.split(".");
|
|
177
|
+
if (parts.length < 2 || !parts[1])
|
|
178
|
+
return null;
|
|
179
|
+
try {
|
|
180
|
+
const base64 = parts[1]
|
|
181
|
+
.replace(/-/g, "+")
|
|
182
|
+
.replace(/_/g, "/")
|
|
183
|
+
.padEnd(Math.ceil(parts[1].length / 4) * 4, "=");
|
|
184
|
+
const parsed = JSON.parse(Buffer.from(base64, "base64").toString("utf8"));
|
|
185
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
186
|
+
return null;
|
|
187
|
+
return parsed;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function readJwtExpiresAt(token) {
|
|
194
|
+
const payload = decodeJwtPayload(token);
|
|
195
|
+
const exp = payload?.exp;
|
|
196
|
+
if (typeof exp !== "number" || !Number.isFinite(exp) || exp <= 0)
|
|
197
|
+
return undefined;
|
|
198
|
+
return Math.floor(exp * 1000);
|
|
199
|
+
}
|
|
200
|
+
function isFreshCodexToken(credentials, now) {
|
|
201
|
+
if (!credentials.oauthAccessToken)
|
|
202
|
+
return false;
|
|
203
|
+
if (typeof credentials.expiresAt !== "number")
|
|
204
|
+
return false;
|
|
205
|
+
return credentials.expiresAt > now.getTime() + CODEX_LOCAL_TOKEN_REFRESH_MARGIN_MS;
|
|
206
|
+
}
|
|
207
|
+
function readCodexLocalAuthCredentials(homeDir) {
|
|
175
208
|
const authPath = path.join(homeDir, ".codex", "auth.json");
|
|
176
209
|
try {
|
|
177
210
|
const raw = fs.readFileSync(authPath, "utf8");
|
|
178
211
|
const parsed = JSON.parse(raw);
|
|
179
|
-
const
|
|
180
|
-
|
|
212
|
+
const accessToken = typeof parsed.tokens?.access_token === "string" ? parsed.tokens.access_token.trim() : "";
|
|
213
|
+
if (!accessToken)
|
|
214
|
+
return {};
|
|
215
|
+
const refreshToken = typeof parsed.tokens?.refresh_token === "string" ? parsed.tokens.refresh_token.trim() : "";
|
|
216
|
+
const expiresAt = readJwtExpiresAt(accessToken);
|
|
217
|
+
return {
|
|
218
|
+
oauthAccessToken: accessToken,
|
|
219
|
+
...(refreshToken ? { refreshToken } : {}),
|
|
220
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
221
|
+
};
|
|
181
222
|
}
|
|
182
223
|
catch {
|
|
183
|
-
return
|
|
224
|
+
return {};
|
|
184
225
|
}
|
|
185
226
|
}
|
|
227
|
+
function isCodexLoginStatusReady(result) {
|
|
228
|
+
if (result.error || result.status !== 0)
|
|
229
|
+
return false;
|
|
230
|
+
const output = `${typeof result.stdout === "string" ? result.stdout : ""}\n${typeof result.stderr === "string" ? result.stderr : ""}`;
|
|
231
|
+
return output.toLowerCase().includes("logged in");
|
|
232
|
+
}
|
|
186
233
|
function ensurePromptInput(promptInput, provider) {
|
|
187
234
|
if (promptInput)
|
|
188
235
|
return promptInput;
|
|
@@ -224,6 +271,7 @@ function validateAnthropicToken(token) {
|
|
|
224
271
|
async function collectRuntimeAuthCredentials(input, deps) {
|
|
225
272
|
const spawnSync = deps.spawnSync ?? child_process_1.spawnSync;
|
|
226
273
|
const homeDir = deps.homeDir ?? os.homedir();
|
|
274
|
+
const now = deps.now ?? (() => new Date());
|
|
227
275
|
if (input.provider === "github-copilot") {
|
|
228
276
|
let token = process.env.GH_TOKEN?.trim() || process.env.GITHUB_TOKEN?.trim() || "";
|
|
229
277
|
if (!token) {
|
|
@@ -267,9 +315,13 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
267
315
|
return { githubToken: token, baseUrl };
|
|
268
316
|
}
|
|
269
317
|
if (input.provider === "openai-codex") {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
318
|
+
writeAuthProgress(input, "checking local Codex login...");
|
|
319
|
+
const localStatus = spawnSync("codex", ["login", "status"], { encoding: "utf8" });
|
|
320
|
+
const localCredentials = readCodexLocalAuthCredentials(homeDir);
|
|
321
|
+
if (isCodexLoginStatusReady(localStatus) && isFreshCodexToken(localCredentials, now())) {
|
|
322
|
+
writeAuthProgress(input, "using existing openai-codex local login...");
|
|
323
|
+
return localCredentials;
|
|
324
|
+
}
|
|
273
325
|
(0, runtime_1.emitNervesEvent)({
|
|
274
326
|
component: "daemon",
|
|
275
327
|
event: "daemon.auth_codex_login_start",
|
|
@@ -285,11 +337,11 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
285
337
|
throw new Error(`'codex login' exited with status ${result.status}.`);
|
|
286
338
|
}
|
|
287
339
|
writeAuthProgress(input, "openai-codex login complete; reading local Codex token...");
|
|
288
|
-
const
|
|
289
|
-
if (!
|
|
340
|
+
const credentials = readCodexLocalAuthCredentials(homeDir);
|
|
341
|
+
if (!credentials.oauthAccessToken) {
|
|
290
342
|
throw new Error("Codex login completed but no token was found in ~/.codex/auth.json. Re-run `codex login` and try again.");
|
|
291
343
|
}
|
|
292
|
-
return
|
|
344
|
+
return credentials;
|
|
293
345
|
}
|
|
294
346
|
if (input.provider === "anthropic") {
|
|
295
347
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -97,6 +97,14 @@ function defaultWriteRaw(text) {
|
|
|
97
97
|
process.stdout.write(text);
|
|
98
98
|
}
|
|
99
99
|
/* v8 ignore stop */
|
|
100
|
+
function resolveDaemonBootEntryPath(homeDir) {
|
|
101
|
+
const repoRoot = (0, identity_1.getRepoRoot)();
|
|
102
|
+
const versionManagerRootMarker = `${path.sep}.ouro-cli${path.sep}versions${path.sep}`;
|
|
103
|
+
if ((0, runtime_mode_1.detectRuntimeMode)(repoRoot) === "production" && repoRoot.includes(versionManagerRootMarker)) {
|
|
104
|
+
return path.join((0, ouro_version_manager_1.getOuroCliHome)(homeDir), "CurrentVersion", "node_modules", "@ouro.bot", "cli", "dist", "heart", "daemon", "daemon-entry.js");
|
|
105
|
+
}
|
|
106
|
+
return path.join(repoRoot, "dist", "heart", "daemon", "daemon-entry.js");
|
|
107
|
+
}
|
|
100
108
|
/**
|
|
101
109
|
* Read the runtimeVersion from the first .ouro bundle's bundle-meta.json.
|
|
102
110
|
* Returns undefined if none found or unreadable.
|
|
@@ -218,7 +226,7 @@ function defaultEnsureDaemonBootPersistence(socketPath) {
|
|
|
218
226
|
mkdirp: (dir) => fs.mkdirSync(dir, { recursive: true }),
|
|
219
227
|
homeDir,
|
|
220
228
|
};
|
|
221
|
-
const entryPath =
|
|
229
|
+
const entryPath = resolveDaemonBootEntryPath(homeDir);
|
|
222
230
|
/* v8 ignore next -- covered via mock in daemon-cli-defaults.test.ts; v8 on CI attributes the real fs.existsSync branch to the non-mock load @preserve */
|
|
223
231
|
if (!fs.existsSync(entryPath)) {
|
|
224
232
|
(0, runtime_1.emitNervesEvent)({
|