@ouro.bot/cli 0.1.0-alpha.364 → 0.1.0-alpha.365
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 +15 -6
- package/changelog.json +9 -0
- package/dist/heart/auth/auth-flow.js +25 -110
- package/dist/heart/config.js +69 -55
- package/dist/heart/core.js +83 -33
- package/dist/heart/daemon/agent-config-check.js +41 -238
- package/dist/heart/daemon/agentic-repair.js +1 -1
- package/dist/heart/daemon/cli-defaults.js +15 -68
- package/dist/heart/daemon/cli-exec.js +246 -89
- package/dist/heart/daemon/cli-parse.js +71 -0
- package/dist/heart/daemon/daemon-cli.js +1 -2
- package/dist/heart/daemon/daemon-entry.js +1 -3
- package/dist/heart/daemon/doctor.js +9 -29
- package/dist/heart/daemon/provider-discovery.js +32 -59
- package/dist/heart/hatch/hatch-flow.js +9 -12
- package/dist/heart/hatch/specialist-prompt.js +1 -1
- package/dist/heart/hatch/specialist-tools.js +21 -1
- package/dist/heart/migrate-config.js +15 -42
- package/dist/heart/provider-binding-resolver.js +6 -7
- package/dist/heart/provider-credentials.js +379 -0
- package/dist/heart/provider-failover.js +3 -11
- package/dist/heart/provider-ping.js +13 -3
- package/dist/heart/provider-state.js +8 -0
- package/dist/heart/provider-visibility.js +3 -6
- package/dist/heart/providers/anthropic-token.js +15 -47
- package/dist/heart/providers/anthropic.js +4 -9
- package/dist/heart/providers/azure.js +3 -3
- package/dist/heart/providers/github-copilot.js +2 -2
- package/dist/heart/providers/minimax-vlm.js +2 -2
- package/dist/heart/providers/minimax.js +1 -1
- package/dist/heart/providers/openai-codex.js +4 -9
- package/dist/repertoire/bitwarden-store.js +63 -17
- package/dist/repertoire/bundle-templates.js +2 -2
- package/dist/repertoire/credential-access.js +47 -467
- package/dist/repertoire/tools-attachments.js +5 -4
- package/dist/repertoire/tools-vault.js +10 -80
- package/dist/repertoire/vault-unlock.js +359 -0
- package/dist/senses/bluebubbles/client.js +39 -4
- package/dist/senses/pipeline.js +0 -1
- package/package.json +1 -1
- package/dist/heart/provider-credential-pool.js +0 -395
|
@@ -0,0 +1,379 @@
|
|
|
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.splitProviderCredentialFields = splitProviderCredentialFields;
|
|
37
|
+
exports.providerCredentialsVaultPath = providerCredentialsVaultPath;
|
|
38
|
+
exports.providerCredentialItemName = providerCredentialItemName;
|
|
39
|
+
exports.providerCredentialMachineHomeDir = providerCredentialMachineHomeDir;
|
|
40
|
+
exports.readProviderCredentialPool = readProviderCredentialPool;
|
|
41
|
+
exports.refreshProviderCredentialPool = refreshProviderCredentialPool;
|
|
42
|
+
exports.createProviderCredentialRecord = createProviderCredentialRecord;
|
|
43
|
+
exports.cacheProviderCredentialRecords = cacheProviderCredentialRecords;
|
|
44
|
+
exports.readCachedProviderCredentialRecord = readCachedProviderCredentialRecord;
|
|
45
|
+
exports.readProviderCredentialRecord = readProviderCredentialRecord;
|
|
46
|
+
exports.upsertProviderCredential = upsertProviderCredential;
|
|
47
|
+
exports.redactProviderCredentialPool = redactProviderCredentialPool;
|
|
48
|
+
exports.summarizeProviderCredentialPool = summarizeProviderCredentialPool;
|
|
49
|
+
exports.resetProviderCredentialCache = resetProviderCredentialCache;
|
|
50
|
+
const crypto = __importStar(require("node:crypto"));
|
|
51
|
+
const os = __importStar(require("node:os"));
|
|
52
|
+
const runtime_1 = require("../nerves/runtime");
|
|
53
|
+
const credential_access_1 = require("../repertoire/credential-access");
|
|
54
|
+
const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot"];
|
|
55
|
+
const VALID_PROVENANCE_SOURCES = ["auth-flow", "manual"];
|
|
56
|
+
const VAULT_ITEM_PREFIX = "providers/";
|
|
57
|
+
const PROVIDER_FIELD_SPLITS = {
|
|
58
|
+
anthropic: {
|
|
59
|
+
credentials: ["setupToken", "refreshToken", "expiresAt"],
|
|
60
|
+
config: [],
|
|
61
|
+
},
|
|
62
|
+
"openai-codex": {
|
|
63
|
+
credentials: ["oauthAccessToken"],
|
|
64
|
+
config: [],
|
|
65
|
+
},
|
|
66
|
+
azure: {
|
|
67
|
+
credentials: ["apiKey"],
|
|
68
|
+
config: ["endpoint", "deployment", "apiVersion", "managedIdentityClientId"],
|
|
69
|
+
},
|
|
70
|
+
minimax: {
|
|
71
|
+
credentials: ["apiKey"],
|
|
72
|
+
config: [],
|
|
73
|
+
},
|
|
74
|
+
"github-copilot": {
|
|
75
|
+
credentials: ["githubToken"],
|
|
76
|
+
config: ["baseUrl"],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
let cachedPools = new Map();
|
|
80
|
+
function isRecord(value) {
|
|
81
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
function isAgentProvider(value) {
|
|
84
|
+
return VALID_PROVIDERS.includes(value);
|
|
85
|
+
}
|
|
86
|
+
function isProvenanceSource(value) {
|
|
87
|
+
return typeof value === "string" && VALID_PROVENANCE_SOURCES.includes(value);
|
|
88
|
+
}
|
|
89
|
+
function isCredentialValue(value) {
|
|
90
|
+
return typeof value === "string" || typeof value === "number";
|
|
91
|
+
}
|
|
92
|
+
function isPresentCredentialValue(value) {
|
|
93
|
+
if (!isCredentialValue(value))
|
|
94
|
+
return false;
|
|
95
|
+
if (typeof value === "string")
|
|
96
|
+
return value.trim().length > 0;
|
|
97
|
+
return Number.isFinite(value) && value !== 0;
|
|
98
|
+
}
|
|
99
|
+
function copyKnownFields(source, fields) {
|
|
100
|
+
const result = {};
|
|
101
|
+
for (const field of fields) {
|
|
102
|
+
const value = source[field];
|
|
103
|
+
if (isPresentCredentialValue(value)) {
|
|
104
|
+
result[field] = value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function splitProviderCredentialFields(provider, rawConfig) {
|
|
110
|
+
const fields = PROVIDER_FIELD_SPLITS[provider];
|
|
111
|
+
return {
|
|
112
|
+
credentials: copyKnownFields(rawConfig, fields.credentials),
|
|
113
|
+
config: copyKnownFields(rawConfig, fields.config),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function providerCredentialsVaultPath(agentName) {
|
|
117
|
+
return `vault:${agentName}:${VAULT_ITEM_PREFIX}*`;
|
|
118
|
+
}
|
|
119
|
+
function providerCredentialItemName(provider) {
|
|
120
|
+
return `${VAULT_ITEM_PREFIX}${provider}`;
|
|
121
|
+
}
|
|
122
|
+
function providerCredentialMachineHomeDir(homeDir) {
|
|
123
|
+
return homeDir ?? os.homedir();
|
|
124
|
+
}
|
|
125
|
+
function stableJson(value) {
|
|
126
|
+
if (Array.isArray(value))
|
|
127
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
128
|
+
if (isRecord(value)) {
|
|
129
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
130
|
+
}
|
|
131
|
+
return JSON.stringify(value);
|
|
132
|
+
}
|
|
133
|
+
function makeRevision(payload) {
|
|
134
|
+
const hash = crypto
|
|
135
|
+
.createHash("sha256")
|
|
136
|
+
.update(stableJson({
|
|
137
|
+
provider: payload.provider,
|
|
138
|
+
credentials: payload.credentials,
|
|
139
|
+
config: payload.config,
|
|
140
|
+
provenance: payload.provenance,
|
|
141
|
+
updatedAt: payload.updatedAt,
|
|
142
|
+
}))
|
|
143
|
+
.digest("hex")
|
|
144
|
+
.slice(0, 16);
|
|
145
|
+
return `vault_${hash}`;
|
|
146
|
+
}
|
|
147
|
+
function validateProviderCredentialPayload(value, expectedProvider) {
|
|
148
|
+
if (!isRecord(value))
|
|
149
|
+
throw new Error("provider credential payload must be an object");
|
|
150
|
+
if (value.schemaVersion !== 1)
|
|
151
|
+
throw new Error("provider credential payload schemaVersion must be 1");
|
|
152
|
+
if (value.kind !== "provider-credential")
|
|
153
|
+
throw new Error("provider credential payload kind must be provider-credential");
|
|
154
|
+
if (typeof value.provider !== "string" || !isAgentProvider(value.provider)) {
|
|
155
|
+
throw new Error("provider credential payload provider must be valid");
|
|
156
|
+
}
|
|
157
|
+
if (expectedProvider && value.provider !== expectedProvider) {
|
|
158
|
+
throw new Error(`provider credential payload provider must be ${expectedProvider}`);
|
|
159
|
+
}
|
|
160
|
+
if (typeof value.updatedAt !== "string" || value.updatedAt.length === 0) {
|
|
161
|
+
throw new Error("provider credential payload updatedAt must be non-empty");
|
|
162
|
+
}
|
|
163
|
+
if (!isRecord(value.credentials))
|
|
164
|
+
throw new Error("provider credential payload credentials must be an object");
|
|
165
|
+
if (!isRecord(value.config))
|
|
166
|
+
throw new Error("provider credential payload config must be an object");
|
|
167
|
+
for (const [field, fieldValue] of Object.entries(value.credentials)) {
|
|
168
|
+
if (!isCredentialValue(fieldValue))
|
|
169
|
+
throw new Error(`credentials.${field} must be a string or number`);
|
|
170
|
+
}
|
|
171
|
+
for (const [field, fieldValue] of Object.entries(value.config)) {
|
|
172
|
+
if (!isCredentialValue(fieldValue))
|
|
173
|
+
throw new Error(`config.${field} must be a string or number`);
|
|
174
|
+
}
|
|
175
|
+
if (!isRecord(value.provenance))
|
|
176
|
+
throw new Error("provider credential payload provenance must be an object");
|
|
177
|
+
if (!isProvenanceSource(value.provenance.source)) {
|
|
178
|
+
throw new Error("provider credential payload provenance.source must be auth-flow or manual");
|
|
179
|
+
}
|
|
180
|
+
if (typeof value.provenance.updatedAt !== "string" || value.provenance.updatedAt.length === 0) {
|
|
181
|
+
throw new Error("provider credential payload provenance.updatedAt must be non-empty");
|
|
182
|
+
}
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
function recordFromPayload(payload) {
|
|
186
|
+
return {
|
|
187
|
+
provider: payload.provider,
|
|
188
|
+
revision: makeRevision(payload),
|
|
189
|
+
updatedAt: payload.updatedAt,
|
|
190
|
+
credentials: { ...payload.credentials },
|
|
191
|
+
config: { ...payload.config },
|
|
192
|
+
provenance: { ...payload.provenance },
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function missingPool(agentName, error = "provider credentials have not been loaded from vault") {
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
reason: "missing",
|
|
199
|
+
poolPath: providerCredentialsVaultPath(agentName),
|
|
200
|
+
error,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function cacheResult(agentName, result) {
|
|
204
|
+
cachedPools.set(agentName, result);
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
function resultForProvider(poolResult, provider) {
|
|
208
|
+
if (!poolResult.ok)
|
|
209
|
+
return poolResult;
|
|
210
|
+
const record = poolResult.pool.providers[provider];
|
|
211
|
+
if (!record) {
|
|
212
|
+
return {
|
|
213
|
+
ok: false,
|
|
214
|
+
reason: "missing",
|
|
215
|
+
poolPath: poolResult.poolPath,
|
|
216
|
+
error: `${provider} credentials are missing from ${poolResult.poolPath}`,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return { ok: true, poolPath: poolResult.poolPath, record };
|
|
220
|
+
}
|
|
221
|
+
function readProviderCredentialPool(agentName) {
|
|
222
|
+
return cachedPools.get(agentName) ?? missingPool(agentName);
|
|
223
|
+
}
|
|
224
|
+
async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
225
|
+
try {
|
|
226
|
+
const store = (0, credential_access_1.getCredentialStore)(agentName);
|
|
227
|
+
const items = await store.list();
|
|
228
|
+
const providers = {};
|
|
229
|
+
let updatedAt = new Date(0).toISOString();
|
|
230
|
+
for (const item of items) {
|
|
231
|
+
if (!item.domain.startsWith(VAULT_ITEM_PREFIX))
|
|
232
|
+
continue;
|
|
233
|
+
const provider = item.domain.slice(VAULT_ITEM_PREFIX.length);
|
|
234
|
+
if (!isAgentProvider(provider))
|
|
235
|
+
continue;
|
|
236
|
+
const raw = await store.getRawSecret(item.domain, "password");
|
|
237
|
+
const payload = validateProviderCredentialPayload(JSON.parse(raw), provider);
|
|
238
|
+
const record = recordFromPayload(payload);
|
|
239
|
+
providers[provider] = record;
|
|
240
|
+
if (record.updatedAt > updatedAt)
|
|
241
|
+
updatedAt = record.updatedAt;
|
|
242
|
+
}
|
|
243
|
+
const pool = {
|
|
244
|
+
schemaVersion: 1,
|
|
245
|
+
updatedAt,
|
|
246
|
+
providers,
|
|
247
|
+
};
|
|
248
|
+
(0, runtime_1.emitNervesEvent)({
|
|
249
|
+
component: "config/identity",
|
|
250
|
+
event: "config.provider_credentials_loaded",
|
|
251
|
+
message: "loaded provider credentials from vault",
|
|
252
|
+
meta: { agentName, providerCount: Object.keys(providers).length },
|
|
253
|
+
});
|
|
254
|
+
return cacheResult(agentName, { ok: true, poolPath: providerCredentialsVaultPath(agentName), pool });
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
const cached = cachedPools.get(agentName);
|
|
258
|
+
const result = {
|
|
259
|
+
ok: false,
|
|
260
|
+
reason: "unavailable",
|
|
261
|
+
poolPath: providerCredentialsVaultPath(agentName),
|
|
262
|
+
error: error instanceof Error ? error.message : String(error),
|
|
263
|
+
};
|
|
264
|
+
(0, runtime_1.emitNervesEvent)({
|
|
265
|
+
level: "error",
|
|
266
|
+
component: "config/identity",
|
|
267
|
+
event: "config.provider_credentials_unavailable",
|
|
268
|
+
message: "provider credentials unavailable",
|
|
269
|
+
meta: { agentName, reason: result.reason, poolPath: result.poolPath },
|
|
270
|
+
});
|
|
271
|
+
if (options.preserveCachedOnFailure && cached?.ok)
|
|
272
|
+
return cached;
|
|
273
|
+
return cacheResult(agentName, result);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function createProviderCredentialRecord(input) {
|
|
277
|
+
const updatedAt = (input.now ?? new Date()).toISOString();
|
|
278
|
+
const payload = {
|
|
279
|
+
schemaVersion: 1,
|
|
280
|
+
kind: "provider-credential",
|
|
281
|
+
provider: input.provider,
|
|
282
|
+
updatedAt,
|
|
283
|
+
credentials: { ...input.credentials },
|
|
284
|
+
config: { ...input.config },
|
|
285
|
+
provenance: {
|
|
286
|
+
source: input.provenance.source,
|
|
287
|
+
updatedAt,
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
return recordFromPayload(payload);
|
|
291
|
+
}
|
|
292
|
+
function cacheProviderCredentialRecords(agentName, records, now = new Date()) {
|
|
293
|
+
const providers = {};
|
|
294
|
+
let updatedAt = now.toISOString();
|
|
295
|
+
for (const record of records) {
|
|
296
|
+
providers[record.provider] = record;
|
|
297
|
+
if (record.updatedAt > updatedAt)
|
|
298
|
+
updatedAt = record.updatedAt;
|
|
299
|
+
}
|
|
300
|
+
const pool = {
|
|
301
|
+
schemaVersion: 1,
|
|
302
|
+
updatedAt,
|
|
303
|
+
providers,
|
|
304
|
+
};
|
|
305
|
+
return cacheResult(agentName, { ok: true, poolPath: providerCredentialsVaultPath(agentName), pool });
|
|
306
|
+
}
|
|
307
|
+
function readCachedProviderCredentialRecord(agentName, provider) {
|
|
308
|
+
return resultForProvider(readProviderCredentialPool(agentName), provider);
|
|
309
|
+
}
|
|
310
|
+
async function readProviderCredentialRecord(agentName, provider, options = {}) {
|
|
311
|
+
const cached = readCachedProviderCredentialRecord(agentName, provider);
|
|
312
|
+
if (cached.ok || options.refreshIfMissing === false)
|
|
313
|
+
return cached;
|
|
314
|
+
return resultForProvider(await refreshProviderCredentialPool(agentName), provider);
|
|
315
|
+
}
|
|
316
|
+
async function upsertProviderCredential(input) {
|
|
317
|
+
const updatedAt = (input.now ?? new Date()).toISOString();
|
|
318
|
+
const payload = {
|
|
319
|
+
schemaVersion: 1,
|
|
320
|
+
kind: "provider-credential",
|
|
321
|
+
provider: input.provider,
|
|
322
|
+
updatedAt,
|
|
323
|
+
credentials: { ...input.credentials },
|
|
324
|
+
config: { ...input.config },
|
|
325
|
+
provenance: {
|
|
326
|
+
source: input.provenance.source,
|
|
327
|
+
updatedAt,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
const record = recordFromPayload(payload);
|
|
331
|
+
const store = (0, credential_access_1.getCredentialStore)(input.agentName);
|
|
332
|
+
await store.store(providerCredentialItemName(input.provider), {
|
|
333
|
+
username: input.provider,
|
|
334
|
+
password: JSON.stringify(payload),
|
|
335
|
+
notes: "Ouro provider credentials. The vault item password is a versioned JSON payload.",
|
|
336
|
+
});
|
|
337
|
+
await refreshProviderCredentialPool(input.agentName);
|
|
338
|
+
(0, runtime_1.emitNervesEvent)({
|
|
339
|
+
component: "config/identity",
|
|
340
|
+
event: "config.provider_credential_upserted",
|
|
341
|
+
message: "upserted provider credential record in vault",
|
|
342
|
+
meta: { agentName: input.agentName, provider: input.provider, revision: record.revision, source: record.provenance.source },
|
|
343
|
+
});
|
|
344
|
+
return record;
|
|
345
|
+
}
|
|
346
|
+
function redactProviderCredentialPool(pool) {
|
|
347
|
+
const providers = {};
|
|
348
|
+
for (const [provider, record] of Object.entries(pool.providers)) {
|
|
349
|
+
const redactedCredentials = {};
|
|
350
|
+
for (const key of Object.keys(record.credentials)) {
|
|
351
|
+
redactedCredentials[key] = "[redacted]";
|
|
352
|
+
}
|
|
353
|
+
providers[provider] = {
|
|
354
|
+
...record,
|
|
355
|
+
credentials: redactedCredentials,
|
|
356
|
+
config: { ...record.config },
|
|
357
|
+
provenance: { ...record.provenance },
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
...pool,
|
|
362
|
+
providers,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function summarizeProviderCredentialPool(pool) {
|
|
366
|
+
return {
|
|
367
|
+
providers: Object.entries(pool.providers).map(([, record]) => ({
|
|
368
|
+
provider: record.provider,
|
|
369
|
+
revision: record.revision,
|
|
370
|
+
source: record.provenance.source,
|
|
371
|
+
updatedAt: record.updatedAt,
|
|
372
|
+
credentialFields: Object.keys(record.credentials).sort(),
|
|
373
|
+
configFields: Object.keys(record.config).sort(),
|
|
374
|
+
})),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function resetProviderCredentialCache() {
|
|
378
|
+
cachedPools = new Map();
|
|
379
|
+
}
|
|
@@ -8,7 +8,7 @@ exports.runMachineProviderFailoverInventory = runMachineProviderFailoverInventor
|
|
|
8
8
|
const identity_1 = require("./identity");
|
|
9
9
|
const provider_ping_1 = require("./provider-ping");
|
|
10
10
|
const provider_models_1 = require("./provider-models");
|
|
11
|
-
const
|
|
11
|
+
const provider_credentials_1 = require("./provider-credentials");
|
|
12
12
|
const runtime_1 = require("../nerves/runtime");
|
|
13
13
|
const CLASSIFICATION_LABELS = {
|
|
14
14
|
"auth-failure": "authentication failed",
|
|
@@ -33,14 +33,8 @@ function formatErrorDetail(errorMessage, errorSummary) {
|
|
|
33
33
|
return detail.length > 300 ? `${detail.slice(0, 297)}...` : detail;
|
|
34
34
|
}
|
|
35
35
|
function formatCredentialProvenanceLabel(candidate) {
|
|
36
|
-
if (candidate.contributedByAgent && candidate.source) {
|
|
37
|
-
return `credentials from ${candidate.contributedByAgent} via ${candidate.source}`;
|
|
38
|
-
}
|
|
39
|
-
if (candidate.contributedByAgent) {
|
|
40
|
-
return `credentials from ${candidate.contributedByAgent}`;
|
|
41
|
-
}
|
|
42
36
|
if (candidate.source) {
|
|
43
|
-
return `credentials
|
|
37
|
+
return `credentials in vault via ${candidate.source}`;
|
|
44
38
|
}
|
|
45
39
|
return undefined;
|
|
46
40
|
}
|
|
@@ -200,7 +194,6 @@ function handleFailoverReply(reply, context) {
|
|
|
200
194
|
lane: currentLane,
|
|
201
195
|
...(candidate.credentialRevision ? { credentialRevision: candidate.credentialRevision } : {}),
|
|
202
196
|
...(candidate.source ? { source: candidate.source } : {}),
|
|
203
|
-
...(candidate.contributedByAgent ? { contributedByAgent: candidate.contributedByAgent } : {}),
|
|
204
197
|
};
|
|
205
198
|
}
|
|
206
199
|
}
|
|
@@ -211,12 +204,11 @@ function candidateFromCredentialRecord(record) {
|
|
|
211
204
|
provider: record.provider,
|
|
212
205
|
credentialRevision: record.revision,
|
|
213
206
|
source: record.provenance.source,
|
|
214
|
-
contributedByAgent: record.provenance.contributedByAgent,
|
|
215
207
|
};
|
|
216
208
|
}
|
|
217
209
|
async function runMachineProviderFailoverInventory(agentName, currentProvider, options = {}) {
|
|
218
210
|
const ping = options.ping ?? provider_ping_1.pingProvider;
|
|
219
|
-
const poolResult = (0,
|
|
211
|
+
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName);
|
|
220
212
|
const providers = Object.keys(identity_1.PROVIDER_CREDENTIALS).filter((provider) => provider !== currentProvider);
|
|
221
213
|
const inventory = { ready: [], unavailable: [], unconfigured: [] };
|
|
222
214
|
if (!poolResult.ok) {
|
|
@@ -11,10 +11,10 @@ const azure_1 = require("./providers/azure");
|
|
|
11
11
|
const minimax_1 = require("./providers/minimax");
|
|
12
12
|
const openai_codex_1 = require("./providers/openai-codex");
|
|
13
13
|
const github_copilot_1 = require("./providers/github-copilot");
|
|
14
|
-
const auth_flow_1 = require("./auth/auth-flow");
|
|
15
14
|
const provider_models_1 = require("./provider-models");
|
|
16
15
|
const runtime_1 = require("../nerves/runtime");
|
|
17
16
|
const provider_attempt_1 = require("./provider-attempt");
|
|
17
|
+
const provider_credentials_1 = require("./provider-credentials");
|
|
18
18
|
const PING_TIMEOUT_MS = 10_000;
|
|
19
19
|
const PING_PROMPT = "ping";
|
|
20
20
|
const CHAT_PING_MAX_TOKENS = 1;
|
|
@@ -212,10 +212,20 @@ const PINGABLE_PROVIDERS = ["anthropic", "openai-codex", "azure", "minimax", "gi
|
|
|
212
212
|
async function runHealthInventory(agentName, currentProvider, deps = {}) {
|
|
213
213
|
/* v8 ignore next -- default: tests inject ping dep @preserve */
|
|
214
214
|
const ping = deps.ping ?? pingProvider;
|
|
215
|
-
const
|
|
215
|
+
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName);
|
|
216
216
|
const providers = PINGABLE_PROVIDERS.filter((p) => p !== currentProvider);
|
|
217
|
+
if (!poolResult.ok) {
|
|
218
|
+
return Object.fromEntries(providers.map((provider) => [
|
|
219
|
+
provider,
|
|
220
|
+
{ ok: false, classification: "auth-failure", message: poolResult.error },
|
|
221
|
+
]));
|
|
222
|
+
}
|
|
217
223
|
const results = await Promise.all(providers.map(async (provider) => {
|
|
218
|
-
const
|
|
224
|
+
const record = poolResult.pool.providers[provider];
|
|
225
|
+
if (!record) {
|
|
226
|
+
return [provider, { ok: false, classification: "auth-failure", message: "no credentials configured" }];
|
|
227
|
+
}
|
|
228
|
+
const config = { ...record.config, ...record.credentials };
|
|
219
229
|
const result = await ping(provider, config);
|
|
220
230
|
return [provider, result];
|
|
221
231
|
}));
|
|
@@ -149,6 +149,14 @@ function getProviderStatePath(agentRoot) {
|
|
|
149
149
|
function readProviderState(agentRoot) {
|
|
150
150
|
const statePath = getProviderStatePath(agentRoot);
|
|
151
151
|
let raw;
|
|
152
|
+
try {
|
|
153
|
+
if (!fs.existsSync(statePath)) {
|
|
154
|
+
return { ok: false, reason: "missing", statePath, error: "provider state not found" };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return { ok: false, reason: "invalid", statePath, error: String(error) };
|
|
159
|
+
}
|
|
152
160
|
try {
|
|
153
161
|
raw = fs.readFileSync(statePath, "utf-8");
|
|
154
162
|
}
|
|
@@ -16,7 +16,6 @@ function credentialVisibility(binding) {
|
|
|
16
16
|
return {
|
|
17
17
|
status: "present",
|
|
18
18
|
source: credential.source,
|
|
19
|
-
contributedByAgent: credential.contributedByAgent,
|
|
20
19
|
revision: credential.revision,
|
|
21
20
|
};
|
|
22
21
|
}
|
|
@@ -84,12 +83,10 @@ function buildAgentProviderVisibility(input) {
|
|
|
84
83
|
return visibility;
|
|
85
84
|
}
|
|
86
85
|
function credentialLabel(credential) {
|
|
87
|
-
if (credential.status === "present")
|
|
88
|
-
|
|
89
|
-
return credential.contributedByAgent ? `${source} from ${credential.contributedByAgent}` : source;
|
|
90
|
-
}
|
|
86
|
+
if (credential.status === "present")
|
|
87
|
+
return credential.source ?? "vault";
|
|
91
88
|
if (credential.status === "invalid-pool")
|
|
92
|
-
return "
|
|
89
|
+
return "vault unavailable";
|
|
93
90
|
return "missing";
|
|
94
91
|
}
|
|
95
92
|
function readinessLabel(readiness) {
|
|
@@ -1,46 +1,12 @@
|
|
|
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
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.needsRefresh = needsRefresh;
|
|
37
4
|
exports.refreshAnthropicToken = refreshAnthropicToken;
|
|
38
5
|
exports.persistTokenState = persistTokenState;
|
|
39
6
|
exports.ensureFreshToken = ensureFreshToken;
|
|
40
7
|
/* v8 ignore start -- OAuth token lifecycle: requires live API calls, tested via integration @preserve */
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
8
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
-
const
|
|
9
|
+
const provider_credentials_1 = require("../provider-credentials");
|
|
44
10
|
const OAUTH_TOKEN_ENDPOINT = "https://console.anthropic.com/v1/oauth/token";
|
|
45
11
|
const OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
46
12
|
const REFRESH_MARGIN_MS = 5 * 60 * 1000; // refresh 5 minutes before expiry
|
|
@@ -114,19 +80,21 @@ async function refreshAnthropicToken(refreshToken, fetchImpl = fetch) {
|
|
|
114
80
|
}
|
|
115
81
|
}
|
|
116
82
|
/**
|
|
117
|
-
* Persist refreshed token state back to
|
|
83
|
+
* Persist refreshed token state back to the agent vault.
|
|
118
84
|
*/
|
|
119
|
-
function persistTokenState(agentName, state) {
|
|
85
|
+
async function persistTokenState(agentName, state) {
|
|
120
86
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
87
|
+
await (0, provider_credentials_1.upsertProviderCredential)({
|
|
88
|
+
agentName,
|
|
89
|
+
provider: "anthropic",
|
|
90
|
+
credentials: {
|
|
91
|
+
setupToken: state.accessToken,
|
|
92
|
+
refreshToken: state.refreshToken,
|
|
93
|
+
expiresAt: state.expiresAt,
|
|
94
|
+
},
|
|
95
|
+
config: {},
|
|
96
|
+
provenance: { source: "auth-flow" },
|
|
97
|
+
});
|
|
130
98
|
/* v8 ignore start -- defensive: persistence failure must not crash the provider @preserve */
|
|
131
99
|
}
|
|
132
100
|
catch (error) {
|
|
@@ -157,7 +125,7 @@ async function ensureFreshToken(currentToken, refreshToken, expiresAt, agentName
|
|
|
157
125
|
if (!newState) {
|
|
158
126
|
return currentToken; // refresh failed — try the old token
|
|
159
127
|
}
|
|
160
|
-
persistTokenState(agentName, newState);
|
|
128
|
+
await persistTokenState(agentName, newState);
|
|
161
129
|
return newState.accessToken;
|
|
162
130
|
}
|
|
163
131
|
/* v8 ignore stop */
|
|
@@ -49,9 +49,6 @@ const error_classification_1 = require("./error-classification");
|
|
|
49
49
|
const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
|
|
50
50
|
const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
|
|
51
51
|
const ANTHROPIC_OAUTH_BETA_HEADER = "claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14,interleaved-thinking-2025-05-14";
|
|
52
|
-
function getAnthropicSecretsPathForGuidance() {
|
|
53
|
-
return (0, identity_1.getAgentSecretsPath)();
|
|
54
|
-
}
|
|
55
52
|
function getAnthropicAgentNameForGuidance() {
|
|
56
53
|
return (0, identity_1.getAgentName)();
|
|
57
54
|
}
|
|
@@ -59,10 +56,8 @@ function getAnthropicSetupTokenInstructions() {
|
|
|
59
56
|
const agentName = getAnthropicAgentNameForGuidance();
|
|
60
57
|
return [
|
|
61
58
|
"Fix:",
|
|
62
|
-
` 1. Run \`ouro auth --agent ${agentName}\``,
|
|
63
|
-
|
|
64
|
-
" 3. Confirm providers.anthropic.setupToken is set",
|
|
65
|
-
" 4. After reauth, retry the failed ouro command or reconnect this session.",
|
|
59
|
+
` 1. Run \`ouro auth --agent ${agentName} --provider anthropic\``,
|
|
60
|
+
" 2. After reauth, retry the failed ouro command or reconnect this session.",
|
|
66
61
|
].join("\n");
|
|
67
62
|
}
|
|
68
63
|
function getAnthropicReauthGuidance(reason) {
|
|
@@ -72,7 +67,7 @@ function getAnthropicReauthGuidance(reason) {
|
|
|
72
67
|
getAnthropicSetupTokenInstructions(),
|
|
73
68
|
].join("\n");
|
|
74
69
|
}
|
|
75
|
-
function resolveAnthropicSetupTokenCredential(anthropicConfig
|
|
70
|
+
function resolveAnthropicSetupTokenCredential(anthropicConfig) {
|
|
76
71
|
const token = anthropicConfig.setupToken?.trim();
|
|
77
72
|
if (!token) {
|
|
78
73
|
throw new Error(getAnthropicReauthGuidance("Anthropic provider is selected but no setup-token credential was found."));
|
|
@@ -418,7 +413,7 @@ function createAnthropicProviderRuntime(model, anthropicConfig = (0, config_1.ge
|
|
|
418
413
|
meta: { provider: "anthropic" },
|
|
419
414
|
});
|
|
420
415
|
if (!anthropicConfig.setupToken) {
|
|
421
|
-
throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected
|
|
416
|
+
throw new Error(getAnthropicReauthGuidance("provider 'anthropic' is selected but anthropic.setupToken is missing in the agent vault."));
|
|
422
417
|
}
|
|
423
418
|
const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
|
|
424
419
|
const capabilities = new Set();
|
|
@@ -68,9 +68,9 @@ function createAzureTokenProvider(managedIdentityClientId) {
|
|
|
68
68
|
const detail = err instanceof Error ? err.message : String(err);
|
|
69
69
|
throw new Error(`Azure OpenAI authentication failed: ${detail}\n` +
|
|
70
70
|
"To fix this, either:\n" +
|
|
71
|
-
" 1.
|
|
71
|
+
" 1. Run `ouro auth --agent <agent> --provider azure`, or\n" +
|
|
72
72
|
" 2. Run 'az login' to authenticate with your Azure account (for local dev), or\n" +
|
|
73
|
-
" 3. Attach a managed identity to your App Service and
|
|
73
|
+
" 3. Attach a managed identity to your App Service and store azure.managedIdentityClientId in the agent vault.");
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
}
|
|
@@ -84,7 +84,7 @@ function createAzureProviderRuntime(model, azureConfig = (0, config_1.getAzureCo
|
|
|
84
84
|
meta: { provider: "azure", authMethod },
|
|
85
85
|
});
|
|
86
86
|
if (!(azureConfig.endpoint && azureConfig.deployment)) {
|
|
87
|
-
throw new Error("provider 'azure' is selected
|
|
87
|
+
throw new Error("provider 'azure' is selected but azure endpoint/deployment is incomplete in the agent vault. Run `ouro auth --agent <agent> --provider azure`.");
|
|
88
88
|
}
|
|
89
89
|
const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
|
|
90
90
|
const capabilities = new Set();
|
|
@@ -22,10 +22,10 @@ function createGithubCopilotProviderRuntime(model, config = (0, config_1.getGith
|
|
|
22
22
|
meta: { provider: "github-copilot" },
|
|
23
23
|
});
|
|
24
24
|
if (!config.githubToken) {
|
|
25
|
-
throw new Error("provider 'github-copilot' is selected
|
|
25
|
+
throw new Error("provider 'github-copilot' is selected but github-copilot.githubToken is missing in the agent vault. Run `ouro auth --agent <agent> --provider github-copilot`.");
|
|
26
26
|
}
|
|
27
27
|
if (!config.baseUrl) {
|
|
28
|
-
throw new Error("provider 'github-copilot' is selected
|
|
28
|
+
throw new Error("provider 'github-copilot' is selected but github-copilot.baseUrl is missing in the agent vault. Run `ouro auth --agent <agent> --provider github-copilot`.");
|
|
29
29
|
}
|
|
30
30
|
const isCompletionsModel = model.startsWith("claude");
|
|
31
31
|
const modelCaps = (0, model_capabilities_1.getModelCapabilities)(model);
|