@leg3ndy/otto-bridge 1.0.3 → 1.0.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 +20 -10
- package/dist/cli_terminal.js +239 -25
- package/dist/main.js +22 -11
- 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.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).
|
|
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.5.tgz
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Na linha `1.0.
|
|
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.
|
|
45
45
|
|
|
46
|
-
No macOS, a linha `1.0.
|
|
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`.
|
|
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.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.
|
|
49
49
|
|
|
50
50
|
## Publicacao
|
|
51
51
|
|
|
@@ -143,11 +143,21 @@ Dentro do console, use:
|
|
|
143
143
|
- `/model thinking` para `OttoAI Thinking`
|
|
144
144
|
- `/status` para ver detalhes técnicos do bridge e do runtime
|
|
145
145
|
|
|
146
|
+
No modo `OttoAI Thinking`, o terminal agora marca explicitamente o trecho de raciocínio com `Pensando (OttoAI Thinking)` e separa esse bloco da resposta final do Otto.
|
|
147
|
+
|
|
146
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.
|
|
147
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
|
+
|
|
148
158
|
### WhatsApp Web em background
|
|
149
159
|
|
|
150
|
-
Fluxo recomendado na linha `1.0.
|
|
160
|
+
Fluxo recomendado na linha `1.0.5`:
|
|
151
161
|
|
|
152
162
|
```bash
|
|
153
163
|
otto-bridge extensions --install whatsappweb
|
|
@@ -157,13 +167,13 @@ otto-bridge extensions --status whatsappweb
|
|
|
157
167
|
|
|
158
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.
|
|
159
169
|
|
|
160
|
-
Contrato da linha `1.0.
|
|
170
|
+
Contrato da linha `1.0.5`:
|
|
161
171
|
|
|
162
172
|
- `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
|
|
163
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
|
|
164
174
|
- ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
|
|
165
175
|
|
|
166
|
-
## Handoff rapido da linha 1.0.
|
|
176
|
+
## Handoff rapido da linha 1.0.5
|
|
167
177
|
|
|
168
178
|
Ja fechado no codigo:
|
|
169
179
|
|
package/dist/cli_terminal.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { createInterface } from "node:readline/promises";
|
|
4
|
+
import { cursorTo, moveCursor, } from "node:readline";
|
|
3
5
|
import process, { stdin as input, stdout as output } from "node:process";
|
|
4
6
|
import { defaultDeviceName, getBridgeConfigPath, loadBridgeConfig, resolveApiBaseUrl, resolveExecutorConfig, } from "./config.js";
|
|
5
7
|
import { streamDeviceCliChat, } from "./chat_cli_client.js";
|
|
@@ -48,6 +50,8 @@ const MAX_RENDERED_LIST_ENTRIES = 28;
|
|
|
48
50
|
const MAX_RENDERED_LIST_ENTRIES_COMPACT = 10;
|
|
49
51
|
const MAX_RENDERED_FILE_CHARS = 6_000;
|
|
50
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";
|
|
51
55
|
class CliRuntimeSession {
|
|
52
56
|
config;
|
|
53
57
|
runtime = null;
|
|
@@ -119,6 +123,18 @@ class CliRuntimeSession {
|
|
|
119
123
|
});
|
|
120
124
|
await delay(350);
|
|
121
125
|
}
|
|
126
|
+
async waitForReady(timeoutMs = 4500) {
|
|
127
|
+
const startedAt = Date.now();
|
|
128
|
+
for (;;) {
|
|
129
|
+
if (this.status !== "starting") {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await delay(120);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
122
138
|
async replaceConfig(nextConfig) {
|
|
123
139
|
await this.stop();
|
|
124
140
|
this.config = nextConfig;
|
|
@@ -174,6 +190,13 @@ function style(text, color, enabled = true) {
|
|
|
174
190
|
function supportsAnsi() {
|
|
175
191
|
return Boolean(output.isTTY);
|
|
176
192
|
}
|
|
193
|
+
function clearScreen() {
|
|
194
|
+
if (supportsAnsi()) {
|
|
195
|
+
output.write("\u001b[2J\u001b[3J\u001b[H");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
console.clear();
|
|
199
|
+
}
|
|
177
200
|
function renderBanner() {
|
|
178
201
|
const enabled = supportsAnsi();
|
|
179
202
|
const lines = OTTOAI_BANNER.map((line) => style(line, ANSI.brandBlue, enabled));
|
|
@@ -455,30 +478,54 @@ function buildWelcomeCard(runtimeSession, modelMode) {
|
|
|
455
478
|
];
|
|
456
479
|
}
|
|
457
480
|
function printHubScreen(runtimeSession, modelMode) {
|
|
458
|
-
|
|
481
|
+
clearScreen();
|
|
459
482
|
console.log(renderBanner());
|
|
460
483
|
console.log("");
|
|
461
484
|
console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
|
|
462
485
|
}
|
|
463
486
|
function printConsoleScreen(runtimeSession, modelMode) {
|
|
464
|
-
|
|
487
|
+
clearScreen();
|
|
465
488
|
console.log(renderBanner());
|
|
466
489
|
console.log("");
|
|
467
490
|
console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
|
|
468
491
|
console.log("");
|
|
469
|
-
console.log(style("Peça algo ao Otto", `${ANSI.bold}${ANSI.white}`, supportsAnsi()));
|
|
470
|
-
printSoft("Comandos: /help, /model [fast|thinking], /status, /clear, /exit");
|
|
471
|
-
console.log("");
|
|
472
492
|
}
|
|
473
493
|
function renderPromptFrameLine(width, edgeLeft, edgeRight) {
|
|
474
494
|
return style(`${edgeLeft}${"─".repeat(width)}${edgeRight}`, ANSI.brandBlue, supportsAnsi());
|
|
475
495
|
}
|
|
496
|
+
function renderConsolePromptContentLine(text, width, tone) {
|
|
497
|
+
const enabled = supportsAnsi();
|
|
498
|
+
const border = style("│", ANSI.brandBlue, enabled);
|
|
499
|
+
if (tone === "input") {
|
|
500
|
+
const prompt = `${style(">", `${ANSI.bold}${ANSI.white}`, enabled)} ${" ".repeat(Math.max(0, width - 2))}`;
|
|
501
|
+
return `${border} ${prompt} ${border}`;
|
|
502
|
+
}
|
|
503
|
+
const clipped = padRight(truncate(text, width), width);
|
|
504
|
+
const body = tone === "placeholder"
|
|
505
|
+
? style(clipped, `${ANSI.bold}${ANSI.white}`, enabled)
|
|
506
|
+
: style(clipped, ANSI.slateItalic, enabled);
|
|
507
|
+
return `${border} ${body} ${border}`;
|
|
508
|
+
}
|
|
476
509
|
async function askConsoleInput(rl) {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
510
|
+
if (!supportsAnsi()) {
|
|
511
|
+
printMuted(CONSOLE_PLACEHOLDER);
|
|
512
|
+
printSoft(`Comandos: ${CONSOLE_COMMAND_HINT}`);
|
|
513
|
+
return normalizeText(await question(rl, "> "));
|
|
514
|
+
}
|
|
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);
|
|
482
529
|
return answer;
|
|
483
530
|
}
|
|
484
531
|
function printAssistantMessage(message) {
|
|
@@ -492,9 +539,42 @@ function extractJobOutcome(job) {
|
|
|
492
539
|
const result = job.result && typeof job.result === "object"
|
|
493
540
|
? job.result
|
|
494
541
|
: {};
|
|
495
|
-
|
|
542
|
+
const outcome = result.outcome && typeof result.outcome === "object"
|
|
496
543
|
? result.outcome
|
|
497
544
|
: {};
|
|
545
|
+
if (Object.keys(outcome).length > 0) {
|
|
546
|
+
return outcome;
|
|
547
|
+
}
|
|
548
|
+
const payload = job.payload && typeof job.payload === "object"
|
|
549
|
+
? job.payload
|
|
550
|
+
: {};
|
|
551
|
+
const actions = Array.isArray(payload.actions) ? payload.actions : [];
|
|
552
|
+
const firstAction = actions.find((item) => item && typeof item === "object");
|
|
553
|
+
const actionType = normalizeText(firstAction?.type).toLowerCase();
|
|
554
|
+
if (actionType === "list_files" && result.file_listing && typeof result.file_listing === "object") {
|
|
555
|
+
const listing = result.file_listing;
|
|
556
|
+
return {
|
|
557
|
+
action_type: "list_files",
|
|
558
|
+
path: normalizeText(firstAction?.path),
|
|
559
|
+
resolved_path: normalizeText(listing.resolved_path),
|
|
560
|
+
listed_item_count: listing.item_count,
|
|
561
|
+
total_item_count: listing.total_item_count,
|
|
562
|
+
limit_applied: listing.limit_applied,
|
|
563
|
+
entries: Array.isArray(listing.entries) ? listing.entries : [],
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
if (actionType === "read_file" && result.read_file && typeof result.read_file === "object") {
|
|
567
|
+
const readFile = result.read_file;
|
|
568
|
+
return {
|
|
569
|
+
action_type: "read_file",
|
|
570
|
+
path: normalizeText(firstAction?.path),
|
|
571
|
+
resolved_path: normalizeText(readFile.resolved_path),
|
|
572
|
+
content: typeof readFile.content === "string" ? readFile.content : undefined,
|
|
573
|
+
content_chunks: Array.isArray(readFile.chunks) ? readFile.chunks : [],
|
|
574
|
+
binary_notice: normalizeText(readFile.binary_notice),
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
return {};
|
|
498
578
|
}
|
|
499
579
|
function outcomePathLabel(outcome) {
|
|
500
580
|
return normalizeText(outcome.resolved_path || outcome.path);
|
|
@@ -572,8 +652,46 @@ function printStructuredOutcome(job) {
|
|
|
572
652
|
if (!rendered) {
|
|
573
653
|
return;
|
|
574
654
|
}
|
|
655
|
+
console.log("");
|
|
575
656
|
console.log(rendered);
|
|
576
657
|
}
|
|
658
|
+
function resolveTerminalShellCommand() {
|
|
659
|
+
if (process.platform === "win32") {
|
|
660
|
+
return {
|
|
661
|
+
command: process.env.COMSPEC || "cmd.exe",
|
|
662
|
+
args: [],
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
command: process.env.SHELL || "/bin/zsh",
|
|
667
|
+
args: ["-l"],
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
export async function runTerminalShell(options) {
|
|
671
|
+
clearScreen();
|
|
672
|
+
console.log(renderBanner());
|
|
673
|
+
console.log("");
|
|
674
|
+
printSection("Terminal");
|
|
675
|
+
printMuted(options?.fromHub
|
|
676
|
+
? "Saindo temporariamente para o shell local. Digite `exit` para voltar ao Otto Bridge."
|
|
677
|
+
: "Abrindo o shell local. Digite `exit` para encerrar e voltar ao terminal anterior.");
|
|
678
|
+
console.log("");
|
|
679
|
+
const shell = resolveTerminalShellCommand();
|
|
680
|
+
await new Promise((resolve, reject) => {
|
|
681
|
+
const child = spawn(shell.command, shell.args, {
|
|
682
|
+
stdio: "inherit",
|
|
683
|
+
env: process.env,
|
|
684
|
+
});
|
|
685
|
+
child.on("error", reject);
|
|
686
|
+
child.on("exit", (code, signal) => {
|
|
687
|
+
if (signal || code === 0 || code === 130) {
|
|
688
|
+
resolve();
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
reject(new Error(`Terminal local encerrou com code ${code ?? "unknown"}.`));
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
}
|
|
577
695
|
function buildConversationSummary(summary, job) {
|
|
578
696
|
const rendered = renderStructuredOutcome(job, { compact: true });
|
|
579
697
|
if (!rendered) {
|
|
@@ -693,7 +811,9 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
693
811
|
const sessionId = randomUUID();
|
|
694
812
|
const conversation = [];
|
|
695
813
|
const printConsoleHelp = () => {
|
|
814
|
+
printSection("Console");
|
|
696
815
|
printSoft("Comandos: /help, /model [fast|thinking], /status, /clear, /exit");
|
|
816
|
+
printSoft("Bridge: otto-bridge terminal, otto-bridge extensions --install <name>, otto-bridge update");
|
|
697
817
|
};
|
|
698
818
|
const handlePrompt = async (promptText) => {
|
|
699
819
|
const normalizedPrompt = normalizeText(promptText);
|
|
@@ -748,6 +868,8 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
748
868
|
}
|
|
749
869
|
let streamedAssistant = "";
|
|
750
870
|
let assistantPrefixPrinted = false;
|
|
871
|
+
let reasoningPrefixPrinted = false;
|
|
872
|
+
let contentSeparatedFromReasoning = false;
|
|
751
873
|
let handoffPayload = null;
|
|
752
874
|
await streamDeviceCliChat(config, {
|
|
753
875
|
messages: conversation,
|
|
@@ -771,10 +893,25 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
771
893
|
if (errorMessage) {
|
|
772
894
|
throw new Error(errorMessage);
|
|
773
895
|
}
|
|
896
|
+
const reasoningChunk = typeof event.content === "string" && eventType === "reasoning"
|
|
897
|
+
? event.content
|
|
898
|
+
: "";
|
|
899
|
+
if (reasoningChunk) {
|
|
900
|
+
if (!reasoningPrefixPrinted) {
|
|
901
|
+
output.write(`${style("Pensando (OttoAI Thinking)\n", ANSI.brandBlue, supportsAnsi())}`);
|
|
902
|
+
reasoningPrefixPrinted = true;
|
|
903
|
+
}
|
|
904
|
+
output.write(style(reasoningChunk, ANSI.slateItalic, supportsAnsi()));
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
774
907
|
const contentChunk = typeof event.content === "string" ? event.content : "";
|
|
775
908
|
if (!contentChunk) {
|
|
776
909
|
return;
|
|
777
910
|
}
|
|
911
|
+
if (reasoningPrefixPrinted && !contentSeparatedFromReasoning) {
|
|
912
|
+
output.write("\n\n");
|
|
913
|
+
contentSeparatedFromReasoning = true;
|
|
914
|
+
}
|
|
778
915
|
if (!assistantPrefixPrinted) {
|
|
779
916
|
output.write(`${style("•", ANSI.brandBlue, supportsAnsi())} `);
|
|
780
917
|
assistantPrefixPrinted = true;
|
|
@@ -789,13 +926,13 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
789
926
|
if (handoffPayload) {
|
|
790
927
|
const handoffData = handoffPayload;
|
|
791
928
|
const bridgeSummary = extractBridgeHandoffSummary(handoffData);
|
|
792
|
-
if (bridgeSummary) {
|
|
793
|
-
printAssistantMessage(bridgeSummary);
|
|
794
|
-
}
|
|
795
929
|
const job = handoffData.job && typeof handoffData.job === "object"
|
|
796
930
|
? handoffData.job
|
|
797
931
|
: null;
|
|
798
932
|
const jobId = normalizeText(job?.id);
|
|
933
|
+
if (bridgeSummary && !jobId) {
|
|
934
|
+
printAssistantMessage(bridgeSummary);
|
|
935
|
+
}
|
|
799
936
|
if (jobId) {
|
|
800
937
|
finalAssistantSummary = await followConsoleJob(rl, config, jobId);
|
|
801
938
|
}
|
|
@@ -810,7 +947,6 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
810
947
|
}
|
|
811
948
|
}
|
|
812
949
|
};
|
|
813
|
-
printConsoleHelp();
|
|
814
950
|
if (options?.initialPrompt) {
|
|
815
951
|
await handlePrompt(options.initialPrompt);
|
|
816
952
|
}
|
|
@@ -830,7 +966,36 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
830
966
|
async function printStatusView(rl, config, runtimeSession) {
|
|
831
967
|
printSection("Bridge Status");
|
|
832
968
|
renderStatusOverview(config, runtimeSession).forEach((line) => console.log(line));
|
|
833
|
-
await
|
|
969
|
+
await pauseForEnter(rl);
|
|
970
|
+
}
|
|
971
|
+
async function printHelpView(rl) {
|
|
972
|
+
printSection("Ajuda");
|
|
973
|
+
console.log(renderInfoCard([
|
|
974
|
+
{ text: "Otto Bridge CLI", tone: "title" },
|
|
975
|
+
{ text: "", tone: "muted" },
|
|
976
|
+
{ text: "otto-bridge", tone: "primary" },
|
|
977
|
+
{ text: "Abre o hub principal e sobe o runtime local automaticamente.", tone: "muted" },
|
|
978
|
+
{ text: "", tone: "muted" },
|
|
979
|
+
{ text: "otto-bridge terminal", tone: "primary" },
|
|
980
|
+
{ text: "Abre um shell local interativo sem desligar o bridge.", tone: "muted" },
|
|
981
|
+
{ text: "", tone: "muted" },
|
|
982
|
+
{ text: "otto-bridge setup", tone: "primary" },
|
|
983
|
+
{ text: "Repareia o dispositivo e atualiza config/executor.", tone: "muted" },
|
|
984
|
+
{ text: "", tone: "muted" },
|
|
985
|
+
{ text: "otto-bridge console", tone: "primary" },
|
|
986
|
+
{ text: "Abre direto o console do Otto no terminal.", tone: "muted" },
|
|
987
|
+
{ text: "", tone: "muted" },
|
|
988
|
+
{ text: "otto-bridge extensions --install <name>", tone: "primary" },
|
|
989
|
+
{ text: "Instala uma extensao local do bridge.", tone: "muted" },
|
|
990
|
+
{ text: "", tone: "muted" },
|
|
991
|
+
{ text: "otto-bridge update --dry-run | otto-bridge update", tone: "primary" },
|
|
992
|
+
{ text: "Mostra ou aplica a atualizacao global do pacote.", tone: "muted" },
|
|
993
|
+
{ text: "", tone: "muted" },
|
|
994
|
+
{ text: "otto-bridge version | otto-bridge unpair", tone: "primary" },
|
|
995
|
+
{ text: "Mostra a versao instalada ou remove o pairing local.", tone: "muted" },
|
|
996
|
+
{ text: "", tone: "muted" },
|
|
997
|
+
{ text: "Dentro do console: /help, /model fast|thinking, /status, /clear, /exit", tone: "muted" },
|
|
998
|
+
]));
|
|
834
999
|
await pauseForEnter(rl);
|
|
835
1000
|
}
|
|
836
1001
|
async function pickHomeChoice(rl, paired) {
|
|
@@ -838,32 +1003,46 @@ async function pickHomeChoice(rl, paired) {
|
|
|
838
1003
|
const options = paired
|
|
839
1004
|
? [
|
|
840
1005
|
`${style("1.", ANSI.brandBlue, supportsAnsi())} Otto Console`,
|
|
841
|
-
`${style("2.", ANSI.brandBlue, supportsAnsi())}
|
|
842
|
-
`${style("3.", ANSI.brandBlue, supportsAnsi())}
|
|
843
|
-
`${style("4.", ANSI.brandBlue, supportsAnsi())}
|
|
844
|
-
`${style("5.", ANSI.brandBlue, supportsAnsi())}
|
|
1006
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} Terminal`,
|
|
1007
|
+
`${style("3.", ANSI.brandBlue, supportsAnsi())} Setup / parear novamente`,
|
|
1008
|
+
`${style("4.", ANSI.brandBlue, supportsAnsi())} Status detalhado`,
|
|
1009
|
+
`${style("5.", ANSI.brandBlue, supportsAnsi())} Extensões instaladas`,
|
|
1010
|
+
`${style("6.", ANSI.brandBlue, supportsAnsi())} Ajuda`,
|
|
1011
|
+
`${style("7.", ANSI.brandBlue, supportsAnsi())} Sair`,
|
|
845
1012
|
]
|
|
846
1013
|
: [
|
|
847
1014
|
`${style("1.", ANSI.brandBlue, supportsAnsi())} Pairing setup`,
|
|
848
|
-
`${style("2.", ANSI.brandBlue, supportsAnsi())}
|
|
1015
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} Terminal`,
|
|
1016
|
+
`${style("3.", ANSI.brandBlue, supportsAnsi())} Ajuda`,
|
|
1017
|
+
`${style("4.", ANSI.brandBlue, supportsAnsi())} Sair`,
|
|
849
1018
|
];
|
|
850
1019
|
console.log(options.join("\n"));
|
|
851
1020
|
const answer = await ask(rl, "Escolha");
|
|
852
1021
|
if (!paired) {
|
|
853
|
-
|
|
1022
|
+
if (answer === "1")
|
|
1023
|
+
return "setup";
|
|
1024
|
+
if (answer === "2")
|
|
1025
|
+
return "terminal";
|
|
1026
|
+
if (answer === "3")
|
|
1027
|
+
return "help";
|
|
1028
|
+
return "exit";
|
|
854
1029
|
}
|
|
855
1030
|
if (answer === "1")
|
|
856
1031
|
return "console";
|
|
857
1032
|
if (answer === "2")
|
|
858
|
-
return "
|
|
1033
|
+
return "terminal";
|
|
859
1034
|
if (answer === "3")
|
|
860
|
-
return "
|
|
1035
|
+
return "setup";
|
|
861
1036
|
if (answer === "4")
|
|
1037
|
+
return "status";
|
|
1038
|
+
if (answer === "5")
|
|
862
1039
|
return "extensions";
|
|
1040
|
+
if (answer === "6")
|
|
1041
|
+
return "help";
|
|
863
1042
|
return "exit";
|
|
864
1043
|
}
|
|
865
1044
|
export async function launchInteractiveCli(options) {
|
|
866
|
-
|
|
1045
|
+
let rl = await createPromptInterface();
|
|
867
1046
|
let runtimeSession = null;
|
|
868
1047
|
try {
|
|
869
1048
|
let config = await loadBridgeConfig();
|
|
@@ -874,6 +1053,7 @@ export async function launchInteractiveCli(options) {
|
|
|
874
1053
|
if (config && setup.openConsole) {
|
|
875
1054
|
runtimeSession = new CliRuntimeSession(config);
|
|
876
1055
|
await runtimeSession.ensureStarted();
|
|
1056
|
+
await runtimeSession.waitForReady();
|
|
877
1057
|
await runOttoConsole(rl, config, runtimeSession);
|
|
878
1058
|
}
|
|
879
1059
|
if (!config) {
|
|
@@ -882,22 +1062,54 @@ export async function launchInteractiveCli(options) {
|
|
|
882
1062
|
}
|
|
883
1063
|
runtimeSession = runtimeSession || new CliRuntimeSession(config);
|
|
884
1064
|
await runtimeSession.ensureStarted();
|
|
1065
|
+
await runtimeSession.waitForReady();
|
|
885
1066
|
for (;;) {
|
|
886
1067
|
printHubScreen(runtimeSession, "fast");
|
|
887
1068
|
const choice = await pickHomeChoice(rl, true);
|
|
888
1069
|
if (choice === "exit") {
|
|
889
1070
|
break;
|
|
890
1071
|
}
|
|
1072
|
+
if (choice === "help") {
|
|
1073
|
+
await printHelpView(rl);
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
if (choice === "terminal") {
|
|
1077
|
+
rl.close();
|
|
1078
|
+
await runTerminalShell({ fromHub: true });
|
|
1079
|
+
rl = await createPromptInterface();
|
|
1080
|
+
const refreshedConfig = await loadBridgeConfig();
|
|
1081
|
+
if (!refreshedConfig) {
|
|
1082
|
+
await runtimeSession?.stop().catch(() => undefined);
|
|
1083
|
+
runtimeSession = null;
|
|
1084
|
+
printWarning("Pairing local removido no terminal. Encerrando o hub.");
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
if (JSON.stringify(refreshedConfig) !== JSON.stringify(config)) {
|
|
1088
|
+
config = refreshedConfig;
|
|
1089
|
+
if (runtimeSession) {
|
|
1090
|
+
await runtimeSession.replaceConfig(refreshedConfig);
|
|
1091
|
+
await runtimeSession.waitForReady();
|
|
1092
|
+
}
|
|
1093
|
+
else {
|
|
1094
|
+
runtimeSession = new CliRuntimeSession(refreshedConfig);
|
|
1095
|
+
await runtimeSession.ensureStarted();
|
|
1096
|
+
await runtimeSession.waitForReady();
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
891
1101
|
if (choice === "setup") {
|
|
892
1102
|
const setup = await runSetupWizard(rl);
|
|
893
1103
|
if (setup.config) {
|
|
894
1104
|
config = setup.config;
|
|
895
1105
|
if (runtimeSession) {
|
|
896
1106
|
await runtimeSession.replaceConfig(setup.config);
|
|
1107
|
+
await runtimeSession.waitForReady();
|
|
897
1108
|
}
|
|
898
1109
|
else {
|
|
899
1110
|
runtimeSession = new CliRuntimeSession(setup.config);
|
|
900
1111
|
await runtimeSession.ensureStarted();
|
|
1112
|
+
await runtimeSession.waitForReady();
|
|
901
1113
|
}
|
|
902
1114
|
}
|
|
903
1115
|
if (setup.config && setup.openConsole && runtimeSession) {
|
|
@@ -938,6 +1150,7 @@ export async function runSetupCommand(options) {
|
|
|
938
1150
|
if (setup.config && setup.openConsole) {
|
|
939
1151
|
runtimeSession = new CliRuntimeSession(setup.config);
|
|
940
1152
|
await runtimeSession.ensureStarted();
|
|
1153
|
+
await runtimeSession.waitForReady();
|
|
941
1154
|
await runOttoConsole(rl, setup.config, runtimeSession);
|
|
942
1155
|
}
|
|
943
1156
|
}
|
|
@@ -966,6 +1179,7 @@ export async function runConsoleCommand(initialPrompt) {
|
|
|
966
1179
|
}
|
|
967
1180
|
runtimeSession = new CliRuntimeSession(config);
|
|
968
1181
|
await runtimeSession.ensureStarted();
|
|
1182
|
+
await runtimeSession.waitForReady();
|
|
969
1183
|
await runOttoConsole(rl, config, runtimeSession, { initialPrompt });
|
|
970
1184
|
}
|
|
971
1185
|
catch (error) {
|
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) {
|
|
@@ -104,29 +104,37 @@ function resolveExecutorOverrides(args, current) {
|
|
|
104
104
|
}
|
|
105
105
|
function printUsage() {
|
|
106
106
|
console.log(`Usage:
|
|
107
|
-
otto-bridge
|
|
108
|
-
otto-bridge setup
|
|
109
|
-
otto-bridge console
|
|
110
|
-
otto-bridge
|
|
107
|
+
otto-bridge abre o hub principal e sobe o runtime local
|
|
108
|
+
otto-bridge setup refaz o pairing no terminal
|
|
109
|
+
otto-bridge console abre direto o console do Otto
|
|
110
|
+
otto-bridge terminal abre um shell local e volta ao hub ao sair
|
|
111
|
+
otto-bridge status mostra o pairing atual em JSON
|
|
111
112
|
otto-bridge extensions --list
|
|
112
113
|
otto-bridge extensions --install <name>
|
|
113
114
|
otto-bridge extensions --setup <name>
|
|
114
115
|
otto-bridge extensions --status <name>
|
|
115
116
|
otto-bridge extensions --uninstall <name>
|
|
116
|
-
otto-bridge version
|
|
117
117
|
otto-bridge update [--tag latest|next] [--dry-run]
|
|
118
118
|
otto-bridge unpair
|
|
119
|
+
otto-bridge version
|
|
120
|
+
|
|
121
|
+
Console:
|
|
122
|
+
/help
|
|
123
|
+
/model fast
|
|
124
|
+
/model thinking
|
|
125
|
+
/status
|
|
126
|
+
/clear
|
|
127
|
+
/exit
|
|
119
128
|
|
|
120
129
|
Examples:
|
|
121
130
|
otto-bridge
|
|
122
|
-
otto-bridge
|
|
123
|
-
otto-bridge
|
|
131
|
+
otto-bridge terminal
|
|
132
|
+
otto-bridge status
|
|
124
133
|
otto-bridge extensions --install <name>
|
|
125
134
|
otto-bridge extensions --setup <name>
|
|
126
|
-
otto-bridge
|
|
127
|
-
otto-bridge extensions --list
|
|
128
|
-
otto-bridge version
|
|
135
|
+
otto-bridge update
|
|
129
136
|
otto-bridge update --dry-run
|
|
137
|
+
otto-bridge unpair
|
|
130
138
|
otto-bridge --version`);
|
|
131
139
|
}
|
|
132
140
|
function printVersion() {
|
|
@@ -557,6 +565,9 @@ async function main() {
|
|
|
557
565
|
case "console":
|
|
558
566
|
await runConsoleCommand(option(args, "prompt"));
|
|
559
567
|
return;
|
|
568
|
+
case "terminal":
|
|
569
|
+
await runTerminalShell();
|
|
570
|
+
return;
|
|
560
571
|
case "pair":
|
|
561
572
|
await runPairCommand(args);
|
|
562
573
|
return;
|
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.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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leg3ndy/otto-bridge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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.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"], {
|