@ouro.bot/cli 0.1.0-alpha.123 → 0.1.0-alpha.124
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.124",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Anthropic OAuth tokens now auto-refresh before expiry. The provider checks token freshness before each turn, exchanges the refresh token for a new access token, and persists the result. No more 400 errors from expired setup tokens."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
4
10
|
{
|
|
5
11
|
"version": "0.1.0-alpha.123",
|
|
6
12
|
"changes": [
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.needsRefresh = needsRefresh;
|
|
37
|
+
exports.refreshAnthropicToken = refreshAnthropicToken;
|
|
38
|
+
exports.persistTokenState = persistTokenState;
|
|
39
|
+
exports.ensureFreshToken = ensureFreshToken;
|
|
40
|
+
/* v8 ignore start -- OAuth token lifecycle: requires live API calls, tested via integration @preserve */
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const identity_1 = require("../identity");
|
|
44
|
+
const OAUTH_TOKEN_ENDPOINT = "https://console.anthropic.com/v1/oauth/token";
|
|
45
|
+
const OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
46
|
+
const REFRESH_MARGIN_MS = 5 * 60 * 1000; // refresh 5 minutes before expiry
|
|
47
|
+
/**
|
|
48
|
+
* Check if the Anthropic OAuth token needs refreshing.
|
|
49
|
+
* Returns true if no expiresAt is set (legacy token) or if within 5 min of expiry.
|
|
50
|
+
*/
|
|
51
|
+
function needsRefresh(expiresAt) {
|
|
52
|
+
if (!expiresAt)
|
|
53
|
+
return true; // legacy token with no expiry — always try refresh
|
|
54
|
+
return Date.now() > expiresAt - REFRESH_MARGIN_MS;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Refresh an Anthropic OAuth access token using the refresh token.
|
|
58
|
+
* Returns the new token state or null if refresh fails.
|
|
59
|
+
*/
|
|
60
|
+
async function refreshAnthropicToken(refreshToken, fetchImpl = fetch) {
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetchImpl(OAUTH_TOKEN_ENDPOINT, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { "Content-Type": "application/json" },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
grant_type: "refresh_token",
|
|
67
|
+
refresh_token: refreshToken,
|
|
68
|
+
client_id: OAUTH_CLIENT_ID,
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
(0, runtime_1.emitNervesEvent)({
|
|
73
|
+
level: "warn",
|
|
74
|
+
component: "engine",
|
|
75
|
+
event: "engine.anthropic_token_refresh_failed",
|
|
76
|
+
message: `token refresh failed: ${response.status}`,
|
|
77
|
+
meta: { status: response.status },
|
|
78
|
+
});
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const json = await response.json();
|
|
82
|
+
if (!json.access_token) {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
level: "warn",
|
|
85
|
+
component: "engine",
|
|
86
|
+
event: "engine.anthropic_token_refresh_failed",
|
|
87
|
+
message: "token refresh returned no access_token",
|
|
88
|
+
meta: {},
|
|
89
|
+
});
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const state = {
|
|
93
|
+
accessToken: json.access_token,
|
|
94
|
+
refreshToken: json.refresh_token ?? refreshToken, // keep old if not returned
|
|
95
|
+
expiresAt: Date.now() + (json.expires_in ?? 28800) * 1000, // default 8h
|
|
96
|
+
};
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
component: "engine",
|
|
99
|
+
event: "engine.anthropic_token_refreshed",
|
|
100
|
+
message: "anthropic OAuth token refreshed",
|
|
101
|
+
meta: { expiresAt: new Date(state.expiresAt).toISOString() },
|
|
102
|
+
});
|
|
103
|
+
return state;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
(0, runtime_1.emitNervesEvent)({
|
|
107
|
+
level: "warn",
|
|
108
|
+
component: "engine",
|
|
109
|
+
event: "engine.anthropic_token_refresh_error",
|
|
110
|
+
message: "token refresh threw",
|
|
111
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
112
|
+
});
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Persist refreshed token state back to secrets.json.
|
|
118
|
+
*/
|
|
119
|
+
function persistTokenState(agentName, state) {
|
|
120
|
+
try {
|
|
121
|
+
const secretsPath = (0, identity_1.getAgentSecretsPath)(agentName);
|
|
122
|
+
const raw = fs.readFileSync(secretsPath, "utf-8");
|
|
123
|
+
const secrets = JSON.parse(raw);
|
|
124
|
+
secrets.providers = secrets.providers ?? {};
|
|
125
|
+
secrets.providers.anthropic = secrets.providers.anthropic ?? {};
|
|
126
|
+
secrets.providers.anthropic.setupToken = state.accessToken;
|
|
127
|
+
secrets.providers.anthropic.refreshToken = state.refreshToken;
|
|
128
|
+
secrets.providers.anthropic.expiresAt = state.expiresAt;
|
|
129
|
+
fs.writeFileSync(secretsPath, JSON.stringify(secrets, null, 2) + "\n", "utf-8");
|
|
130
|
+
/* v8 ignore start -- defensive: persistence failure must not crash the provider @preserve */
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
(0, runtime_1.emitNervesEvent)({
|
|
134
|
+
level: "warn",
|
|
135
|
+
component: "engine",
|
|
136
|
+
event: "engine.anthropic_token_persist_error",
|
|
137
|
+
message: "failed to persist refreshed token",
|
|
138
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/* v8 ignore stop */
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Ensure the Anthropic token is fresh. If expired, refresh and persist.
|
|
145
|
+
* Returns the current valid access token, or null if refresh failed and
|
|
146
|
+
* the existing token is expired.
|
|
147
|
+
*/
|
|
148
|
+
async function ensureFreshToken(currentToken, refreshToken, expiresAt, agentName, fetchImpl) {
|
|
149
|
+
if (!needsRefresh(expiresAt)) {
|
|
150
|
+
return currentToken; // still fresh
|
|
151
|
+
}
|
|
152
|
+
if (!refreshToken) {
|
|
153
|
+
// No refresh token — use the current token as-is (may be expired)
|
|
154
|
+
return currentToken;
|
|
155
|
+
}
|
|
156
|
+
const newState = await refreshAnthropicToken(refreshToken, fetchImpl);
|
|
157
|
+
if (!newState) {
|
|
158
|
+
return currentToken; // refresh failed — try the old token
|
|
159
|
+
}
|
|
160
|
+
persistTokenState(agentName, newState);
|
|
161
|
+
return newState.accessToken;
|
|
162
|
+
}
|
|
163
|
+
/* v8 ignore stop */
|
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -389,18 +422,43 @@ function createAnthropicProviderRuntime(config) {
|
|
|
389
422
|
if (modelCaps.reasoningEffort)
|
|
390
423
|
capabilities.add("reasoning-effort");
|
|
391
424
|
const credential = resolveAnthropicSetupTokenCredential();
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
425
|
+
const fullConfig = config ?? (0, config_1.getAnthropicConfig)();
|
|
426
|
+
const refreshToken = fullConfig.refreshToken;
|
|
427
|
+
const expiresAt = fullConfig.expiresAt;
|
|
428
|
+
function createClient(token) {
|
|
429
|
+
return new sdk_1.default({
|
|
430
|
+
authToken: token,
|
|
431
|
+
timeout: 30000,
|
|
432
|
+
maxRetries: 0,
|
|
433
|
+
defaultHeaders: {
|
|
434
|
+
"anthropic-beta": ANTHROPIC_OAUTH_BETA_HEADER,
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
let currentToken = credential.token;
|
|
439
|
+
let client = createClient(currentToken);
|
|
440
|
+
/* v8 ignore start -- token refresh: dynamic import + ensureFreshToken, tested via integration @preserve */
|
|
441
|
+
async function ensureClient() {
|
|
442
|
+
try {
|
|
443
|
+
const { ensureFreshToken } = await Promise.resolve().then(() => __importStar(require("./anthropic-token")));
|
|
444
|
+
const { getAgentName } = await Promise.resolve().then(() => __importStar(require("../identity")));
|
|
445
|
+
const freshToken = await ensureFreshToken(currentToken, refreshToken, expiresAt, getAgentName());
|
|
446
|
+
if (freshToken !== currentToken) {
|
|
447
|
+
currentToken = freshToken;
|
|
448
|
+
client = createClient(freshToken);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
// refresh failed — use existing client
|
|
453
|
+
}
|
|
454
|
+
return client;
|
|
455
|
+
}
|
|
456
|
+
/* v8 ignore stop */
|
|
400
457
|
return {
|
|
401
458
|
id: "anthropic",
|
|
402
459
|
model: anthropicConfig.model,
|
|
403
|
-
client
|
|
460
|
+
/* v8 ignore next -- getter: returns mutable client ref @preserve */
|
|
461
|
+
get client() { return client; },
|
|
404
462
|
capabilities,
|
|
405
463
|
supportedReasoningEfforts: modelCaps.reasoningEffort,
|
|
406
464
|
resetTurnState(_messages) {
|
|
@@ -409,8 +467,9 @@ function createAnthropicProviderRuntime(config) {
|
|
|
409
467
|
appendToolOutput(_callId, _output) {
|
|
410
468
|
// Anthropic uses canonical messages for tool_result tracking.
|
|
411
469
|
},
|
|
412
|
-
streamTurn(request) {
|
|
413
|
-
|
|
470
|
+
async streamTurn(request) {
|
|
471
|
+
const freshClient = await ensureClient();
|
|
472
|
+
return streamAnthropicMessages(freshClient, anthropicConfig.model, request);
|
|
414
473
|
},
|
|
415
474
|
/* v8 ignore next 3 -- delegation: classification logic tested via classifyAnthropicError @preserve */
|
|
416
475
|
classifyError(error) {
|