@ouro.bot/cli 0.1.0-alpha.697 → 0.1.0-alpha.698
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,15 @@
|
|
|
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.698",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Open the daemon socket before provider credential warm-up and preload only configured lane providers so slow vault reads cannot make ouro up falsely report a dead daemon.",
|
|
8
|
+
"Require explicitly selected provider credential refreshes to load every requested provider before replacing cached provider state.",
|
|
9
|
+
"Return structured RSVP smoke guidance when the latest snapshot is missing or invalid, with hermetic CLI coverage that cannot send live BlueBubbles messages from tests.",
|
|
10
|
+
"Keep native RSVP update notifications on the legacy message skeleton while replacing only the obsolete script-vs-Slugger footer copy."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.0-alpha.697",
|
|
6
15
|
"changes": [
|
|
@@ -119,6 +119,35 @@ if (!ipcState.installed) {
|
|
|
119
119
|
message: "starting private-runtime process entrypoint",
|
|
120
120
|
meta: { entry: "private-runtime", agentName },
|
|
121
121
|
});
|
|
122
|
+
async function selectedProviderTargets(agentName) {
|
|
123
|
+
try {
|
|
124
|
+
const [{ readAgentConfigForAgent }, { getAgentBundlesRoot }] = await Promise.all([
|
|
125
|
+
Promise.resolve().then(() => __importStar(require("./auth/auth-flow"))),
|
|
126
|
+
Promise.resolve().then(() => __importStar(require("./identity"))),
|
|
127
|
+
]);
|
|
128
|
+
const { config } = readAgentConfigForAgent(agentName, getAgentBundlesRoot());
|
|
129
|
+
return [...new Set([config.humanFacing.provider, config.agentFacing.provider])];
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
(0, runtime_1.emitNervesEvent)({
|
|
133
|
+
level: "warn",
|
|
134
|
+
component: "senses",
|
|
135
|
+
event: "senses.provider_refresh_skipped",
|
|
136
|
+
message: "skipping private-runtime provider credential refresh because agent config could not be read",
|
|
137
|
+
meta: {
|
|
138
|
+
entry: "private-runtime",
|
|
139
|
+
agentName,
|
|
140
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive non-Error agent-config failures @preserve */ String(error),
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function providerPoolMissingTargets(providerPool, providers) {
|
|
147
|
+
if (!providerPool.ok)
|
|
148
|
+
return true;
|
|
149
|
+
return providers.some((provider) => !providerPool.pool.providers[provider]);
|
|
150
|
+
}
|
|
122
151
|
// Dynamic import: agent-entry is process-start wiring that starts a sense process.
|
|
123
152
|
// Using dynamic import avoids a static heart/ -> senses/ dependency.
|
|
124
153
|
Promise.resolve().then(() => __importStar(require("./runtime-credentials"))).then(async ({ readMachineRuntimeCredentialConfig, readRuntimeCredentialConfig, refreshMachineRuntimeCredentialConfig, refreshRuntimeCredentialConfig, applyRuntimeCredentialBootstrapMessage, waitForRuntimeCredentialBootstrap, }) => {
|
|
@@ -135,8 +164,9 @@ Promise.resolve().then(() => __importStar(require("./runtime-credentials"))).the
|
|
|
135
164
|
void refreshRuntimeCredentialConfig(agentName, { preserveCachedOnFailure: true }).catch(() => undefined);
|
|
136
165
|
}
|
|
137
166
|
const providerPool = readProviderCredentialPool(agentName);
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
const providerTargets = await selectedProviderTargets(agentName);
|
|
168
|
+
if (providerTargets.length > 0 && providerPoolMissingTargets(providerPool, providerTargets)) {
|
|
169
|
+
await refreshProviderCredentialPool(agentName, { preserveCachedOnFailure: true, providers: providerTargets }).catch(() => undefined);
|
|
140
170
|
}
|
|
141
171
|
/* v8 ignore next 7 -- process-start best-effort machine credential refresh runs in a child entrypoint and is covered operationally by daemon startup tests @preserve */
|
|
142
172
|
if (!readMachineRuntimeCredentialConfig(agentName).ok) {
|
|
@@ -68,6 +68,7 @@ const context_loss_sentinel_1 = require("../context-loss-sentinel");
|
|
|
68
68
|
const provider_credentials_1 = require("../provider-credentials");
|
|
69
69
|
const runtime_credentials_1 = require("../runtime-credentials");
|
|
70
70
|
const machine_identity_1 = require("../machine-identity");
|
|
71
|
+
const auth_flow_1 = require("../auth/auth-flow");
|
|
71
72
|
function parseSocketPath(argv) {
|
|
72
73
|
const socketIndex = argv.indexOf("--socket");
|
|
73
74
|
if (socketIndex >= 0) {
|
|
@@ -483,16 +484,116 @@ function writeStopCommandHealthState() {
|
|
|
483
484
|
// Health writes are best-effort during shutdown.
|
|
484
485
|
}
|
|
485
486
|
}
|
|
487
|
+
function providerPreloadTargets(agent) {
|
|
488
|
+
try {
|
|
489
|
+
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(agent, (0, identity_1.getAgentBundlesRoot)());
|
|
490
|
+
return [...new Set([config.humanFacing.provider, config.agentFacing.provider])];
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
(0, runtime_1.emitNervesEvent)({
|
|
494
|
+
level: "warn",
|
|
495
|
+
component: "daemon",
|
|
496
|
+
event: "daemon.provider_preload_skipped",
|
|
497
|
+
message: "skipping provider credential preload because agent config could not be read",
|
|
498
|
+
meta: {
|
|
499
|
+
agent,
|
|
500
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive non-Error config-read failures @preserve */ String(error),
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function providerPreloadMissingTargets(result, providers) {
|
|
507
|
+
return providers.filter((provider) => !result.pool.providers[provider]);
|
|
508
|
+
}
|
|
486
509
|
async function preloadProviderCredentialPools() {
|
|
487
|
-
await Promise.all(managedAgents.map((agent) =>
|
|
510
|
+
await Promise.all(managedAgents.map(async (agent) => {
|
|
511
|
+
const providers = providerPreloadTargets(agent);
|
|
512
|
+
if (providers.length === 0)
|
|
513
|
+
return;
|
|
514
|
+
const result = await (0, provider_credentials_1.refreshProviderCredentialPool)(agent, { preserveCachedOnFailure: true, providers, skipCache: true });
|
|
515
|
+
if (result.ok) {
|
|
516
|
+
const missingProviders = providerPreloadMissingTargets(result, providers);
|
|
517
|
+
if (missingProviders.length > 0) {
|
|
518
|
+
(0, runtime_1.emitNervesEvent)({
|
|
519
|
+
level: "warn",
|
|
520
|
+
component: "daemon",
|
|
521
|
+
event: "daemon.provider_preload_unavailable",
|
|
522
|
+
message: "provider credential preload returned an incomplete selected provider cache",
|
|
523
|
+
meta: {
|
|
524
|
+
agent,
|
|
525
|
+
reason: "missing",
|
|
526
|
+
error: `missing selected providers: ${missingProviders.join(", ")}`,
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const records = Object.values(result.pool.providers).filter((record) => !!record);
|
|
532
|
+
(0, provider_credentials_1.cacheProviderCredentialRecords)(agent, records, new Date(result.pool.updatedAt));
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
(0, runtime_1.emitNervesEvent)({
|
|
536
|
+
level: "warn",
|
|
537
|
+
component: "daemon",
|
|
538
|
+
event: "daemon.provider_preload_unavailable",
|
|
539
|
+
message: "provider credential preload could not refresh selected provider cache",
|
|
540
|
+
meta: {
|
|
541
|
+
agent,
|
|
542
|
+
reason: result.reason,
|
|
543
|
+
error: result.error,
|
|
544
|
+
},
|
|
545
|
+
});
|
|
546
|
+
}));
|
|
547
|
+
}
|
|
548
|
+
function startProviderCredentialPoolPreload() {
|
|
549
|
+
return preloadProviderCredentialPools().catch((error) => {
|
|
550
|
+
(0, runtime_1.emitNervesEvent)({
|
|
551
|
+
level: "error",
|
|
552
|
+
component: "daemon",
|
|
553
|
+
event: "daemon.provider_preload_error",
|
|
554
|
+
message: "provider credential preload failed after daemon startup",
|
|
555
|
+
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive non-Error provider preload failures @preserve */ String(error) },
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
function scheduleStartupSentinelAfterProviderPreload(agent, preload) {
|
|
560
|
+
void preload.then(async () => {
|
|
561
|
+
const result = await refreshDaemonSentinel(agent, "daemon_startup");
|
|
562
|
+
if (result.status === "critical") {
|
|
563
|
+
(0, runtime_1.emitNervesEvent)({
|
|
564
|
+
level: "error",
|
|
565
|
+
component: "daemon",
|
|
566
|
+
event: "daemon.startup_sentinel_error",
|
|
567
|
+
message: "startup Sentinel refresh reported critical after provider preload",
|
|
568
|
+
meta: {
|
|
569
|
+
agent,
|
|
570
|
+
error: result.message,
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
/* v8 ignore start -- refreshDaemonSentinel handles ordinary Sentinel failures; this is a promise-chain guard. @preserve */
|
|
575
|
+
}).catch((error) => {
|
|
576
|
+
(0, runtime_1.emitNervesEvent)({
|
|
577
|
+
level: "error",
|
|
578
|
+
component: "daemon",
|
|
579
|
+
event: "daemon.startup_sentinel_error",
|
|
580
|
+
message: "startup Sentinel refresh failed after provider preload",
|
|
581
|
+
meta: {
|
|
582
|
+
agent,
|
|
583
|
+
error: error instanceof Error ? error.message : String(error),
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
/* v8 ignore stop */
|
|
488
588
|
}
|
|
489
589
|
/* v8 ignore start -- habit wiring: lambdas delegate to processManager/fs; tested via HabitScheduler unit tests @preserve */
|
|
490
|
-
void
|
|
590
|
+
void daemon.start().then(async () => {
|
|
591
|
+
const providerPreload = startProviderCredentialPoolPreload();
|
|
491
592
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
492
593
|
const ouroPath = (0, os_cron_deps_1.resolveOuroBinaryPath)();
|
|
493
594
|
const osCronDeps = (0, os_cron_deps_1.createRealOsCronDeps)();
|
|
494
595
|
for (const agent of managedAgents) {
|
|
495
|
-
|
|
596
|
+
scheduleStartupSentinelAfterProviderPreload(agent, providerPreload);
|
|
496
597
|
const bundleRoot = path.join(bundlesRoot, `${agent}.ouro`);
|
|
497
598
|
const habitsDir = path.join(bundleRoot, "habits");
|
|
498
599
|
const degradedComponent = `habits:${agent}`;
|
|
@@ -241,6 +241,7 @@ function readProviderCredentialPool(agentName) {
|
|
|
241
241
|
}
|
|
242
242
|
async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
243
243
|
const providersToRead = selectedProviders(options.providers);
|
|
244
|
+
const requiresAllSelectedProviders = !!options.providers && options.providers.length > 0;
|
|
244
245
|
try {
|
|
245
246
|
const store = (0, credential_access_1.getCredentialStore)(agentName);
|
|
246
247
|
options.onProgress?.(`reading vault items for ${agentName}...`);
|
|
@@ -266,6 +267,21 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
|
266
267
|
updatedAt = record.updatedAt;
|
|
267
268
|
}
|
|
268
269
|
options.onProgress?.("parsing provider credentials...");
|
|
270
|
+
const missingRequestedProviders = requiresAllSelectedProviders
|
|
271
|
+
? providersToRead.filter((provider) => !providers[provider])
|
|
272
|
+
: [];
|
|
273
|
+
if (missingRequestedProviders.length > 0) {
|
|
274
|
+
const cached = cachedPools.get(agentName);
|
|
275
|
+
const result = {
|
|
276
|
+
ok: false,
|
|
277
|
+
reason: "missing",
|
|
278
|
+
poolPath: providerCredentialsVaultPath(agentName),
|
|
279
|
+
error: `requested provider credentials missing from ${providerCredentialsVaultPath(agentName)}: ${missingRequestedProviders.join(", ")}`,
|
|
280
|
+
};
|
|
281
|
+
if (options.preserveCachedOnFailure && cached?.ok)
|
|
282
|
+
return cached;
|
|
283
|
+
return options.skipCache ? result : cacheResult(agentName, result);
|
|
284
|
+
}
|
|
269
285
|
const pool = {
|
|
270
286
|
schemaVersion: 1,
|
|
271
287
|
updatedAt,
|
package/dist/rsvp/cli.js
CHANGED
|
@@ -178,8 +178,21 @@ function readBaselineSnapshot(agentRoot) {
|
|
|
178
178
|
? parseSnapshotFromFile(snapshotFilePath(agentRoot, snapshotId))
|
|
179
179
|
: null;
|
|
180
180
|
}
|
|
181
|
-
function
|
|
182
|
-
|
|
181
|
+
function resolveLatestSnapshot(agentRoot) {
|
|
182
|
+
const filePath = latestSnapshotPath(agentRoot);
|
|
183
|
+
if (!fs.existsSync(filePath)) {
|
|
184
|
+
return { ok: false, path: filePath, message: "latest RSVP snapshot missing; run `ouro rsvp refresh --mode shadow --no-send` first" };
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
return { ok: true, snapshot: parseSnapshotFromFile(filePath) };
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
path: filePath,
|
|
193
|
+
message: error instanceof Error ? error.message : /* v8 ignore next -- fs and snapshot parsing failures are Error instances. @preserve */ String(error),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
183
196
|
}
|
|
184
197
|
function machineIdForCli() {
|
|
185
198
|
return (0, machine_identity_1.loadOrCreateMachineIdentity)().machineId;
|
|
@@ -609,7 +622,19 @@ async function executeSmoke(command, deps) {
|
|
|
609
622
|
result: configResult,
|
|
610
623
|
});
|
|
611
624
|
}
|
|
612
|
-
const
|
|
625
|
+
const snapshotResult = resolveLatestSnapshot(agentRoot);
|
|
626
|
+
if (!snapshotResult.ok) {
|
|
627
|
+
return {
|
|
628
|
+
...basePayload(command, false, "RSVP smoke requires latest RSVP snapshot before follow-up can run", {
|
|
629
|
+
allowSend: command.allowSend === true,
|
|
630
|
+
sendAllowed: false,
|
|
631
|
+
requires: "latest RSVP snapshot",
|
|
632
|
+
result: snapshotResult,
|
|
633
|
+
}),
|
|
634
|
+
ok: false,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
const snapshot = snapshotResult.snapshot;
|
|
613
638
|
const question = command.question ?? "who is pending?";
|
|
614
639
|
const answer = (0, query_1.queryRsvpSnapshot)(snapshot, { query: question });
|
|
615
640
|
const wantsLiveSend = command.mode === "live" && command.allowSend === true;
|
|
@@ -4,6 +4,7 @@ exports.guestDisplayName = guestDisplayName;
|
|
|
4
4
|
exports.computeRsvpDelta = computeRsvpDelta;
|
|
5
5
|
exports.renderRsvpReport = renderRsvpReport;
|
|
6
6
|
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
const RSVP_REPORT_FOOTER = "🤖 Beep boop! Automated RSVP update from Slugger — no need to reply here!";
|
|
7
8
|
function byId(left, right) {
|
|
8
9
|
return left.id.localeCompare(right.id);
|
|
9
10
|
}
|
|
@@ -108,7 +109,7 @@ function hasChanges(delta) {
|
|
|
108
109
|
function renderRsvpReport(delta) {
|
|
109
110
|
const lines = ["RSVP Update — Ari & Rachel", ""];
|
|
110
111
|
if (delta.isFirstRun) {
|
|
111
|
-
lines.push("First
|
|
112
|
+
lines.push("First check — here's the current summary:", "");
|
|
112
113
|
}
|
|
113
114
|
else if (!hasChanges(delta)) {
|
|
114
115
|
lines.push("No changes since last check.", "");
|
|
@@ -144,6 +145,7 @@ function renderRsvpReport(delta) {
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
lines.push(summaryLine(delta.summary));
|
|
148
|
+
lines.push("", RSVP_REPORT_FOOTER);
|
|
147
149
|
const report = lines.join("\n");
|
|
148
150
|
(0, runtime_1.emitNervesEvent)({
|
|
149
151
|
component: "rsvp",
|