@ouro.bot/cli 0.1.0-alpha.375 → 0.1.0-alpha.377

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.
@@ -64,6 +64,32 @@ function isAffirmativeAnswer(answer) {
64
64
  function writeDeclinedRepair(degraded, command, deps) {
65
65
  deps.writeStdout(`repair skipped for ${degraded.agent}; run \`${command}\` later.`);
66
66
  }
67
+ function runnableRepairActionFor(degraded) {
68
+ if (isVaultUnlockIssue(degraded)) {
69
+ return { kind: "vault-unlock", label: "vault unlock", command: vaultUnlockCommandFor(degraded) };
70
+ }
71
+ if (isCredentialIssue(degraded)) {
72
+ return {
73
+ kind: "provider-auth",
74
+ label: "provider auth",
75
+ command: authCommandFor(degraded),
76
+ provider: extractProviderFromFixHint(degraded.fixHint),
77
+ };
78
+ }
79
+ return undefined;
80
+ }
81
+ function writeRepairQueueSummary(degraded, deps) {
82
+ const repairable = degraded
83
+ .map((entry) => ({ entry, action: runnableRepairActionFor(entry) }))
84
+ .filter((item) => item.action !== undefined);
85
+ if (repairable.length < 2)
86
+ return;
87
+ const lines = [
88
+ "repair queue:",
89
+ ...repairable.map(({ entry, action }) => ` - ${entry.agent}: ${action.label}: \`${action.command}\``),
90
+ ];
91
+ deps.writeStdout(lines.join("\n"));
92
+ }
67
93
  async function runInteractiveRepair(degraded, deps) {
68
94
  (0, runtime_1.emitNervesEvent)({
69
95
  level: "info",
@@ -76,10 +102,11 @@ async function runInteractiveRepair(degraded, deps) {
76
102
  return { repairsAttempted: false };
77
103
  }
78
104
  let repairsAttempted = false;
105
+ writeRepairQueueSummary(degraded, deps);
79
106
  for (const entry of degraded) {
80
- if (isVaultUnlockIssue(entry)) {
81
- const unlockCommand = vaultUnlockCommandFor(entry);
82
- const answer = await deps.promptInput(`run \`${unlockCommand}\` now? [y/n] `);
107
+ const action = runnableRepairActionFor(entry);
108
+ if (action?.kind === "vault-unlock") {
109
+ const answer = await deps.promptInput(`run \`${action.command}\` now? [y/n] `);
83
110
  if (isAffirmativeAnswer(answer)) {
84
111
  try {
85
112
  if (!deps.runVaultUnlock) {
@@ -104,17 +131,15 @@ async function runInteractiveRepair(degraded, deps) {
104
131
  }
105
132
  }
106
133
  else {
107
- writeDeclinedRepair(entry, unlockCommand, deps);
134
+ writeDeclinedRepair(entry, action.command, deps);
108
135
  }
109
136
  }
110
- else if (isCredentialIssue(entry)) {
111
- const provider = extractProviderFromFixHint(entry.fixHint);
112
- const authCommand = authCommandFor(entry);
113
- const answer = await deps.promptInput(`run \`${authCommand}\` now? [y/n] `);
137
+ else if (action?.kind === "provider-auth") {
138
+ const answer = await deps.promptInput(`run \`${action.command}\` now? [y/n] `);
114
139
  if (isAffirmativeAnswer(answer)) {
115
140
  try {
116
- if (provider) {
117
- await deps.runAuthFlow(entry.agent, provider);
141
+ if (action.provider) {
142
+ await deps.runAuthFlow(entry.agent, action.provider);
118
143
  }
119
144
  else {
120
145
  await deps.runAuthFlow(entry.agent);
@@ -135,7 +160,7 @@ async function runInteractiveRepair(degraded, deps) {
135
160
  }
136
161
  }
137
162
  else {
138
- writeDeclinedRepair(entry, authCommand, deps);
163
+ writeDeclinedRepair(entry, action.command, deps);
139
164
  }
140
165
  }
141
166
  else if (isConfigError(entry)) {
@@ -36,7 +36,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getRuntimeMetadata = getRuntimeMetadata;
37
37
  const crypto_1 = require("crypto");
38
38
  const fs = __importStar(require("fs"));
39
- const os = __importStar(require("os"));
40
39
  const path = __importStar(require("path"));
41
40
  const childProcess = __importStar(require("child_process"));
42
41
  const identity_1 = require("../identity");
@@ -87,19 +86,7 @@ function readLastUpdated(repoRoot, packageJsonPath, statSyncImpl, execFileSyncIm
87
86
  return { value: UNKNOWN_METADATA, source: "unknown" };
88
87
  }
89
88
  }
90
- function readHomeDir() {
91
- const homedirImpl = optionalFunction(os, "homedir");
92
- if (!homedirImpl) {
93
- return null;
94
- }
95
- try {
96
- return homedirImpl.call(os);
97
- }
98
- catch {
99
- return null;
100
- }
101
- }
102
- function listConfigTargets(bundlesRoot, secretsRoot, daemonLoggingPath, readdirSyncImpl) {
89
+ function listConfigTargets(bundlesRoot, daemonLoggingPath, readdirSyncImpl) {
103
90
  if (!readdirSyncImpl)
104
91
  return [];
105
92
  const targets = new Set();
@@ -117,19 +104,6 @@ function listConfigTargets(bundlesRoot, secretsRoot, daemonLoggingPath, readdirS
117
104
  catch {
118
105
  // ignore unreadable bundle roots
119
106
  }
120
- if (secretsRoot) {
121
- try {
122
- const secretEntries = readdirSyncImpl(secretsRoot, { withFileTypes: true });
123
- for (const entry of secretEntries) {
124
- if (!entry.isDirectory())
125
- continue;
126
- targets.add(path.join(secretsRoot, entry.name, "secrets.json"));
127
- }
128
- }
129
- catch {
130
- // ignore unreadable secrets roots
131
- }
132
- }
133
107
  return [...targets].sort();
134
108
  }
135
109
  function readConfigFingerprint(targets, readFileSyncImpl, existsSyncImpl) {
@@ -172,8 +146,6 @@ function readConfigFingerprint(targets, readFileSyncImpl, existsSyncImpl) {
172
146
  function getRuntimeMetadata(deps = {}) {
173
147
  const repoRoot = deps.repoRoot ?? (0, identity_1.getRepoRoot)();
174
148
  const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
175
- const homeDir = readHomeDir();
176
- const secretsRoot = deps.secretsRoot ?? (homeDir ? path.join(homeDir, ".agentsecrets") : null);
177
149
  const daemonLoggingPath = deps.daemonLoggingPath ?? (0, identity_1.getAgentDaemonLoggingConfigPath)();
178
150
  const readFileSyncImpl = deps.readFileSync ?? optionalFunction(fs, "readFileSync")?.bind(fs) ?? null;
179
151
  const statSyncImpl = deps.statSync ?? optionalFunction(fs, "statSync")?.bind(fs) ?? null;
@@ -191,7 +163,7 @@ function getRuntimeMetadata(deps = {}) {
191
163
  throw new Error("git unavailable");
192
164
  }))
193
165
  : { value: UNKNOWN_METADATA, source: "unknown" };
194
- const configTargets = listConfigTargets(bundlesRoot, secretsRoot, daemonLoggingPath, readdirSyncImpl);
166
+ const configTargets = listConfigTargets(bundlesRoot, daemonLoggingPath, readdirSyncImpl);
195
167
  const configFingerprint = readConfigFingerprint(configTargets, readFileSyncImpl, existsSyncImpl);
196
168
  (0, runtime_1.emitNervesEvent)({
197
169
  component: "daemon",
@@ -39,6 +39,7 @@ const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
40
  const runtime_1 = require("../../nerves/runtime");
41
41
  const identity_1 = require("../identity");
42
+ const runtime_credentials_1 = require("../runtime-credentials");
42
43
  const sense_truth_1 = require("../sense-truth");
43
44
  const process_manager_1 = require("./process-manager");
44
45
  const DEFAULT_TEAMS_PORT = 3978;
@@ -87,22 +88,6 @@ function readAgentSenses(agentJsonPath) {
87
88
  }
88
89
  return defaults;
89
90
  }
90
- function readSecretsPayload(secretsPath) {
91
- try {
92
- const raw = fs.readFileSync(secretsPath, "utf-8");
93
- const parsed = JSON.parse(raw);
94
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
95
- return { payload: {}, error: "invalid secrets.json object" };
96
- }
97
- return { payload: parsed, error: null };
98
- }
99
- catch (error) {
100
- return {
101
- payload: {},
102
- error: error instanceof Error ? error.message : String(error),
103
- };
104
- }
105
- }
106
91
  function textField(record, key) {
107
92
  const value = record?.[key];
108
93
  return typeof value === "string" ? value.trim() : "";
@@ -111,13 +96,18 @@ function numberField(record, key, fallback) {
111
96
  const value = record?.[key];
112
97
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
113
98
  }
114
- function senseFactsFromSecrets(agent, senses, secretsPath) {
99
+ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig) {
115
100
  const base = {
116
101
  cli: { configured: true, detail: "local interactive terminal" },
117
102
  teams: { configured: false, detail: "not enabled in agent.json" },
118
103
  bluebubbles: { configured: false, detail: "not enabled in agent.json" },
119
104
  };
120
- const { payload, error } = readSecretsPayload(secretsPath);
105
+ const payload = runtimeConfig.ok ? runtimeConfig.config : {};
106
+ const unavailableDetail = runtimeConfig.ok
107
+ ? ""
108
+ : runtimeConfig.reason === "missing"
109
+ ? `missing vault runtime/config (${agent})`
110
+ : `vault runtime/config unavailable (${runtimeConfig.error})`;
121
111
  const teams = payload.teams;
122
112
  const teamsChannel = payload.teamsChannel;
123
113
  const bluebubbles = payload.bluebubbles;
@@ -137,9 +127,7 @@ function senseFactsFromSecrets(agent, senses, secretsPath) {
137
127
  }
138
128
  : {
139
129
  configured: false,
140
- detail: error && !fs.existsSync(secretsPath)
141
- ? `missing secrets.json (${agent})`
142
- : `missing ${missing.join("/")}`,
130
+ detail: runtimeConfig.ok ? `missing ${missing.join("/")}` : unavailableDetail,
143
131
  };
144
132
  }
145
133
  if (senses.bluebubbles.enabled) {
@@ -155,13 +143,17 @@ function senseFactsFromSecrets(agent, senses, secretsPath) {
155
143
  }
156
144
  : {
157
145
  configured: false,
158
- detail: error && !fs.existsSync(secretsPath)
159
- ? `missing secrets.json (${agent})`
160
- : `missing ${missing.join("/")}`,
146
+ detail: runtimeConfig.ok ? `missing ${missing.join("/")}` : unavailableDetail,
161
147
  };
162
148
  }
163
149
  return base;
164
150
  }
151
+ function senseRepairHint(agent, sense) {
152
+ if (sense === "teams") {
153
+ return `Run 'ouro vault config set --agent ${agent} --key teams.clientId', teams.clientSecret, and teams.tenantId; then run 'ouro up' again.`;
154
+ }
155
+ return `Run 'ouro vault config set --agent ${agent} --key bluebubbles.serverUrl' and bluebubbles.password; then run 'ouro up' again.`;
156
+ }
165
157
  function parseSenseSnapshotName(name) {
166
158
  const parts = name.split(":");
167
159
  if (parts.length !== 2)
@@ -235,16 +227,15 @@ class DaemonSenseManager {
235
227
  bundlesRoot;
236
228
  constructor(options) {
237
229
  const bundlesRoot = options.bundlesRoot ?? path.join(os.homedir(), "AgentBundles");
238
- const secretsRoot = options.secretsRoot ?? path.join(os.homedir(), ".agentsecrets");
239
230
  this.bundlesRoot = bundlesRoot;
240
231
  this.contexts = new Map(options.agents.map((agent) => {
241
232
  const senses = readAgentSenses(path.join(bundlesRoot, `${agent}.ouro`, "agent.json"));
242
- const facts = senseFactsFromSecrets(agent, senses, path.join(secretsRoot, agent, "secrets.json"));
233
+ const facts = senseFactsFromRuntimeConfig(agent, senses, (0, runtime_credentials_1.readRuntimeCredentialConfig)(agent));
243
234
  return [agent, { senses, facts }];
244
235
  }));
245
236
  const managedSenseAgents = [...this.contexts.entries()].flatMap(([agent, context]) => {
246
237
  return ["teams", "bluebubbles"]
247
- .filter((sense) => context.senses[sense].enabled && context.facts[sense].configured)
238
+ .filter((sense) => context.senses[sense].enabled)
248
239
  .map((sense) => ({
249
240
  name: `${agent}:${sense}`,
250
241
  agentArg: agent,
@@ -255,6 +246,24 @@ class DaemonSenseManager {
255
246
  });
256
247
  this.processManager = options.processManager ?? new process_manager_1.DaemonProcessManager({
257
248
  agents: managedSenseAgents,
249
+ configCheck: async (name) => {
250
+ const parsed = parseSenseSnapshotName(name);
251
+ if (!parsed)
252
+ return { ok: true };
253
+ const context = this.contexts.get(parsed.agent);
254
+ if (!context)
255
+ return { ok: true };
256
+ const refreshed = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(parsed.agent, { preserveCachedOnFailure: true });
257
+ context.facts = senseFactsFromRuntimeConfig(parsed.agent, context.senses, refreshed);
258
+ const fact = context.facts[parsed.sense];
259
+ if (fact.configured)
260
+ return { ok: true };
261
+ return {
262
+ ok: false,
263
+ error: `${parsed.sense} is enabled for ${parsed.agent} but runtime credentials are not ready: ${fact.detail}`,
264
+ fix: senseRepairHint(parsed.agent, parsed.sense),
265
+ };
266
+ },
258
267
  });
259
268
  (0, runtime_1.emitNervesEvent)({
260
269
  component: "channels",
@@ -290,6 +299,7 @@ class DaemonSenseManager {
290
299
  runtime.set(parsed.agent, current);
291
300
  }
292
301
  const rows = [...this.contexts.entries()].flatMap(([agent, context]) => {
302
+ context.facts = senseFactsFromRuntimeConfig(agent, context.senses, (0, runtime_credentials_1.readRuntimeCredentialConfig)(agent));
293
303
  const blueBubblesRuntimeFacts = readBlueBubblesRuntimeFacts(agent, this.bundlesRoot, runtime.get(agent)?.bluebubbles);
294
304
  const runtimeInfo = {
295
305
  cli: { configured: true },
@@ -48,7 +48,6 @@ exports.getAgentDaemonLogsDir = getAgentDaemonLogsDir;
48
48
  exports.getAgentDaemonLoggingConfigPath = getAgentDaemonLoggingConfigPath;
49
49
  exports.getAgentMessagesRoot = getAgentMessagesRoot;
50
50
  exports.getAgentToolsRoot = getAgentToolsRoot;
51
- exports.getAgentSecretsPath = getAgentSecretsPath;
52
51
  exports.loadAgentConfig = loadAgentConfig;
53
52
  exports.setAgentName = setAgentName;
54
53
  exports.setAgentConfigOverride = setAgentConfigOverride;
@@ -248,12 +247,6 @@ function getAgentMessagesRoot(agentName) {
248
247
  function getAgentToolsRoot(agentName) {
249
248
  return path.join(getAgentStateRoot(resolveOptionalAgentName(agentName)), "tools");
250
249
  }
251
- /**
252
- * Returns the conventional secrets path: `~/.agentsecrets/<agentName>/secrets.json`
253
- */
254
- function getAgentSecretsPath(agentName = getAgentName()) {
255
- return path.join(os.homedir(), ".agentsecrets", agentName, "secrets.json");
256
- }
257
250
  const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot"];
258
251
  function isValidProvider(value) {
259
252
  return typeof value === "string" && VALID_PROVIDERS.includes(value);
@@ -0,0 +1,181 @@
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.RUNTIME_CONFIG_ITEM_NAME = void 0;
37
+ exports.readRuntimeCredentialConfig = readRuntimeCredentialConfig;
38
+ exports.cacheRuntimeCredentialConfig = cacheRuntimeCredentialConfig;
39
+ exports.refreshRuntimeCredentialConfig = refreshRuntimeCredentialConfig;
40
+ exports.upsertRuntimeCredentialConfig = upsertRuntimeCredentialConfig;
41
+ exports.resetRuntimeCredentialConfigCache = resetRuntimeCredentialConfigCache;
42
+ const crypto = __importStar(require("node:crypto"));
43
+ const runtime_1 = require("../nerves/runtime");
44
+ const credential_access_1 = require("../repertoire/credential-access");
45
+ const identity_1 = require("./identity");
46
+ exports.RUNTIME_CONFIG_ITEM_NAME = "runtime/config";
47
+ let cachedRuntimeConfigs = new Map();
48
+ function isRecord(value) {
49
+ return !!value && typeof value === "object" && !Array.isArray(value);
50
+ }
51
+ function stableJson(value) {
52
+ if (Array.isArray(value))
53
+ return `[${value.map(stableJson).join(",")}]`;
54
+ if (isRecord(value)) {
55
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
56
+ }
57
+ return JSON.stringify(value);
58
+ }
59
+ function runtimeConfigVaultPath(agentName) {
60
+ return `vault:${agentName}:${exports.RUNTIME_CONFIG_ITEM_NAME}`;
61
+ }
62
+ function runtimeConfigRevision(payload) {
63
+ return `runtime_${crypto
64
+ .createHash("sha256")
65
+ .update(stableJson(payload))
66
+ .digest("hex")
67
+ .slice(0, 16)}`;
68
+ }
69
+ function validateRuntimeCredentialPayload(value) {
70
+ if (!isRecord(value))
71
+ throw new Error("runtime credential payload must be an object");
72
+ if (value.schemaVersion !== 1)
73
+ throw new Error("runtime credential payload schemaVersion must be 1");
74
+ if (value.kind !== "runtime-config")
75
+ throw new Error("runtime credential payload kind must be runtime-config");
76
+ if (typeof value.updatedAt !== "string" || value.updatedAt.trim().length === 0) {
77
+ throw new Error("runtime credential payload updatedAt must be non-empty");
78
+ }
79
+ if (!isRecord(value.config))
80
+ throw new Error("runtime credential payload config must be an object");
81
+ return value;
82
+ }
83
+ function resultFromPayload(agentName, payload) {
84
+ return {
85
+ ok: true,
86
+ itemPath: runtimeConfigVaultPath(agentName),
87
+ config: { ...payload.config },
88
+ revision: runtimeConfigRevision(payload),
89
+ updatedAt: payload.updatedAt,
90
+ };
91
+ }
92
+ function missingRuntimeConfig(agentName) {
93
+ return {
94
+ ok: false,
95
+ reason: "missing",
96
+ itemPath: runtimeConfigVaultPath(agentName),
97
+ error: `no runtime credentials stored at ${runtimeConfigVaultPath(agentName)}`,
98
+ };
99
+ }
100
+ function cacheResult(agentName, result) {
101
+ cachedRuntimeConfigs.set(agentName, result);
102
+ return result;
103
+ }
104
+ function readRuntimeCredentialConfig(agentName = (0, identity_1.getAgentName)()) {
105
+ return cachedRuntimeConfigs.get(agentName) ?? missingRuntimeConfig(agentName);
106
+ }
107
+ function cacheRuntimeCredentialConfig(agentName, config, now = new Date()) {
108
+ const payload = {
109
+ schemaVersion: 1,
110
+ kind: "runtime-config",
111
+ updatedAt: now.toISOString(),
112
+ config: { ...config },
113
+ };
114
+ return cacheResult(agentName, resultFromPayload(agentName, payload));
115
+ }
116
+ async function refreshRuntimeCredentialConfig(agentName, options = {}) {
117
+ try {
118
+ const store = (0, credential_access_1.getCredentialStore)(agentName);
119
+ const raw = await store.getRawSecret(exports.RUNTIME_CONFIG_ITEM_NAME, "password");
120
+ const payload = validateRuntimeCredentialPayload(JSON.parse(raw));
121
+ const result = resultFromPayload(agentName, payload);
122
+ (0, runtime_1.emitNervesEvent)({
123
+ component: "config/identity",
124
+ event: "config.runtime_credentials_loaded",
125
+ message: "loaded runtime credentials from vault",
126
+ meta: { agentName, itemPath: result.itemPath, revision: result.revision },
127
+ });
128
+ return cacheResult(agentName, result);
129
+ }
130
+ catch (error) {
131
+ const message = error instanceof Error ? error.message : String(error);
132
+ const cached = cachedRuntimeConfigs.get(agentName);
133
+ const reason = message.includes(`no credential found for domain "${exports.RUNTIME_CONFIG_ITEM_NAME}"`)
134
+ ? "missing"
135
+ : message.includes("runtime credential payload")
136
+ ? "invalid"
137
+ : "unavailable";
138
+ const result = {
139
+ ok: false,
140
+ reason,
141
+ itemPath: runtimeConfigVaultPath(agentName),
142
+ error: reason === "missing" ? `no runtime credentials stored at ${runtimeConfigVaultPath(agentName)}` : message,
143
+ };
144
+ (0, runtime_1.emitNervesEvent)({
145
+ level: reason === "missing" ? "warn" : "error",
146
+ component: "config/identity",
147
+ event: "config.runtime_credentials_unavailable",
148
+ message: "runtime credentials unavailable",
149
+ meta: { agentName, reason, itemPath: result.itemPath },
150
+ });
151
+ if (options.preserveCachedOnFailure && cached?.ok)
152
+ return cached;
153
+ return cacheResult(agentName, result);
154
+ }
155
+ }
156
+ async function upsertRuntimeCredentialConfig(agentName, config, now = new Date()) {
157
+ const payload = {
158
+ schemaVersion: 1,
159
+ kind: "runtime-config",
160
+ updatedAt: now.toISOString(),
161
+ config: { ...config },
162
+ };
163
+ const store = (0, credential_access_1.getCredentialStore)(agentName);
164
+ await store.store(exports.RUNTIME_CONFIG_ITEM_NAME, {
165
+ username: "runtime/config",
166
+ password: JSON.stringify(payload),
167
+ notes: "Ouro runtime credentials for senses and integrations. Provider credentials live in providers/* items.",
168
+ });
169
+ const result = resultFromPayload(agentName, payload);
170
+ (0, runtime_1.emitNervesEvent)({
171
+ component: "config/identity",
172
+ event: "config.runtime_credentials_upserted",
173
+ message: "upserted runtime credential config in vault",
174
+ meta: { agentName, itemPath: result.itemPath, revision: result.revision },
175
+ });
176
+ cacheResult(agentName, result);
177
+ return result;
178
+ }
179
+ function resetRuntimeCredentialConfigCache() {
180
+ cachedRuntimeConfigs = new Map();
181
+ }
@@ -146,18 +146,7 @@ function readSenseStatusLines() {
146
146
  teams: { enabled: false },
147
147
  bluebubbles: { enabled: false },
148
148
  };
149
- let payload = {};
150
- try {
151
- const raw = fs.readFileSync((0, identity_1.getAgentSecretsPath)(), "utf-8");
152
- const parsed = JSON.parse(raw);
153
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
154
- payload = parsed;
155
- }
156
- }
157
- catch { /* v8 ignore start -- defensive: fallback on read failure @preserve */
158
- payload = {};
159
- /* v8 ignore stop */
160
- }
149
+ const payload = (0, config_1.loadConfig)();
161
150
  const teams = payload.teams;
162
151
  const bluebubbles = payload.bluebubbles;
163
152
  const configured = {
@@ -64,6 +64,7 @@ const ouro_version_manager_1 = require("../heart/versioning/ouro-version-manager
64
64
  const tools_1 = require("../repertoire/tools");
65
65
  const skills_1 = require("../repertoire/skills");
66
66
  const identity_1 = require("../heart/identity");
67
+ const config_1 = require("../heart/config");
67
68
  const runtime_mode_1 = require("../heart/daemon/runtime-mode");
68
69
  const types_1 = require("./friends/types");
69
70
  const trust_explanation_1 = require("./friends/trust-explanation");
@@ -416,17 +417,7 @@ function localSenseStatusLines() {
416
417
  teams: { enabled: false },
417
418
  bluebubbles: { enabled: false },
418
419
  };
419
- let payload = {};
420
- try {
421
- const raw = fs.readFileSync((0, identity_1.getAgentSecretsPath)(), "utf-8");
422
- const parsed = JSON.parse(raw);
423
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
424
- payload = parsed;
425
- }
426
- }
427
- catch {
428
- payload = {};
429
- }
420
+ const payload = (0, config_1.loadConfig)();
430
421
  const teams = payload.teams;
431
422
  const bluebubbles = payload.bluebubbles;
432
423
  const configured = {
@@ -454,13 +445,13 @@ function senseRuntimeGuidance(channel, preReadStatusLines) {
454
445
  lines.push("sense states:");
455
446
  lines.push("- interactive = available when opened by the user instead of kept running by the daemon");
456
447
  lines.push("- disabled = turned off in agent.json");
457
- lines.push("- needs_config = enabled but missing required secrets.json values");
448
+ lines.push("- needs_config = enabled but missing required vault runtime/config values");
458
449
  lines.push("- ready = enabled and configured; `ouro up` should bring it online");
459
450
  lines.push("- running = enabled and currently active");
460
451
  lines.push("- error = enabled but unhealthy");
461
- lines.push("If asked how to enable another sense, I explain the relevant agent.json senses entry and required secrets.json fields instead of guessing.");
462
- lines.push("teams setup truth: enable `senses.teams.enabled`, then provide `teams.clientId`, `teams.clientSecret`, and `teams.tenantId` in secrets.json.");
463
- lines.push("bluebubbles setup truth: enable `senses.bluebubbles.enabled`, then provide `bluebubbles.serverUrl` and `bluebubbles.password` in secrets.json.");
452
+ lines.push("If asked how to enable another sense, I explain the relevant agent.json senses entry and required agent-vault runtime/config fields instead of guessing.");
453
+ lines.push("teams setup truth: enable `senses.teams.enabled`, then store `teams.clientId`, `teams.clientSecret`, and `teams.tenantId` in the agent vault runtime/config item.");
454
+ lines.push("bluebubbles setup truth: enable `senses.bluebubbles.enabled`, then store `bluebubbles.serverUrl` and `bluebubbles.password` in the agent vault runtime/config item.");
464
455
  if (channel === "cli") {
465
456
  lines.push("cli is interactive: it is available when the user opens it, not something `ouro up` daemonizes.");
466
457
  }
@@ -127,7 +127,7 @@ exports.notesToolDefinitions = [
127
127
  try {
128
128
  const key = (0, config_1.getIntegrationsConfig)().perplexityApiKey;
129
129
  if (!key)
130
- return "error: perplexityApiKey not configured in secrets.json";
130
+ return "error: perplexityApiKey not configured in the agent vault runtime/config item";
131
131
  const res = await fetch("https://api.perplexity.ai/search", {
132
132
  method: "POST",
133
133
  headers: {
@@ -111,6 +111,7 @@ function lockedMessage(config, store) {
111
111
  "This can happen on a new computer, after a local profile or hostname migration, or if the local unlock entry was removed.",
112
112
  "",
113
113
  `Run \`${command}\` and enter the vault unlock secret from the operator password manager.`,
114
+ "If nobody saved that unlock secret, Ouro cannot recover it; create or rotate the agent vault and re-enter credentials.",
114
115
  ].join("\n");
115
116
  }
116
117
  function validateStoreKind(store) {
@@ -1,13 +1,70 @@
1
1
  "use strict";
2
2
  // Thin entrypoint for `npm run bluebubbles` / `node dist/senses/bluebubbles/entry.js --agent <name>`.
3
3
  // Separated from index.ts so the BlueBubbles adapter stays testable.
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
4
37
  Object.defineProperty(exports, "__esModule", { value: true });
5
- if (!process.argv.includes("--agent")) {
38
+ const agentArgIndex = process.argv.indexOf("--agent");
39
+ const agentName = agentArgIndex >= 0 ? process.argv[agentArgIndex + 1] : undefined;
40
+ if (!agentName) {
6
41
  // eslint-disable-next-line no-console -- pre-boot guard: --agent check before imports
7
42
  console.error("Missing required --agent <name> argument.\nUsage: node dist/senses/bluebubbles/entry.js --agent ouroboros");
8
43
  process.exit(1);
9
44
  }
10
- const index_1 = require("./index");
11
45
  const runtime_logging_1 = require("../../heart/daemon/runtime-logging");
46
+ const runtime_1 = require("../../nerves/runtime");
12
47
  (0, runtime_logging_1.configureDaemonRuntimeLogger)("bluebubbles");
13
- (0, index_1.startBlueBubblesApp)();
48
+ (0, runtime_1.emitNervesEvent)({
49
+ component: "senses",
50
+ event: "senses.entry_boot",
51
+ message: "booting BlueBubbles entrypoint",
52
+ meta: { entry: "bluebubbles", agentName },
53
+ });
54
+ Promise.resolve().then(() => __importStar(require("../../heart/runtime-credentials"))).then(async ({ refreshRuntimeCredentialConfig }) => {
55
+ await refreshRuntimeCredentialConfig(agentName, { preserveCachedOnFailure: true }).catch(() => undefined);
56
+ const { startBlueBubblesApp } = await Promise.resolve().then(() => __importStar(require("./index")));
57
+ await startBlueBubblesApp();
58
+ })
59
+ .catch((error) => {
60
+ (0, runtime_1.emitNervesEvent)({
61
+ level: "error",
62
+ component: "senses",
63
+ event: "senses.entry_error",
64
+ message: "BlueBubbles entrypoint failed",
65
+ meta: { entry: "bluebubbles", agentName, error: error instanceof Error ? error.message : String(error) },
66
+ });
67
+ // eslint-disable-next-line no-console -- fatal startup guard for sense process
68
+ console.error(error instanceof Error ? error.message : String(error));
69
+ process.exit(1);
70
+ });