@leg3ndy/otto-bridge 1.1.4 → 1.1.5

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/README.md CHANGED
@@ -15,7 +15,7 @@ Para o estado atual da arquitetura, capacidades entregues, limitacoes e roadmap
15
15
 
16
16
  Para o corte de arquitetura do `0.9.0`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_0_9_0_RELEASE.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_0_9_0_RELEASE.md).
17
17
 
18
- Para o patch atual `1.1.4`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_4_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_4_PATCH.md). Para o corte funcional da linha `1.1.0`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md).
18
+ Para o patch atual `1.1.5`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_5_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_5_PATCH.md). Para o corte funcional da linha `1.1.0`, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_1_0_RELEASE.md).
19
19
 
20
20
  ## Distribuicao
21
21
 
@@ -38,14 +38,14 @@ Enquanto o pacote nao estiver publicado, voce pode gerar um tarball local:
38
38
 
39
39
  ```bash
40
40
  npm pack
41
- npm install -g ./leg3ndy-otto-bridge-1.1.4.tgz
41
+ npm install -g ./leg3ndy-otto-bridge-1.1.5.tgz
42
42
  ```
43
43
 
44
- Na linha `1.1.4`, `playwright` segue como dependencia obrigatoria no `otto-bridge`. O primeiro `npm install -g @leg3ndy/otto-bridge` pode demorar mais porque instala o browser persistente usado pelo WhatsApp Web e pelos fluxos web em background do bridge.
44
+ Na linha `1.1.5`, `playwright` segue como dependencia obrigatoria no `otto-bridge`. O primeiro `npm install -g @leg3ndy/otto-bridge` pode demorar mais porque instala o browser persistente usado pelo WhatsApp Web e pelos fluxos web em background do bridge.
45
45
 
46
- No macOS, a linha `1.1.4` usa o provider `macos-helper`, um helper `WKWebView` sem Dock para o WhatsApp Web. O helper sobe com user-agent de Chrome moderno para evitar o bloqueio do WhatsApp ao detectar Safari/WebKit. O runtime antigo com Chromium/Playwright fica disponivel apenas como override explicito via `OTTO_BRIDGE_WHATSAPP_RUNTIME_PROVIDER=embedded-playwright`.
46
+ No macOS, a linha `1.1.5` usa o provider `macos-helper`, um helper `WKWebView` sem Dock para o WhatsApp Web. O helper sobe com user-agent de Chrome moderno para evitar o bloqueio do WhatsApp ao detectar Safari/WebKit. O runtime antigo com Chromium/Playwright fica disponivel apenas como override explicito via `OTTO_BRIDGE_WHATSAPP_RUNTIME_PROVIDER=embedded-playwright`.
47
47
 
48
- No nivel arquitetural, o `0.9.0` marcou a mudanca de papel do bridge: ele publica tools e resultados estruturados para o Otto, em vez de injetar resposta pronta como caminho principal do chat. O `1.0.0` oficializou isso como runtime agentico; o `1.1.4` mantem a camada workspace-first com rail de coding, trust/policy por workspace, source control first-class, working set persistido e grounding remoto por repositório, enquanto corrige a regressao do `Otto Console` e volta ao renderer fixo com header no topo, transcript acima do composer, menu com setas e palette de comandos ao digitar `/`.
48
+ No nivel arquitetural, o `0.9.0` marcou a mudanca de papel do bridge: ele publica tools e resultados estruturados para o Otto, em vez de injetar resposta pronta como caminho principal do chat. O `1.0.0` oficializou isso como runtime agentico; o `1.1.5` mantem a camada workspace-first com rail de coding, trust/policy por workspace, source control first-class, working set persistido e grounding remoto por repositório, enquanto corrige o `Otto Console` para voltar ao scrollback real, aceitar digitação enquanto o Otto streama e reaproveitar a tela do hub sem duplicar o banner.
49
49
 
50
50
  ## Publicacao
51
51
 
@@ -170,7 +170,7 @@ Esse comando abre um shell local interativo para instalar extensoes, rodar coman
170
170
 
171
171
  ### WhatsApp Web em background
172
172
 
173
- Fluxo recomendado na linha `1.1.4`:
173
+ Fluxo recomendado na linha `1.1.5`:
174
174
 
175
175
  ```bash
176
176
  otto-bridge extensions --install whatsappweb
@@ -180,13 +180,13 @@ otto-bridge extensions --status whatsappweb
180
180
 
181
181
  O setup agora abre o login do WhatsApp Web no helper/background browser do proprio bridge. Depois do QR code, o Otto usa a sessao local em background, sem depender de aba visivel no Safari.
182
182
 
183
- Contrato da linha `1.1.4`:
183
+ Contrato da linha `1.1.5`:
184
184
 
185
185
  - `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
186
186
  - `otto-bridge`: mantem o browser persistente do WhatsApp vivo em background enquanto o runtime do hub estiver ativo, sem depender de uma aba aberta no Safari
187
187
  - ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
188
188
 
189
- ## Handoff rapido da linha 1.1.4
189
+ ## Handoff rapido da linha 1.1.5
190
190
 
191
191
  Ja fechado no codigo:
192
192
 
@@ -540,7 +540,7 @@ class ConsoleScreenRenderer {
540
540
  modelMode;
541
541
  approvalMode;
542
542
  headerFactory;
543
- transcript = [];
543
+ liveEntries = [];
544
544
  onResize = () => {
545
545
  this.render();
546
546
  };
@@ -551,7 +551,8 @@ class ConsoleScreenRenderer {
551
551
  approvalStatusSuffix = null;
552
552
  slashSuggestions = [];
553
553
  selectedSuggestionIndex = 0;
554
- usingAlternateBuffer = false;
554
+ renderedOnce = false;
555
+ renderedCursorLineIndex = 0;
555
556
  constructor(modelMode, approvalMode, headerFactory = () => []) {
556
557
  this.modelMode = modelMode;
557
558
  this.approvalMode = approvalMode;
@@ -562,8 +563,6 @@ class ConsoleScreenRenderer {
562
563
  return;
563
564
  }
564
565
  this.active = true;
565
- output.write("\u001b[?1049h");
566
- this.usingAlternateBuffer = true;
567
566
  output.on("resize", this.onResize);
568
567
  this.render();
569
568
  }
@@ -572,34 +571,40 @@ class ConsoleScreenRenderer {
572
571
  return;
573
572
  }
574
573
  output.off("resize", this.onResize);
574
+ this.clearRenderBlock();
575
575
  this.active = false;
576
576
  output.write("\u001b[?25h");
577
- if (this.usingAlternateBuffer) {
578
- output.write("\u001b[?1049l");
579
- this.usingAlternateBuffer = false;
580
- }
581
577
  }
582
578
  isActive() {
583
579
  return this.active;
584
580
  }
585
581
  clearTranscript() {
586
- this.transcript.splice(0, this.transcript.length);
582
+ this.liveEntries.splice(0, this.liveEntries.length);
583
+ this.renderedOnce = false;
584
+ clearScreen();
585
+ output.write(this.headerFactory().join("\n"));
586
+ output.write("\n\n");
587
587
  this.render();
588
588
  }
589
589
  pushEntry(entry) {
590
590
  const id = this.nextEntryId++;
591
- this.transcript.push({
591
+ const nextEntry = {
592
592
  id,
593
593
  ...entry,
594
- });
595
- this.render();
594
+ };
595
+ if (nextEntry.text.length === 0 && (Boolean(nextEntry.prefix) || nextEntry.tone === "reasoning")) {
596
+ this.liveEntries.push(nextEntry);
597
+ this.render();
598
+ return id;
599
+ }
600
+ this.printCommittedEntry(nextEntry);
596
601
  return id;
597
602
  }
598
603
  appendToEntry(id, text) {
599
604
  if (!id || !text) {
600
605
  return;
601
606
  }
602
- const entry = this.transcript.find((item) => item.id === id);
607
+ const entry = this.liveEntries.find((item) => item.id === id);
603
608
  if (!entry) {
604
609
  return;
605
610
  }
@@ -627,6 +632,15 @@ class ConsoleScreenRenderer {
627
632
  this.selectedSuggestionIndex = 0;
628
633
  this.render();
629
634
  }
635
+ commitLiveEntries() {
636
+ if (this.liveEntries.length === 0) {
637
+ return;
638
+ }
639
+ const width = this.getRenderWidth();
640
+ const lines = this.liveEntries.flatMap((entry) => this.buildEntryLines(entry, width));
641
+ this.liveEntries.splice(0, this.liveEntries.length);
642
+ this.printLinesAbove(lines);
643
+ }
630
644
  setConversationMessages(messages) {
631
645
  this.conversationMessages = [...messages];
632
646
  this.render();
@@ -665,19 +679,50 @@ class ConsoleScreenRenderer {
665
679
  }
666
680
  return rendered;
667
681
  }
682
+ getRenderWidth() {
683
+ return Math.max(48, Number(output.columns || 96));
684
+ }
685
+ clearRenderBlock() {
686
+ if (!this.renderedOnce) {
687
+ return;
688
+ }
689
+ output.write("\u001b[?25l");
690
+ cursorTo(output, 0);
691
+ if (this.renderedCursorLineIndex > 0) {
692
+ moveCursor(output, 0, -this.renderedCursorLineIndex);
693
+ }
694
+ clearScreenDown(output);
695
+ output.write("\u001b[?25h");
696
+ this.renderedOnce = false;
697
+ this.renderedCursorLineIndex = 0;
698
+ }
699
+ printLinesAbove(lines) {
700
+ if (!this.active) {
701
+ return;
702
+ }
703
+ this.clearRenderBlock();
704
+ if (lines.length > 0) {
705
+ output.write(lines.join("\n"));
706
+ output.write("\n");
707
+ }
708
+ this.render();
709
+ }
710
+ printCommittedEntry(entry) {
711
+ this.printLinesAbove(this.buildEntryLines(entry, this.getRenderWidth()));
712
+ }
668
713
  render() {
669
714
  if (!this.active) {
670
715
  return;
671
716
  }
672
717
  const enabled = supportsAnsi();
673
- const width = Math.max(48, Number(output.columns || 96));
674
- const height = Math.max(12, Number(output.rows || 24));
675
- const headerLines = this.headerFactory();
718
+ const width = this.getRenderWidth();
676
719
  const separator = style("─".repeat(width), ANSI.brandBlue, enabled);
677
720
  const composer = renderConsoleComposerLines(this.draftValue, width, enabled);
678
721
  const suggestionLines = buildConsoleSlashSuggestionLines(this.slashSuggestions, this.selectedSuggestionIndex, width, enabled);
679
722
  const usageTokens = Math.min(estimateConsoleContextTokens(this.conversationMessages, this.draftValue), getCliModelContextWindowTokens(this.modelMode));
680
- const footerLines = [
723
+ const liveLines = this.liveEntries.flatMap((entry) => this.buildEntryLines(entry, width));
724
+ const blockLines = [
725
+ ...liveLines,
681
726
  separator,
682
727
  ...composer.renderedLines,
683
728
  ...suggestionLines,
@@ -685,18 +730,17 @@ class ConsoleScreenRenderer {
685
730
  buildConsoleFooterStatusLine(width, this.modelMode, usageTokens, enabled),
686
731
  buildConsoleFooterApprovalLine(this.approvalMode, enabled, this.approvalStatusSuffix),
687
732
  ];
688
- const visibleHeader = headerLines.slice(0, Math.max(0, height - footerLines.length));
689
- const transcriptHeight = Math.max(0, height - visibleHeader.length - footerLines.length);
690
- const transcriptLines = this.transcript.flatMap((entry) => this.buildEntryLines(entry, width));
691
- const visibleTranscript = transcriptLines.slice(-transcriptHeight);
692
- const paddedTranscript = [
693
- ...Array.from({ length: Math.max(0, transcriptHeight - visibleTranscript.length) }, () => ""),
694
- ...visibleTranscript,
695
- ];
733
+ this.clearRenderBlock();
696
734
  output.write("\u001b[?25l");
697
- output.write("\u001b[H\u001b[2J");
698
- output.write([...visibleHeader, ...paddedTranscript, ...footerLines].join("\n"));
699
- cursorTo(output, Math.min(width - 1, CONSOLE_COMPOSER_CURSOR_COLUMN + composer.cursorColumn), visibleHeader.length + transcriptHeight + 1 + composer.cursorLineIndex);
735
+ output.write(blockLines.join("\n"));
736
+ this.renderedOnce = true;
737
+ this.renderedCursorLineIndex = liveLines.length + 1 + composer.cursorLineIndex;
738
+ const linesBelowCursor = blockLines.length - 1 - this.renderedCursorLineIndex;
739
+ cursorTo(output, Math.min(width - 1, CONSOLE_COMPOSER_CURSOR_COLUMN + composer.cursorColumn));
740
+ if (linesBelowCursor > 0) {
741
+ moveCursor(output, 0, -linesBelowCursor);
742
+ }
743
+ cursorTo(output, Math.min(width - 1, CONSOLE_COMPOSER_CURSOR_COLUMN + composer.cursorColumn));
700
744
  output.write("\u001b[?25h");
701
745
  }
702
746
  }
@@ -709,7 +753,7 @@ export function createConsoleScreenRenderer(modelMode, approvalMode, runtimeSess
709
753
  return null;
710
754
  }
711
755
  const renderer = new ConsoleScreenRenderer(modelMode, approvalMode, () => buildConsoleHeaderLines(runtimeSession));
712
- if (options?.autoActivate ?? true) {
756
+ if (options?.autoActivate ?? false) {
713
757
  renderer.activate();
714
758
  }
715
759
  return renderer;
@@ -1411,6 +1455,183 @@ export function tryConsumeControlSequence(buffer) {
1411
1455
  }
1412
1456
  return null;
1413
1457
  }
1458
+ class ConsoleInputController {
1459
+ rl;
1460
+ ui;
1461
+ options;
1462
+ active = false;
1463
+ value = "";
1464
+ controlBuffer = "";
1465
+ selectedSuggestionIndex = 0;
1466
+ queuedValues = [];
1467
+ pendingResolvers = [];
1468
+ terminalError = null;
1469
+ constructor(rl, ui, options) {
1470
+ this.rl = rl;
1471
+ this.ui = ui;
1472
+ this.options = options;
1473
+ }
1474
+ activate() {
1475
+ if (this.active || typeof input.setRawMode !== "function" || !input.isTTY) {
1476
+ return;
1477
+ }
1478
+ this.active = true;
1479
+ this.rl.pause();
1480
+ input.setRawMode(true);
1481
+ input.resume();
1482
+ input.on("data", this.onData);
1483
+ this.render();
1484
+ }
1485
+ dispose() {
1486
+ if (!this.active) {
1487
+ return;
1488
+ }
1489
+ input.removeListener("data", this.onData);
1490
+ input.setRawMode(false);
1491
+ input.pause();
1492
+ this.rl.resume();
1493
+ this.active = false;
1494
+ this.ui.resetComposer();
1495
+ }
1496
+ nextValue() {
1497
+ if (this.queuedValues.length > 0) {
1498
+ return Promise.resolve(this.queuedValues.shift() || "");
1499
+ }
1500
+ if (this.terminalError) {
1501
+ return Promise.reject(this.terminalError);
1502
+ }
1503
+ return new Promise((resolve, reject) => {
1504
+ this.pendingResolvers.push({ resolve, reject });
1505
+ });
1506
+ }
1507
+ getVisibleSuggestions() {
1508
+ const suggestions = resolveConsoleSlashSuggestions(this.value).slice(0, 6);
1509
+ if (this.selectedSuggestionIndex >= suggestions.length) {
1510
+ this.selectedSuggestionIndex = Math.max(0, suggestions.length - 1);
1511
+ }
1512
+ return suggestions;
1513
+ }
1514
+ render() {
1515
+ this.ui.setComposerState(this.value, this.getVisibleSuggestions(), this.selectedSuggestionIndex);
1516
+ }
1517
+ enqueueValue(value) {
1518
+ const next = this.pendingResolvers.shift();
1519
+ if (next) {
1520
+ next.resolve(value);
1521
+ return;
1522
+ }
1523
+ this.queuedValues.push(value);
1524
+ }
1525
+ rejectPending(error) {
1526
+ while (this.pendingResolvers.length > 0) {
1527
+ const next = this.pendingResolvers.shift();
1528
+ next?.reject(error);
1529
+ }
1530
+ }
1531
+ closeWithError(error) {
1532
+ this.terminalError = error;
1533
+ this.rejectPending(error);
1534
+ this.dispose();
1535
+ }
1536
+ applySelectedSuggestion() {
1537
+ const suggestions = this.getVisibleSuggestions();
1538
+ const selected = suggestions[this.selectedSuggestionIndex];
1539
+ if (!selected) {
1540
+ return false;
1541
+ }
1542
+ if (normalizeText(this.value) === selected.insertText) {
1543
+ return false;
1544
+ }
1545
+ this.value = selected.insertText;
1546
+ this.selectedSuggestionIndex = 0;
1547
+ this.render();
1548
+ return true;
1549
+ }
1550
+ consumeControlBuffer() {
1551
+ while (this.controlBuffer.length > 0) {
1552
+ const parsed = tryConsumeControlSequence(this.controlBuffer);
1553
+ if (!parsed) {
1554
+ this.controlBuffer = "";
1555
+ return;
1556
+ }
1557
+ if (parsed.action === "incomplete") {
1558
+ return;
1559
+ }
1560
+ this.controlBuffer = this.controlBuffer.slice(parsed.consumed);
1561
+ if (parsed.action === "ignore") {
1562
+ continue;
1563
+ }
1564
+ if (parsed.action === "cycle_approval") {
1565
+ void Promise.resolve(this.options?.onCycleApprovalMode?.()).finally(() => {
1566
+ this.render();
1567
+ });
1568
+ continue;
1569
+ }
1570
+ if (parsed.action === "move_up") {
1571
+ const suggestions = this.getVisibleSuggestions();
1572
+ if (suggestions.length > 0) {
1573
+ this.selectedSuggestionIndex = this.selectedSuggestionIndex > 0
1574
+ ? this.selectedSuggestionIndex - 1
1575
+ : suggestions.length - 1;
1576
+ this.render();
1577
+ }
1578
+ continue;
1579
+ }
1580
+ if (parsed.action === "move_down") {
1581
+ const suggestions = this.getVisibleSuggestions();
1582
+ if (suggestions.length > 0) {
1583
+ this.selectedSuggestionIndex = this.selectedSuggestionIndex < suggestions.length - 1
1584
+ ? this.selectedSuggestionIndex + 1
1585
+ : 0;
1586
+ this.render();
1587
+ }
1588
+ continue;
1589
+ }
1590
+ if (parsed.action === "newline") {
1591
+ this.value += "\n";
1592
+ this.selectedSuggestionIndex = 0;
1593
+ this.render();
1594
+ }
1595
+ }
1596
+ }
1597
+ onData = (chunk) => {
1598
+ const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
1599
+ for (const char of Array.from(text)) {
1600
+ if (this.controlBuffer || char === "\u001b") {
1601
+ this.controlBuffer += char;
1602
+ this.consumeControlBuffer();
1603
+ continue;
1604
+ }
1605
+ if (char === "\u0003") {
1606
+ this.closeWithError(createCliExitError());
1607
+ return;
1608
+ }
1609
+ if (char === "\r" || char === "\n") {
1610
+ if (this.applySelectedSuggestion()) {
1611
+ continue;
1612
+ }
1613
+ const submittedValue = normalizeText(this.value);
1614
+ this.value = "";
1615
+ this.selectedSuggestionIndex = 0;
1616
+ this.render();
1617
+ this.enqueueValue(submittedValue);
1618
+ continue;
1619
+ }
1620
+ if (char === "\u007f" || char === "\b") {
1621
+ this.value = this.value.slice(0, -1);
1622
+ this.selectedSuggestionIndex = 0;
1623
+ this.render();
1624
+ continue;
1625
+ }
1626
+ if (char === "\t") {
1627
+ this.applySelectedSuggestion();
1628
+ continue;
1629
+ }
1630
+ this.value += char;
1631
+ this.render();
1632
+ }
1633
+ };
1634
+ }
1414
1635
  async function askConsoleInput(rl, options) {
1415
1636
  if (!supportsAnsi() || typeof input.setRawMode !== "function" || !input.isTTY) {
1416
1637
  return normalizeText(await question(rl, "> "));
@@ -1888,10 +2109,20 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
1888
2109
  let activeModel = "fast";
1889
2110
  let sessionId = randomUUID();
1890
2111
  const conversation = [];
1891
- const ui = createConsoleScreenRenderer(activeModel, config.approvalMode, runtimeSession);
2112
+ const ui = createConsoleScreenRenderer(activeModel, config.approvalMode, runtimeSession, {
2113
+ autoActivate: false,
2114
+ });
1892
2115
  if (!ui) {
1893
2116
  printConsoleScreen(runtimeSession);
1894
2117
  }
2118
+ else if (options?.reuseHubHeader) {
2119
+ output.write(`${style(`Comandos: ${CONSOLE_COMMAND_HINT}`, ANSI.slateItalic, supportsAnsi())}\n\n`);
2120
+ ui.activate();
2121
+ }
2122
+ else {
2123
+ printConsoleScreen(runtimeSession);
2124
+ ui.activate();
2125
+ }
1895
2126
  const renderConversationState = () => {
1896
2127
  ui?.setConversationMessages(conversation);
1897
2128
  ui?.setModelMode(activeModel);
@@ -1962,7 +2193,16 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
1962
2193
  const cycleApprovalMode = () => {
1963
2194
  void setApprovalMode(getNextApprovalMode(config.approvalMode));
1964
2195
  };
2196
+ const inputController = ui
2197
+ ? new ConsoleInputController(rl, ui, {
2198
+ onCycleApprovalMode: cycleApprovalMode,
2199
+ })
2200
+ : null;
2201
+ inputController?.activate();
1965
2202
  const readConsoleInput = async () => {
2203
+ if (inputController) {
2204
+ return await inputController.nextValue();
2205
+ }
1966
2206
  return await askConsoleInput(rl, {
1967
2207
  ui,
1968
2208
  onCycleApprovalMode: cycleApprovalMode,
@@ -2322,6 +2562,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
2322
2562
  }
2323
2563
  streamedAssistant += contentChunk;
2324
2564
  });
2565
+ ui?.commitLiveEntries();
2325
2566
  if (assistantPrefixPrinted && !ui) {
2326
2567
  output.write("\n");
2327
2568
  }
@@ -2366,6 +2607,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
2366
2607
  }
2367
2608
  }
2368
2609
  finally {
2610
+ inputController?.dispose();
2369
2611
  ui?.dispose();
2370
2612
  }
2371
2613
  }
@@ -2410,6 +2652,14 @@ function renderHomeOptionLine(label, selected) {
2410
2652
  }
2411
2653
  return `${style("▸", ANSI.brandBlue, supportsAnsi())} ${style(label, `${ANSI.bold}${ANSI.white}`, supportsAnsi())}`;
2412
2654
  }
2655
+ function clearHomeMenuBlock(optionCount) {
2656
+ if (!supportsAnsi() || !output.isTTY) {
2657
+ return;
2658
+ }
2659
+ cursorTo(output, 0);
2660
+ moveCursor(output, 0, -(optionCount + 4));
2661
+ clearScreenDown(output);
2662
+ }
2413
2663
  async function pickHomeChoice(rl, paired, renderBaseScreen) {
2414
2664
  const options = paired
2415
2665
  ? [
@@ -2496,9 +2746,15 @@ async function pickHomeChoice(rl, paired, renderBaseScreen) {
2496
2746
  return;
2497
2747
  }
2498
2748
  if (char === "\r" || char === "\n") {
2749
+ const selectedOption = options[selectedIndex];
2499
2750
  cleanup();
2500
- output.write("\n");
2501
- resolve(options[selectedIndex].value);
2751
+ if (selectedOption?.value === "console") {
2752
+ clearHomeMenuBlock(options.length);
2753
+ }
2754
+ else {
2755
+ output.write("\n");
2756
+ }
2757
+ resolve(selectedOption?.value || "exit");
2502
2758
  return;
2503
2759
  }
2504
2760
  }
@@ -2601,7 +2857,7 @@ export async function launchInteractiveCli(options) {
2601
2857
  continue;
2602
2858
  }
2603
2859
  if (choice === "console" && runtimeSession) {
2604
- await runOttoConsole(rl, config, runtimeSession);
2860
+ await runOttoConsole(rl, config, runtimeSession, { reuseHubHeader: true });
2605
2861
  continue;
2606
2862
  }
2607
2863
  if (choice === "status" && runtimeSession) {
package/dist/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const BRIDGE_CONFIG_VERSION = 1;
2
- export const BRIDGE_VERSION = "1.1.4";
2
+ export const BRIDGE_VERSION = "1.1.5";
3
3
  export const BRIDGE_PACKAGE_NAME = "@leg3ndy/otto-bridge";
4
4
  export const DEFAULT_API_BASE_URL = "http://localhost:8000";
5
5
  export const DEFAULT_POLL_INTERVAL_MS = 3000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leg3ndy/otto-bridge",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local companion for Otto Bridge device pairing and WebSocket runtime.",
@@ -24,7 +24,7 @@ if (!existsSync(mainPath)) {
24
24
  process.exit(0);
25
25
  }
26
26
 
27
- console.log("\n[otto-bridge] Welcome to OTTOAI 1.1.4");
27
+ console.log("\n[otto-bridge] Welcome to OTTOAI 1.1.5");
28
28
  console.log("[otto-bridge] Vamos iniciar o setup interativo do bridge.\n");
29
29
 
30
30
  const result = spawnSync(process.execPath, [mainPath, "setup", "--postinstall"], {