@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.
Files changed (41) hide show
  1. package/README.md +15 -6
  2. package/changelog.json +9 -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 +246 -89
  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/repertoire/bitwarden-store.js +63 -17
  33. package/dist/repertoire/bundle-templates.js +2 -2
  34. package/dist/repertoire/credential-access.js +47 -467
  35. package/dist/repertoire/tools-attachments.js +5 -4
  36. package/dist/repertoire/tools-vault.js +10 -80
  37. package/dist/repertoire/vault-unlock.js +359 -0
  38. package/dist/senses/bluebubbles/client.js +39 -4
  39. package/dist/senses/pipeline.js +0 -1
  40. package/package.json +1 -1
  41. package/dist/heart/provider-credential-pool.js +0 -395
@@ -85,8 +85,8 @@ async function minimaxVlmDescribe(params) {
85
85
  if (!params.apiKey) {
86
86
  // We deliberately do NOT emit _start for param-validation errors — there's
87
87
  // no meaningful "started a request" to pair with. Only the _error fires.
88
- emitError(params, "minimax VLM: API key is empty — re-run credential setup or add a minimax key to secrets.json");
89
- throw new Error("minimax VLM: API key is empty — re-run credential setup or add a minimax key to secrets.json");
88
+ emitError(params, "minimax VLM: API key is empty — run `ouro auth --agent <agent> --provider minimax`");
89
+ throw new Error("minimax VLM: API key is empty — run `ouro auth --agent <agent> --provider minimax`");
90
90
  }
91
91
  if (!params.prompt) {
92
92
  emitError(params, "minimax VLM: missing prompt — supply a targeted question (e.g. 'what's the flight number in the bottom-right?') and retry");
@@ -29,7 +29,7 @@ function createMinimaxProviderRuntime(model, minimaxConfig = (0, config_1.getMin
29
29
  meta: { provider: "minimax" },
30
30
  });
31
31
  if (!minimaxConfig.apiKey) {
32
- throw new Error("provider 'minimax' is selected in agent.json but providers.minimax.apiKey is missing in secrets.json.");
32
+ throw new Error("provider 'minimax' is selected but minimax.apiKey is missing in the agent vault. Run `ouro auth --agent <agent> --provider minimax`.");
33
33
  }
34
34
  // Registry consulted; MiniMax models return empty defaults (no capabilities to derive)
35
35
  (0, model_capabilities_1.getModelCapabilities)(model);
@@ -20,9 +20,6 @@ const OPENAI_CODEX_AUTH_FAILURE_MARKERS = [
20
20
  "invalid bearer token",
21
21
  ];
22
22
  const OPENAI_CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
23
- function getOpenAICodexSecretsPathForGuidance() {
24
- return (0, identity_1.getAgentSecretsPath)();
25
- }
26
23
  function getOpenAICodexAgentNameForGuidance() {
27
24
  return (0, identity_1.getAgentName)();
28
25
  }
@@ -30,11 +27,9 @@ function getOpenAICodexOAuthInstructions() {
30
27
  const agentName = getOpenAICodexAgentNameForGuidance();
31
28
  return [
32
29
  "Fix:",
33
- ` 1. Run \`ouro auth --agent ${agentName}\``,
34
- ` 2. Open ${getOpenAICodexSecretsPathForGuidance()}`,
35
- " 3. Confirm providers.openai-codex.oauthAccessToken is set",
36
- " 4. This provider uses chatgpt.com/backend-api/codex/responses (not api.openai.com/responses).",
37
- " 5. After reauth, retry the failed ouro command or reconnect this session.",
30
+ ` 1. Run \`ouro auth --agent ${agentName} --provider openai-codex\``,
31
+ " 2. This provider uses chatgpt.com/backend-api/codex/responses (not api.openai.com/responses).",
32
+ " 3. After reauth, retry the failed ouro command or reconnect this session.",
38
33
  ].join("\n");
39
34
  }
40
35
  function getOpenAICodexReauthGuidance(reason) {
@@ -95,7 +90,7 @@ function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getO
95
90
  meta: { provider: "openai-codex" },
96
91
  });
97
92
  if (!codexConfig.oauthAccessToken) {
98
- throw new Error(getOpenAICodexReauthGuidance("provider 'openai-codex' is selected in agent.json but providers.openai-codex.oauthAccessToken is missing in secrets.json."));
93
+ throw new Error(getOpenAICodexReauthGuidance("provider 'openai-codex' is selected but openai-codex.oauthAccessToken is missing in the agent vault."));
99
94
  }
100
95
  const token = codexConfig.oauthAccessToken.trim();
101
96
  if (!token) {
@@ -2,24 +2,59 @@
2
2
  /**
3
3
  * Bitwarden CLI credential store — wraps `bw` CLI for the agent's own vault.
4
4
  *
5
- * Unlike AacCredentialStore (which accesses someone else's vault via approval),
6
- * this store authenticates directly as the agent using its own master password.
5
+ * This store authenticates directly as the agent using its own master password.
7
6
  * The agent owns the vault, so no human-in-the-loop is needed.
8
7
  *
9
8
  * Requires the `bw` CLI to be installed. Session tokens are cached process-local.
10
9
  */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
11
43
  Object.defineProperty(exports, "__esModule", { value: true });
12
44
  exports.BitwardenCredentialStore = void 0;
13
45
  const node_child_process_1 = require("node:child_process");
46
+ const fs = __importStar(require("node:fs"));
14
47
  const runtime_1 = require("../nerves/runtime");
15
48
  const bw_installer_1 = require("./bw-installer");
16
49
  // ---------------------------------------------------------------------------
17
50
  // bw CLI wrapper
18
51
  // ---------------------------------------------------------------------------
19
- function execBw(args, sessionToken) {
20
- const env = sessionToken
21
- ? { ...process.env, BW_SESSION: sessionToken }
22
- : process.env;
52
+ function execBw(args, sessionToken, appDataDir) {
53
+ const env = {
54
+ ...process.env,
55
+ ...(sessionToken ? { BW_SESSION: sessionToken } : {}),
56
+ ...(appDataDir ? { BITWARDENCLI_APPDATA_DIR: appDataDir } : {}),
57
+ };
23
58
  return new Promise((resolve, reject) => {
24
59
  (0, node_child_process_1.execFile)("bw", args, { timeout: 30_000, env }, (err, stdout) => {
25
60
  if (err) {
@@ -62,11 +97,13 @@ class BitwardenCredentialStore {
62
97
  serverUrl;
63
98
  email;
64
99
  masterPassword;
100
+ appDataDir;
65
101
  sessionToken = null;
66
- constructor(serverUrl, email, masterPassword) {
102
+ constructor(serverUrl, email, masterPassword, options = {}) {
67
103
  this.serverUrl = serverUrl;
68
104
  this.email = email;
69
105
  this.masterPassword = masterPassword;
106
+ this.appDataDir = options.appDataDir;
70
107
  }
71
108
  isReady() {
72
109
  return true;
@@ -79,6 +116,9 @@ class BitwardenCredentialStore {
79
116
  async login() {
80
117
  // Ensure bw CLI is installed before any bw commands
81
118
  await (0, bw_installer_1.ensureBwCli)();
119
+ if (this.appDataDir) {
120
+ fs.mkdirSync(this.appDataDir, { recursive: true, mode: 0o700 });
121
+ }
82
122
  let lastError;
83
123
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
84
124
  try {
@@ -112,7 +152,7 @@ class BitwardenCredentialStore {
112
152
  // Check current status
113
153
  let status = {};
114
154
  try {
115
- const raw = await execBw(["status"]);
155
+ const raw = await execBw(["status"], undefined, this.appDataDir);
116
156
  status = JSON.parse(raw);
117
157
  }
118
158
  catch (err) {
@@ -125,7 +165,7 @@ class BitwardenCredentialStore {
125
165
  // Configure server URL if needed (only works when logged out)
126
166
  if (status.status === "unauthenticated" || !status.serverUrl) {
127
167
  try {
128
- await execBw(["config", "server", this.serverUrl]);
168
+ await execBw(["config", "server", this.serverUrl], undefined, this.appDataDir);
129
169
  }
130
170
  catch {
131
171
  // "Logout required" means already logged in — that's fine, skip config
@@ -133,12 +173,12 @@ class BitwardenCredentialStore {
133
173
  }
134
174
  if (status.status === "locked") {
135
175
  // Already logged in, just needs unlock
136
- const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"]);
176
+ const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"], undefined, this.appDataDir);
137
177
  this.sessionToken = unlockOutput.trim();
138
178
  }
139
179
  else if (status.status === "unauthenticated" || !status.status) {
140
180
  // Not logged in — full login
141
- const loginOutput = await execBw(["login", this.email, this.masterPassword, "--raw"]);
181
+ const loginOutput = await execBw(["login", this.email, this.masterPassword, "--raw"], undefined, this.appDataDir);
142
182
  try {
143
183
  const parsed = JSON.parse(loginOutput);
144
184
  this.sessionToken = parsed.access_token ?? loginOutput.trim();
@@ -149,7 +189,7 @@ class BitwardenCredentialStore {
149
189
  }
150
190
  else {
151
191
  // Status is "unlocked" — already good, just need the session token
152
- const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"]);
192
+ const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"], undefined, this.appDataDir);
153
193
  this.sessionToken = unlockOutput.trim();
154
194
  }
155
195
  }
@@ -221,8 +261,9 @@ class BitwardenCredentialStore {
221
261
  meta: { domain, backend: "bitwarden" },
222
262
  });
223
263
  const session = await this.ensureSession();
224
- // Create a new login item
264
+ const existing = await this.findItemByDomain(domain, session);
225
265
  const item = {
266
+ ...(existing ?? {}),
226
267
  type: 1, // Login type
227
268
  name: domain,
228
269
  login: {
@@ -233,7 +274,12 @@ class BitwardenCredentialStore {
233
274
  notes: data.notes ?? null,
234
275
  };
235
276
  const encoded = Buffer.from(JSON.stringify(item)).toString("base64");
236
- await execBw(["create", "item", encoded], session);
277
+ if (existing) {
278
+ await execBw(["edit", "item", existing.id, encoded], session, this.appDataDir);
279
+ }
280
+ else {
281
+ await execBw(["create", "item", encoded], session, this.appDataDir);
282
+ }
237
283
  (0, runtime_1.emitNervesEvent)({
238
284
  event: "repertoire.bw_credential_store_end",
239
285
  component: "repertoire",
@@ -250,7 +296,7 @@ class BitwardenCredentialStore {
250
296
  });
251
297
  const session = await this.ensureSession();
252
298
  try {
253
- const stdout = await execBw(["list", "items"], session);
299
+ const stdout = await execBw(["list", "items"], session, this.appDataDir);
254
300
  const items = JSON.parse(stdout);
255
301
  const results = items.map((item) => ({
256
302
  domain: item.name,
@@ -294,7 +340,7 @@ class BitwardenCredentialStore {
294
340
  });
295
341
  return false;
296
342
  }
297
- await execBw(["delete", "item", item.id], session);
343
+ await execBw(["delete", "item", item.id], session, this.appDataDir);
298
344
  (0, runtime_1.emitNervesEvent)({
299
345
  event: "repertoire.bw_credential_delete_end",
300
346
  component: "repertoire",
@@ -306,7 +352,7 @@ class BitwardenCredentialStore {
306
352
  // --- Private ---
307
353
  async findItemByDomain(domain, session) {
308
354
  try {
309
- const stdout = await execBw(["list", "items", "--search", domain], session);
355
+ const stdout = await execBw(["list", "items", "--search", domain], session, this.appDataDir);
310
356
  const items = JSON.parse(stdout);
311
357
  // Find exact match by name
312
358
  return items.find((item) => item.name === domain) ?? items[0] ?? null;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * - Runtime state (sessions, logs, runtime files) — stale data with no
10
10
  * value for review or history.
11
- * - Credentials — real secrets live in `~/.agentsecrets`, but defense
11
+ * - Credentials — real secrets live in the agent vault, but defense
12
12
  * in depth in case anything leaks into the bundle.
13
13
  * - Editor / OS noise (.DS_Store, .idea/, etc.).
14
14
  * - Build artifacts (rare in bundles, but possible).
@@ -32,7 +32,7 @@ exports.PII_BUNDLE_DIRECTORIES = exports.BUNDLE_GITIGNORE_TEMPLATE = void 0;
32
32
  exports.BUNDLE_GITIGNORE_TEMPLATE = `# Runtime state — sessions, logs, runtime files, never tracked
33
33
  state/
34
34
 
35
- # Credentials — never tracked. Real secrets live in ~/.agentsecrets, but
35
+ # Credentials — never tracked. Real secrets live in the agent vault, but
36
36
  # defense in depth in case anything leaks into the bundle.
37
37
  .env
38
38
  .env.*