@leg3ndy/otto-bridge 1.0.5 → 1.0.7

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 a release atual `1.0.5`, com novo modo `Terminal`, input-box mais proximo do estilo Claude e ajuda expandida, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_0_5_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_0_5_PATCH.md).
18
+ Para a release atual `1.0.7`, com hotfix do input-box TTY do console, veja [`leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_0_7_PATCH.md`](../leg3ndy-ai-backend/docs/otto-bridge/releases/OTTO_BRIDGE_1_0_7_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.5.tgz
41
+ npm install -g ./leg3ndy-otto-bridge-1.0.7.tgz
42
42
  ```
43
43
 
44
- Na linha `1.0.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.
44
+ Na linha `1.0.7`, `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.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`.
46
+ No macOS, a linha `1.0.7` 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.5` adiciona o modo `Terminal`, reorganiza o input-box do console para um fluxo mais proximo do Claude e amplia a ajuda publica sem voltar a poluir a superficie com aliases legados.
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.7` preserva esse fluxo e corrige o freeze do input-box TTY introduzido no refinamento visual do console.
49
49
 
50
50
  ## Publicacao
51
51
 
@@ -157,7 +157,7 @@ Esse comando abre um shell local interativo para instalar extensoes, rodar coman
157
157
 
158
158
  ### WhatsApp Web em background
159
159
 
160
- Fluxo recomendado na linha `1.0.5`:
160
+ Fluxo recomendado na linha `1.0.7`:
161
161
 
162
162
  ```bash
163
163
  otto-bridge extensions --install whatsappweb
@@ -167,13 +167,13 @@ otto-bridge extensions --status whatsappweb
167
167
 
168
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.
169
169
 
170
- Contrato da linha `1.0.5`:
170
+ Contrato da linha `1.0.7`:
171
171
 
172
172
  - `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
173
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
174
174
  - ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
175
175
 
176
- ## Handoff rapido da linha 1.0.5
176
+ ## Handoff rapido da linha 1.0.7
177
177
 
178
178
  Ja fechado no codigo:
179
179
 
@@ -59,6 +59,7 @@ class CliRuntimeSession {
59
59
  status = "offline";
60
60
  detail = "Aguardando pairing.";
61
61
  lastError = null;
62
+ releaseNotice = null;
62
63
  constructor(config) {
63
64
  this.config = config;
64
65
  }
@@ -92,8 +93,24 @@ class CliRuntimeSession {
92
93
  }
93
94
  return;
94
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;
95
105
  case "update_available":
96
- this.detail = truncate(event.message || this.detail, 160);
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
+ };
97
114
  return;
98
115
  default:
99
116
  return;
@@ -180,6 +197,9 @@ class CliRuntimeSession {
180
197
  getLastError() {
181
198
  return this.lastError;
182
199
  }
200
+ getReleaseNotice() {
201
+ return this.releaseNotice;
202
+ }
183
203
  }
184
204
  function style(text, color, enabled = true) {
185
205
  if (!enabled) {
@@ -400,6 +420,7 @@ function extractConfirmationPrompt(job) {
400
420
  || payload.confirmation_message) || "O Otto está aguardando sua confirmação para continuar.";
401
421
  }
402
422
  function renderStatusOverview(config, runtimeSession) {
423
+ const releaseNotice = runtimeSession?.getReleaseNotice();
403
424
  return [
404
425
  `${style("Device", ANSI.brandBlue, supportsAnsi())}: ${config.deviceName}`,
405
426
  `${style("Device ID", ANSI.brandBlue, supportsAnsi())}: ${config.deviceId}`,
@@ -410,6 +431,21 @@ function renderStatusOverview(config, runtimeSession) {
410
431
  ...(runtimeSession?.getStatusDetail()
411
432
  ? [`${style("Runtime note", ANSI.brandBlue, supportsAnsi())}: ${runtimeSession.getStatusDetail()}`]
412
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
+ : []),
413
449
  `${style("Config", ANSI.brandBlue, supportsAnsi())}: ${getBridgeConfigPath()}`,
414
450
  ];
415
451
  }
@@ -442,6 +478,9 @@ function styleCardLine(line, tone, enabled) {
442
478
  if (tone === "title") {
443
479
  return `${ANSI.bold}${ANSI.brandBlue}${line}${ANSI.reset}`;
444
480
  }
481
+ if (tone === "warning") {
482
+ return `${ANSI.bold}${ANSI.amber}${line}${ANSI.reset}`;
483
+ }
445
484
  if (tone === "muted") {
446
485
  return `${ANSI.slateItalic}${line}${ANSI.reset}`;
447
486
  }
@@ -477,17 +516,50 @@ function buildWelcomeCard(runtimeSession, modelMode) {
477
516
  { text: renderRuntimeHeadline(runtimeSession), tone: "muted" },
478
517
  ];
479
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
+ }
480
542
  function printHubScreen(runtimeSession, modelMode) {
481
543
  clearScreen();
482
544
  console.log(renderBanner());
483
545
  console.log("");
484
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
+ }
485
552
  }
486
553
  function printConsoleScreen(runtimeSession, modelMode) {
487
554
  clearScreen();
488
555
  console.log(renderBanner());
489
556
  console.log("");
490
557
  console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
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) {
@@ -507,26 +579,90 @@ function renderConsolePromptContentLine(text, width, tone) {
507
579
  return `${border} ${body} ${border}`;
508
580
  }
509
581
  async function askConsoleInput(rl) {
510
- if (!supportsAnsi()) {
511
- printMuted(CONSOLE_PLACEHOLDER);
582
+ if (!supportsAnsi() || typeof input.setRawMode !== "function" || !input.isTTY) {
512
583
  printSoft(`Comandos: ${CONSOLE_COMMAND_HINT}`);
584
+ console.log("");
513
585
  return normalizeText(await question(rl, "> "));
514
586
  }
515
- const availableWidth = Number(output.columns || 96);
516
- const inputWidth = Math.max(42, Math.min(availableWidth - 8, 116));
517
- const top = renderPromptFrameLine(inputWidth + 2, "┌", "┐");
518
- const placeholderLine = renderConsolePromptContentLine(CONSOLE_PLACEHOLDER, inputWidth, "placeholder");
519
- const commandsLine = renderConsolePromptContentLine(`Comandos: ${CONSOLE_COMMAND_HINT}`, inputWidth, "commands");
520
- const inputLine = renderConsolePromptContentLine("", inputWidth, "input");
521
- const bottom = renderPromptFrameLine(inputWidth + 2, "└", "┘");
522
- output.write(`${top}\n${placeholderLine}\n${commandsLine}\n${inputLine}\n${bottom}\n`);
523
- cursorTo(output, 0);
524
- moveCursor(output, 0, -2);
525
- cursorTo(output, 4);
526
- const answer = normalizeText(await question(rl, ""));
527
- cursorTo(output, 0);
528
- moveCursor(output, 0, 1);
529
- return answer;
587
+ rl.pause();
588
+ input.setRawMode(true);
589
+ input.resume();
590
+ return await new Promise((resolve, reject) => {
591
+ const enabled = supportsAnsi();
592
+ const availableWidth = Number(output.columns || 96);
593
+ const innerWidth = Math.max(42, Math.min(availableWidth - 8, 116));
594
+ const sectionTopOffsetFromInputLine = 3;
595
+ let renderedOnce = false;
596
+ let value = "";
597
+ const cleanup = () => {
598
+ input.removeListener("data", onData);
599
+ input.setRawMode(false);
600
+ input.pause();
601
+ rl.resume();
602
+ };
603
+ const renderInputContent = () => {
604
+ const promptPlain = "> ";
605
+ const promptStyled = style(">", `${ANSI.bold}${ANSI.white}`, enabled);
606
+ const maxValueLength = Math.max(0, innerWidth - promptPlain.length);
607
+ if (!value) {
608
+ const placeholder = truncate(CONSOLE_PLACEHOLDER, maxValueLength);
609
+ const padded = `${style(placeholder, ANSI.slate, enabled)}${" ".repeat(Math.max(0, maxValueLength - placeholder.length))}`;
610
+ return `${promptStyled} ${padded}`;
611
+ }
612
+ const visibleValue = value.length > maxValueLength
613
+ ? value.slice(value.length - maxValueLength)
614
+ : value;
615
+ return `${promptStyled} ${visibleValue}${" ".repeat(Math.max(0, maxValueLength - visibleValue.length))}`;
616
+ };
617
+ const render = () => {
618
+ if (renderedOnce) {
619
+ cursorTo(output, 0);
620
+ moveCursor(output, 0, -sectionTopOffsetFromInputLine);
621
+ }
622
+ else {
623
+ renderedOnce = true;
624
+ }
625
+ const commands = style(`Comandos: ${CONSOLE_COMMAND_HINT}`, ANSI.slateItalic, enabled);
626
+ const top = renderPromptFrameLine(innerWidth + 2, "┌", "┐");
627
+ const border = style("│", ANSI.brandBlue, enabled);
628
+ const middle = `${border} ${renderInputContent()} ${border}`;
629
+ const bottom = renderPromptFrameLine(innerWidth + 2, "└", "┘");
630
+ output.write("\u001b[J");
631
+ output.write(`${commands}\n\n${top}\n${middle}\n${bottom}\n`);
632
+ cursorTo(output, 0);
633
+ moveCursor(output, 0, -2);
634
+ const visibleValueLength = Math.min(value.length, Math.max(0, innerWidth - 2));
635
+ cursorTo(output, 4 + visibleValueLength);
636
+ };
637
+ const onData = (chunk) => {
638
+ const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
639
+ for (const char of Array.from(text)) {
640
+ if (char === "\u0003") {
641
+ cleanup();
642
+ reject(createCliExitError());
643
+ return;
644
+ }
645
+ if (char === "\r" || char === "\n") {
646
+ cleanup();
647
+ output.write("\n");
648
+ resolve(normalizeText(value));
649
+ return;
650
+ }
651
+ if (char === "\u007f" || char === "\b") {
652
+ value = value.slice(0, -1);
653
+ render();
654
+ continue;
655
+ }
656
+ if (char === "\u001b") {
657
+ continue;
658
+ }
659
+ value += char;
660
+ render();
661
+ }
662
+ };
663
+ render();
664
+ input.on("data", onData);
665
+ });
530
666
  }
531
667
  function printAssistantMessage(message) {
532
668
  const text = normalizeText(message);
@@ -647,6 +783,16 @@ export function renderStructuredOutcome(job, options) {
647
783
  }
648
784
  return lines.join("\n").trim();
649
785
  }
786
+ export function renderBridgeReleaseNoticeCard(notice) {
787
+ return renderInfoCard(buildBridgeReleaseCard({
788
+ kind: notice.kind,
789
+ message: normalizeText(notice.message),
790
+ currentVersion: normalizeText(notice.currentVersion),
791
+ latestVersion: normalizeText(notice.latestVersion),
792
+ minSupportedVersion: normalizeText(notice.minSupportedVersion),
793
+ updateCommand: normalizeText(notice.updateCommand) || "otto-bridge update",
794
+ }));
795
+ }
650
796
  function printStructuredOutcome(job) {
651
797
  const rendered = renderStructuredOutcome(job);
652
798
  if (!rendered) {
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({ type: "update_required", message });
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({ type: "update_available", message });
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.5";
2
+ export const BRIDGE_VERSION = "1.0.7";
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.5",
3
+ "version": "1.0.7",
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.0.5");
27
+ console.log("\n[otto-bridge] Welcome to OTTOAI 1.0.7");
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"], {