@ouro.bot/cli 0.1.0-alpha.692 → 0.1.0-alpha.693
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 +7 -0
- package/dist/heart/daemon/cli-exec.js +4 -0
- package/dist/heart/daemon/doctor.js +78 -55
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
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.693",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Load vault-backed provider credentials before manual context-loss Sentinel refreshes so a fresh CLI process cannot report false not-loaded provider warnings.",
|
|
8
|
+
"Have doctor lifecycle checks read the machine-local daemon log alongside bundle-local logs so stale historical bundle logs cannot mask live daemon activity."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
4
11
|
{
|
|
5
12
|
"version": "0.1.0-alpha.692",
|
|
6
13
|
"changes": [
|
|
@@ -7647,6 +7647,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7647
7647
|
throw new Error("work sentinel requires --agent <name>");
|
|
7648
7648
|
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
7649
7649
|
const agentRoot = deps.agentBundleRoot ?? path.join(bundlesRoot, `${command.agent}.ouro`);
|
|
7650
|
+
if (command.kind === "work.sentinel.refresh") {
|
|
7651
|
+
await (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent, { preserveCachedOnFailure: true });
|
|
7652
|
+
}
|
|
7650
7653
|
const sentinel = command.kind === "work.sentinel.refresh"
|
|
7651
7654
|
? await refreshContextLossSentinel(command.agent, agentRoot, { trigger: "manual_cli", homeDir: deps.homeDir })
|
|
7652
7655
|
: readContextLossSentinelView(agentRoot, { limit: 20 });
|
|
@@ -7885,6 +7888,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7885
7888
|
fetchImpl: deps.fetchImpl ?? fetch,
|
|
7886
7889
|
socketPath: deps.socketPath,
|
|
7887
7890
|
bundlesRoot: deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(),
|
|
7891
|
+
daemonLogsDir: path.join((0, ouro_version_manager_1.getOuroCliHome)(deps.homeDir), "daemon", "logs"),
|
|
7888
7892
|
homedir: os.homedir(),
|
|
7889
7893
|
envPath: process.env.PATH ?? "",
|
|
7890
7894
|
platform: process.platform,
|
|
@@ -783,83 +783,106 @@ function computeSummary(categories) {
|
|
|
783
783
|
* to answer the operator's question after the daemon has gone silent: "did
|
|
784
784
|
* it crash? when did it last do anything? did it just upgrade?"
|
|
785
785
|
*
|
|
786
|
-
* Reads daemon.ndjson
|
|
787
|
-
*
|
|
786
|
+
* Reads the machine-local daemon.ndjson when available, plus any
|
|
787
|
+
* bundle-local copies left by older runtimes. The newest parseable event
|
|
788
|
+
* is the liveness signal; stale bundle logs cannot mask a live daemon.
|
|
788
789
|
*/
|
|
789
790
|
function checkLifecycle(deps) {
|
|
790
791
|
const checks = [];
|
|
791
792
|
const HOUR_MS = 60 * 60 * 1000;
|
|
792
793
|
const STALE_THRESHOLD_MS = 5 * 60 * 1000;
|
|
793
|
-
const
|
|
794
|
+
const now = Date.now();
|
|
795
|
+
const cutoff = now - HOUR_MS;
|
|
796
|
+
const logPaths = [];
|
|
797
|
+
const seenLogPaths = new Set();
|
|
798
|
+
const addLogPath = (candidate) => {
|
|
799
|
+
if (seenLogPaths.has(candidate) || !deps.existsSync(candidate))
|
|
800
|
+
return;
|
|
801
|
+
seenLogPaths.add(candidate);
|
|
802
|
+
logPaths.push(candidate);
|
|
803
|
+
};
|
|
804
|
+
if (deps.daemonLogsDir) {
|
|
805
|
+
addLogPath(`${deps.daemonLogsDir}/daemon.ndjson`);
|
|
806
|
+
}
|
|
794
807
|
const agents = discoverAgents(deps);
|
|
795
|
-
let logPath = null;
|
|
796
808
|
for (const agentDir of agents) {
|
|
797
|
-
|
|
798
|
-
if (deps.existsSync(candidate)) {
|
|
799
|
-
logPath = candidate;
|
|
800
|
-
break;
|
|
801
|
-
}
|
|
809
|
+
addLogPath(`${deps.bundlesRoot}/${agentDir}/state/daemon/logs/daemon.ndjson`);
|
|
802
810
|
}
|
|
803
|
-
if (
|
|
804
|
-
checks.push({ label: "daemon log readable", status: "warn", detail: "no daemon.ndjson found in
|
|
811
|
+
if (logPaths.length === 0) {
|
|
812
|
+
checks.push({ label: "daemon log readable", status: "warn", detail: "no daemon.ndjson found in daemon or agent bundle logs" });
|
|
805
813
|
return { name: "Lifecycle", checks };
|
|
806
814
|
}
|
|
807
815
|
let lastTs = null;
|
|
808
816
|
let lastEvent = null;
|
|
817
|
+
let lastEventTimeMs = Number.NEGATIVE_INFINITY;
|
|
809
818
|
let startCount = 0;
|
|
810
819
|
let installCount = 0;
|
|
811
820
|
let installVersions = [];
|
|
812
821
|
let processErrors = [];
|
|
813
822
|
let lastEntryAgeMs = Number.POSITIVE_INFINITY;
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
823
|
+
let readCount = 0;
|
|
824
|
+
const readErrors = [];
|
|
825
|
+
for (const logPath of logPaths) {
|
|
826
|
+
try {
|
|
827
|
+
// Read the whole log via deps.readFileSync, then take the tail. For a
|
|
828
|
+
// chatty daemon this can be a few MB; we only inspect the last 5000
|
|
829
|
+
// lines which is enough for the last hour of activity. If the file is
|
|
830
|
+
// small (typical case), reading it all is cheap.
|
|
831
|
+
const raw = deps.readFileSync(logPath);
|
|
832
|
+
readCount++;
|
|
833
|
+
const allLines = raw.split("\n").filter((l) => l.trim());
|
|
834
|
+
const usable = allLines.length > 5000 ? allLines.slice(-5000) : allLines;
|
|
835
|
+
for (const line of usable) {
|
|
836
|
+
let parsed;
|
|
837
|
+
try {
|
|
838
|
+
parsed = JSON.parse(line);
|
|
839
|
+
}
|
|
840
|
+
catch {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
const ts = typeof parsed.ts === "string" ? parsed.ts : null;
|
|
844
|
+
const event = typeof parsed.event === "string" ? parsed.event : null;
|
|
845
|
+
if (!ts || !event)
|
|
846
|
+
continue;
|
|
847
|
+
const tsMs = Date.parse(ts);
|
|
848
|
+
if (Number.isNaN(tsMs))
|
|
849
|
+
continue;
|
|
850
|
+
if (tsMs > lastEventTimeMs) {
|
|
851
|
+
lastEventTimeMs = tsMs;
|
|
852
|
+
lastTs = ts;
|
|
853
|
+
lastEvent = event;
|
|
854
|
+
lastEntryAgeMs = Math.max(0, now - tsMs);
|
|
855
|
+
}
|
|
856
|
+
if (tsMs < cutoff)
|
|
857
|
+
continue;
|
|
858
|
+
if (event === "daemon.daemon_started")
|
|
859
|
+
startCount++;
|
|
860
|
+
if (event === "daemon.cli_version_install_end") {
|
|
861
|
+
installCount++;
|
|
862
|
+
const meta = parsed.meta;
|
|
863
|
+
const ver = typeof meta?.version === "string" ? meta.version : null;
|
|
864
|
+
if (ver)
|
|
865
|
+
installVersions.push(ver);
|
|
866
|
+
}
|
|
867
|
+
if (event === "daemon.agent_process_error") {
|
|
868
|
+
const meta = parsed.meta;
|
|
869
|
+
const reason = typeof meta?.reason === "string" ? meta.reason : "unknown";
|
|
870
|
+
const agent = typeof meta?.agent === "string" ? meta.agent : "unknown";
|
|
871
|
+
processErrors.push(`${agent}: ${reason}`);
|
|
872
|
+
}
|
|
856
873
|
}
|
|
857
874
|
}
|
|
875
|
+
catch (error) {
|
|
876
|
+
readErrors.push(`${logPath}: ${error instanceof Error ? error.message : /* v8 ignore next -- non-Error throw is unreachable from deps.readFileSync (always Error) @preserve */ String(error)}`);
|
|
877
|
+
}
|
|
858
878
|
}
|
|
859
|
-
|
|
860
|
-
checks.push({ label: "daemon log readable", status: "fail", detail: `read failed: ${
|
|
879
|
+
if (readCount === 0) {
|
|
880
|
+
checks.push({ label: "daemon log readable", status: "fail", detail: `read failed: ${readErrors.join("; ")}` });
|
|
861
881
|
return { name: "Lifecycle", checks };
|
|
862
882
|
}
|
|
883
|
+
if (readErrors.length > 0) {
|
|
884
|
+
checks.push({ label: "daemon log readable", status: "warn", detail: `some daemon logs were unreadable: ${readErrors.join("; ")}` });
|
|
885
|
+
}
|
|
863
886
|
if (lastTs === null) {
|
|
864
887
|
checks.push({ label: "recent daemon activity", status: "warn", detail: "no parseable events in tail of daemon.ndjson" });
|
|
865
888
|
}
|