@ouro.bot/cli 0.1.0-alpha.372 → 0.1.0-alpha.374

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,22 @@
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.374",
6
+ "changes": [
7
+ "SerpentGuide bootstrap now stays in the credential picker when a selected discovered credential fails ping, letting the human choose another saved source or enter a new key without restarting `ouro hatch`.",
8
+ "The stale discovered-credential retry path is covered by a focused SerpentGuide bootstrap test that fails the first provider ping and succeeds after manual replacement credentials.",
9
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the SerpentGuide credential retry release."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.373",
14
+ "changes": [
15
+ "SerpentGuide bootstrap now discovers provider credentials from unlockable installed agent vaults, so a new hatch can reuse sources such as `minimax from slugger's vault` without creating persistent SerpentGuide credentials.",
16
+ "SerpentGuide credential options now preserve provenance for installed-agent vault sources and env sources while keeping secret values out of terminal labels.",
17
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the SerpentGuide installed-agent credential discovery release."
18
+ ]
19
+ },
4
20
  {
5
21
  "version": "0.1.0-alpha.372",
6
22
  "changes": [
@@ -244,13 +244,23 @@ async function defaultRunSerpentGuide() {
244
244
  let providerRaw;
245
245
  let credentials = {};
246
246
  let providerConfig = {};
247
+ let selectedCredentialPayload = {};
247
248
  const tempDir = path.join(os.tmpdir(), `ouro-hatch-${crypto.randomUUID()}`);
248
249
  try {
249
250
  const discovered = [];
250
- const existingBundleCount = (0, specialist_orchestrator_1.listExistingBundles)((0, identity_1.getAgentBundlesRoot)()).length;
251
+ const existingBundles = (0, specialist_orchestrator_1.listExistingBundles)((0, identity_1.getAgentBundlesRoot)());
252
+ const existingBundleCount = existingBundles.length;
251
253
  const hatchVerb = existingBundleCount > 0 ? "let's hatch a new agent." : "let's hatch your first agent.";
252
254
  // Default models per provider (used when entering new credentials)
253
255
  const defaultModels = provider_models_1.DEFAULT_PROVIDER_MODELS;
256
+ const { pingProvider } = await Promise.resolve().then(() => __importStar(require("../provider-ping")));
257
+ const installedAgentCreds = await (0, provider_discovery_1.discoverInstalledAgentCredentials)(existingBundles);
258
+ for (const cred of installedAgentCreds) {
259
+ discovered.push({
260
+ ...cred,
261
+ providerConfig: { model: defaultModels[cred.provider], ...cred.providerConfig },
262
+ });
263
+ }
254
264
  // Scan environment variables for API keys using the shared helper
255
265
  const envCreds = (0, provider_discovery_1.scanEnvVarCredentials)(process.env);
256
266
  const envDiscovered = [];
@@ -265,26 +275,48 @@ async function defaultRunSerpentGuide() {
265
275
  envDiscovered.push({ ...enriched, envVar: firstEnvVar });
266
276
  discovered.push(enriched);
267
277
  }
268
- if (discovered.length > 0) {
269
- process.stdout.write(`\n\ud83d\udc0d welcome to ouroboros! ${hatchVerb}\n`);
270
- process.stdout.write("i found existing API credentials:\n\n");
271
- const unique = [...new Map(discovered.map((d) => [`${d.provider}`, d])).values()];
272
- for (let i = 0; i < unique.length; i++) {
273
- const model = unique[i].providerConfig.model || unique[i].providerConfig.deployment || "";
274
- const modelLabel = model ? `, ${model}` : "";
275
- const envMatch = envDiscovered.find((e) => e.provider === unique[i].provider && unique[i].agentName === "env");
276
- const sourceLabel = envMatch ? `from env: $${envMatch.envVar}` : `from ${unique[i].agentName}`;
277
- process.stdout.write(` ${i + 1}. ${unique[i].provider}${modelLabel} (${sourceLabel})\n`);
278
+ let welcomed = false;
279
+ while (true) {
280
+ if (!welcomed) {
281
+ process.stdout.write(`\n\ud83d\udc0d welcome to ouroboros! ${hatchVerb}\n`);
282
+ welcomed = true;
278
283
  }
279
- process.stdout.write("\n");
280
- const choice = await coldPrompt("use one of these? enter number, or 'new' for a different key: ");
281
- const idx = parseInt(choice, 10) - 1;
282
- if (idx >= 0 && idx < unique.length) {
283
- providerRaw = unique[idx].provider;
284
- credentials = unique[idx].credentials;
285
- providerConfig = unique[idx].providerConfig;
284
+ if (discovered.length > 0) {
285
+ process.stdout.write("i found existing API credentials:\n\n");
286
+ const credentialOptions = discovered;
287
+ for (let i = 0; i < credentialOptions.length; i++) {
288
+ const model = credentialOptions[i].providerConfig.model || credentialOptions[i].providerConfig.deployment || "";
289
+ const modelLabel = model ? `, ${model}` : "";
290
+ const envMatch = envDiscovered.find((e) => e.provider === credentialOptions[i].provider && credentialOptions[i].agentName === "env");
291
+ const sourceLabel = (0, provider_discovery_1.describeDiscoveredCredentialSource)(credentialOptions[i], envMatch?.envVar);
292
+ process.stdout.write(` ${i + 1}. ${credentialOptions[i].provider}${modelLabel} (${sourceLabel})\n`);
293
+ }
294
+ process.stdout.write("\n");
295
+ const choice = await coldPrompt("use one of these? enter number, or 'new' for a different key, or 'q' to cancel: ");
296
+ if (["q", "quit", "cancel"].includes(choice.toLowerCase())) {
297
+ coldRl.close();
298
+ return null;
299
+ }
300
+ const idx = parseInt(choice, 10) - 1;
301
+ if (idx >= 0 && idx < credentialOptions.length) {
302
+ providerRaw = credentialOptions[idx].provider;
303
+ credentials = credentialOptions[idx].credentials;
304
+ providerConfig = credentialOptions[idx].providerConfig;
305
+ }
306
+ else {
307
+ const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex/github-copilot): ");
308
+ if (!(0, cli_parse_1.isAgentProvider)(pRaw)) {
309
+ process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
310
+ coldRl.close();
311
+ return null;
312
+ }
313
+ providerRaw = pRaw;
314
+ providerConfig = { model: defaultModels[providerRaw] };
315
+ credentials = await (0, auth_flow_1.collectRuntimeAuthCredentials)({ agentName: "SerpentGuide", provider: providerRaw, promptInput: coldPrompt }, {});
316
+ }
286
317
  }
287
318
  else {
319
+ process.stdout.write("i need an API key to power our conversation.\n\n");
288
320
  const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex/github-copilot): ");
289
321
  if (!(0, cli_parse_1.isAgentProvider)(pRaw)) {
290
322
  process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
@@ -295,23 +327,21 @@ async function defaultRunSerpentGuide() {
295
327
  providerConfig = { model: defaultModels[providerRaw] };
296
328
  credentials = await (0, auth_flow_1.collectRuntimeAuthCredentials)({ agentName: "SerpentGuide", provider: providerRaw, promptInput: coldPrompt }, {});
297
329
  }
298
- }
299
- else {
300
- process.stdout.write(`\n\ud83d\udc0d welcome to ouroboros! ${hatchVerb}\n`);
301
- process.stdout.write("i need an API key to power our conversation.\n\n");
302
- const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex/github-copilot): ");
303
- if (!(0, cli_parse_1.isAgentProvider)(pRaw)) {
304
- process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
305
- coldRl.close();
306
- return null;
330
+ selectedCredentialPayload = { ...providerConfig, ...credentials };
331
+ const pingResult = await pingProvider(providerRaw, selectedCredentialPayload);
332
+ if (pingResult.ok) {
333
+ break;
334
+ }
335
+ process.stdout.write(`credentials didn't work (${pingResult.message}). `);
336
+ if (discovered.length > 0) {
337
+ process.stdout.write("choose another saved credential or enter 'new'.\n\n");
338
+ }
339
+ else {
340
+ process.stdout.write("let's try again.\n\n");
307
341
  }
308
- providerRaw = pRaw;
309
- providerConfig = { model: defaultModels[providerRaw] };
310
- credentials = await (0, auth_flow_1.collectRuntimeAuthCredentials)({ agentName: "SerpentGuide", provider: providerRaw, promptInput: coldPrompt }, {});
311
342
  }
312
343
  coldRl.close();
313
344
  process.stdout.write("\n");
314
- const selectedCredentialPayload = { ...providerConfig, ...credentials };
315
345
  const split = (0, provider_credentials_1.splitProviderCredentialFields)(providerRaw, selectedCredentialPayload);
316
346
  (0, provider_credentials_1.cacheProviderCredentialRecords)("SerpentGuide", [
317
347
  (0, provider_credentials_1.createProviderCredentialRecord)({
@@ -347,14 +377,6 @@ async function defaultRunSerpentGuide() {
347
377
  agentFacing: { provider: providerRaw, model: resolvedModel },
348
378
  phrases,
349
379
  });
350
- // Ping-verify credentials before entering the serpent guide session
351
- const { pingProvider } = await Promise.resolve().then(() => __importStar(require("../provider-ping")));
352
- const pingResult = await pingProvider(providerRaw, selectedCredentialPayload);
353
- if (!pingResult.ok) {
354
- process.stdout.write(`credentials didn't work (${pingResult.message}). run 'ouro hatch' to try again.\n`);
355
- return null;
356
- }
357
- const existingBundles = (0, specialist_orchestrator_1.listExistingBundles)(bundlesRoot);
358
380
  const systemPrompt = (0, specialist_prompt_1.buildSpecialistSystemPrompt)(soulText, identity.content, existingBundles, {
359
381
  tempDir,
360
382
  provider: providerRaw,
@@ -8,6 +8,8 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.scanEnvVarCredentials = scanEnvVarCredentials;
11
+ exports.discoverInstalledAgentCredentials = discoverInstalledAgentCredentials;
12
+ exports.describeDiscoveredCredentialSource = describeDiscoveredCredentialSource;
11
13
  exports.discoverWorkingProvider = discoverWorkingProvider;
12
14
  const identity_1 = require("../identity");
13
15
  const provider_credentials_1 = require("../provider-credentials");
@@ -45,14 +47,36 @@ function stringifyProviderFields(fields) {
45
47
  }
46
48
  return result;
47
49
  }
48
- function discoveredFromVaultRecord(record) {
50
+ function discoveredFromVaultRecord(record, agentName = "vault") {
49
51
  return {
50
52
  provider: record.provider,
51
- agentName: "vault",
53
+ agentName,
52
54
  credentials: stringifyProviderFields(record.credentials),
53
55
  providerConfig: stringifyProviderFields(record.config),
54
56
  };
55
57
  }
58
+ async function discoverInstalledAgentCredentials(agentNames) {
59
+ const discovered = [];
60
+ for (const agentName of agentNames) {
61
+ if (agentName === "SerpentGuide")
62
+ continue;
63
+ const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName, { preserveCachedOnFailure: true });
64
+ if (!poolResult.ok)
65
+ continue;
66
+ for (const record of Object.values(poolResult.pool.providers)) {
67
+ if (!record)
68
+ continue;
69
+ discovered.push(discoveredFromVaultRecord(record, agentName));
70
+ }
71
+ }
72
+ return discovered;
73
+ }
74
+ function describeDiscoveredCredentialSource(credential, envVar) {
75
+ if (credential.agentName === "env") {
76
+ return envVar ? `from env: $${envVar}` : "from env";
77
+ }
78
+ return `from ${credential.agentName}'s vault`;
79
+ }
56
80
  async function discoverWorkingProvider(deps) {
57
81
  const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(deps.agentName);
58
82
  if (!poolResult.ok) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.372",
3
+ "version": "0.1.0-alpha.374",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",