@ouro.bot/cli 0.1.0-alpha.363 → 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.
Files changed (44) hide show
  1. package/README.md +16 -7
  2. package/changelog.json +17 -0
  3. package/dist/heart/auth/auth-flow.js +25 -110
  4. package/dist/heart/config.js +69 -55
  5. package/dist/heart/core.js +83 -33
  6. package/dist/heart/daemon/agent-config-check.js +41 -238
  7. package/dist/heart/daemon/agentic-repair.js +1 -1
  8. package/dist/heart/daemon/cli-defaults.js +15 -68
  9. package/dist/heart/daemon/cli-exec.js +334 -102
  10. package/dist/heart/daemon/cli-parse.js +71 -0
  11. package/dist/heart/daemon/daemon-cli.js +1 -2
  12. package/dist/heart/daemon/daemon-entry.js +1 -3
  13. package/dist/heart/daemon/doctor.js +9 -29
  14. package/dist/heart/daemon/provider-discovery.js +32 -59
  15. package/dist/heart/hatch/hatch-flow.js +9 -12
  16. package/dist/heart/hatch/specialist-prompt.js +1 -1
  17. package/dist/heart/hatch/specialist-tools.js +21 -1
  18. package/dist/heart/migrate-config.js +15 -42
  19. package/dist/heart/provider-binding-resolver.js +6 -7
  20. package/dist/heart/provider-credentials.js +379 -0
  21. package/dist/heart/provider-failover.js +3 -11
  22. package/dist/heart/provider-ping.js +13 -3
  23. package/dist/heart/provider-state.js +8 -0
  24. package/dist/heart/provider-visibility.js +3 -6
  25. package/dist/heart/providers/anthropic-token.js +15 -47
  26. package/dist/heart/providers/anthropic.js +4 -9
  27. package/dist/heart/providers/azure.js +3 -3
  28. package/dist/heart/providers/github-copilot.js +2 -2
  29. package/dist/heart/providers/minimax-vlm.js +2 -2
  30. package/dist/heart/providers/minimax.js +1 -1
  31. package/dist/heart/providers/openai-codex.js +4 -9
  32. package/dist/heart/versioning/ouro-path-installer.js +10 -5
  33. package/dist/mind/prompt.js +1 -1
  34. package/dist/repertoire/bitwarden-store.js +63 -17
  35. package/dist/repertoire/bundle-templates.js +2 -2
  36. package/dist/repertoire/credential-access.js +47 -467
  37. package/dist/repertoire/tools-attachments.js +5 -4
  38. package/dist/repertoire/tools-vault.js +10 -80
  39. package/dist/repertoire/vault-unlock.js +359 -0
  40. package/dist/senses/bluebubbles/client.js +39 -4
  41. package/dist/senses/pipeline.js +0 -1
  42. package/package.json +1 -1
  43. package/skills/configure-dev-tools.md +10 -0
  44. 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 provider_credential_pool_1 = require("./provider-credential-pool");
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 from this machine via ${candidate.source}`;
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, provider_credential_pool_1.readProviderCredentialPool)(options.homeDir);
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 { secrets } = (0, auth_flow_1.loadAgentSecrets)(agentName);
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 config = secrets.providers[provider];
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
- const source = credential.source ?? "unknown";
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 "invalid pool";
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 identity_1 = require("../identity");
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 secrets.json.
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
- 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");
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
- ` 2. Open ${getAnthropicSecretsPathForGuidance()}`,
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 = (0, config_1.getAnthropicConfig)()) {
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 in agent.json but providers.anthropic.setupToken is missing in secrets.json."));
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. Set providers.azure.apiKey in secrets.json, or\n" +
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 set providers.azure.managedIdentityClientId in secrets.json (for deployed environments)");
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 in agent.json but providers.azure is incomplete in secrets.json.");
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 in agent.json but providers.github-copilot.githubToken is missing in secrets.json.");
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 in agent.json but providers.github-copilot.baseUrl is missing in secrets.json.");
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);