@ouro.bot/cli 0.1.0-alpha.269 → 0.1.0-alpha.270

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,12 @@
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.270",
6
+ "changes": [
7
+ "feat(daemon): `ouro status` now shows a new `Agents` section listing every discovered bundle with its enabled/disabled state. Previously disabled agents were completely invisible in status \u2014 the Senses/Workers/Git Sync sections only iterate managed (enabled) bundles, so a bundle with `\"enabled\": false` in agent.json left no trace in the output. New helper `listAllBundleAgents()` in `agent-discovery.ts` walks the bundles root and returns `{ name, enabled }` for every `<name>.ouro` with a parseable agent.json, and `listEnabledBundleAgents()` now delegates to it. Daemon status payload carries a new `agents: BundleAgentRow[]` field (backward-compat optional in the parser). The stopped-daemon renderer also reads bundles directly from disk so the Agents section works when the daemon is down."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.269",
6
12
  "changes": [
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listAllBundleAgents = listAllBundleAgents;
36
37
  exports.listEnabledBundleAgents = listEnabledBundleAgents;
37
38
  exports.listBundleSyncRows = listBundleSyncRows;
38
39
  const fs = __importStar(require("fs"));
@@ -40,7 +41,17 @@ const path = __importStar(require("path"));
40
41
  const child_process_1 = require("child_process");
41
42
  const identity_1 = require("../identity");
42
43
  const runtime_1 = require("../../nerves/runtime");
43
- function listEnabledBundleAgents(options = {}) {
44
+ /**
45
+ * Walk the bundles root and return one row per `<name>.ouro` directory whose
46
+ * `agent.json` is readable and parseable. Includes both enabled and disabled
47
+ * agents — the caller decides what to do with the `enabled` flag.
48
+ *
49
+ * Bundles whose `agent.json` is missing, malformed, or unreadable are skipped
50
+ * silently (they aren't real agents from the harness's perspective).
51
+ *
52
+ * Sorted alphabetically by name for stable display.
53
+ */
54
+ function listAllBundleAgents(options = {}) {
44
55
  const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
45
56
  const readdirSync = options.readdirSync ?? fs.readdirSync;
46
57
  const readFileSync = options.readFileSync ?? fs.readFileSync;
@@ -75,11 +86,12 @@ function listEnabledBundleAgents(options = {}) {
75
86
  catch {
76
87
  continue;
77
88
  }
78
- if (enabled) {
79
- discovered.push(agentName);
80
- }
89
+ discovered.push({ name: agentName, enabled });
81
90
  }
82
- return discovered.sort((left, right) => left.localeCompare(right));
91
+ return discovered.sort((left, right) => left.name.localeCompare(right.name));
92
+ }
93
+ function listEnabledBundleAgents(options = {}) {
94
+ return listAllBundleAgents(options).filter((row) => row.enabled).map((row) => row.name);
83
95
  }
84
96
  /**
85
97
  * Read the per-agent sync block from each enabled bundle's agent.json.
@@ -74,13 +74,16 @@ function parseStatusPayload(data) {
74
74
  const senses = raw.senses;
75
75
  const workers = raw.workers;
76
76
  const sync = raw.sync;
77
+ const agents = raw.agents;
77
78
  if (!overview || typeof overview !== "object" || Array.isArray(overview))
78
79
  return null;
79
80
  if (!Array.isArray(senses) || !Array.isArray(workers))
80
81
  return null;
81
- // sync is optional for backward compatibility — older daemons may omit it
82
+ // sync and agents are optional for backward compatibility — older daemons may omit them
82
83
  if (sync !== undefined && !Array.isArray(sync))
83
84
  return null;
85
+ if (agents !== undefined && !Array.isArray(agents))
86
+ return null;
84
87
  const parsedOverview = {
85
88
  daemon: stringField(overview.daemon) ?? "unknown",
86
89
  health: stringField(overview.health) ?? "unknown",
@@ -156,15 +159,27 @@ function parseStatusPayload(data) {
156
159
  parsed.gitInitialized = gitInitialized;
157
160
  return parsed;
158
161
  });
162
+ const parsedAgents = (agents ?? []).map((entry) => {
163
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
164
+ return null;
165
+ const row = entry;
166
+ const name = stringField(row.name);
167
+ const enabled = booleanField(row.enabled);
168
+ if (!name || enabled === null)
169
+ return null;
170
+ return { name, enabled };
171
+ });
159
172
  if (parsedSenses.some((row) => row === null) ||
160
173
  parsedWorkers.some((row) => row === null) ||
161
- parsedSync.some((row) => row === null))
174
+ parsedSync.some((row) => row === null) ||
175
+ parsedAgents.some((row) => row === null))
162
176
  return null;
163
177
  return {
164
178
  overview: parsedOverview,
165
179
  senses: parsedSenses,
166
180
  workers: parsedWorkers,
167
181
  sync: parsedSync,
182
+ agents: parsedAgents,
168
183
  };
169
184
  }
170
185
  // ── ANSI color helpers (private) ──
@@ -258,6 +273,21 @@ function formatDaemonStatusOutput(response, fallback) {
258
273
  lines.push(kvLine("Health", `${statusDot(ov.health)} ${ov.health}`));
259
274
  lines.push(kvLine("Updated", ov.lastUpdated));
260
275
  lines.push("");
276
+ // ── Agents ──
277
+ // Every discovered bundle, including disabled ones. The Senses/Workers/
278
+ // Git Sync sections below only show enabled bundles, so without this
279
+ // section disabled agents would be invisible in `ouro status`.
280
+ if (payload.agents.length > 0) {
281
+ lines.push(` ${teal("──")} ${bold("Agents")} ${teal("─".repeat(37))}`);
282
+ const agentNameWidth = Math.max(12, ...payload.agents.map((r) => r.name.length));
283
+ for (const row of payload.agents) {
284
+ const name = row.name.padEnd(agentNameWidth);
285
+ const dot = row.enabled ? green("●") : dim("○");
286
+ const stateText = row.enabled ? "enabled " : "disabled";
287
+ lines.push(` ${name} ${dot} ${stateText}`);
288
+ }
289
+ lines.push("");
290
+ }
261
291
  // ── Senses ──
262
292
  if (payload.senses.length > 0) {
263
293
  lines.push(` ${teal("──")} ${bold("Senses")} ${teal("─".repeat(37))}`);
@@ -360,7 +390,7 @@ function formatVersionOutput() {
360
390
  return mode === "dev" ? `${version} (dev)` : version;
361
391
  /* v8 ignore stop */
362
392
  }
363
- function buildStoppedStatusPayload(socketPath, syncRows = []) {
393
+ function buildStoppedStatusPayload(socketPath, syncRows = [], agentRows = []) {
364
394
  const metadata = (0, runtime_metadata_1.getRuntimeMetadata)();
365
395
  const repoRoot = (0, identity_1.getRepoRoot)();
366
396
  return {
@@ -381,12 +411,15 @@ function buildStoppedStatusPayload(socketPath, syncRows = []) {
381
411
  senses: [],
382
412
  workers: [],
383
413
  sync: syncRows,
414
+ agents: agentRows,
384
415
  };
385
416
  }
386
417
  function daemonUnavailableStatusOutput(socketPath, healthFilePath) {
387
- // Read per-agent sync config from disk so the user still sees it when the daemon is down.
388
- // Best-effort: any fs error returns [] and the section is omitted.
418
+ // Read per-agent sync config and bundle list from disk so the user still
419
+ // sees them when the daemon is down. Best-effort: any fs error returns []
420
+ // and the corresponding section is omitted.
389
421
  let syncRows = [];
422
+ let agentRows = [];
390
423
  try {
391
424
  syncRows = (0, agent_discovery_1.listBundleSyncRows)();
392
425
  }
@@ -394,6 +427,12 @@ function daemonUnavailableStatusOutput(socketPath, healthFilePath) {
394
427
  // listBundleSyncRows already swallows fs errors internally; this catch is a defensive
395
428
  // safety net for environments where the fs module itself is partially mocked.
396
429
  }
430
+ try {
431
+ agentRows = (0, agent_discovery_1.listAllBundleAgents)();
432
+ }
433
+ catch {
434
+ // Same defensive safety net for the bundle list.
435
+ }
397
436
  /* v8 ignore start — tombstone read tested in daemon-status-tombstone.test; branch misreported @preserve */
398
437
  const tombstone = (0, daemon_tombstone_1.readDaemonTombstone)();
399
438
  const deathLine = tombstone
@@ -404,7 +443,7 @@ function daemonUnavailableStatusOutput(socketPath, healthFilePath) {
404
443
  formatDaemonStatusOutput({
405
444
  ok: true,
406
445
  summary: "daemon not running",
407
- data: buildStoppedStatusPayload(socketPath, syncRows),
446
+ data: buildStoppedStatusPayload(socketPath, syncRows, agentRows),
408
447
  }, "daemon not running"),
409
448
  "",
410
449
  ];
@@ -368,6 +368,7 @@ class OuroDaemon {
368
368
  const senses = this.senseManager?.listSenseRows() ?? [];
369
369
  const repoRoot = (0, identity_1.getRepoRoot)();
370
370
  const sync = (0, agent_discovery_1.listBundleSyncRows)({ bundlesRoot: this.bundlesRoot });
371
+ const agents = (0, agent_discovery_1.listAllBundleAgents)({ bundlesRoot: this.bundlesRoot });
371
372
  return {
372
373
  overview: {
373
374
  daemon: "running",
@@ -383,6 +384,7 @@ class OuroDaemon {
383
384
  workers,
384
385
  senses,
385
386
  sync,
387
+ agents,
386
388
  };
387
389
  }
388
390
  async start() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.269",
3
+ "version": "0.1.0-alpha.270",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",