@ouro.bot/cli 0.1.0-alpha.431 → 0.1.0-alpha.433

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.
@@ -14,7 +14,7 @@ exports.getCommandHelp = getCommandHelp;
14
14
  exports.COMMAND_REGISTRY = {
15
15
  up: {
16
16
  category: "Lifecycle",
17
- description: "Start the ouro daemon. In a human TTY, bare `ouro` opens the home screen instead; noninteractive shells still route bare `ouro` to `ouro up`.",
17
+ description: "Prepare the house: start the background runtime, refresh what this machine needs, and surface anything still asking for care. In a human TTY, bare `ouro` opens the home screen instead; noninteractive shells still route bare `ouro` to `ouro up`.",
18
18
  usage: "ouro [up] [--no-repair]",
19
19
  example: "ouro up --no-repair",
20
20
  },
@@ -32,7 +32,7 @@ exports.COMMAND_REGISTRY = {
32
32
  },
33
33
  status: {
34
34
  category: "Lifecycle",
35
- description: "Show daemon status",
35
+ description: "Show the house status",
36
36
  usage: "ouro status",
37
37
  example: "ouro status",
38
38
  },
@@ -273,7 +273,7 @@ const SUBCOMMAND_HELP = {
273
273
  example: "ouro auth switch --provider minimax",
274
274
  },
275
275
  "connect perplexity": {
276
- description: "Connect Perplexity search for this agent",
276
+ description: "Connect portable Perplexity search that travels with this agent",
277
277
  usage: "ouro connect perplexity [--agent <name>]",
278
278
  example: "ouro connect perplexity",
279
279
  },
@@ -283,17 +283,17 @@ const SUBCOMMAND_HELP = {
283
283
  example: "ouro connect providers",
284
284
  },
285
285
  "connect embeddings": {
286
- description: "Connect memory embeddings for this agent",
286
+ description: "Connect portable memory embeddings that travel with this agent",
287
287
  usage: "ouro connect embeddings [--agent <name>]",
288
288
  example: "ouro connect embeddings",
289
289
  },
290
290
  "connect teams": {
291
- description: "Connect Microsoft Teams credentials and enable the Teams sense",
291
+ description: "Connect portable Microsoft Teams credentials and enable the Teams sense",
292
292
  usage: "ouro connect teams [--agent <name>]",
293
293
  example: "ouro connect teams",
294
294
  },
295
295
  "connect bluebubbles": {
296
- description: "Attach BlueBubbles iMessage to this machine only",
296
+ description: "Attach BlueBubbles iMessage to this machine only; it does not travel with the agent",
297
297
  usage: "ouro connect bluebubbles [--agent <name>]",
298
298
  example: "ouro connect bluebubbles",
299
299
  },
@@ -6,6 +6,8 @@ exports.renderOuroHomeScreen = renderOuroHomeScreen;
6
6
  exports.renderAgentPickerScreen = renderAgentPickerScreen;
7
7
  exports.resolveNamedAgentSelection = resolveNamedAgentSelection;
8
8
  exports.renderHumanReadinessBoard = renderHumanReadinessBoard;
9
+ exports.renderHumanCommandBoard = renderHumanCommandBoard;
10
+ exports.renderHouseStatusScreen = renderHouseStatusScreen;
9
11
  const runtime_1 = require("../../nerves/runtime");
10
12
  const terminal_ui_1 = require("./terminal-ui");
11
13
  function renderScreenEvent(screen) {
@@ -34,7 +36,7 @@ function buildOuroHomeActions(agents) {
34
36
  }));
35
37
  return [
36
38
  ...actions,
37
- { key: String(actions.length + 1), label: "Bring the system online", kind: "up", command: "ouro up" },
39
+ { key: String(actions.length + 1), label: "Prepare the house", kind: "up", command: "ouro up" },
38
40
  { key: String(actions.length + 2), label: "Connect an agent", kind: "connect", command: "ouro connect --agent <agent>" },
39
41
  { key: String(actions.length + 3), label: "Repair an agent", kind: "repair", command: "ouro repair --agent <agent>" },
40
42
  { key: String(actions.length + 4), label: "Show help", kind: "help", command: "ouro --help" },
@@ -59,7 +61,9 @@ function renderOuroHomeScreen(options) {
59
61
  const sections = [
60
62
  {
61
63
  title: options.agents.length === 0 ? "Start here" : "Around the house",
62
- lines: actions.map((action) => `${action.key}. ${action.label}`),
64
+ lines: options.agents.length === 0
65
+ ? ["No agents are home yet. Hatch someone new or bring an existing bundle aboard."]
66
+ : options.agents.map((agent) => `${agent} is home and ready when called.`),
63
67
  },
64
68
  ];
65
69
  const actionRows = actions.map((action, index) => ({
@@ -79,7 +83,7 @@ function renderOuroHomeScreen(options) {
79
83
  title: "Ouro home",
80
84
  summary: options.agents.length === 0
81
85
  ? "Hatch someone new or bring an existing bundle aboard."
82
- : "Pick an agent or system action without memorizing commands.",
86
+ : "Choose who to wake or what to prepare without memorizing commands.",
83
87
  sections,
84
88
  actions: actionRows,
85
89
  prompt: `Choose [1-${actions.length}] or type a name: `,
@@ -125,6 +129,19 @@ function renderHumanReadinessBoard(options) {
125
129
  ...item.detailLines,
126
130
  ],
127
131
  }));
132
+ return renderHumanCommandBoard({
133
+ title: options.title,
134
+ subtitle: options.subtitle,
135
+ summary: options.snapshot.summary,
136
+ isTTY: options.isTTY,
137
+ columns: options.columns,
138
+ sections,
139
+ actions: options.snapshot.nextActions,
140
+ prompt: options.prompt,
141
+ });
142
+ }
143
+ function renderHumanCommandBoard(options) {
144
+ renderScreenEvent("command-board");
128
145
  return (0, terminal_ui_1.renderTerminalBoard)({
129
146
  isTTY: options.isTTY,
130
147
  columns: options.columns,
@@ -132,9 +149,88 @@ function renderHumanReadinessBoard(options) {
132
149
  subtitle: options.subtitle,
133
150
  },
134
151
  title: options.title,
135
- summary: options.snapshot.summary,
136
- sections,
137
- actions: options.snapshot.nextActions,
152
+ summary: options.summary,
153
+ sections: options.sections,
154
+ actions: options.actions,
138
155
  prompt: options.prompt,
139
156
  });
140
157
  }
158
+ function renderHouseStatusScreen(options) {
159
+ renderScreenEvent("house-status");
160
+ const sections = [
161
+ {
162
+ title: "House pulse",
163
+ lines: [
164
+ `Daemon: ${options.payload.overview.daemon}`,
165
+ `Health: ${options.payload.overview.health}`,
166
+ `Outlook: ${options.payload.overview.outlookUrl}`,
167
+ `Updated: ${options.payload.overview.lastUpdated}`,
168
+ ],
169
+ },
170
+ ];
171
+ if (options.payload.agents.length > 0) {
172
+ sections.push({
173
+ title: "Agents",
174
+ lines: options.payload.agents.map((agent) => `${agent.name} — ${agent.enabled ? "enabled" : "disabled"}`),
175
+ });
176
+ }
177
+ if (options.payload.providers.length > 0) {
178
+ sections.push({
179
+ title: "Providers",
180
+ lines: options.payload.providers.map((provider) => {
181
+ const detail = [provider.readiness, provider.detail, provider.source, provider.credential].filter(Boolean).join("; ");
182
+ return `${provider.agent} ${provider.lane} — ${provider.provider} / ${provider.model}${detail ? ` — ${detail}` : ""}`;
183
+ }),
184
+ });
185
+ }
186
+ if (options.payload.senses.length > 0) {
187
+ sections.push({
188
+ title: "Senses",
189
+ lines: options.payload.senses.map((sense) => {
190
+ const status = sense.enabled ? sense.status : "disabled";
191
+ return `${sense.agent} — ${sense.label ?? sense.sense} — ${status}${sense.detail ? ` — ${sense.detail}` : ""}`;
192
+ }),
193
+ });
194
+ }
195
+ if (options.payload.workers.length > 0) {
196
+ sections.push({
197
+ title: "Workers",
198
+ lines: options.payload.workers.map((worker) => {
199
+ const details = [`restarts: ${worker.restartCount}`];
200
+ if (worker.pid !== null)
201
+ details.unshift(`pid ${worker.pid}`);
202
+ if (worker.lastExitCode !== null)
203
+ details.push(`exit=${worker.lastExitCode}`);
204
+ if (worker.lastSignal !== null)
205
+ details.push(`signal=${worker.lastSignal}`);
206
+ if (worker.errorReason)
207
+ details.push(`error: ${worker.errorReason}`);
208
+ if (worker.fixHint)
209
+ details.push(`fix: ${worker.fixHint}`);
210
+ return `${worker.agent} — ${worker.worker} — ${worker.status} — ${details.join("; ")}`;
211
+ }),
212
+ });
213
+ }
214
+ if (options.payload.sync.length > 0) {
215
+ sections.push({
216
+ title: "Git sync",
217
+ lines: options.payload.sync.map((row) => {
218
+ if (!row.enabled)
219
+ return `${row.agent} — disabled`;
220
+ if (row.gitInitialized === false)
221
+ return `${row.agent} — needs git init`;
222
+ if (row.remoteUrl)
223
+ return `${row.agent} — ${row.remote} -> ${row.remoteUrl}`;
224
+ return `${row.agent} — local only`;
225
+ }),
226
+ });
227
+ }
228
+ return renderHumanCommandBoard({
229
+ title: "House status",
230
+ subtitle: "The house is awake enough to answer clearly.",
231
+ summary: "What is awake, resting, or asking for care.",
232
+ isTTY: options.isTTY,
233
+ columns: options.columns,
234
+ sections,
235
+ });
236
+ }
@@ -7,6 +7,7 @@ exports.wrapPlain = wrapPlain;
7
7
  exports.renderOuroMasthead = renderOuroMasthead;
8
8
  exports.formatActionActorLabel = formatActionActorLabel;
9
9
  exports.renderTerminalBoard = renderTerminalBoard;
10
+ exports.renderTerminalOperation = renderTerminalOperation;
10
11
  const runtime_1 = require("../../nerves/runtime");
11
12
  const RESET = "\x1b[0m";
12
13
  const BOLD = "\x1b[1m";
@@ -87,11 +88,14 @@ function renderPanelPlain(title, lines) {
87
88
  function mastheadArt(columns) {
88
89
  if ((columns ?? 88) >= 74) {
89
90
  return [
90
- " ____ _ _ ____ ___ ____ ___ ____ ___ ____",
91
- " / __ \\| | | | _ \\ / _ \\| _ \\ / _ \\| __ ) / _ \\| _ \\",
92
- "| | | | | | | |_) | | | | |_) | | | | _ \\| | | | |_) |",
93
- "| |__| | |_| | _ <| |_| | _ <| |_| | |_) | |_| | _ <",
94
- " \\____/ \\___/|_| \\_\\\\___/|_| \\_\\\\___/|____/ \\___/|_| \\_\\",
91
+ " .----------------------------.",
92
+ " .--' O U R O B O R O S '--.",
93
+ " .' .--------------------. '.",
94
+ " / / .--------------. \\ \\",
95
+ " \\ \\ '--------------' / /",
96
+ " '. '--------------------' .'",
97
+ " '--._ _.--'",
98
+ " '------------------------'",
95
99
  ];
96
100
  }
97
101
  return [
@@ -101,10 +105,11 @@ function mastheadArt(columns) {
101
105
  }
102
106
  function renderOuroMasthead(options) {
103
107
  const lines = mastheadArt(options.columns);
108
+ const subtitle = options.subtitle ?? "the house wakes when called";
104
109
  const branded = [
105
110
  ...lines,
106
111
  "OUROBOROS",
107
- ...(options.subtitle ? [options.subtitle] : []),
112
+ subtitle,
108
113
  ];
109
114
  if (!options.isTTY) {
110
115
  return `${branded.join("\n")}\n`;
@@ -112,7 +117,7 @@ function renderOuroMasthead(options) {
112
117
  const ttyLines = [
113
118
  ...lines.map((line, index) => color(line, index < 2 ? GLOW : SCALE, true)),
114
119
  color("OUROBOROS", BONE, true),
115
- ...(options.subtitle ? [color(options.subtitle, MIST)] : []),
120
+ color(subtitle, MIST),
116
121
  ];
117
122
  return `${ttyLines.join("\n")}\n`;
118
123
  }
@@ -126,17 +131,19 @@ function renderActionLine(action) {
126
131
  return `${action.label} ${chips.join(" ")}`;
127
132
  }
128
133
  function renderTerminalBoard(options) {
129
- (0, runtime_1.emitNervesEvent)({
130
- component: "daemon",
131
- event: "daemon.terminal_board_rendered",
132
- message: "rendered shared terminal board",
133
- meta: {
134
- title: options.title,
135
- sections: options.sections?.length ?? 0,
136
- actions: options.actions?.length ?? 0,
137
- tty: options.isTTY,
138
- },
139
- });
134
+ if (!options.suppressEvent) {
135
+ (0, runtime_1.emitNervesEvent)({
136
+ component: "daemon",
137
+ event: "daemon.terminal_board_rendered",
138
+ message: "rendered shared terminal board",
139
+ meta: {
140
+ title: options.title,
141
+ sections: options.sections?.length ?? 0,
142
+ actions: options.actions?.length ?? 0,
143
+ tty: options.isTTY,
144
+ },
145
+ });
146
+ }
140
147
  const width = boardWidth(options.columns);
141
148
  const blocks = [];
142
149
  blocks.push(renderOuroMasthead({
@@ -167,3 +174,45 @@ function renderTerminalBoard(options) {
167
174
  }
168
175
  return `${blocks.join("\n\n")}\n`;
169
176
  }
177
+ function formatOperationStep(step) {
178
+ const marker = step.status === "done"
179
+ ? "✓"
180
+ : step.status === "failed"
181
+ ? "✗"
182
+ : step.status === "active"
183
+ ? "→"
184
+ : "○";
185
+ const detail = step.detail ? ` — ${step.detail}` : "";
186
+ return `${marker} ${step.label}${detail}`;
187
+ }
188
+ function renderTerminalOperation(options) {
189
+ const steps = options.steps ?? [];
190
+ const currentLines = options.currentStep
191
+ ? [
192
+ options.currentStep.label,
193
+ ...(options.currentStep.detailLines ?? []),
194
+ ]
195
+ : ["Standing by."];
196
+ const progressLines = steps.length > 0
197
+ ? steps.map((step) => formatOperationStep(step))
198
+ : ["No active steps yet."];
199
+ return renderTerminalBoard({
200
+ isTTY: options.isTTY,
201
+ columns: options.columns,
202
+ masthead: options.masthead,
203
+ title: options.title,
204
+ summary: options.summary,
205
+ sections: [
206
+ {
207
+ title: "Right now",
208
+ lines: currentLines,
209
+ },
210
+ {
211
+ title: "Progress",
212
+ lines: progressLines,
213
+ },
214
+ ],
215
+ prompt: options.prompt,
216
+ suppressEvent: options.suppressEvent,
217
+ });
218
+ }
@@ -14,6 +14,7 @@
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.CommandProgress = exports.UpProgress = void 0;
16
16
  const runtime_1 = require("../../nerves/runtime");
17
+ const terminal_ui_1 = require("./terminal-ui");
17
18
  // ── ANSI constants (shared with startup-tui.ts pattern) ──
18
19
  const SPINNER_FRAMES = "\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F";
19
20
  const RESET = "\x1b[0m";
@@ -33,6 +34,7 @@ function splitDetailLines(detail) {
33
34
  class UpProgress {
34
35
  write;
35
36
  isTTY;
37
+ columns;
36
38
  now;
37
39
  autoRender;
38
40
  renderIntervalMs;
@@ -51,6 +53,7 @@ class UpProgress {
51
53
  this.write = options?.write ?? ((text) => process.stdout.write(text));
52
54
  /* v8 ignore next -- thin wrapper: real isTTY check injected for testability @preserve */
53
55
  this.isTTY = options?.isTTY ?? (process.stdout.isTTY === true);
56
+ this.columns = options?.columns;
54
57
  /* v8 ignore next -- thin wrapper: real Date.now injected for testability @preserve */
55
58
  this.now = options?.now ?? (() => Date.now());
56
59
  this.autoRender = options?.autoRender ?? false;
@@ -191,28 +194,7 @@ class UpProgress {
191
194
  if (!this.isTTY) {
192
195
  return "";
193
196
  }
194
- const lines = [];
195
- // Completed phases
196
- for (const phase of this.completed) {
197
- const detailStr = phase.detail ? ` ${DIM}\u2014 ${phase.detail}${RESET}` : "";
198
- if (phase.status === "failure") {
199
- lines.push(` ${RED}\u2717${RESET} ${phase.label}${detailStr}`);
200
- }
201
- else {
202
- lines.push(` ${GREEN}\u2713${RESET} ${phase.label}${detailStr}`);
203
- }
204
- }
205
- // Current phase with spinner
206
- if (this.currentPhase) {
207
- const elapsed = now - this.currentPhase.startedAt;
208
- const elapsedSec = (elapsed / 1000).toFixed(1);
209
- const frameIndex = Math.floor(elapsed / 80) % SPINNER_FRAMES.length;
210
- const spinner = SPINNER_FRAMES[frameIndex];
211
- lines.push(` ${BOLD}${spinner}${RESET} ${this.currentPhase.label} ${DIM}(${elapsedSec}s)${RESET}`);
212
- for (const detailLine of splitDetailLines(this.currentPhase.detail)) {
213
- lines.push(` ${DIM}${detailLine}${RESET}`);
214
- }
215
- }
197
+ const lines = this.renderLines(now);
216
198
  let output = "";
217
199
  if (this.prevLineCount > 0) {
218
200
  output += `\x1b[${this.prevLineCount}A`;
@@ -262,9 +244,69 @@ class UpProgress {
262
244
  }
263
245
  flushRender() {
264
246
  const output = this.render(this.now());
265
- if (output) {
266
- this.write(output);
247
+ this.write(output);
248
+ }
249
+ renderLines(now) {
250
+ if (this.eventScope === "up") {
251
+ return this.renderUpScreen(now);
252
+ }
253
+ const lines = [];
254
+ for (const phase of this.completed) {
255
+ const detailStr = phase.detail ? ` ${DIM}\u2014 ${phase.detail}${RESET}` : "";
256
+ if (phase.status === "failure") {
257
+ lines.push(` ${RED}\u2717${RESET} ${phase.label}${detailStr}`);
258
+ }
259
+ else {
260
+ lines.push(` ${GREEN}\u2713${RESET} ${phase.label}${detailStr}`);
261
+ }
262
+ }
263
+ if (this.currentPhase) {
264
+ const elapsed = now - this.currentPhase.startedAt;
265
+ const elapsedSec = (elapsed / 1000).toFixed(1);
266
+ const frameIndex = Math.floor(elapsed / 80) % SPINNER_FRAMES.length;
267
+ const spinner = SPINNER_FRAMES[frameIndex];
268
+ lines.push(` ${BOLD}${spinner}${RESET} ${this.currentPhase.label} ${DIM}(${elapsedSec}s)${RESET}`);
269
+ for (const detailLine of splitDetailLines(this.currentPhase.detail)) {
270
+ lines.push(` ${DIM}${detailLine}${RESET}`);
271
+ }
272
+ }
273
+ return lines;
274
+ }
275
+ renderUpScreen(now) {
276
+ const steps = this.completed.map((phase) => ({
277
+ label: phase.label,
278
+ status: phase.status === "failure" ? "failed" : "done",
279
+ detail: phase.detail,
280
+ }));
281
+ let currentStepLabel = "Standing by.";
282
+ let currentStepDetails = [];
283
+ if (this.currentPhase) {
284
+ const elapsed = now - this.currentPhase.startedAt;
285
+ const elapsedSec = (elapsed / 1000).toFixed(1);
286
+ const frameIndex = Math.floor(elapsed / 80) % SPINNER_FRAMES.length;
287
+ const spinner = SPINNER_FRAMES[frameIndex];
288
+ currentStepLabel = `${spinner} ${this.currentPhase.label} (${elapsedSec}s)`;
289
+ currentStepDetails = splitDetailLines(this.currentPhase.detail);
290
+ steps.push({
291
+ label: this.currentPhase.label,
292
+ status: "active",
293
+ });
267
294
  }
295
+ return (0, terminal_ui_1.renderTerminalOperation)({
296
+ isTTY: true,
297
+ columns: this.columns,
298
+ masthead: {
299
+ subtitle: "Preparing the house.",
300
+ },
301
+ title: "Preparing the house",
302
+ summary: "Ouro is warming the background systems and checking what still needs care before anyone steps in.",
303
+ currentStep: {
304
+ label: currentStepLabel,
305
+ detailLines: currentStepDetails,
306
+ },
307
+ steps,
308
+ suppressEvent: true,
309
+ }).trimEnd().split("\n");
268
310
  }
269
311
  }
270
312
  exports.UpProgress = UpProgress;
@@ -33,12 +33,14 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CLI_UPDATE_CHECK_TIMEOUT_MS = void 0;
36
37
  exports.checkForUpdate = checkForUpdate;
37
38
  exports.startUpdateChecker = startUpdateChecker;
38
39
  exports.stopUpdateChecker = stopUpdateChecker;
39
40
  const semver = __importStar(require("semver"));
40
41
  const runtime_1 = require("../../nerves/runtime");
41
42
  const DEFAULT_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
43
+ exports.CLI_UPDATE_CHECK_TIMEOUT_MS = 3_000;
42
44
  async function checkForUpdate(currentVersion, deps) {
43
45
  (0, runtime_1.emitNervesEvent)({
44
46
  component: "daemon",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.431",
3
+ "version": "0.1.0-alpha.433",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",