@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.
- package/README.md +16 -7
- package/changelog.json +17 -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 +334 -102
- 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/heart/versioning/ouro-path-installer.js +10 -5
- package/dist/mind/prompt.js +1 -1
- 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/skills/configure-dev-tools.md +10 -0
- 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 —
|
|
89
|
-
throw new Error("minimax VLM: API key is empty —
|
|
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
|
|
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
|
-
|
|
35
|
-
" 3.
|
|
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
|
|
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) {
|
|
@@ -63,16 +63,21 @@ function writeWrapperScript(scriptPath, mkdirSync, writeFileSync, chmodSync) {
|
|
|
63
63
|
writeFileSync(scriptPath, WRAPPER_SCRIPT, { mode: 0o755 });
|
|
64
64
|
chmodSync(scriptPath, 0o755);
|
|
65
65
|
}
|
|
66
|
-
function detectShellProfile(homeDir, shell) {
|
|
66
|
+
function detectShellProfile(homeDir, shell, platform) {
|
|
67
67
|
if (!shell)
|
|
68
68
|
return null;
|
|
69
69
|
const base = path.basename(shell);
|
|
70
70
|
if (base === "zsh")
|
|
71
71
|
return path.join(homeDir, ".zshrc");
|
|
72
72
|
if (base === "bash") {
|
|
73
|
-
// macOS uses .bash_profile
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
// macOS uses .bash_profile; Linux/WSL uses .bashrc (the default
|
|
74
|
+
// interactive shell config on Debian/Ubuntu). Writing to .bash_profile
|
|
75
|
+
// on Linux often has no effect because non-login shells skip it.
|
|
76
|
+
/* v8 ignore next -- ?? fallback: callers always pass platform from deps @preserve */
|
|
77
|
+
const effectivePlatform = platform ?? process.platform;
|
|
78
|
+
return effectivePlatform === "darwin"
|
|
79
|
+
? path.join(homeDir, ".bash_profile")
|
|
80
|
+
: path.join(homeDir, ".bashrc");
|
|
76
81
|
}
|
|
77
82
|
if (base === "fish")
|
|
78
83
|
return path.join(homeDir, ".config", "fish", "config.fish");
|
|
@@ -250,7 +255,7 @@ function installOuroCommand(deps = {}) {
|
|
|
250
255
|
const pathReady = isBinDirInPath(binDir, envPath);
|
|
251
256
|
let shellProfileUpdated = null;
|
|
252
257
|
if (!pathReady) {
|
|
253
|
-
const profilePath = detectShellProfile(homeDir, shell);
|
|
258
|
+
const profilePath = detectShellProfile(homeDir, shell, platform);
|
|
254
259
|
if (profilePath) {
|
|
255
260
|
try {
|
|
256
261
|
let existing = "";
|
package/dist/mind/prompt.js
CHANGED
|
@@ -382,7 +382,7 @@ function runtimeInfoSection(channel, options) {
|
|
|
382
382
|
lines.push(`process type: ${processTypeLabel(channel)}`);
|
|
383
383
|
lines.push(`daemon: ${daemonStatus(options?.daemonRunning)}`);
|
|
384
384
|
lines.push(`mcp serve: i can expose my tools to dev tools via \`ouro mcp-serve\`. see the configure-dev-tools skill for setup.`);
|
|
385
|
-
lines.push(`harness docs: the harness repo has docs/ and skills/ with guides for setup, operations, and capabilities. docs/ does NOT ship in the npm package — in production, fetch from https://github.com/ouroborosbot/ouroboros/tree/main/docs instead. in dev mode, read from ${sourceRoot}/docs/. when someone asks about setup, installation, cross-machine cloning, deployment, testing, auth, or how i work — consult the docs
|
|
385
|
+
lines.push(`harness docs: the harness repo has docs/ and skills/ with guides for setup, operations, and capabilities. docs/ does NOT ship in the npm package — in production, fetch from https://github.com/ouroborosbot/ouroboros/tree/main/docs instead. in dev mode, read from ${sourceRoot}/docs/. when someone asks about setup, installation, cross-machine cloning, deployment, testing, auth, or how i work — consult the docs first. NEVER guess about how the harness works. if the docs don't answer the question, investigate in code. if i discover the docs are stale or missing coverage, open a PR to fix them — stale docs cause the same damage as wrong answers.`);
|
|
386
386
|
if (channel === "cli") {
|
|
387
387
|
lines.push("i introduce myself on boot with a fun random greeting.");
|
|
388
388
|
}
|
|
@@ -2,24 +2,59 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Bitwarden CLI credential store — wraps `bw` CLI for the agent's own vault.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
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 =
|
|
21
|
-
|
|
22
|
-
:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.*
|