@ouro.bot/cli 0.1.0-alpha.256 → 0.1.0-alpha.258

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,21 @@
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.258",
6
+ "changes": [
7
+ "fix(tui): deferred ESC handling fixes arrow keys and Alt+Enter.",
8
+ "fix(tui): green cursor background.",
9
+ "fix(tui): clipboard fallback for macOS temp screenshots, proper path splitting and unescaping for image drag-drop."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.257",
14
+ "changes": [
15
+ "test(daemon): convert all `daemon-cli.test.ts` auth/thoughts/config tests to use `os.tmpdir()` via the new `createTmpBundle()` helper. Previously these tests wrote real bundles to `~/AgentBundles/auth-local-${Date.now()}.ouro` etc., relying on `try/finally` cleanup that doesn't fire on test interruption \u2014 leaking bundle directories into the developer's home and inflating noise on the running daemon. The `REAL_BUNDLES_WRITE_ALLOWLIST` ratchet in `test-isolation.contract.test.ts` is now empty.",
16
+ "fix(daemon/cli-exec): plumb `bundlesRoot` and `secretsRoot` deps through the auth.run / auth.verify / auth.switch / config.model / config.models / thoughts handlers so tests can route reads/writes to a tmpdir without monkey-patching the identity module. Production code paths still default to `getAgentBundlesRoot()` and `~/.agentsecrets`."
17
+ ]
18
+ },
4
19
  {
5
20
  "version": "0.1.0-alpha.256",
6
21
  "changes": [
@@ -1345,7 +1345,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1345
1345
  }
1346
1346
  // ── auth (local, no daemon socket needed) ──
1347
1347
  if (command.kind === "auth.run") {
1348
- const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent).config.humanFacing.provider;
1348
+ const provider = command.provider ?? (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot).config.humanFacing.provider;
1349
1349
  /* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
1350
1350
  const authRunner = deps.runAuthFlow ?? (await Promise.resolve().then(() => __importStar(require("../auth/auth-flow")))).runRuntimeAuthFlow;
1351
1351
  const result = await authRunner({
@@ -1359,7 +1359,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1359
1359
  // Verify the credentials actually work by pinging the provider
1360
1360
  /* v8 ignore start -- integration: real API ping after auth @preserve */
1361
1361
  try {
1362
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
1362
+ const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent, { secretsRoot: deps.secretsRoot });
1363
1363
  const status = await verifyProviderCredentials(provider, secrets.providers);
1364
1364
  deps.writeStdout(`${provider}: ${status}`);
1365
1365
  }
@@ -1372,7 +1372,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1372
1372
  // ── auth verify (local, no daemon socket needed) ──
1373
1373
  /* v8 ignore start -- auth verify/switch: tested in daemon-cli.test.ts but v8 traces differ in CI @preserve */
1374
1374
  if (command.kind === "auth.verify") {
1375
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
1375
+ const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent, { secretsRoot: deps.secretsRoot });
1376
1376
  const providers = secrets.providers;
1377
1377
  if (command.provider) {
1378
1378
  const status = await verifyProviderCredentials(command.provider, providers);
@@ -1391,7 +1391,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1391
1391
  }
1392
1392
  // ── auth switch (local, no daemon socket needed) ──
1393
1393
  if (command.kind === "auth.switch") {
1394
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
1394
+ const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent, { secretsRoot: deps.secretsRoot });
1395
1395
  const providerSecrets = secrets.providers[command.provider];
1396
1396
  if (!providerSecrets || !hasStoredCredentials(command.provider, providerSecrets)) {
1397
1397
  const message = `no credentials stored for ${command.provider}. Run \`ouro auth --agent ${command.agent} --provider ${command.provider}\` first.`;
@@ -1406,11 +1406,11 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1406
1406
  return message;
1407
1407
  }
1408
1408
  if (command.facing) {
1409
- (0, auth_flow_1.writeAgentProviderSelection)(command.agent, command.facing, command.provider);
1409
+ (0, auth_flow_1.writeAgentProviderSelection)(command.agent, command.facing, command.provider, deps.bundlesRoot);
1410
1410
  }
1411
1411
  else {
1412
- (0, auth_flow_1.writeAgentProviderSelection)(command.agent, "human", command.provider);
1413
- (0, auth_flow_1.writeAgentProviderSelection)(command.agent, "agent", command.provider);
1412
+ (0, auth_flow_1.writeAgentProviderSelection)(command.agent, "human", command.provider, deps.bundlesRoot);
1413
+ (0, auth_flow_1.writeAgentProviderSelection)(command.agent, "agent", command.provider, deps.bundlesRoot);
1414
1414
  }
1415
1415
  const message = `switched ${command.agent} to ${command.provider} (verified working)`;
1416
1416
  deps.writeStdout(message);
@@ -1420,14 +1420,14 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1420
1420
  // ── config models (local, no daemon socket needed) ──
1421
1421
  /* v8 ignore start -- config models: tested via daemon-cli.test.ts @preserve */
1422
1422
  if (command.kind === "config.models") {
1423
- const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
1423
+ const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
1424
1424
  const provider = config.humanFacing.provider;
1425
1425
  if (provider !== "github-copilot") {
1426
1426
  const message = `model listing not available for ${provider} — check provider documentation.`;
1427
1427
  deps.writeStdout(message);
1428
1428
  return message;
1429
1429
  }
1430
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
1430
+ const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent, { secretsRoot: deps.secretsRoot });
1431
1431
  const ghConfig = secrets.providers["github-copilot"];
1432
1432
  if (!ghConfig.githubToken || !ghConfig.baseUrl) {
1433
1433
  throw new Error(`github-copilot credentials not configured. Run \`ouro auth --agent ${command.agent} --provider github-copilot\` first.`);
@@ -1454,10 +1454,10 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1454
1454
  if (command.kind === "config.model") {
1455
1455
  const facing = command.facing ?? "human";
1456
1456
  // Validate model availability for github-copilot before writing
1457
- const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent);
1457
+ const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
1458
1458
  const facingConfig = facing === "human" ? config.humanFacing : config.agentFacing;
1459
1459
  if (facingConfig.provider === "github-copilot") {
1460
- const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent);
1460
+ const { secrets } = (0, auth_flow_1.loadAgentSecrets)(command.agent, { secretsRoot: deps.secretsRoot });
1461
1461
  const ghConfig = secrets.providers["github-copilot"];
1462
1462
  if (ghConfig.githubToken && ghConfig.baseUrl) {
1463
1463
  const fetchFn = deps.fetchImpl ?? fetch;
@@ -1482,7 +1482,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1482
1482
  }
1483
1483
  }
1484
1484
  }
1485
- const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, facing, command.modelName);
1485
+ const { provider, previousModel } = (0, auth_flow_1.writeAgentModel)(command.agent, facing, command.modelName, { bundlesRoot: deps.bundlesRoot });
1486
1486
  const message = previousModel
1487
1487
  ? `updated ${command.agent} model on ${provider}: ${previousModel} → ${command.modelName}`
1488
1488
  : `set ${command.agent} model on ${provider}: ${command.modelName}`;
@@ -1569,7 +1569,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
1569
1569
  if (command.kind === "thoughts") {
1570
1570
  try {
1571
1571
  const agentName = command.agent ?? (0, identity_1.getAgentName)();
1572
- const agentRoot = path.join((0, identity_1.getAgentBundlesRoot)(), `${agentName}.ouro`);
1572
+ /* v8 ignore next -- production fallback: tests always inject bundlesRoot via createTmpBundle @preserve */
1573
+ const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
1574
+ const agentRoot = path.join(bundlesRoot, `${agentName}.ouro`);
1573
1575
  const sessionFilePath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
1574
1576
  if (command.json) {
1575
1577
  try {
@@ -50,6 +50,7 @@ exports.resolveImageContent = resolveImageContent;
50
50
  * files, and produces OpenAI-compatible `image_url` content blocks.
51
51
  */
52
52
  const promises_1 = require("fs/promises");
53
+ const child_process_1 = require("child_process");
53
54
  const path = __importStar(require("path"));
54
55
  const runtime_1 = require("../../nerves/runtime");
55
56
  /** Matches image file extensions (case-insensitive) */
@@ -58,10 +59,14 @@ exports.IMAGE_EXTENSION_REGEX = /\.(png|jpe?g|gif|webp)$/i;
58
59
  function isImagePath(str) {
59
60
  return exports.IMAGE_EXTENSION_REGEX.test(unescapePath(str));
60
61
  }
61
- /** Convert backslash-escaped spaces to plain spaces (macOS drag-drop format) */
62
+ /** Strip shell escape backslashes from a path (macOS/Linux drag-drop format) */
62
63
  function unescapePath(str) {
63
- return str.replace(/\\ /g, " ");
64
+ // Handle double-backslashes first (actual backslash in filename)
65
+ // then remove single escape backslashes
66
+ return str.replace(/\\(.)/g, "$1");
64
67
  }
68
+ /** Detect temp screenshot paths from macOS screencapture */
69
+ const TEMP_SCREENSHOT_RE = /\/TemporaryItems\/.*screencaptureui.*\/Screenshot/i;
65
70
  /** Map file extension to MIME media type */
66
71
  function extensionToMediaType(ext) {
67
72
  switch (ext.toLowerCase()) {
@@ -80,41 +85,85 @@ function extensionToMediaType(ext) {
80
85
  }
81
86
  /**
82
87
  * Read an image file and return base64 + media type.
83
- * Returns null on any error (file not found, permission denied, etc.)
88
+ * Falls back to clipboard for temp screenshot paths (macOS screencapture
89
+ * creates ephemeral files in TemporaryItems that are cleaned up quickly).
90
+ * Returns null on any error.
84
91
  */
85
92
  async function tryReadImage(absolutePath) {
93
+ // Try reading the file directly first
86
94
  try {
87
95
  const buffer = await (0, promises_1.readFile)(absolutePath);
88
- const ext = path.extname(absolutePath);
96
+ if (buffer.length > 0) {
97
+ const ext = path.extname(absolutePath);
98
+ return {
99
+ base64: Buffer.from(buffer).toString("base64"),
100
+ mediaType: extensionToMediaType(ext),
101
+ };
102
+ }
103
+ }
104
+ catch {
105
+ // File not found or permission denied — try clipboard fallback
106
+ }
107
+ // Clipboard fallback for macOS temp screenshots
108
+ /* v8 ignore start -- clipboard fallback requires real macOS screencapture @preserve */
109
+ if (process.platform === "darwin" && TEMP_SCREENSHOT_RE.test(absolutePath)) {
110
+ const clipboardImage = await getImageFromClipboard();
111
+ if (clipboardImage)
112
+ return clipboardImage;
113
+ }
114
+ /* v8 ignore stop */
115
+ (0, runtime_1.emitNervesEvent)({
116
+ component: "senses",
117
+ event: "senses.image_read_error",
118
+ message: `Failed to read image: ${absolutePath}`,
119
+ meta: { path: absolutePath },
120
+ });
121
+ return null;
122
+ }
123
+ /**
124
+ * Read image data from macOS clipboard via osascript.
125
+ * Used as fallback when temp screenshot files are already cleaned up.
126
+ */
127
+ /* v8 ignore start -- macOS clipboard integration @preserve */
128
+ async function getImageFromClipboard() {
129
+ const tmpPath = "/tmp/ouro_clipboard_image.png";
130
+ try {
131
+ // Check if clipboard has image data
132
+ (0, child_process_1.execFileSync)("osascript", ["-e", "the clipboard as «class PNGf»"], { stdio: "pipe" });
133
+ // Save clipboard image to temp file
134
+ (0, child_process_1.execFileSync)("osascript", [
135
+ "-e", "set png_data to (the clipboard as «class PNGf»)",
136
+ "-e", `set fp to open for access POSIX file "${tmpPath}" with write permission`,
137
+ "-e", "write png_data to fp",
138
+ "-e", "close access fp",
139
+ ], { stdio: "pipe" });
140
+ const buffer = await (0, promises_1.readFile)(tmpPath);
141
+ void (0, promises_1.unlink)(tmpPath).catch(() => { });
142
+ if (buffer.length === 0)
143
+ return null;
89
144
  return {
90
145
  base64: Buffer.from(buffer).toString("base64"),
91
- mediaType: extensionToMediaType(ext),
146
+ mediaType: "image/png",
92
147
  };
93
148
  }
94
149
  catch {
95
- (0, runtime_1.emitNervesEvent)({
96
- component: "senses",
97
- event: "senses.image_read_error",
98
- message: `Failed to read image: ${absolutePath}`,
99
- meta: { path: absolutePath },
100
- });
101
150
  return null;
102
151
  }
103
152
  }
153
+ /* v8 ignore stop */
104
154
  /** Format an image reference placeholder */
105
155
  function formatImageRef(n) {
106
156
  return `[Image #${n}]`;
107
157
  }
108
158
  /**
109
- * Scan input for absolute path tokens ending with image extensions.
110
- * Matches `/` followed by non-whitespace chars (allowing `\ ` for escaped spaces)
111
- * ending with an image extension.
159
+ * Scan input for absolute image file paths and replace with [Image #N] references.
160
+ * Handles backslash-escaped characters (macOS drag-drop) and paths with spaces.
112
161
  */
113
162
  function replacePathsWithRefs(input) {
114
163
  const images = new Map();
115
- // Match absolute paths starting with / that end with an image extension.
116
- // Allow backslash-escaped spaces (like `\ ` from macOS drag-drop).
117
- const pathRegex = /(\/(?:[^\s]|\\ )+\.(?:png|jpe?g|gif|webp))/gi;
164
+ // Match absolute paths: start with /, contain non-whitespace or backslash-escaped chars,
165
+ // end with an image extension. The (?:\\.|\S) alternation allows `\ ` sequences.
166
+ const pathRegex = /(?:\/(?:\\.|[^\s])+\.(?:png|jpe?g|gif|webp))/gi;
118
167
  let counter = 0;
119
168
  const text = input.replace(pathRegex, (match) => {
120
169
  counter++;
@@ -163,9 +163,10 @@ function InputArea({ onSubmit, onCtrlC, history, queuedInputs, onPopQueue, agent
163
163
  const cursorRef = (0, react_1.useRef)(0);
164
164
  const historyIdx = (0, react_1.useRef)(-1);
165
165
  const savedInput = (0, react_1.useRef)("");
166
- // Track last ESC timestamp for Alt+Enter detection.
167
- // Ink 3.2 splits \x1b\r into separate escape + return events.
168
- // We detect the pattern by checking if Return arrives within 50ms of Escape.
166
+ // Deferred ESC handling: Ink 3.2 fires escape events for ESC prefix of arrow
167
+ // keys (\x1b[D) and Alt+Enter (\x1b\r). We defer ESC actions by 80ms — if
168
+ // another key arrives in that window, it was an escape sequence, not standalone ESC.
169
+ const escTimerRef = (0, react_1.useRef)(null);
169
170
  const lastEscTime = (0, react_1.useRef)(0);
170
171
  const tooltipTimerRef = (0, react_1.useRef)(null);
171
172
  (0, react_1.useEffect)(() => () => {
@@ -211,25 +212,36 @@ function InputArea({ onSubmit, onCtrlC, history, queuedInputs, onPopQueue, agent
211
212
  setTooltip("");
212
213
  if (key.escape) {
213
214
  lastEscTime.current = Date.now();
214
- if (inputRef.current) {
215
- updateInput("");
216
- historyIdx.current = -1;
217
- setTooltip("Esc again to clear");
218
- if (tooltipTimerRef.current)
219
- clearTimeout(tooltipTimerRef.current);
220
- tooltipTimerRef.current = setTimeout(() => setTooltip(""), 2000);
221
- }
222
- else if (queuedInputs.length > 0) {
223
- // Pop all queued messages into input for editing
224
- const items = onPopQueue();
225
- updateInput(items.join("\n"));
226
- historyIdx.current = -1;
227
- }
228
- else {
229
- setTooltip("");
230
- }
215
+ // Defer ESC action — if another key arrives within 80ms, this was an escape
216
+ // sequence prefix (arrow key, Alt+Enter), not a standalone Escape press.
217
+ if (escTimerRef.current)
218
+ clearTimeout(escTimerRef.current);
219
+ escTimerRef.current = setTimeout(() => {
220
+ escTimerRef.current = null;
221
+ if (inputRef.current) {
222
+ updateInput("");
223
+ historyIdx.current = -1;
224
+ setTooltip("Esc again to clear");
225
+ if (tooltipTimerRef.current)
226
+ clearTimeout(tooltipTimerRef.current);
227
+ tooltipTimerRef.current = setTimeout(() => setTooltip(""), 2000);
228
+ }
229
+ else if (queuedInputs.length > 0) {
230
+ const items = onPopQueue();
231
+ updateInput(items.join("\n"));
232
+ historyIdx.current = -1;
233
+ }
234
+ else {
235
+ setTooltip("");
236
+ }
237
+ }, 80);
231
238
  return;
232
239
  }
240
+ // Cancel pending ESC action — this key is part of an escape sequence
241
+ if (escTimerRef.current) {
242
+ clearTimeout(escTimerRef.current);
243
+ escTimerRef.current = null;
244
+ }
233
245
  if (key.return) {
234
246
  // Alt+Enter: detect via key.meta OR recent ESC (within 50ms — Ink splits \x1b\r)
235
247
  const recentEsc = (Date.now() - lastEscTime.current) < 50;
@@ -389,8 +401,8 @@ function InputArea({ onSubmit, onCtrlC, history, queuedInputs, onPopQueue, agent
389
401
  });
390
402
  const isMultiline = input.includes("\n");
391
403
  const inputLines = input.split("\n");
392
- return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(termWidth()) }), isMultiline ? ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: inputLines.map((line, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: i === 0 ? ") " : "· " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: line }), i === inputLines.length - 1 ? (cursorVisible ? (0, jsx_runtime_1.jsx)(ink_1.Text, { inverse: true, children: " " }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " })) : null] }, i))) })) : ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: ") " }), !input && queuedInputs.length > 0 ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: "Press up to edit queued messages" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: input.slice(0, cursorPos) }), cursorVisible
393
- ? (0, jsx_runtime_1.jsx)(ink_1.Text, { inverse: true, children: input[cursorPos] ?? " " })
404
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(termWidth()) }), isMultiline ? ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: inputLines.map((line, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: i === 0 ? ") " : "· " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: line }), i === inputLines.length - 1 ? (cursorVisible ? (0, jsx_runtime_1.jsx)(ink_1.Text, { backgroundColor: OURO.scale, color: "#000000", children: " " }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " })) : null] }, i))) })) : ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.teal, bold: true, children: ") " }), !input && queuedInputs.length > 0 ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.shadow, children: "Press up to edit queued messages" })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: input.slice(0, cursorPos) }), cursorVisible
405
+ ? (0, jsx_runtime_1.jsx)(ink_1.Text, { backgroundColor: OURO.scale, color: "#000000", children: input[cursorPos] ?? " " })
394
406
  : (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: input[cursorPos] ?? " " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: OURO.bone, children: input.slice(cursorPos + 1) })] }))] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "─".repeat(termWidth()) }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [" ", agentName, model ? ` · ${model}` : "", " \u00B7 /help"] }), (0, jsx_runtime_1.jsx)(ink_1.Box, { flexGrow: 1 }), tooltip ? (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: tooltip }) : (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "opt+enter for newline" })] })] }));
395
407
  }
396
408
  // ─── Main TUI Component ─────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.256",
3
+ "version": "0.1.0-alpha.258",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",