@leg3ndy/otto-bridge 1.0.0 → 1.0.2

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_0_9_0_RELEASE.md`](../leg3ndy-ai-backend/docs/OTTO_BRIDGE_0_9_0_RELEASE.md).
17
17
 
18
- Para a release atual `1.0.0`, com runtime agentico formal e CLI interativo do bridge, veja [`leg3ndy-ai-backend/docs/OTTO_BRIDGE_1_0_0_RELEASE.md`](../leg3ndy-ai-backend/docs/OTTO_BRIDGE_1_0_0_RELEASE.md).
18
+ Para a release atual `1.0.2`, com runtime agentico formal, hub terminal refinado e console alinhado ao Otto da web, veja [`leg3ndy-ai-backend/docs/OTTO_BRIDGE_1_0_2_PATCH.md`](../leg3ndy-ai-backend/docs/OTTO_BRIDGE_1_0_2_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.0.tgz
41
+ npm install -g ./leg3ndy-otto-bridge-1.0.2.tgz
42
42
  ```
43
43
 
44
- Na linha `1.0.0`, `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.2`, `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.0` 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.2` 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` oficializa isso como runtime agentico e adiciona uma interface terminal propria do bridge para setup e interacao local com o Otto.
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.2` consolida o hub terminal como fluxo principal, esconde aliases legados da superfície pública e alinha o console ao comportamento normal do Otto antes de fazer handoff local.
49
49
 
50
50
  ## Publicacao
51
51
 
@@ -76,14 +76,13 @@ otto-bridge help
76
76
  otto-bridge --help
77
77
  ```
78
78
 
79
- ### Abrir o hub interativo
79
+ ### Abrir o hub interativo e ligar o runtime
80
80
 
81
81
  ```bash
82
82
  otto-bridge
83
- otto-bridge home
84
83
  ```
85
84
 
86
- Em TTY, o comando sem argumentos agora abre o hub interativo com banner, setup, status, extensoes e o `Otto Console`.
85
+ Em TTY, o comando sem argumentos agora abre o hub interativo com banner, setup, status, extensoes e o `Otto Console`. Se ja existir pairing salvo, o próprio `otto-bridge` sobe o runtime local automaticamente e mostra o estado da conexão no hub.
87
86
 
88
87
  ### Setup interativo
89
88
 
@@ -110,24 +109,24 @@ Opcoes suportadas:
110
109
 
111
110
  No macOS, o caminho recomendado agora e o executor nativo do Otto Bridge. Se nenhum `--executor` for informado, o `pair` usa `native-macos` por padrao no Mac.
112
111
 
113
- ### Rodar o bridge
112
+ ### Runtime principal do bridge
114
113
 
115
114
  ```bash
116
- otto-bridge run
115
+ otto-bridge
117
116
  ```
118
117
 
119
- Se o executor estiver salvo no `config.json`, o `run` usa essa configuracao por padrao.
118
+ Esse agora e o fluxo principal do produto: o hub abre, conecta o runtime local e deixa o Otto pronto para handoff e approvals sem depender de nenhum comando separado de runtime.
120
119
 
121
- Para forcar o executor nativo no macOS sem reparar:
120
+ Se precisar do executor nativo no macOS sem reparar, atualize o pairing/config e reabra o hub:
122
121
 
123
122
  ```bash
124
- otto-bridge run --executor native-macos
123
+ otto-bridge setup
125
124
  ```
126
125
 
127
- O adapter `clawd-cursor` continua disponivel como override opcional:
126
+ O adapter `clawd-cursor` continua disponivel como override opcional no pairing legado:
128
127
 
129
128
  ```bash
130
- otto-bridge run --executor clawd-cursor --clawd-url http://127.0.0.1:3847
129
+ otto-bridge pair --executor clawd-cursor --clawd-url http://127.0.0.1:3847
131
130
  ```
132
131
 
133
132
  ### Falar com o Otto no terminal
@@ -136,11 +135,11 @@ otto-bridge run --executor clawd-cursor --clawd-url http://127.0.0.1:3847
136
135
  otto-bridge console
137
136
  ```
138
137
 
139
- O console sobe o runtime local em background no mesmo processo, envia prompts naturais ao backend usando `device_token`, acompanha `device_job` por polling e resolve `confirm_required` diretamente no terminal.
138
+ O console usa a mesma sessão local ligada pelo `otto-bridge`, envia prompts naturais ao backend usando `device_token`, respeita quota/plano do usuário e só vira handoff local quando o pedido realmente tiver cara de ação no computador. Quando houver `device_job`, ele acompanha polling e resolve `confirm_required` no terminal.
140
139
 
141
140
  ### WhatsApp Web em background
142
141
 
143
- Fluxo recomendado na linha `1.0.0`:
142
+ Fluxo recomendado na linha `1.0.2`:
144
143
 
145
144
  ```bash
146
145
  otto-bridge extensions --install whatsappweb
@@ -150,13 +149,13 @@ otto-bridge extensions --status whatsappweb
150
149
 
151
150
  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.
152
151
 
153
- Contrato da linha `1.0.0`:
152
+ Contrato da linha `1.0.2`:
154
153
 
155
154
  - `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
156
- - `otto-bridge run`: mantem o browser persistente do WhatsApp vivo em background enquanto o runtime estiver ativo, sem depender de uma aba aberta no Safari
157
- - ao parar o `otto-bridge run`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
155
+ - `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
156
+ - ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
158
157
 
159
- ## Handoff rapido da linha 1.0.0
158
+ ## Handoff rapido da linha 1.0.2
160
159
 
161
160
  Ja fechado no codigo:
162
161
 
@@ -190,8 +189,8 @@ Ja fechado no codigo:
190
189
  - o `instruction_updater` agora e um descriptor explicito do runtime, com `source_entrypoints`, `target_paths` e `recommended_validation_stage_ids` quando o plano toca `AGENTS.md`, `README.md` ou `docs/`
191
190
  - `workspace.tests` agora consegue resolver `profile=auto` via `validation_ladder`, executando stages como `typecheck`, `build`, `node_test`, `lint` e `pytest` por stack, com snapshot agregado por etapa
192
191
  - o executor `native-macos` agora publica `runtime_hook_trace` somando hooks de lifecycle e tool-use (`session_start`, `pre_tool_use`, `post_tool_use`, `validation_ladder_started/completed`, `session_end`), preparando enforcement, replay fino e metricas futuras
193
- - o bridge agora possui um CLI interativo proprio com setup inicial, hub terminal e `Otto Console` para prompts locais pelo proprio terminal
194
- - o backend agora expoe um caminho `device-auth` para o console do bridge submeter prompt e acompanhar approval/job usando apenas `device_token`
192
+ - o bridge agora possui um CLI interativo proprio com setup inicial, hub terminal e `Otto Console` para conversar com o Otto pelo proprio terminal
193
+ - o backend agora expoe caminhos `device-auth` para o console do bridge usar chat com quota/plano (`/v1/devices/cli/chat/completions`) e acompanhar approval/job usando apenas `device_token`
195
194
  - o caminho principal do bridge usa `summary`/`narration_context` no lugar de resposta automatica pronta
196
195
  - `read_file` agora entrega conteudo completo segmentado em `content_chunks` para o Otto
197
196
  - `list_files` sem `limit` agora lista o diretorio inteiro, sem fallback silencioso para 40 itens
@@ -0,0 +1,91 @@
1
+ function normalizeBaseUrl(apiBaseUrl) {
2
+ return String(apiBaseUrl || "").trim().replace(/\/+$/, "");
3
+ }
4
+ function buildDeviceAuthHeaders(deviceToken, headers) {
5
+ const next = new Headers(headers || {});
6
+ if (deviceToken) {
7
+ next.set("Authorization", `Bearer ${deviceToken}`);
8
+ }
9
+ return next;
10
+ }
11
+ function parseApiError(payload, fallbackStatus) {
12
+ if (payload && typeof payload === "object") {
13
+ const detail = "detail" in payload ? payload.detail : null;
14
+ if (typeof detail === "string" && detail.trim()) {
15
+ return new Error(detail.trim());
16
+ }
17
+ if (detail && typeof detail === "object") {
18
+ const message = "message" in detail ? detail.message : null;
19
+ if (typeof message === "string" && message.trim()) {
20
+ return new Error(message.trim());
21
+ }
22
+ }
23
+ const error = "error" in payload ? payload.error : null;
24
+ if (typeof error === "string" && error.trim()) {
25
+ return new Error(error.trim());
26
+ }
27
+ }
28
+ return new Error(`HTTP ${fallbackStatus}`);
29
+ }
30
+ export async function streamDeviceCliChat(config, request, onEvent) {
31
+ const url = `${normalizeBaseUrl(config.apiBaseUrl)}/v1/devices/cli/chat/completions`;
32
+ let response;
33
+ try {
34
+ response = await fetch(url, {
35
+ method: "POST",
36
+ headers: buildDeviceAuthHeaders(config.deviceToken, {
37
+ "Content-Type": "application/json",
38
+ }),
39
+ body: JSON.stringify(request),
40
+ });
41
+ }
42
+ catch (error) {
43
+ const detail = error instanceof Error ? error.message : String(error);
44
+ throw new Error(`Request failed for ${url}: ${detail}`);
45
+ }
46
+ if (!response.ok) {
47
+ const payload = await response.json().catch(() => null);
48
+ throw parseApiError(payload, response.status);
49
+ }
50
+ if (!response.body) {
51
+ throw new Error("Empty stream response");
52
+ }
53
+ const reader = response.body.getReader();
54
+ const decoder = new TextDecoder();
55
+ let buffer = "";
56
+ const flushBlock = async (rawBlock) => {
57
+ const lines = rawBlock
58
+ .split("\n")
59
+ .map((line) => line.trimEnd())
60
+ .filter((line) => line.startsWith("data:"));
61
+ if (!lines.length) {
62
+ return;
63
+ }
64
+ const payloadText = lines
65
+ .map((line) => line.slice(5).trimStart())
66
+ .join("\n")
67
+ .trim();
68
+ if (!payloadText) {
69
+ return;
70
+ }
71
+ const payload = JSON.parse(payloadText);
72
+ await onEvent(payload);
73
+ };
74
+ for (;;) {
75
+ const { done, value } = await reader.read();
76
+ buffer += decoder.decode(value || new Uint8Array(), { stream: !done });
77
+ let separatorIndex = buffer.indexOf("\n\n");
78
+ while (separatorIndex >= 0) {
79
+ const block = buffer.slice(0, separatorIndex);
80
+ buffer = buffer.slice(separatorIndex + 2);
81
+ await flushBlock(block);
82
+ separatorIndex = buffer.indexOf("\n\n");
83
+ }
84
+ if (done) {
85
+ break;
86
+ }
87
+ }
88
+ if (buffer.trim()) {
89
+ await flushBlock(buffer);
90
+ }
91
+ }