@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 +22 -23
- package/dist/chat_cli_client.js +91 -0
- package/dist/cli_terminal.js +378 -95
- package/dist/executors/native_macos.js +1 -1
- package/dist/local_automations.js +15 -10
- package/dist/main.js +15 -55
- package/dist/runtime.js +80 -22
- 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_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.
|
|
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.
|
|
41
|
+
npm install -g ./leg3ndy-otto-bridge-1.0.2.tgz
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Na linha `1.0.
|
|
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.
|
|
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`
|
|
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
|
-
###
|
|
112
|
+
### Runtime principal do bridge
|
|
114
113
|
|
|
115
114
|
```bash
|
|
116
|
-
otto-bridge
|
|
115
|
+
otto-bridge
|
|
117
116
|
```
|
|
118
117
|
|
|
119
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
138
|
+
O console usa a mesma sessão local já 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.
|
|
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.
|
|
152
|
+
Contrato da linha `1.0.2`:
|
|
154
153
|
|
|
155
154
|
- `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
|
|
156
|
-
- `otto-bridge
|
|
157
|
-
- ao
|
|
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.
|
|
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
|
|
194
|
-
- o backend agora expoe
|
|
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
|
+
}
|