@ouro.bot/cli 0.1.0-alpha.349 → 0.1.0-alpha.350

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.350",
6
+ "changes": [
7
+ "`ouro up`, hatch, and doctor now use the same local provider-state and machine-credential readiness path: new agents get bootstrapped `state/providers.json`, startup checks ping the selected lane/provider/model with machine-wide credentials, readiness is persisted per lane, legacy per-agent secrets migrate into `~/.agentsecrets/providers.json`, and doctor checks provider-pool/state safety without exposing secrets."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.349",
6
12
  "changes": [
@@ -39,9 +39,19 @@ const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const identity_1 = require("../identity");
41
41
  const runtime_1 = require("../../nerves/runtime");
42
+ const provider_models_1 = require("../provider-models");
43
+ const machine_identity_1 = require("../machine-identity");
44
+ const provider_state_1 = require("../provider-state");
45
+ const provider_credential_pool_1 = require("../provider-credential-pool");
42
46
  function isAgentProvider(value) {
43
47
  return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
44
48
  }
49
+ function agentRootFor(agentName, bundlesRoot) {
50
+ return path.join(bundlesRoot, `${agentName}.ouro`);
51
+ }
52
+ function configPathFor(agentName, bundlesRoot) {
53
+ return path.join(agentRootFor(agentName, bundlesRoot), "agent.json");
54
+ }
45
55
  function formatFacingList(facings) {
46
56
  if (facings.length === 1)
47
57
  return facings[0];
@@ -199,6 +209,119 @@ function readConfigCheckContext(agentName, bundlesRoot, secretsRoot) {
199
209
  },
200
210
  };
201
211
  }
212
+ function readAgentConfigForProviderState(agentName, bundlesRoot) {
213
+ const agentJsonPath = configPathFor(agentName, bundlesRoot);
214
+ let raw;
215
+ try {
216
+ raw = fs.readFileSync(agentJsonPath, "utf-8");
217
+ }
218
+ catch {
219
+ return {
220
+ ok: false,
221
+ result: {
222
+ ok: false,
223
+ error: `agent.json not found at ${agentJsonPath}`,
224
+ fix: `Run 'ouro hatch ${agentName}' to create the agent bundle, or verify that ${bundlesRoot}/${agentName}.ouro/ exists.`,
225
+ },
226
+ };
227
+ }
228
+ let parsed;
229
+ try {
230
+ parsed = JSON.parse(raw);
231
+ }
232
+ catch {
233
+ return {
234
+ ok: false,
235
+ result: {
236
+ ok: false,
237
+ error: `agent.json at ${agentJsonPath} contains invalid JSON`,
238
+ fix: `Open ${agentJsonPath} and fix the JSON syntax.`,
239
+ },
240
+ };
241
+ }
242
+ if (parsed.enabled === false) {
243
+ return { ok: true, disabled: true, agentJsonPath, parsed };
244
+ }
245
+ return { ok: true, disabled: false, agentJsonPath, parsed };
246
+ }
247
+ function readFacingForBootstrap(parsed, facing, agentName, agentJsonPath) {
248
+ const providerResult = resolveFacingProvider(parsed, facing, agentName, agentJsonPath);
249
+ if (!providerResult.ok) {
250
+ return {
251
+ ok: false,
252
+ result: {
253
+ ok: false,
254
+ error: providerResult.result.error,
255
+ fix: `Run 'ouro use --agent ${agentName} --lane ${facing === "humanFacing" ? "outward" : "inner"} --provider <provider> --model <model>' to configure this machine's provider binding.`,
256
+ },
257
+ };
258
+ }
259
+ const raw = parsed[facing];
260
+ const model = typeof raw.model === "string" && raw.model.trim().length > 0
261
+ ? raw.model.trim()
262
+ : (0, provider_models_1.getDefaultModelForProvider)(providerResult.selected.provider);
263
+ return { ok: true, provider: providerResult.selected.provider, model };
264
+ }
265
+ function bootstrapMissingProviderState(input) {
266
+ const outward = readFacingForBootstrap(input.parsed, "humanFacing", input.agentName, input.agentJsonPath);
267
+ if (!outward.ok)
268
+ return { ok: false, error: outward.result.error, fix: outward.result.fix };
269
+ const inner = readFacingForBootstrap(input.parsed, "agentFacing", input.agentName, input.agentJsonPath);
270
+ if (!inner.ok)
271
+ return { ok: false, error: inner.result.error, fix: inner.result.fix };
272
+ const now = new Date();
273
+ const homeDir = (0, provider_credential_pool_1.providerCredentialHomeDirFromSecretsRoot)(input.secretsRoot);
274
+ const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)({ homeDir, now: () => now });
275
+ const state = (0, provider_state_1.bootstrapProviderStateFromAgentConfig)({
276
+ machineId: machine.machineId,
277
+ now,
278
+ agentConfig: {
279
+ humanFacing: { provider: outward.provider, model: outward.model },
280
+ agentFacing: { provider: inner.provider, model: inner.model },
281
+ },
282
+ });
283
+ const agentRoot = agentRootFor(input.agentName, input.bundlesRoot);
284
+ (0, provider_state_1.writeProviderState)(agentRoot, state);
285
+ (0, runtime_1.emitNervesEvent)({
286
+ component: "daemon",
287
+ event: "daemon.provider_state_bootstrapped",
288
+ message: "bootstrapped local provider state from agent config",
289
+ meta: { agent: input.agentName, agentRoot },
290
+ });
291
+ return { ok: true, agentRoot, state };
292
+ }
293
+ function readOrBootstrapProviderStateForCheck(agentName, bundlesRoot, secretsRoot) {
294
+ const configResult = readAgentConfigForProviderState(agentName, bundlesRoot);
295
+ if (!configResult.ok)
296
+ return { ok: false, result: configResult.result };
297
+ if (configResult.disabled)
298
+ return { ok: true, disabled: true };
299
+ const agentRoot = agentRootFor(agentName, bundlesRoot);
300
+ const stateResult = (0, provider_state_1.readProviderState)(agentRoot);
301
+ if (stateResult.ok) {
302
+ return { ok: true, disabled: false, agentRoot, state: stateResult.state };
303
+ }
304
+ if (stateResult.reason === "invalid") {
305
+ return {
306
+ ok: false,
307
+ result: {
308
+ ok: false,
309
+ error: `provider state for ${agentName} is invalid at ${stateResult.statePath}: ${stateResult.error}`,
310
+ fix: `Run 'ouro use --agent ${agentName} --lane outward --provider <provider> --model <model> --force' to rewrite this machine's provider binding.`,
311
+ },
312
+ };
313
+ }
314
+ const bootstrap = bootstrapMissingProviderState({
315
+ agentName,
316
+ bundlesRoot,
317
+ secretsRoot,
318
+ parsed: configResult.parsed,
319
+ agentJsonPath: configResult.agentJsonPath,
320
+ });
321
+ if (!bootstrap.ok)
322
+ return { ok: false, result: bootstrap };
323
+ return { ok: true, disabled: false, agentRoot: bootstrap.agentRoot, state: bootstrap.state };
324
+ }
202
325
  function validateSelectedProviderSecrets(agentName, context) {
203
326
  for (const [provider, facings] of selectedProviderMap(context.selectedProviders)) {
204
327
  const desc = identity_1.PROVIDER_CREDENTIALS[provider];
@@ -246,16 +369,101 @@ function emitConfigValid(agentName, context, liveProviderCheck) {
246
369
  },
247
370
  });
248
371
  }
249
- function providerPingFailureResult(agentName, provider, facings, result) {
250
- const selectedBy = formatFacingList(facings);
251
- const authFix = `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials.`;
252
- const verifyFix = `Run 'ouro auth verify --agent ${agentName} --provider ${provider}' for details.`;
372
+ function providerCredentialConfig(record) {
373
+ return {
374
+ ...record.credentials,
375
+ ...record.config,
376
+ };
377
+ }
378
+ function pingAttemptCount(result) {
379
+ if (Array.isArray(result.attempts))
380
+ return result.attempts.length;
381
+ return undefined;
382
+ }
383
+ function writeLaneReadiness(input) {
384
+ const binding = input.state.lanes[input.lane];
385
+ const checkedAt = new Date().toISOString();
386
+ input.state.updatedAt = checkedAt;
387
+ input.state.readiness[input.lane] = {
388
+ status: input.status,
389
+ provider: binding.provider,
390
+ model: binding.model,
391
+ checkedAt,
392
+ credentialRevision: input.credentialRevision,
393
+ ...(input.error ? { error: input.error } : {}),
394
+ ...(input.attempts !== undefined ? { attempts: input.attempts } : {}),
395
+ };
396
+ (0, provider_state_1.writeProviderState)(input.agentRoot, input.state);
397
+ }
398
+ function legacyProviderCredentialCandidates(agentName, secretsRoot) {
399
+ const secretsPath = path.join(secretsRoot, agentName, "secrets.json");
400
+ let parsed;
401
+ try {
402
+ parsed = JSON.parse(fs.readFileSync(secretsPath, "utf-8"));
403
+ }
404
+ catch {
405
+ return [];
406
+ }
407
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
408
+ return [];
409
+ const providers = parsed.providers;
410
+ if (!providers || typeof providers !== "object" || Array.isArray(providers))
411
+ return [];
412
+ const candidates = [];
413
+ for (const [providerKey, rawConfig] of Object.entries(providers)) {
414
+ if (!isAgentProvider(providerKey) || !rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
415
+ continue;
416
+ }
417
+ const split = (0, provider_credential_pool_1.splitProviderCredentialFields)(providerKey, rawConfig);
418
+ if (Object.keys(split.credentials).length === 0 && Object.keys(split.config).length === 0)
419
+ continue;
420
+ candidates.push({ provider: providerKey, credentials: split.credentials, config: split.config });
421
+ }
422
+ return candidates;
423
+ }
424
+ function readPoolWithLegacyMigration(agentName, homeDir, secretsRoot) {
425
+ const initial = (0, provider_credential_pool_1.readProviderCredentialPool)(homeDir);
426
+ if (initial.ok || initial.reason === "invalid")
427
+ return initial;
428
+ const candidates = legacyProviderCredentialCandidates(agentName, secretsRoot);
429
+ for (const candidate of candidates) {
430
+ (0, provider_credential_pool_1.upsertProviderCredential)({
431
+ homeDir,
432
+ provider: candidate.provider,
433
+ credentials: candidate.credentials,
434
+ config: candidate.config,
435
+ provenance: {
436
+ source: "legacy-agent-secrets",
437
+ contributedByAgent: agentName,
438
+ },
439
+ });
440
+ }
441
+ return candidates.length > 0 ? (0, provider_credential_pool_1.readProviderCredentialPool)(homeDir) : initial;
442
+ }
443
+ function missingCredentialResult(agentName, lane, provider, model, poolPath) {
444
+ return {
445
+ ok: false,
446
+ error: `${lane} provider ${provider} model ${model} has no credentials in the machine provider pool at ${poolPath}`,
447
+ fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to authenticate this machine, or run 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>' to choose a working provider/model.`,
448
+ };
449
+ }
450
+ function invalidPoolResult(agentName, lane, provider, model, pool) {
451
+ return {
452
+ ok: false,
453
+ error: `${lane} provider ${provider} model ${model} cannot read machine provider credentials at ${pool.poolPath}: ${pool.error}`,
454
+ fix: `Fix ${pool.poolPath}, then run 'ouro auth --agent ${agentName} --provider ${provider}' or 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model> --force'.`,
455
+ };
456
+ }
457
+ function failedPingResult(agentName, lane, provider, model, result) {
253
458
  return {
254
459
  ok: false,
255
- error: `selected provider ${provider} for ${selectedBy} failed health check: ${result.message}`,
256
- fix: `${result.classification === "auth-failure" ? authFix : verifyFix} Or switch the affected facing to a working provider.`,
460
+ error: `${lane} provider ${provider} model ${model} failed live check: ${result.message}`,
461
+ fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials, or run 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>' to switch this lane.`,
257
462
  };
258
463
  }
464
+ function credentialRecordForLane(pool, provider) {
465
+ return pool.providers[provider];
466
+ }
259
467
  /**
260
468
  * Pre-spawn validation: ensures agent.json exists and required secrets are present.
261
469
  * Returns `{ ok: true }` when the agent is ready to run, or a descriptive error
@@ -273,20 +481,81 @@ function checkAgentConfig(agentName, bundlesRoot, secretsRoot) {
273
481
  return { ok: true };
274
482
  }
275
483
  async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, secretsRoot, deps = {}) {
276
- const contextResult = readConfigCheckContext(agentName, bundlesRoot, secretsRoot);
277
- if (!contextResult.ok)
278
- return contextResult.result;
279
- const context = contextResult.context;
280
- const structural = validateSelectedProviderSecrets(agentName, context);
281
- if (!structural.ok)
282
- return structural;
484
+ const stateResult = readOrBootstrapProviderStateForCheck(agentName, bundlesRoot, secretsRoot);
485
+ if (!stateResult.ok)
486
+ return stateResult.result;
487
+ if (stateResult.disabled)
488
+ return { ok: true };
283
489
  const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
284
- for (const [provider, facings] of selectedProviderMap(context.selectedProviders)) {
285
- const result = await ping(provider, context.providers[provider]);
490
+ const homeDir = (0, provider_credential_pool_1.providerCredentialHomeDirFromSecretsRoot)(secretsRoot);
491
+ const poolResult = readPoolWithLegacyMigration(agentName, homeDir, secretsRoot);
492
+ const pingGroups = new Map();
493
+ const lanes = ["outward", "inner"];
494
+ for (const lane of lanes) {
495
+ const binding = stateResult.state.lanes[lane];
496
+ if (!poolResult.ok) {
497
+ if (poolResult.reason === "missing") {
498
+ return missingCredentialResult(agentName, lane, binding.provider, binding.model, poolResult.poolPath);
499
+ }
500
+ return invalidPoolResult(agentName, lane, binding.provider, binding.model, {
501
+ ...poolResult,
502
+ reason: "invalid",
503
+ });
504
+ }
505
+ const record = credentialRecordForLane(poolResult.pool, binding.provider);
506
+ if (!record) {
507
+ return missingCredentialResult(agentName, lane, binding.provider, binding.model, poolResult.poolPath);
508
+ }
509
+ const key = `${binding.provider}\0${binding.model}\0${record.revision}`;
510
+ const group = pingGroups.get(key);
511
+ if (group) {
512
+ group.lanes.push(lane);
513
+ }
514
+ else {
515
+ pingGroups.set(key, {
516
+ provider: binding.provider,
517
+ model: binding.model,
518
+ record,
519
+ lanes: [lane],
520
+ });
521
+ }
522
+ }
523
+ for (const group of pingGroups.values()) {
524
+ const result = await ping(group.provider, providerCredentialConfig(group.record), { model: group.model });
286
525
  if (!result.ok) {
287
- return providerPingFailureResult(agentName, provider, facings, result);
526
+ for (const lane of group.lanes) {
527
+ writeLaneReadiness({
528
+ agentRoot: stateResult.agentRoot,
529
+ state: stateResult.state,
530
+ lane,
531
+ status: "failed",
532
+ credentialRevision: group.record.revision,
533
+ error: result.message,
534
+ attempts: pingAttemptCount(result),
535
+ });
536
+ }
537
+ return failedPingResult(agentName, group.lanes[0], group.provider, group.model, result);
538
+ }
539
+ for (const lane of group.lanes) {
540
+ writeLaneReadiness({
541
+ agentRoot: stateResult.agentRoot,
542
+ state: stateResult.state,
543
+ lane,
544
+ status: "ready",
545
+ credentialRevision: group.record.revision,
546
+ attempts: pingAttemptCount(result),
547
+ });
288
548
  }
289
549
  }
290
- emitConfigValid(agentName, context, true);
550
+ (0, runtime_1.emitNervesEvent)({
551
+ component: "daemon",
552
+ event: "daemon.agent_config_valid",
553
+ message: "agent config validation passed",
554
+ meta: {
555
+ agent: agentName,
556
+ providers: [...new Set([...pingGroups.values()].map((group) => group.provider))],
557
+ liveProviderCheck: true,
558
+ },
559
+ });
291
560
  return { ok: true };
292
561
  }
@@ -461,11 +461,7 @@ async function resolveHatchInput(command, deps) {
461
461
  }
462
462
  // ── Provider state CLI helpers ──
463
463
  function providerCliHomeDir(deps) {
464
- if (!deps.secretsRoot)
465
- return os.homedir();
466
- return path.basename(deps.secretsRoot) === ".agentsecrets"
467
- ? path.dirname(deps.secretsRoot)
468
- : deps.secretsRoot;
464
+ return (0, provider_credential_pool_1.providerCredentialHomeDirFromSecretsRoot)(deps.secretsRoot);
469
465
  }
470
466
  function providerCliAgentRoot(command, deps) {
471
467
  return path.join(deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(), `${command.agent}.ouro`);
@@ -95,6 +95,19 @@ function readJsonObject(deps, filePath) {
95
95
  return null;
96
96
  }
97
97
  }
98
+ const SENSITIVE_CONFIG_KEYS = ["apiKey", "token", "secret", "password"];
99
+ function credentialKeyLeaks(raw) {
100
+ return SENSITIVE_CONFIG_KEYS.filter((key) => raw.includes(`"${key}"`));
101
+ }
102
+ function checkCredentialLeak(checks, label, raw) {
103
+ const found = credentialKeyLeaks(raw);
104
+ if (found.length > 0) {
105
+ checks.push({ label, status: "warn", detail: `contains credential-looking keys: ${found.join(", ")}` });
106
+ }
107
+ else {
108
+ checks.push({ label, status: "pass", detail: "no credential keys" });
109
+ }
110
+ }
98
111
  function checkAgents(deps) {
99
112
  const checks = [];
100
113
  if (!deps.existsSync(deps.bundlesRoot)) {
@@ -287,6 +300,23 @@ function checkHabits(deps) {
287
300
  function checkSecurity(deps) {
288
301
  const checks = [];
289
302
  const agents = discoverAgents(deps);
303
+ const providerPoolPath = `${deps.secretsRoot}/providers.json`;
304
+ if (deps.existsSync(providerPoolPath)) {
305
+ const stat = deps.statSync(providerPoolPath);
306
+ const worldReadable = (stat.mode & 0o004) !== 0;
307
+ checks.push({
308
+ label: "machine provider credentials perms",
309
+ status: worldReadable ? "warn" : "pass",
310
+ detail: worldReadable ? "world-readable — consider chmod 600" : "not world-readable",
311
+ });
312
+ try {
313
+ JSON.parse(deps.readFileSync(providerPoolPath));
314
+ checks.push({ label: "machine provider credentials", status: "pass", detail: "readable JSON" });
315
+ }
316
+ catch {
317
+ checks.push({ label: "machine provider credentials", status: "fail", detail: "unparseable JSON" });
318
+ }
319
+ }
290
320
  for (const agentDir of agents) {
291
321
  const agentName = agentDir.replace(/\.ouro$/, "");
292
322
  const secretsPath = `${deps.secretsRoot}/${agentName}/secrets.json`;
@@ -308,8 +338,7 @@ function checkSecurity(deps) {
308
338
  if (deps.existsSync(configPath)) {
309
339
  try {
310
340
  const raw = deps.readFileSync(configPath);
311
- const sensitiveKeys = ["apiKey", "token", "secret", "password"];
312
- const found = sensitiveKeys.filter((key) => raw.includes(`"${key}"`));
341
+ const found = credentialKeyLeaks(raw);
313
342
  if (found.length > 0) {
314
343
  checks.push({ label: `${agentDir} credential leak`, status: "warn", detail: `agent.json contains keys: ${found.join(", ")}` });
315
344
  }
@@ -321,6 +350,15 @@ function checkSecurity(deps) {
321
350
  checks.push({ label: `${agentDir} credential leak`, status: "fail", detail: "could not read agent.json" });
322
351
  }
323
352
  }
353
+ const providerStatePath = `${deps.bundlesRoot}/${agentDir}/state/providers.json`;
354
+ if (deps.existsSync(providerStatePath)) {
355
+ try {
356
+ checkCredentialLeak(checks, `${agentDir} state/providers.json credential leak`, deps.readFileSync(providerStatePath));
357
+ }
358
+ catch {
359
+ checks.push({ label: `${agentDir} state/providers.json credential leak`, status: "fail", detail: "could not read state/providers.json" });
360
+ }
361
+ }
324
362
  }
325
363
  if (checks.length === 0) {
326
364
  checks.push({ label: "security", status: "warn", detail: "no agents found" });
@@ -44,6 +44,9 @@ const runtime_1 = require("../../nerves/runtime");
44
44
  const auth_flow_1 = require("../auth/auth-flow");
45
45
  const provider_models_1 = require("../provider-models");
46
46
  const habit_parser_1 = require("../habits/habit-parser");
47
+ const machine_identity_1 = require("../machine-identity");
48
+ const provider_credential_pool_1 = require("../provider-credential-pool");
49
+ const provider_state_1 = require("../provider-state");
47
50
  const hatch_specialist_1 = require("./hatch-specialist");
48
51
  function requiredCredentialKeys(provider) {
49
52
  return identity_1.PROVIDER_CREDENTIALS[provider].required;
@@ -132,6 +135,22 @@ function writeHatchlingAgentConfig(bundleRoot, input) {
132
135
  template.enabled = true;
133
136
  fs.writeFileSync(path.join(bundleRoot, "agent.json"), `${JSON.stringify(template, null, 2)}\n`, "utf-8");
134
137
  }
138
+ function writeHatchlingProviderState(bundleRoot, input, secretsRoot, now) {
139
+ const model = (0, provider_models_1.getDefaultModelForProvider)(input.provider);
140
+ const machine = (0, machine_identity_1.loadOrCreateMachineIdentity)({
141
+ homeDir: (0, provider_credential_pool_1.providerCredentialHomeDirFromSecretsRoot)(secretsRoot),
142
+ now: () => now,
143
+ });
144
+ const state = (0, provider_state_1.bootstrapProviderStateFromAgentConfig)({
145
+ machineId: machine.machineId,
146
+ now,
147
+ agentConfig: {
148
+ humanFacing: { provider: input.provider, model },
149
+ agentFacing: { provider: input.provider, model },
150
+ },
151
+ });
152
+ (0, provider_state_1.writeProviderState)(bundleRoot, state);
153
+ }
135
154
  async function runHatchFlow(input, deps = {}) {
136
155
  (0, runtime_1.emitNervesEvent)({
137
156
  component: "daemon",
@@ -170,6 +189,7 @@ async function runHatchFlow(input, deps = {}) {
170
189
  writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
171
190
  writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
172
191
  writeHatchlingAgentConfig(bundleRoot, input);
192
+ writeHatchlingProviderState(bundleRoot, input, secretsRoot, now);
173
193
  writeDiaryScaffold(bundleRoot);
174
194
  writeFriendImprint(bundleRoot, input.humanName, now);
175
195
  writeHeartbeatHabit(bundleRoot, now);
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.splitProviderCredentialFields = splitProviderCredentialFields;
37
37
  exports.getProviderCredentialPoolPath = getProviderCredentialPoolPath;
38
+ exports.providerCredentialHomeDirFromSecretsRoot = providerCredentialHomeDirFromSecretsRoot;
38
39
  exports.validateProviderCredentialPool = validateProviderCredentialPool;
39
40
  exports.readProviderCredentialPool = readProviderCredentialPool;
40
41
  exports.writeProviderCredentialPool = writeProviderCredentialPool;
@@ -130,6 +131,13 @@ function makeRevision() {
130
131
  function getProviderCredentialPoolPath(homeDir = os.homedir()) {
131
132
  return path.join(homeDir, ".agentsecrets", "providers.json");
132
133
  }
134
+ function providerCredentialHomeDirFromSecretsRoot(secretsRoot) {
135
+ if (!secretsRoot)
136
+ return os.homedir();
137
+ return path.basename(secretsRoot) === ".agentsecrets"
138
+ ? path.dirname(secretsRoot)
139
+ : secretsRoot;
140
+ }
133
141
  function validateProviderCredentialPool(value) {
134
142
  if (!isRecord(value)) {
135
143
  throw new Error("provider credential pool must be an object");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.349",
3
+ "version": "0.1.0-alpha.350",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",