@leg3ndy/otto-bridge 1.1.3 → 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 +8 -8
- package/dist/cli_terminal.js +346 -55
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +1 -1
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.
|
|
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.
|
|
41
|
+
npm install -g ./leg3ndy-otto-bridge-1.1.5.tgz
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Na linha `1.1.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
189
|
+
## Handoff rapido da linha 1.1.5
|
|
190
190
|
|
|
191
191
|
Ja fechado no codigo:
|
|
192
192
|
|
package/dist/cli_terminal.js
CHANGED
|
@@ -509,6 +509,21 @@ function buildConsoleFooterApprovalLine(mode, enabled, statusSuffix) {
|
|
|
509
509
|
const hint = statusSuffix || "Shift+Tab para alternar";
|
|
510
510
|
return style(`${state.label} (${hint})`, state.tone === "warning" ? ANSI.red : state.tone === "primary" ? ANSI.amber : ANSI.slate, enabled);
|
|
511
511
|
}
|
|
512
|
+
function buildConsoleSlashSuggestionLines(suggestions, selectedSuggestionIndex, width, enabled) {
|
|
513
|
+
if (!suggestions.length) {
|
|
514
|
+
return [];
|
|
515
|
+
}
|
|
516
|
+
const suggestionWidth = Math.max(24, width - 4);
|
|
517
|
+
return suggestions.map((item, index) => {
|
|
518
|
+
const selected = index === selectedSuggestionIndex;
|
|
519
|
+
const prefix = selected
|
|
520
|
+
? style("›", `${ANSI.bold}${ANSI.brandBlue}`, enabled)
|
|
521
|
+
: style(" ", ANSI.slate, enabled);
|
|
522
|
+
const command = style(item.command, selected ? `${ANSI.bold}${ANSI.white}` : ANSI.brandBlue, enabled);
|
|
523
|
+
const description = style(truncate(item.description, Math.max(12, suggestionWidth - item.command.length - 4)), selected ? ANSI.white : ANSI.slate, enabled);
|
|
524
|
+
return `${prefix} ${command} ${description}`;
|
|
525
|
+
});
|
|
526
|
+
}
|
|
512
527
|
function styleTranscriptPrefix(text, tone, enabled) {
|
|
513
528
|
if (!enabled || !text) {
|
|
514
529
|
return text;
|
|
@@ -525,7 +540,7 @@ class ConsoleScreenRenderer {
|
|
|
525
540
|
modelMode;
|
|
526
541
|
approvalMode;
|
|
527
542
|
headerFactory;
|
|
528
|
-
|
|
543
|
+
liveEntries = [];
|
|
529
544
|
onResize = () => {
|
|
530
545
|
this.render();
|
|
531
546
|
};
|
|
@@ -534,7 +549,10 @@ class ConsoleScreenRenderer {
|
|
|
534
549
|
draftValue = "";
|
|
535
550
|
conversationMessages = [];
|
|
536
551
|
approvalStatusSuffix = null;
|
|
537
|
-
|
|
552
|
+
slashSuggestions = [];
|
|
553
|
+
selectedSuggestionIndex = 0;
|
|
554
|
+
renderedOnce = false;
|
|
555
|
+
renderedCursorLineIndex = 0;
|
|
538
556
|
constructor(modelMode, approvalMode, headerFactory = () => []) {
|
|
539
557
|
this.modelMode = modelMode;
|
|
540
558
|
this.approvalMode = approvalMode;
|
|
@@ -545,8 +563,6 @@ class ConsoleScreenRenderer {
|
|
|
545
563
|
return;
|
|
546
564
|
}
|
|
547
565
|
this.active = true;
|
|
548
|
-
output.write("\u001b[?1049h");
|
|
549
|
-
this.usingAlternateBuffer = true;
|
|
550
566
|
output.on("resize", this.onResize);
|
|
551
567
|
this.render();
|
|
552
568
|
}
|
|
@@ -555,34 +571,40 @@ class ConsoleScreenRenderer {
|
|
|
555
571
|
return;
|
|
556
572
|
}
|
|
557
573
|
output.off("resize", this.onResize);
|
|
574
|
+
this.clearRenderBlock();
|
|
558
575
|
this.active = false;
|
|
559
576
|
output.write("\u001b[?25h");
|
|
560
|
-
if (this.usingAlternateBuffer) {
|
|
561
|
-
output.write("\u001b[?1049l");
|
|
562
|
-
this.usingAlternateBuffer = false;
|
|
563
|
-
}
|
|
564
577
|
}
|
|
565
578
|
isActive() {
|
|
566
579
|
return this.active;
|
|
567
580
|
}
|
|
568
581
|
clearTranscript() {
|
|
569
|
-
this.
|
|
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");
|
|
570
587
|
this.render();
|
|
571
588
|
}
|
|
572
589
|
pushEntry(entry) {
|
|
573
590
|
const id = this.nextEntryId++;
|
|
574
|
-
|
|
591
|
+
const nextEntry = {
|
|
575
592
|
id,
|
|
576
593
|
...entry,
|
|
577
|
-
}
|
|
578
|
-
|
|
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);
|
|
579
601
|
return id;
|
|
580
602
|
}
|
|
581
603
|
appendToEntry(id, text) {
|
|
582
604
|
if (!id || !text) {
|
|
583
605
|
return;
|
|
584
606
|
}
|
|
585
|
-
const entry = this.
|
|
607
|
+
const entry = this.liveEntries.find((item) => item.id === id);
|
|
586
608
|
if (!entry) {
|
|
587
609
|
return;
|
|
588
610
|
}
|
|
@@ -593,6 +615,32 @@ class ConsoleScreenRenderer {
|
|
|
593
615
|
this.draftValue = value;
|
|
594
616
|
this.render();
|
|
595
617
|
}
|
|
618
|
+
setSlashSuggestions(suggestions, selectedSuggestionIndex = 0) {
|
|
619
|
+
this.slashSuggestions = [...suggestions];
|
|
620
|
+
this.selectedSuggestionIndex = Math.max(0, Math.min(selectedSuggestionIndex, Math.max(0, suggestions.length - 1)));
|
|
621
|
+
this.render();
|
|
622
|
+
}
|
|
623
|
+
setComposerState(value, suggestions, selectedSuggestionIndex = 0) {
|
|
624
|
+
this.draftValue = value;
|
|
625
|
+
this.slashSuggestions = [...suggestions];
|
|
626
|
+
this.selectedSuggestionIndex = Math.max(0, Math.min(selectedSuggestionIndex, Math.max(0, suggestions.length - 1)));
|
|
627
|
+
this.render();
|
|
628
|
+
}
|
|
629
|
+
resetComposer() {
|
|
630
|
+
this.draftValue = "";
|
|
631
|
+
this.slashSuggestions = [];
|
|
632
|
+
this.selectedSuggestionIndex = 0;
|
|
633
|
+
this.render();
|
|
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
|
+
}
|
|
596
644
|
setConversationMessages(messages) {
|
|
597
645
|
this.conversationMessages = [...messages];
|
|
598
646
|
this.render();
|
|
@@ -631,41 +679,84 @@ class ConsoleScreenRenderer {
|
|
|
631
679
|
}
|
|
632
680
|
return rendered;
|
|
633
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
|
+
}
|
|
634
713
|
render() {
|
|
635
714
|
if (!this.active) {
|
|
636
715
|
return;
|
|
637
716
|
}
|
|
638
717
|
const enabled = supportsAnsi();
|
|
639
|
-
const width =
|
|
640
|
-
const height = Math.max(12, Number(output.rows || 24));
|
|
641
|
-
const headerLines = this.headerFactory();
|
|
718
|
+
const width = this.getRenderWidth();
|
|
642
719
|
const separator = style("─".repeat(width), ANSI.brandBlue, enabled);
|
|
643
720
|
const composer = renderConsoleComposerLines(this.draftValue, width, enabled);
|
|
721
|
+
const suggestionLines = buildConsoleSlashSuggestionLines(this.slashSuggestions, this.selectedSuggestionIndex, width, enabled);
|
|
644
722
|
const usageTokens = Math.min(estimateConsoleContextTokens(this.conversationMessages, this.draftValue), getCliModelContextWindowTokens(this.modelMode));
|
|
645
|
-
const
|
|
723
|
+
const liveLines = this.liveEntries.flatMap((entry) => this.buildEntryLines(entry, width));
|
|
724
|
+
const blockLines = [
|
|
725
|
+
...liveLines,
|
|
646
726
|
separator,
|
|
647
727
|
...composer.renderedLines,
|
|
728
|
+
...suggestionLines,
|
|
648
729
|
separator,
|
|
649
730
|
buildConsoleFooterStatusLine(width, this.modelMode, usageTokens, enabled),
|
|
650
731
|
buildConsoleFooterApprovalLine(this.approvalMode, enabled, this.approvalStatusSuffix),
|
|
651
732
|
];
|
|
652
|
-
|
|
653
|
-
const transcriptHeight = Math.max(0, height - visibleHeader.length - footerLines.length);
|
|
654
|
-
const transcriptLines = this.transcript.flatMap((entry) => this.buildEntryLines(entry, width));
|
|
655
|
-
const visibleTranscript = transcriptLines.slice(-transcriptHeight);
|
|
656
|
-
const paddedTranscript = [
|
|
657
|
-
...Array.from({ length: Math.max(0, transcriptHeight - visibleTranscript.length) }, () => ""),
|
|
658
|
-
...visibleTranscript,
|
|
659
|
-
];
|
|
733
|
+
this.clearRenderBlock();
|
|
660
734
|
output.write("\u001b[?25l");
|
|
661
|
-
output.write("\
|
|
662
|
-
|
|
663
|
-
|
|
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));
|
|
664
744
|
output.write("\u001b[?25h");
|
|
665
745
|
}
|
|
666
746
|
}
|
|
667
|
-
function createConsoleScreenRenderer(
|
|
668
|
-
|
|
747
|
+
export function createConsoleScreenRenderer(modelMode, approvalMode, runtimeSession, options) {
|
|
748
|
+
const canRender = (options?.ansiEnabled ?? supportsAnsi())
|
|
749
|
+
&& (options?.inputIsTTY ?? Boolean(input.isTTY))
|
|
750
|
+
&& (options?.outputIsTTY ?? Boolean(output.isTTY))
|
|
751
|
+
&& (options?.hasRawMode ?? typeof input.setRawMode === "function");
|
|
752
|
+
if (!canRender) {
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
const renderer = new ConsoleScreenRenderer(modelMode, approvalMode, () => buildConsoleHeaderLines(runtimeSession));
|
|
756
|
+
if (options?.autoActivate ?? false) {
|
|
757
|
+
renderer.activate();
|
|
758
|
+
}
|
|
759
|
+
return renderer;
|
|
669
760
|
}
|
|
670
761
|
export function resolveCliModelMode(value) {
|
|
671
762
|
const normalized = normalizeText(value).toLowerCase();
|
|
@@ -1364,6 +1455,183 @@ export function tryConsumeControlSequence(buffer) {
|
|
|
1364
1455
|
}
|
|
1365
1456
|
return null;
|
|
1366
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
|
+
}
|
|
1367
1635
|
async function askConsoleInput(rl, options) {
|
|
1368
1636
|
if (!supportsAnsi() || typeof input.setRawMode !== "function" || !input.isTTY) {
|
|
1369
1637
|
return normalizeText(await question(rl, "> "));
|
|
@@ -1401,27 +1669,11 @@ async function askConsoleInput(rl, options) {
|
|
|
1401
1669
|
input.setRawMode(false);
|
|
1402
1670
|
input.pause();
|
|
1403
1671
|
rl.resume();
|
|
1404
|
-
ui?.
|
|
1405
|
-
};
|
|
1406
|
-
const renderSuggestionLines = (width) => {
|
|
1407
|
-
const suggestions = getVisibleSuggestions();
|
|
1408
|
-
if (!suggestions.length) {
|
|
1409
|
-
return [];
|
|
1410
|
-
}
|
|
1411
|
-
const suggestionWidth = Math.max(24, width - 4);
|
|
1412
|
-
return suggestions.map((item, index) => {
|
|
1413
|
-
const selected = index === selectedSuggestionIndex;
|
|
1414
|
-
const prefix = selected
|
|
1415
|
-
? style("›", `${ANSI.bold}${ANSI.brandBlue}`, enabled)
|
|
1416
|
-
: style(" ", ANSI.slate, enabled);
|
|
1417
|
-
const command = style(item.command, selected ? `${ANSI.bold}${ANSI.white}` : ANSI.brandBlue, enabled);
|
|
1418
|
-
const description = style(truncate(item.description, Math.max(12, suggestionWidth - item.command.length - 4)), selected ? ANSI.white : ANSI.slate, enabled);
|
|
1419
|
-
return `${prefix} ${command} ${description}`;
|
|
1420
|
-
});
|
|
1672
|
+
ui?.resetComposer();
|
|
1421
1673
|
};
|
|
1422
1674
|
const renderPromptBlock = () => {
|
|
1423
1675
|
if (ui) {
|
|
1424
|
-
ui.
|
|
1676
|
+
ui.setComposerState(value, getVisibleSuggestions(), selectedSuggestionIndex);
|
|
1425
1677
|
return;
|
|
1426
1678
|
}
|
|
1427
1679
|
const width = getPromptWidth();
|
|
@@ -1429,7 +1681,7 @@ async function askConsoleInput(rl, options) {
|
|
|
1429
1681
|
const modelMode = getModelMode();
|
|
1430
1682
|
const approvalMode = getApprovalMode();
|
|
1431
1683
|
const usageTokens = Math.min(estimateConsoleContextTokens(getConversationMessages(), value), getCliModelContextWindowTokens(modelMode));
|
|
1432
|
-
const suggestionLines =
|
|
1684
|
+
const suggestionLines = buildConsoleSlashSuggestionLines(getVisibleSuggestions(), selectedSuggestionIndex, width, enabled);
|
|
1433
1685
|
const blockLines = [
|
|
1434
1686
|
style("─".repeat(width), ANSI.brandBlue, enabled),
|
|
1435
1687
|
...composer.renderedLines,
|
|
@@ -1530,7 +1782,9 @@ async function askConsoleInput(rl, options) {
|
|
|
1530
1782
|
continue;
|
|
1531
1783
|
}
|
|
1532
1784
|
cleanup();
|
|
1533
|
-
|
|
1785
|
+
if (!ui) {
|
|
1786
|
+
output.write("\n");
|
|
1787
|
+
}
|
|
1534
1788
|
resolve(normalizeText(value));
|
|
1535
1789
|
return;
|
|
1536
1790
|
}
|
|
@@ -1548,7 +1802,9 @@ async function askConsoleInput(rl, options) {
|
|
|
1548
1802
|
renderPromptBlock();
|
|
1549
1803
|
}
|
|
1550
1804
|
};
|
|
1551
|
-
|
|
1805
|
+
if (!ui) {
|
|
1806
|
+
output.on("resize", renderPromptBlock);
|
|
1807
|
+
}
|
|
1552
1808
|
renderPromptBlock();
|
|
1553
1809
|
input.on("data", onData);
|
|
1554
1810
|
});
|
|
@@ -1853,10 +2109,20 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
1853
2109
|
let activeModel = "fast";
|
|
1854
2110
|
let sessionId = randomUUID();
|
|
1855
2111
|
const conversation = [];
|
|
1856
|
-
const ui = createConsoleScreenRenderer(activeModel, config.approvalMode, runtimeSession
|
|
2112
|
+
const ui = createConsoleScreenRenderer(activeModel, config.approvalMode, runtimeSession, {
|
|
2113
|
+
autoActivate: false,
|
|
2114
|
+
});
|
|
1857
2115
|
if (!ui) {
|
|
1858
2116
|
printConsoleScreen(runtimeSession);
|
|
1859
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
|
+
}
|
|
1860
2126
|
const renderConversationState = () => {
|
|
1861
2127
|
ui?.setConversationMessages(conversation);
|
|
1862
2128
|
ui?.setModelMode(activeModel);
|
|
@@ -1927,7 +2193,16 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
1927
2193
|
const cycleApprovalMode = () => {
|
|
1928
2194
|
void setApprovalMode(getNextApprovalMode(config.approvalMode));
|
|
1929
2195
|
};
|
|
2196
|
+
const inputController = ui
|
|
2197
|
+
? new ConsoleInputController(rl, ui, {
|
|
2198
|
+
onCycleApprovalMode: cycleApprovalMode,
|
|
2199
|
+
})
|
|
2200
|
+
: null;
|
|
2201
|
+
inputController?.activate();
|
|
1930
2202
|
const readConsoleInput = async () => {
|
|
2203
|
+
if (inputController) {
|
|
2204
|
+
return await inputController.nextValue();
|
|
2205
|
+
}
|
|
1931
2206
|
return await askConsoleInput(rl, {
|
|
1932
2207
|
ui,
|
|
1933
2208
|
onCycleApprovalMode: cycleApprovalMode,
|
|
@@ -2287,6 +2562,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
2287
2562
|
}
|
|
2288
2563
|
streamedAssistant += contentChunk;
|
|
2289
2564
|
});
|
|
2565
|
+
ui?.commitLiveEntries();
|
|
2290
2566
|
if (assistantPrefixPrinted && !ui) {
|
|
2291
2567
|
output.write("\n");
|
|
2292
2568
|
}
|
|
@@ -2331,6 +2607,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
2331
2607
|
}
|
|
2332
2608
|
}
|
|
2333
2609
|
finally {
|
|
2610
|
+
inputController?.dispose();
|
|
2334
2611
|
ui?.dispose();
|
|
2335
2612
|
}
|
|
2336
2613
|
}
|
|
@@ -2375,6 +2652,14 @@ function renderHomeOptionLine(label, selected) {
|
|
|
2375
2652
|
}
|
|
2376
2653
|
return `${style("▸", ANSI.brandBlue, supportsAnsi())} ${style(label, `${ANSI.bold}${ANSI.white}`, supportsAnsi())}`;
|
|
2377
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
|
+
}
|
|
2378
2663
|
async function pickHomeChoice(rl, paired, renderBaseScreen) {
|
|
2379
2664
|
const options = paired
|
|
2380
2665
|
? [
|
|
@@ -2461,9 +2746,15 @@ async function pickHomeChoice(rl, paired, renderBaseScreen) {
|
|
|
2461
2746
|
return;
|
|
2462
2747
|
}
|
|
2463
2748
|
if (char === "\r" || char === "\n") {
|
|
2749
|
+
const selectedOption = options[selectedIndex];
|
|
2464
2750
|
cleanup();
|
|
2465
|
-
|
|
2466
|
-
|
|
2751
|
+
if (selectedOption?.value === "console") {
|
|
2752
|
+
clearHomeMenuBlock(options.length);
|
|
2753
|
+
}
|
|
2754
|
+
else {
|
|
2755
|
+
output.write("\n");
|
|
2756
|
+
}
|
|
2757
|
+
resolve(selectedOption?.value || "exit");
|
|
2467
2758
|
return;
|
|
2468
2759
|
}
|
|
2469
2760
|
}
|
|
@@ -2566,7 +2857,7 @@ export async function launchInteractiveCli(options) {
|
|
|
2566
2857
|
continue;
|
|
2567
2858
|
}
|
|
2568
2859
|
if (choice === "console" && runtimeSession) {
|
|
2569
|
-
await runOttoConsole(rl, config, runtimeSession);
|
|
2860
|
+
await runOttoConsole(rl, config, runtimeSession, { reuseHubHeader: true });
|
|
2570
2861
|
continue;
|
|
2571
2862
|
}
|
|
2572
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.
|
|
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
package/scripts/postinstall.mjs
CHANGED
|
@@ -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.
|
|
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"], {
|