@leg3ndy/otto-bridge 1.0.4 → 1.0.6
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 +18 -10
- package/dist/cli_terminal.js +269 -33
- package/dist/main.js +9 -1
- package/dist/runtime.js +16 -2
- package/dist/types.js +1 -1
- package/package.json +2 -1
- package/scripts/postinstall.mjs +1 -1
package/README.md
CHANGED
|
@@ -11,11 +11,11 @@ Companion local do Otto para:
|
|
|
11
11
|
|
|
12
12
|
Para um passo a passo de instalacao, pareamento, uso, desconexao e desinstalacao, veja [USER_GUIDE.md](https://github.com/LGCYYL/ottoai/blob/main/otto-bridge/USER_GUIDE.md).
|
|
13
13
|
|
|
14
|
-
Para o estado atual da arquitetura, capacidades entregues, limitacoes e roadmap do Otto Bridge, veja [`leg3ndy-ai-backend/docs/OTTO_BRIDGE_ARCHITECTURE.md`](../leg3ndy-ai-backend/docs/OTTO_BRIDGE_ARCHITECTURE.md).
|
|
14
|
+
Para o estado atual da arquitetura, capacidades entregues, limitacoes e roadmap do Otto Bridge, veja [`leg3ndy-ai-backend/docs/otto-bridge/OTTO_BRIDGE_ARCHITECTURE.md`](../leg3ndy-ai-backend/docs/otto-bridge/OTTO_BRIDGE_ARCHITECTURE.md).
|
|
15
15
|
|
|
16
|
-
Para o corte de arquitetura do `0.9.0`, veja [`leg3ndy-ai-backend/docs/OTTO_BRIDGE_0_9_0_RELEASE.md`](../leg3ndy-ai-backend/docs/OTTO_BRIDGE_0_9_0_RELEASE.md).
|
|
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 a release atual `1.0.
|
|
18
|
+
Para a release atual `1.0.6`, com aviso de update no proprio CLI e input-box refinado do console, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_0_6_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_0_6_PATCH.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.0.
|
|
41
|
+
npm install -g ./leg3ndy-otto-bridge-1.0.6.tgz
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Na linha `1.0.
|
|
44
|
+
Na linha `1.0.6`, `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.0.
|
|
46
|
+
No macOS, a linha `1.0.6` 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.0.
|
|
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.0.6` adiciona aviso de update no proprio CLI e refina o input-box do console sem voltar a poluir a superficie com aliases legados.
|
|
49
49
|
|
|
50
50
|
## Publicacao
|
|
51
51
|
|
|
@@ -147,9 +147,17 @@ No modo `OttoAI Thinking`, o terminal agora marca explicitamente o trecho de rac
|
|
|
147
147
|
|
|
148
148
|
Quando o handoff local devolver resultado estruturado, o CLI agora mostra inline a listagem de arquivos e o conteúdo de `read_file`, em vez de só resumir que executou a tarefa.
|
|
149
149
|
|
|
150
|
+
### Abrir um shell local pelo hub
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
otto-bridge terminal
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Esse comando abre um shell local interativo para instalar extensoes, rodar comandos externos e fazer operacao manual sem encerrar o bridge. No hub principal, a opcao `Terminal` faz a mesma coisa e volta ao menu quando voce sair com `exit`.
|
|
157
|
+
|
|
150
158
|
### WhatsApp Web em background
|
|
151
159
|
|
|
152
|
-
Fluxo recomendado na linha `1.0.
|
|
160
|
+
Fluxo recomendado na linha `1.0.6`:
|
|
153
161
|
|
|
154
162
|
```bash
|
|
155
163
|
otto-bridge extensions --install whatsappweb
|
|
@@ -159,13 +167,13 @@ otto-bridge extensions --status whatsappweb
|
|
|
159
167
|
|
|
160
168
|
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.
|
|
161
169
|
|
|
162
|
-
Contrato da linha `1.0.
|
|
170
|
+
Contrato da linha `1.0.6`:
|
|
163
171
|
|
|
164
172
|
- `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
|
|
165
173
|
- `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
|
|
166
174
|
- ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
|
|
167
175
|
|
|
168
|
-
## Handoff rapido da linha 1.0.
|
|
176
|
+
## Handoff rapido da linha 1.0.6
|
|
169
177
|
|
|
170
178
|
Ja fechado no codigo:
|
|
171
179
|
|
package/dist/cli_terminal.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { createInterface } from "node:readline/promises";
|
|
3
4
|
import { cursorTo, moveCursor, } from "node:readline";
|
|
4
5
|
import process, { stdin as input, stdout as output } from "node:process";
|
|
@@ -49,6 +50,8 @@ const MAX_RENDERED_LIST_ENTRIES = 28;
|
|
|
49
50
|
const MAX_RENDERED_LIST_ENTRIES_COMPACT = 10;
|
|
50
51
|
const MAX_RENDERED_FILE_CHARS = 6_000;
|
|
51
52
|
const MAX_RENDERED_FILE_CHARS_COMPACT = 1_400;
|
|
53
|
+
const CONSOLE_PLACEHOLDER = "Peça algo ao Otto";
|
|
54
|
+
const CONSOLE_COMMAND_HINT = "/help, /model [fast|thinking], /status, /clear, /exit";
|
|
52
55
|
class CliRuntimeSession {
|
|
53
56
|
config;
|
|
54
57
|
runtime = null;
|
|
@@ -56,6 +59,7 @@ class CliRuntimeSession {
|
|
|
56
59
|
status = "offline";
|
|
57
60
|
detail = "Aguardando pairing.";
|
|
58
61
|
lastError = null;
|
|
62
|
+
releaseNotice = null;
|
|
59
63
|
constructor(config) {
|
|
60
64
|
this.config = config;
|
|
61
65
|
}
|
|
@@ -89,8 +93,24 @@ class CliRuntimeSession {
|
|
|
89
93
|
}
|
|
90
94
|
return;
|
|
91
95
|
case "update_required":
|
|
96
|
+
this.releaseNotice = {
|
|
97
|
+
kind: "required",
|
|
98
|
+
message: event.message,
|
|
99
|
+
currentVersion: normalizeText(event.currentVersion),
|
|
100
|
+
latestVersion: normalizeText(event.latestVersion),
|
|
101
|
+
minSupportedVersion: normalizeText(event.minSupportedVersion),
|
|
102
|
+
updateCommand: normalizeText(event.updateCommand) || "otto-bridge update",
|
|
103
|
+
};
|
|
104
|
+
return;
|
|
92
105
|
case "update_available":
|
|
93
|
-
this.
|
|
106
|
+
this.releaseNotice = {
|
|
107
|
+
kind: "available",
|
|
108
|
+
message: event.message,
|
|
109
|
+
currentVersion: normalizeText(event.currentVersion),
|
|
110
|
+
latestVersion: normalizeText(event.latestVersion),
|
|
111
|
+
minSupportedVersion: normalizeText(event.minSupportedVersion),
|
|
112
|
+
updateCommand: normalizeText(event.updateCommand) || "otto-bridge update",
|
|
113
|
+
};
|
|
94
114
|
return;
|
|
95
115
|
default:
|
|
96
116
|
return;
|
|
@@ -177,6 +197,9 @@ class CliRuntimeSession {
|
|
|
177
197
|
getLastError() {
|
|
178
198
|
return this.lastError;
|
|
179
199
|
}
|
|
200
|
+
getReleaseNotice() {
|
|
201
|
+
return this.releaseNotice;
|
|
202
|
+
}
|
|
180
203
|
}
|
|
181
204
|
function style(text, color, enabled = true) {
|
|
182
205
|
if (!enabled) {
|
|
@@ -397,6 +420,7 @@ function extractConfirmationPrompt(job) {
|
|
|
397
420
|
|| payload.confirmation_message) || "O Otto está aguardando sua confirmação para continuar.";
|
|
398
421
|
}
|
|
399
422
|
function renderStatusOverview(config, runtimeSession) {
|
|
423
|
+
const releaseNotice = runtimeSession?.getReleaseNotice();
|
|
400
424
|
return [
|
|
401
425
|
`${style("Device", ANSI.brandBlue, supportsAnsi())}: ${config.deviceName}`,
|
|
402
426
|
`${style("Device ID", ANSI.brandBlue, supportsAnsi())}: ${config.deviceId}`,
|
|
@@ -407,6 +431,21 @@ function renderStatusOverview(config, runtimeSession) {
|
|
|
407
431
|
...(runtimeSession?.getStatusDetail()
|
|
408
432
|
? [`${style("Runtime note", ANSI.brandBlue, supportsAnsi())}: ${runtimeSession.getStatusDetail()}`]
|
|
409
433
|
: []),
|
|
434
|
+
...(releaseNotice
|
|
435
|
+
? [
|
|
436
|
+
`${style("Bridge update", ANSI.brandBlue, supportsAnsi())}: ${releaseNotice.kind === "required" ? "required" : "available"}`,
|
|
437
|
+
...(releaseNotice.currentVersion
|
|
438
|
+
? [`${style("Current version", ANSI.brandBlue, supportsAnsi())}: ${releaseNotice.currentVersion}`]
|
|
439
|
+
: []),
|
|
440
|
+
...(releaseNotice.latestVersion
|
|
441
|
+
? [`${style("Latest version", ANSI.brandBlue, supportsAnsi())}: ${releaseNotice.latestVersion}`]
|
|
442
|
+
: []),
|
|
443
|
+
...(releaseNotice.minSupportedVersion
|
|
444
|
+
? [`${style("Min supported", ANSI.brandBlue, supportsAnsi())}: ${releaseNotice.minSupportedVersion}`]
|
|
445
|
+
: []),
|
|
446
|
+
`${style("Update command", ANSI.brandBlue, supportsAnsi())}: ${releaseNotice.updateCommand || "otto-bridge update"}`,
|
|
447
|
+
]
|
|
448
|
+
: []),
|
|
410
449
|
`${style("Config", ANSI.brandBlue, supportsAnsi())}: ${getBridgeConfigPath()}`,
|
|
411
450
|
];
|
|
412
451
|
}
|
|
@@ -439,6 +478,9 @@ function styleCardLine(line, tone, enabled) {
|
|
|
439
478
|
if (tone === "title") {
|
|
440
479
|
return `${ANSI.bold}${ANSI.brandBlue}${line}${ANSI.reset}`;
|
|
441
480
|
}
|
|
481
|
+
if (tone === "warning") {
|
|
482
|
+
return `${ANSI.bold}${ANSI.amber}${line}${ANSI.reset}`;
|
|
483
|
+
}
|
|
442
484
|
if (tone === "muted") {
|
|
443
485
|
return `${ANSI.slateItalic}${line}${ANSI.reset}`;
|
|
444
486
|
}
|
|
@@ -474,49 +516,151 @@ function buildWelcomeCard(runtimeSession, modelMode) {
|
|
|
474
516
|
{ text: renderRuntimeHeadline(runtimeSession), tone: "muted" },
|
|
475
517
|
];
|
|
476
518
|
}
|
|
519
|
+
function buildBridgeReleaseCard(notice) {
|
|
520
|
+
const isRequired = notice.kind === "required";
|
|
521
|
+
return [
|
|
522
|
+
{
|
|
523
|
+
text: isRequired ? "Atualização obrigatória do bridge" : "Atualização disponível do bridge",
|
|
524
|
+
tone: "warning",
|
|
525
|
+
},
|
|
526
|
+
{ text: "", tone: "muted" },
|
|
527
|
+
{
|
|
528
|
+
text: isRequired
|
|
529
|
+
? `Atual: ${notice.currentVersion || "desconhecida"} | mínima: ${notice.minSupportedVersion || "desconhecida"}`
|
|
530
|
+
: `Atual: ${notice.currentVersion || "desconhecida"} | nova: ${notice.latestVersion || "desconhecida"}`,
|
|
531
|
+
tone: "muted",
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
text: isRequired
|
|
535
|
+
? "Este bridge precisa ser atualizado para continuar suportado."
|
|
536
|
+
: "Há uma nova versão do Otto Bridge pronta para instalar.",
|
|
537
|
+
tone: "muted",
|
|
538
|
+
},
|
|
539
|
+
{ text: `Rode: ${notice.updateCommand || "otto-bridge update"}`, tone: "primary" },
|
|
540
|
+
];
|
|
541
|
+
}
|
|
477
542
|
function printHubScreen(runtimeSession, modelMode) {
|
|
478
543
|
clearScreen();
|
|
479
544
|
console.log(renderBanner());
|
|
480
545
|
console.log("");
|
|
481
546
|
console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
|
|
547
|
+
const releaseNotice = runtimeSession?.getReleaseNotice();
|
|
548
|
+
if (releaseNotice) {
|
|
549
|
+
console.log("");
|
|
550
|
+
console.log(renderInfoCard(buildBridgeReleaseCard(releaseNotice)));
|
|
551
|
+
}
|
|
482
552
|
}
|
|
483
553
|
function printConsoleScreen(runtimeSession, modelMode) {
|
|
484
554
|
clearScreen();
|
|
485
555
|
console.log(renderBanner());
|
|
486
556
|
console.log("");
|
|
487
557
|
console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
558
|
+
const releaseNotice = runtimeSession.getReleaseNotice();
|
|
559
|
+
if (releaseNotice) {
|
|
560
|
+
console.log("");
|
|
561
|
+
console.log(renderInfoCard(buildBridgeReleaseCard(releaseNotice)));
|
|
562
|
+
}
|
|
491
563
|
console.log("");
|
|
492
564
|
}
|
|
493
565
|
function renderPromptFrameLine(width, edgeLeft, edgeRight) {
|
|
494
566
|
return style(`${edgeLeft}${"─".repeat(width)}${edgeRight}`, ANSI.brandBlue, supportsAnsi());
|
|
495
567
|
}
|
|
568
|
+
function renderConsolePromptContentLine(text, width, tone) {
|
|
569
|
+
const enabled = supportsAnsi();
|
|
570
|
+
const border = style("│", ANSI.brandBlue, enabled);
|
|
571
|
+
if (tone === "input") {
|
|
572
|
+
const prompt = `${style(">", `${ANSI.bold}${ANSI.white}`, enabled)} ${" ".repeat(Math.max(0, width - 2))}`;
|
|
573
|
+
return `${border} ${prompt} ${border}`;
|
|
574
|
+
}
|
|
575
|
+
const clipped = padRight(truncate(text, width), width);
|
|
576
|
+
const body = tone === "placeholder"
|
|
577
|
+
? style(clipped, `${ANSI.bold}${ANSI.white}`, enabled)
|
|
578
|
+
: style(clipped, ANSI.slateItalic, enabled);
|
|
579
|
+
return `${border} ${body} ${border}`;
|
|
580
|
+
}
|
|
496
581
|
async function askConsoleInput(rl) {
|
|
497
|
-
if (!supportsAnsi()) {
|
|
582
|
+
if (!supportsAnsi() || typeof input.setRawMode !== "function" || !input.isTTY) {
|
|
583
|
+
printSoft(`Comandos: ${CONSOLE_COMMAND_HINT}`);
|
|
584
|
+
console.log("");
|
|
498
585
|
return normalizeText(await question(rl, "> "));
|
|
499
586
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
587
|
+
rl.pause();
|
|
588
|
+
input.setRawMode(true);
|
|
589
|
+
return await new Promise((resolve, reject) => {
|
|
590
|
+
const enabled = supportsAnsi();
|
|
591
|
+
const availableWidth = Number(output.columns || 96);
|
|
592
|
+
const innerWidth = Math.max(42, Math.min(availableWidth - 8, 116));
|
|
593
|
+
const sectionTopOffsetFromInputLine = 3;
|
|
594
|
+
let renderedOnce = false;
|
|
595
|
+
let value = "";
|
|
596
|
+
const cleanup = () => {
|
|
597
|
+
input.removeListener("data", onData);
|
|
598
|
+
input.setRawMode(false);
|
|
599
|
+
rl.resume();
|
|
600
|
+
};
|
|
601
|
+
const renderInputContent = () => {
|
|
602
|
+
const promptPlain = "> ";
|
|
603
|
+
const promptStyled = style(">", `${ANSI.bold}${ANSI.white}`, enabled);
|
|
604
|
+
const maxValueLength = Math.max(0, innerWidth - promptPlain.length);
|
|
605
|
+
if (!value) {
|
|
606
|
+
const placeholder = truncate(CONSOLE_PLACEHOLDER, maxValueLength);
|
|
607
|
+
const padded = `${style(placeholder, ANSI.slate, enabled)}${" ".repeat(Math.max(0, maxValueLength - placeholder.length))}`;
|
|
608
|
+
return `${promptStyled} ${padded}`;
|
|
609
|
+
}
|
|
610
|
+
const visibleValue = value.length > maxValueLength
|
|
611
|
+
? value.slice(value.length - maxValueLength)
|
|
612
|
+
: value;
|
|
613
|
+
return `${promptStyled} ${visibleValue}${" ".repeat(Math.max(0, maxValueLength - visibleValue.length))}`;
|
|
614
|
+
};
|
|
615
|
+
const render = () => {
|
|
616
|
+
if (renderedOnce) {
|
|
617
|
+
cursorTo(output, 0);
|
|
618
|
+
moveCursor(output, 0, -sectionTopOffsetFromInputLine);
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
renderedOnce = true;
|
|
622
|
+
}
|
|
623
|
+
const commands = style(`Comandos: ${CONSOLE_COMMAND_HINT}`, ANSI.slateItalic, enabled);
|
|
624
|
+
const top = renderPromptFrameLine(innerWidth + 2, "┌", "┐");
|
|
625
|
+
const border = style("│", ANSI.brandBlue, enabled);
|
|
626
|
+
const middle = `${border} ${renderInputContent()} ${border}`;
|
|
627
|
+
const bottom = renderPromptFrameLine(innerWidth + 2, "└", "┘");
|
|
628
|
+
output.write("\u001b[J");
|
|
629
|
+
output.write(`${commands}\n\n${top}\n${middle}\n${bottom}\n`);
|
|
630
|
+
cursorTo(output, 0);
|
|
631
|
+
moveCursor(output, 0, -2);
|
|
632
|
+
const visibleValueLength = Math.min(value.length, Math.max(0, innerWidth - 2));
|
|
633
|
+
cursorTo(output, 4 + visibleValueLength);
|
|
634
|
+
};
|
|
635
|
+
const onData = (chunk) => {
|
|
636
|
+
const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
637
|
+
for (const char of Array.from(text)) {
|
|
638
|
+
if (char === "\u0003") {
|
|
639
|
+
cleanup();
|
|
640
|
+
reject(createCliExitError());
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (char === "\r" || char === "\n") {
|
|
644
|
+
cleanup();
|
|
645
|
+
output.write("\n");
|
|
646
|
+
resolve(normalizeText(value));
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (char === "\u007f" || char === "\b") {
|
|
650
|
+
value = value.slice(0, -1);
|
|
651
|
+
render();
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (char === "\u001b") {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
value += char;
|
|
658
|
+
render();
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
render();
|
|
662
|
+
input.on("data", onData);
|
|
663
|
+
});
|
|
520
664
|
}
|
|
521
665
|
function printAssistantMessage(message) {
|
|
522
666
|
const text = normalizeText(message);
|
|
@@ -637,6 +781,16 @@ export function renderStructuredOutcome(job, options) {
|
|
|
637
781
|
}
|
|
638
782
|
return lines.join("\n").trim();
|
|
639
783
|
}
|
|
784
|
+
export function renderBridgeReleaseNoticeCard(notice) {
|
|
785
|
+
return renderInfoCard(buildBridgeReleaseCard({
|
|
786
|
+
kind: notice.kind,
|
|
787
|
+
message: normalizeText(notice.message),
|
|
788
|
+
currentVersion: normalizeText(notice.currentVersion),
|
|
789
|
+
latestVersion: normalizeText(notice.latestVersion),
|
|
790
|
+
minSupportedVersion: normalizeText(notice.minSupportedVersion),
|
|
791
|
+
updateCommand: normalizeText(notice.updateCommand) || "otto-bridge update",
|
|
792
|
+
}));
|
|
793
|
+
}
|
|
640
794
|
function printStructuredOutcome(job) {
|
|
641
795
|
const rendered = renderStructuredOutcome(job);
|
|
642
796
|
if (!rendered) {
|
|
@@ -645,6 +799,43 @@ function printStructuredOutcome(job) {
|
|
|
645
799
|
console.log("");
|
|
646
800
|
console.log(rendered);
|
|
647
801
|
}
|
|
802
|
+
function resolveTerminalShellCommand() {
|
|
803
|
+
if (process.platform === "win32") {
|
|
804
|
+
return {
|
|
805
|
+
command: process.env.COMSPEC || "cmd.exe",
|
|
806
|
+
args: [],
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
return {
|
|
810
|
+
command: process.env.SHELL || "/bin/zsh",
|
|
811
|
+
args: ["-l"],
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
export async function runTerminalShell(options) {
|
|
815
|
+
clearScreen();
|
|
816
|
+
console.log(renderBanner());
|
|
817
|
+
console.log("");
|
|
818
|
+
printSection("Terminal");
|
|
819
|
+
printMuted(options?.fromHub
|
|
820
|
+
? "Saindo temporariamente para o shell local. Digite `exit` para voltar ao Otto Bridge."
|
|
821
|
+
: "Abrindo o shell local. Digite `exit` para encerrar e voltar ao terminal anterior.");
|
|
822
|
+
console.log("");
|
|
823
|
+
const shell = resolveTerminalShellCommand();
|
|
824
|
+
await new Promise((resolve, reject) => {
|
|
825
|
+
const child = spawn(shell.command, shell.args, {
|
|
826
|
+
stdio: "inherit",
|
|
827
|
+
env: process.env,
|
|
828
|
+
});
|
|
829
|
+
child.on("error", reject);
|
|
830
|
+
child.on("exit", (code, signal) => {
|
|
831
|
+
if (signal || code === 0 || code === 130) {
|
|
832
|
+
resolve();
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
reject(new Error(`Terminal local encerrou com code ${code ?? "unknown"}.`));
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
}
|
|
648
839
|
function buildConversationSummary(summary, job) {
|
|
649
840
|
const rendered = renderStructuredOutcome(job, { compact: true });
|
|
650
841
|
if (!rendered) {
|
|
@@ -764,7 +955,9 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
764
955
|
const sessionId = randomUUID();
|
|
765
956
|
const conversation = [];
|
|
766
957
|
const printConsoleHelp = () => {
|
|
958
|
+
printSection("Console");
|
|
767
959
|
printSoft("Comandos: /help, /model [fast|thinking], /status, /clear, /exit");
|
|
960
|
+
printSoft("Bridge: otto-bridge terminal, otto-bridge extensions --install <name>, otto-bridge update");
|
|
768
961
|
};
|
|
769
962
|
const handlePrompt = async (promptText) => {
|
|
770
963
|
const normalizedPrompt = normalizeText(promptText);
|
|
@@ -927,12 +1120,24 @@ async function printHelpView(rl) {
|
|
|
927
1120
|
{ text: "otto-bridge", tone: "primary" },
|
|
928
1121
|
{ text: "Abre o hub principal e sobe o runtime local automaticamente.", tone: "muted" },
|
|
929
1122
|
{ text: "", tone: "muted" },
|
|
1123
|
+
{ text: "otto-bridge terminal", tone: "primary" },
|
|
1124
|
+
{ text: "Abre um shell local interativo sem desligar o bridge.", tone: "muted" },
|
|
1125
|
+
{ text: "", tone: "muted" },
|
|
930
1126
|
{ text: "otto-bridge setup", tone: "primary" },
|
|
931
1127
|
{ text: "Repareia o dispositivo e atualiza config/executor.", tone: "muted" },
|
|
932
1128
|
{ text: "", tone: "muted" },
|
|
933
1129
|
{ text: "otto-bridge console", tone: "primary" },
|
|
934
1130
|
{ text: "Abre direto o console do Otto no terminal.", tone: "muted" },
|
|
935
1131
|
{ text: "", tone: "muted" },
|
|
1132
|
+
{ text: "otto-bridge extensions --install <name>", tone: "primary" },
|
|
1133
|
+
{ text: "Instala uma extensao local do bridge.", tone: "muted" },
|
|
1134
|
+
{ text: "", tone: "muted" },
|
|
1135
|
+
{ text: "otto-bridge update --dry-run | otto-bridge update", tone: "primary" },
|
|
1136
|
+
{ text: "Mostra ou aplica a atualizacao global do pacote.", tone: "muted" },
|
|
1137
|
+
{ text: "", tone: "muted" },
|
|
1138
|
+
{ text: "otto-bridge version | otto-bridge unpair", tone: "primary" },
|
|
1139
|
+
{ text: "Mostra a versao instalada ou remove o pairing local.", tone: "muted" },
|
|
1140
|
+
{ text: "", tone: "muted" },
|
|
936
1141
|
{ text: "Dentro do console: /help, /model fast|thinking, /status, /clear, /exit", tone: "muted" },
|
|
937
1142
|
]));
|
|
938
1143
|
await pauseForEnter(rl);
|
|
@@ -942,22 +1147,26 @@ async function pickHomeChoice(rl, paired) {
|
|
|
942
1147
|
const options = paired
|
|
943
1148
|
? [
|
|
944
1149
|
`${style("1.", ANSI.brandBlue, supportsAnsi())} Otto Console`,
|
|
945
|
-
`${style("2.", ANSI.brandBlue, supportsAnsi())}
|
|
946
|
-
`${style("3.", ANSI.brandBlue, supportsAnsi())}
|
|
947
|
-
`${style("4.", ANSI.brandBlue, supportsAnsi())}
|
|
948
|
-
`${style("5.", ANSI.brandBlue, supportsAnsi())}
|
|
1150
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} Terminal`,
|
|
1151
|
+
`${style("3.", ANSI.brandBlue, supportsAnsi())} Setup / parear novamente`,
|
|
1152
|
+
`${style("4.", ANSI.brandBlue, supportsAnsi())} Status detalhado`,
|
|
1153
|
+
`${style("5.", ANSI.brandBlue, supportsAnsi())} Extensões instaladas`,
|
|
949
1154
|
`${style("6.", ANSI.brandBlue, supportsAnsi())} Ajuda`,
|
|
1155
|
+
`${style("7.", ANSI.brandBlue, supportsAnsi())} Sair`,
|
|
950
1156
|
]
|
|
951
1157
|
: [
|
|
952
1158
|
`${style("1.", ANSI.brandBlue, supportsAnsi())} Pairing setup`,
|
|
953
|
-
`${style("2.", ANSI.brandBlue, supportsAnsi())}
|
|
1159
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} Terminal`,
|
|
954
1160
|
`${style("3.", ANSI.brandBlue, supportsAnsi())} Ajuda`,
|
|
1161
|
+
`${style("4.", ANSI.brandBlue, supportsAnsi())} Sair`,
|
|
955
1162
|
];
|
|
956
1163
|
console.log(options.join("\n"));
|
|
957
1164
|
const answer = await ask(rl, "Escolha");
|
|
958
1165
|
if (!paired) {
|
|
959
1166
|
if (answer === "1")
|
|
960
1167
|
return "setup";
|
|
1168
|
+
if (answer === "2")
|
|
1169
|
+
return "terminal";
|
|
961
1170
|
if (answer === "3")
|
|
962
1171
|
return "help";
|
|
963
1172
|
return "exit";
|
|
@@ -965,17 +1174,19 @@ async function pickHomeChoice(rl, paired) {
|
|
|
965
1174
|
if (answer === "1")
|
|
966
1175
|
return "console";
|
|
967
1176
|
if (answer === "2")
|
|
968
|
-
return "
|
|
1177
|
+
return "terminal";
|
|
969
1178
|
if (answer === "3")
|
|
970
|
-
return "
|
|
1179
|
+
return "setup";
|
|
971
1180
|
if (answer === "4")
|
|
1181
|
+
return "status";
|
|
1182
|
+
if (answer === "5")
|
|
972
1183
|
return "extensions";
|
|
973
1184
|
if (answer === "6")
|
|
974
1185
|
return "help";
|
|
975
1186
|
return "exit";
|
|
976
1187
|
}
|
|
977
1188
|
export async function launchInteractiveCli(options) {
|
|
978
|
-
|
|
1189
|
+
let rl = await createPromptInterface();
|
|
979
1190
|
let runtimeSession = null;
|
|
980
1191
|
try {
|
|
981
1192
|
let config = await loadBridgeConfig();
|
|
@@ -1006,6 +1217,31 @@ export async function launchInteractiveCli(options) {
|
|
|
1006
1217
|
await printHelpView(rl);
|
|
1007
1218
|
continue;
|
|
1008
1219
|
}
|
|
1220
|
+
if (choice === "terminal") {
|
|
1221
|
+
rl.close();
|
|
1222
|
+
await runTerminalShell({ fromHub: true });
|
|
1223
|
+
rl = await createPromptInterface();
|
|
1224
|
+
const refreshedConfig = await loadBridgeConfig();
|
|
1225
|
+
if (!refreshedConfig) {
|
|
1226
|
+
await runtimeSession?.stop().catch(() => undefined);
|
|
1227
|
+
runtimeSession = null;
|
|
1228
|
+
printWarning("Pairing local removido no terminal. Encerrando o hub.");
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
if (JSON.stringify(refreshedConfig) !== JSON.stringify(config)) {
|
|
1232
|
+
config = refreshedConfig;
|
|
1233
|
+
if (runtimeSession) {
|
|
1234
|
+
await runtimeSession.replaceConfig(refreshedConfig);
|
|
1235
|
+
await runtimeSession.waitForReady();
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
runtimeSession = new CliRuntimeSession(refreshedConfig);
|
|
1239
|
+
await runtimeSession.ensureStarted();
|
|
1240
|
+
await runtimeSession.waitForReady();
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1009
1245
|
if (choice === "setup") {
|
|
1010
1246
|
const setup = await runSetupWizard(rl);
|
|
1011
1247
|
if (setup.config) {
|
package/dist/main.js
CHANGED
|
@@ -6,7 +6,7 @@ import { buildInstalledManagedExtensionState, formatManagedBridgeExtensionStatus
|
|
|
6
6
|
import { pairDevice } from "./pairing.js";
|
|
7
7
|
import { detectWhatsAppBackgroundStatus, runWhatsAppBackgroundSetup, } from "./whatsapp_background.js";
|
|
8
8
|
import { BRIDGE_PACKAGE_NAME, BRIDGE_VERSION, DEFAULT_PAIR_TIMEOUT_SECONDS, DEFAULT_POLL_INTERVAL_MS, } from "./types.js";
|
|
9
|
-
import { launchInteractiveCli, runConsoleCommand, runSetupCommand, } from "./cli_terminal.js";
|
|
9
|
+
import { launchInteractiveCli, runConsoleCommand, runSetupCommand, runTerminalShell, } from "./cli_terminal.js";
|
|
10
10
|
const RUNTIME_STATUS_FRESHNESS_MS = 90_000;
|
|
11
11
|
const UPDATE_RETRY_DELAYS_MS = [0, 8_000, 20_000];
|
|
12
12
|
function parseArgs(argv) {
|
|
@@ -107,6 +107,7 @@ function printUsage() {
|
|
|
107
107
|
otto-bridge abre o hub principal e sobe o runtime local
|
|
108
108
|
otto-bridge setup refaz o pairing no terminal
|
|
109
109
|
otto-bridge console abre direto o console do Otto
|
|
110
|
+
otto-bridge terminal abre um shell local e volta ao hub ao sair
|
|
110
111
|
otto-bridge status mostra o pairing atual em JSON
|
|
111
112
|
otto-bridge extensions --list
|
|
112
113
|
otto-bridge extensions --install <name>
|
|
@@ -127,9 +128,13 @@ Console:
|
|
|
127
128
|
|
|
128
129
|
Examples:
|
|
129
130
|
otto-bridge
|
|
131
|
+
otto-bridge terminal
|
|
132
|
+
otto-bridge status
|
|
130
133
|
otto-bridge extensions --install <name>
|
|
131
134
|
otto-bridge extensions --setup <name>
|
|
135
|
+
otto-bridge update
|
|
132
136
|
otto-bridge update --dry-run
|
|
137
|
+
otto-bridge unpair
|
|
133
138
|
otto-bridge --version`);
|
|
134
139
|
}
|
|
135
140
|
function printVersion() {
|
|
@@ -560,6 +565,9 @@ async function main() {
|
|
|
560
565
|
case "console":
|
|
561
566
|
await runConsoleCommand(option(args, "prompt"));
|
|
562
567
|
return;
|
|
568
|
+
case "terminal":
|
|
569
|
+
await runTerminalShell();
|
|
570
|
+
return;
|
|
563
571
|
case "pair":
|
|
564
572
|
await runPairCommand(args);
|
|
565
573
|
return;
|
package/dist/runtime.js
CHANGED
|
@@ -389,13 +389,27 @@ export class BridgeRuntime {
|
|
|
389
389
|
if (updateRequired) {
|
|
390
390
|
const message = `[otto-bridge] update required current=${this.config.bridgeVersion} min_supported=${minSupportedVersion || "unknown"} latest=${latestVersion || "unknown"} command="${updateCommand}"`;
|
|
391
391
|
this.logWarn(message);
|
|
392
|
-
this.emit({
|
|
392
|
+
this.emit({
|
|
393
|
+
type: "update_required",
|
|
394
|
+
message,
|
|
395
|
+
currentVersion: this.config.bridgeVersion,
|
|
396
|
+
latestVersion,
|
|
397
|
+
minSupportedVersion,
|
|
398
|
+
updateCommand,
|
|
399
|
+
});
|
|
393
400
|
return;
|
|
394
401
|
}
|
|
395
402
|
if (updateAvailable) {
|
|
396
403
|
const message = `[otto-bridge] update available current=${this.config.bridgeVersion} latest=${latestVersion || "unknown"} command="${updateCommand}"`;
|
|
397
404
|
this.logInfo(message);
|
|
398
|
-
this.emit({
|
|
405
|
+
this.emit({
|
|
406
|
+
type: "update_available",
|
|
407
|
+
message,
|
|
408
|
+
currentVersion: this.config.bridgeVersion,
|
|
409
|
+
latestVersion,
|
|
410
|
+
minSupportedVersion,
|
|
411
|
+
updateCommand,
|
|
412
|
+
});
|
|
399
413
|
}
|
|
400
414
|
}
|
|
401
415
|
clearPendingConfirmations(jobId) {
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const BRIDGE_CONFIG_VERSION = 1;
|
|
2
|
-
export const BRIDGE_VERSION = "1.0.
|
|
2
|
+
export const BRIDGE_VERSION = "1.0.6";
|
|
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.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Local companion for Otto Bridge device pairing and WebSocket runtime.",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"pair": "node dist/main.js pair",
|
|
41
41
|
"setup": "node dist/main.js setup",
|
|
42
42
|
"console": "node dist/main.js console",
|
|
43
|
+
"terminal": "node dist/main.js terminal",
|
|
43
44
|
"run": "node dist/main.js run",
|
|
44
45
|
"status": "node dist/main.js status",
|
|
45
46
|
"version:cli": "node dist/main.js version",
|
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.0.
|
|
27
|
+
console.log("\n[otto-bridge] Welcome to OTTOAI 1.0.6");
|
|
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"], {
|