@leg3ndy/otto-bridge 1.0.2 → 1.0.3
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 +16 -8
- package/dist/cli_terminal.js +265 -58
- 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.3`, com console terminal azul da marca, seletor `Fast/Thinking` e rendering estruturado de resultados locais, veja [`leg3ndy-ai-backend/docs/OTTO_BRIDGE_1_0_3_PATCH.md`](../leg3ndy-ai-backend/docs/OTTO_BRIDGE_1_0_3_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.3.tgz
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Na linha `1.0.
|
|
44
|
+
Na linha `1.0.3`, `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.3` 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.3` consolida o hub terminal como fluxo principal, esconde aliases legados da superfície pública, alinha o console ao comportamento normal do Otto antes de fazer handoff local e melhora a leitura visual/estruturada do resultado no proprio terminal.
|
|
49
49
|
|
|
50
50
|
## Publicacao
|
|
51
51
|
|
|
@@ -137,9 +137,17 @@ otto-bridge console
|
|
|
137
137
|
|
|
138
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.
|
|
139
139
|
|
|
140
|
+
Dentro do console, use:
|
|
141
|
+
|
|
142
|
+
- `/model fast` para `OttoAI Fast`
|
|
143
|
+
- `/model thinking` para `OttoAI Thinking`
|
|
144
|
+
- `/status` para ver detalhes técnicos do bridge e do runtime
|
|
145
|
+
|
|
146
|
+
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
|
+
|
|
140
148
|
### WhatsApp Web em background
|
|
141
149
|
|
|
142
|
-
Fluxo recomendado na linha `1.0.
|
|
150
|
+
Fluxo recomendado na linha `1.0.3`:
|
|
143
151
|
|
|
144
152
|
```bash
|
|
145
153
|
otto-bridge extensions --install whatsappweb
|
|
@@ -149,13 +157,13 @@ otto-bridge extensions --status whatsappweb
|
|
|
149
157
|
|
|
150
158
|
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.
|
|
151
159
|
|
|
152
|
-
Contrato da linha `1.0.
|
|
160
|
+
Contrato da linha `1.0.3`:
|
|
153
161
|
|
|
154
162
|
- `otto-bridge extensions --setup whatsappweb`: autentica a sessao uma vez
|
|
155
163
|
- `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
164
|
- ao fechar o `otto-bridge`: o browser em background e desligado, mas a sessao local fica lembrada para o proximo boot
|
|
157
165
|
|
|
158
|
-
## Handoff rapido da linha 1.0.
|
|
166
|
+
## Handoff rapido da linha 1.0.3
|
|
159
167
|
|
|
160
168
|
Ja fechado no codigo:
|
|
161
169
|
|
package/dist/cli_terminal.js
CHANGED
|
@@ -12,9 +12,12 @@ const ANSI = {
|
|
|
12
12
|
reset: "\u001b[0m",
|
|
13
13
|
dim: "\u001b[2m",
|
|
14
14
|
bold: "\u001b[1m",
|
|
15
|
-
|
|
15
|
+
italic: "\u001b[3m",
|
|
16
|
+
brandBlue: "\u001b[38;2;0;119;208m",
|
|
16
17
|
blue: "\u001b[38;5;111m",
|
|
17
18
|
teal: "\u001b[38;5;80m",
|
|
19
|
+
slate: "\u001b[38;5;245m",
|
|
20
|
+
slateItalic: "\u001b[38;5;245m\u001b[3m",
|
|
18
21
|
amber: "\u001b[38;5;221m",
|
|
19
22
|
red: "\u001b[38;5;203m",
|
|
20
23
|
green: "\u001b[38;5;114m",
|
|
@@ -29,6 +32,22 @@ const OTTOAI_BANNER = [
|
|
|
29
32
|
" ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝",
|
|
30
33
|
];
|
|
31
34
|
const CLI_EXIT_SENTINEL = "__OTTO_BRIDGE_CLI_EXIT__";
|
|
35
|
+
const CLI_MODEL_REGISTRY = {
|
|
36
|
+
fast: {
|
|
37
|
+
label: "OttoAI Fast",
|
|
38
|
+
requestModel: "deepseek-chat",
|
|
39
|
+
aliases: ["fast", "chat", "default", "ottoai fast", "otto fast"],
|
|
40
|
+
},
|
|
41
|
+
thinking: {
|
|
42
|
+
label: "OttoAI Thinking",
|
|
43
|
+
requestModel: "deepseek-reasoner",
|
|
44
|
+
aliases: ["thinking", "reasoner", "think", "ottoai thinking", "otto thinking"],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
const MAX_RENDERED_LIST_ENTRIES = 28;
|
|
48
|
+
const MAX_RENDERED_LIST_ENTRIES_COMPACT = 10;
|
|
49
|
+
const MAX_RENDERED_FILE_CHARS = 6_000;
|
|
50
|
+
const MAX_RENDERED_FILE_CHARS_COMPACT = 1_400;
|
|
32
51
|
class CliRuntimeSession {
|
|
33
52
|
config;
|
|
34
53
|
runtime = null;
|
|
@@ -157,14 +176,14 @@ function supportsAnsi() {
|
|
|
157
176
|
}
|
|
158
177
|
function renderBanner() {
|
|
159
178
|
const enabled = supportsAnsi();
|
|
160
|
-
const lines = OTTOAI_BANNER.map((line) => style(line, ANSI.
|
|
179
|
+
const lines = OTTOAI_BANNER.map((line) => style(line, ANSI.brandBlue, enabled));
|
|
161
180
|
const title = `${BRIDGE_PACKAGE_NAME} v${BRIDGE_VERSION}`;
|
|
162
181
|
const subtitle = "Paired local runtime and terminal console";
|
|
163
182
|
return [
|
|
164
183
|
lines.join("\n"),
|
|
165
184
|
"",
|
|
166
|
-
`${style("OTTO BRIDGE", ANSI.
|
|
167
|
-
`${style(subtitle, ANSI.
|
|
185
|
+
`${style("OTTO BRIDGE", ANSI.brandBlue, enabled)} ${style(title, ANSI.white, enabled)}`,
|
|
186
|
+
`${style(subtitle, ANSI.slate, enabled)}`,
|
|
168
187
|
].join("\n");
|
|
169
188
|
}
|
|
170
189
|
function createCliExitError() {
|
|
@@ -182,11 +201,14 @@ function isReadlineClosedError(error) {
|
|
|
182
201
|
}
|
|
183
202
|
function printSection(title) {
|
|
184
203
|
const enabled = supportsAnsi();
|
|
185
|
-
console.log(`\n${style(title, ANSI.
|
|
204
|
+
console.log(`\n${style(title, ANSI.brandBlue, enabled)}`);
|
|
186
205
|
}
|
|
187
206
|
function printMuted(message) {
|
|
188
207
|
console.log(style(message, ANSI.dim, supportsAnsi()));
|
|
189
208
|
}
|
|
209
|
+
function printSoft(message) {
|
|
210
|
+
console.log(style(message, ANSI.slateItalic, supportsAnsi()));
|
|
211
|
+
}
|
|
190
212
|
function printSuccess(message) {
|
|
191
213
|
console.log(style(message, ANSI.green, supportsAnsi()));
|
|
192
214
|
}
|
|
@@ -206,6 +228,40 @@ function truncate(text, max = 180) {
|
|
|
206
228
|
}
|
|
207
229
|
return `${value.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
|
208
230
|
}
|
|
231
|
+
function humanFileSize(value) {
|
|
232
|
+
const size = typeof value === "number" ? value : Number(value);
|
|
233
|
+
if (!Number.isFinite(size) || size < 0) {
|
|
234
|
+
return "";
|
|
235
|
+
}
|
|
236
|
+
if (size < 1024) {
|
|
237
|
+
return `${size} B`;
|
|
238
|
+
}
|
|
239
|
+
if (size < 1024 * 1024) {
|
|
240
|
+
return `${(size / 1024).toFixed(1)} KB`;
|
|
241
|
+
}
|
|
242
|
+
if (size < 1024 * 1024 * 1024) {
|
|
243
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
244
|
+
}
|
|
245
|
+
return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
246
|
+
}
|
|
247
|
+
function getCliModelLabel(mode) {
|
|
248
|
+
return CLI_MODEL_REGISTRY[mode].label;
|
|
249
|
+
}
|
|
250
|
+
function getCliModelRequestModel(mode) {
|
|
251
|
+
return CLI_MODEL_REGISTRY[mode].requestModel;
|
|
252
|
+
}
|
|
253
|
+
export function resolveCliModelMode(value) {
|
|
254
|
+
const normalized = normalizeText(value).toLowerCase();
|
|
255
|
+
if (!normalized) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
for (const [mode, definition] of Object.entries(CLI_MODEL_REGISTRY)) {
|
|
259
|
+
if (definition.aliases.includes(normalized)) {
|
|
260
|
+
return mode;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
209
265
|
function delay(ms) {
|
|
210
266
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
211
267
|
}
|
|
@@ -234,7 +290,7 @@ async function question(rl, prompt) {
|
|
|
234
290
|
async function ask(rl, label, options) {
|
|
235
291
|
const defaultValue = normalizeText(options?.defaultValue);
|
|
236
292
|
const suffix = defaultValue ? ` ${style(`[${defaultValue}]`, ANSI.dim, supportsAnsi())}` : "";
|
|
237
|
-
const answer = normalizeText(await question(rl, `${style("›", ANSI.
|
|
293
|
+
const answer = normalizeText(await question(rl, `${style("›", ANSI.brandBlue, supportsAnsi())} ${label}${suffix}: `));
|
|
238
294
|
if (answer) {
|
|
239
295
|
return answer;
|
|
240
296
|
}
|
|
@@ -247,7 +303,7 @@ async function ask(rl, label, options) {
|
|
|
247
303
|
return await ask(rl, label, options);
|
|
248
304
|
}
|
|
249
305
|
async function askYesNo(rl, promptText, defaultValue = true) {
|
|
250
|
-
const answer = normalizeText(await question(rl, `${style("?", ANSI.
|
|
306
|
+
const answer = normalizeText(await question(rl, `${style("?", ANSI.brandBlue, supportsAnsi())} ${promptText} ${style(defaultValue ? "[Y/n]" : "[y/N]", ANSI.dim, supportsAnsi())}: `)).toLowerCase();
|
|
251
307
|
if (!answer) {
|
|
252
308
|
return defaultValue;
|
|
253
309
|
}
|
|
@@ -259,8 +315,8 @@ async function pauseForEnter(rl, message = "Pressione Enter para continuar") {
|
|
|
259
315
|
async function chooseExecutor(rl, current) {
|
|
260
316
|
const defaultType = current?.type || resolveExecutorConfig().type;
|
|
261
317
|
console.log([
|
|
262
|
-
`${style("1.", ANSI.
|
|
263
|
-
`${style("2.", ANSI.
|
|
318
|
+
`${style("1.", ANSI.brandBlue, supportsAnsi())} native-macos ${style("(Mac real, runtime local)", ANSI.dim, supportsAnsi())}`,
|
|
319
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} mock ${style("(ambiente de teste)", ANSI.dim, supportsAnsi())}`,
|
|
264
320
|
].join("\n"));
|
|
265
321
|
const selection = await ask(rl, "Executor", {
|
|
266
322
|
defaultValue: defaultType === "mock" ? "2" : "1",
|
|
@@ -322,16 +378,16 @@ function extractConfirmationPrompt(job) {
|
|
|
322
378
|
}
|
|
323
379
|
function renderStatusOverview(config, runtimeSession) {
|
|
324
380
|
return [
|
|
325
|
-
`${style("Device", ANSI.
|
|
326
|
-
`${style("Device ID", ANSI.
|
|
327
|
-
`${style("API", ANSI.
|
|
328
|
-
`${style("Executor", ANSI.
|
|
329
|
-
`${style("Approval", ANSI.
|
|
330
|
-
`${style("Runtime", ANSI.
|
|
381
|
+
`${style("Device", ANSI.brandBlue, supportsAnsi())}: ${config.deviceName}`,
|
|
382
|
+
`${style("Device ID", ANSI.brandBlue, supportsAnsi())}: ${config.deviceId}`,
|
|
383
|
+
`${style("API", ANSI.brandBlue, supportsAnsi())}: ${config.apiBaseUrl}`,
|
|
384
|
+
`${style("Executor", ANSI.brandBlue, supportsAnsi())}: ${config.executor.type}`,
|
|
385
|
+
`${style("Approval", ANSI.brandBlue, supportsAnsi())}: ${config.approvalMode}`,
|
|
386
|
+
`${style("Runtime", ANSI.brandBlue, supportsAnsi())}: ${runtimeSession?.getStatusLabel() || "offline"}`,
|
|
331
387
|
...(runtimeSession?.getStatusDetail()
|
|
332
|
-
? [`${style("Runtime note", ANSI.
|
|
388
|
+
? [`${style("Runtime note", ANSI.brandBlue, supportsAnsi())}: ${runtimeSession.getStatusDetail()}`]
|
|
333
389
|
: []),
|
|
334
|
-
`${style("Config", ANSI.
|
|
390
|
+
`${style("Config", ANSI.brandBlue, supportsAnsi())}: ${getBridgeConfigPath()}`,
|
|
335
391
|
];
|
|
336
392
|
}
|
|
337
393
|
function renderRuntimeHeadline(runtimeSession) {
|
|
@@ -356,49 +412,177 @@ function renderRuntimeHeadline(runtimeSession) {
|
|
|
356
412
|
function padRight(text, width) {
|
|
357
413
|
return text.length >= width ? text : `${text}${" ".repeat(width - text.length)}`;
|
|
358
414
|
}
|
|
415
|
+
function styleCardLine(line, tone, enabled) {
|
|
416
|
+
if (!enabled) {
|
|
417
|
+
return line;
|
|
418
|
+
}
|
|
419
|
+
if (tone === "title") {
|
|
420
|
+
return `${ANSI.bold}${ANSI.brandBlue}${line}${ANSI.reset}`;
|
|
421
|
+
}
|
|
422
|
+
if (tone === "muted") {
|
|
423
|
+
return `${ANSI.slateItalic}${line}${ANSI.reset}`;
|
|
424
|
+
}
|
|
425
|
+
return `${ANSI.white}${line}${ANSI.reset}`;
|
|
426
|
+
}
|
|
359
427
|
function renderInfoCard(lines) {
|
|
360
|
-
const
|
|
361
|
-
const
|
|
428
|
+
const enabled = supportsAnsi();
|
|
429
|
+
const normalized = lines.map((line) => ({
|
|
430
|
+
text: truncate(line.text, 82),
|
|
431
|
+
tone: line.tone,
|
|
432
|
+
}));
|
|
433
|
+
const width = Math.max(44, ...normalized.map((line) => line.text.length));
|
|
434
|
+
const top = style(`┌${"─".repeat(width + 2)}┐`, ANSI.brandBlue, enabled);
|
|
435
|
+
const bottom = style(`└${"─".repeat(width + 2)}┘`, ANSI.brandBlue, enabled);
|
|
362
436
|
return [
|
|
363
|
-
|
|
364
|
-
...normalized.map((line) =>
|
|
365
|
-
|
|
437
|
+
top,
|
|
438
|
+
...normalized.map((line) => {
|
|
439
|
+
const border = style("│", ANSI.brandBlue, enabled);
|
|
440
|
+
const content = styleCardLine(padRight(line.text, width), line.tone, enabled);
|
|
441
|
+
return `${border} ${content} ${border}`;
|
|
442
|
+
}),
|
|
443
|
+
bottom,
|
|
366
444
|
].join("\n");
|
|
367
445
|
}
|
|
368
|
-
function
|
|
446
|
+
function buildWelcomeCard(runtimeSession, modelMode) {
|
|
447
|
+
return [
|
|
448
|
+
{ text: "Welcome to OttoAI", tone: "title" },
|
|
449
|
+
{ text: "", tone: "muted" },
|
|
450
|
+
{ text: "/help inside the console, /status for bridge details", tone: "muted" },
|
|
451
|
+
{ text: "", tone: "muted" },
|
|
452
|
+
{ text: `model: ${getCliModelLabel(modelMode)}`, tone: "muted" },
|
|
453
|
+
{ text: `cwd: ${process.cwd()}`, tone: "muted" },
|
|
454
|
+
{ text: renderRuntimeHeadline(runtimeSession), tone: "muted" },
|
|
455
|
+
];
|
|
456
|
+
}
|
|
457
|
+
function printHubScreen(runtimeSession, modelMode) {
|
|
369
458
|
console.clear();
|
|
370
459
|
console.log(renderBanner());
|
|
371
460
|
console.log("");
|
|
372
|
-
console.log(renderInfoCard(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
"model: OttoAI local console",
|
|
376
|
-
`cwd: ${process.cwd()}`,
|
|
377
|
-
renderRuntimeHeadline(runtimeSession),
|
|
378
|
-
]));
|
|
379
|
-
}
|
|
380
|
-
function printConsoleScreen(runtimeSession) {
|
|
461
|
+
console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
|
|
462
|
+
}
|
|
463
|
+
function printConsoleScreen(runtimeSession, modelMode) {
|
|
381
464
|
console.clear();
|
|
382
465
|
console.log(renderBanner());
|
|
383
466
|
console.log("");
|
|
384
|
-
console.log(renderInfoCard(
|
|
385
|
-
"Welcome to OttoAI",
|
|
386
|
-
"/help for commands, /status for bridge details",
|
|
387
|
-
"model: OttoAI local console",
|
|
388
|
-
`cwd: ${process.cwd()}`,
|
|
389
|
-
renderRuntimeHeadline(runtimeSession),
|
|
390
|
-
]));
|
|
467
|
+
console.log(renderInfoCard(buildWelcomeCard(runtimeSession, modelMode)));
|
|
391
468
|
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
|
+
}
|
|
473
|
+
function renderPromptFrameLine(width, edgeLeft, edgeRight) {
|
|
474
|
+
return style(`${edgeLeft}${"─".repeat(width)}${edgeRight}`, ANSI.brandBlue, supportsAnsi());
|
|
392
475
|
}
|
|
393
476
|
async function askConsoleInput(rl) {
|
|
394
|
-
|
|
477
|
+
const availableWidth = Number(output.columns || 90);
|
|
478
|
+
const frameWidth = Math.max(36, Math.min(availableWidth - 2, 88));
|
|
479
|
+
console.log(renderPromptFrameLine(frameWidth, "┌", "┐"));
|
|
480
|
+
const answer = normalizeText(await question(rl, `${style("│", ANSI.brandBlue, supportsAnsi())} ${style(">", `${ANSI.bold}${ANSI.white}`, supportsAnsi())} `));
|
|
481
|
+
console.log(renderPromptFrameLine(frameWidth, "└", "┘"));
|
|
482
|
+
return answer;
|
|
395
483
|
}
|
|
396
484
|
function printAssistantMessage(message) {
|
|
397
485
|
const text = normalizeText(message);
|
|
398
486
|
if (!text) {
|
|
399
487
|
return;
|
|
400
488
|
}
|
|
401
|
-
console.log(`${style("•", ANSI.
|
|
489
|
+
console.log(`${style("•", ANSI.brandBlue, supportsAnsi())} ${text}`);
|
|
490
|
+
}
|
|
491
|
+
function extractJobOutcome(job) {
|
|
492
|
+
const result = job.result && typeof job.result === "object"
|
|
493
|
+
? job.result
|
|
494
|
+
: {};
|
|
495
|
+
return result.outcome && typeof result.outcome === "object"
|
|
496
|
+
? result.outcome
|
|
497
|
+
: {};
|
|
498
|
+
}
|
|
499
|
+
function outcomePathLabel(outcome) {
|
|
500
|
+
return normalizeText(outcome.resolved_path || outcome.path);
|
|
501
|
+
}
|
|
502
|
+
function collectOutcomeFileContent(outcome) {
|
|
503
|
+
const directContent = outcome.content;
|
|
504
|
+
if (typeof directContent === "string" && directContent.trim()) {
|
|
505
|
+
return directContent;
|
|
506
|
+
}
|
|
507
|
+
const chunks = Array.isArray(outcome.content_chunks) ? outcome.content_chunks : [];
|
|
508
|
+
return chunks
|
|
509
|
+
.filter((item) => item && typeof item === "object")
|
|
510
|
+
.map((item) => String(item.text || ""))
|
|
511
|
+
.filter((item) => item.length > 0)
|
|
512
|
+
.join("");
|
|
513
|
+
}
|
|
514
|
+
function formatDirectoryEntry(entry) {
|
|
515
|
+
const name = normalizeText(entry.name) || "(sem nome)";
|
|
516
|
+
const kind = normalizeText(entry.kind).toLowerCase();
|
|
517
|
+
const prefix = kind === "directory" ? "[dir]" : "[file]";
|
|
518
|
+
const size = kind === "file" ? humanFileSize(entry.size_bytes) : "";
|
|
519
|
+
return `${prefix} ${name}${size ? ` (${size})` : ""}`;
|
|
520
|
+
}
|
|
521
|
+
export function renderStructuredOutcome(job, options) {
|
|
522
|
+
const compact = Boolean(options?.compact);
|
|
523
|
+
const outcome = extractJobOutcome(job);
|
|
524
|
+
const actionType = normalizeText(outcome.action_type).toLowerCase();
|
|
525
|
+
const lines = [];
|
|
526
|
+
if (actionType === "list_files") {
|
|
527
|
+
const entries = Array.isArray(outcome.entries)
|
|
528
|
+
? outcome.entries.filter((item) => item && typeof item === "object")
|
|
529
|
+
: [];
|
|
530
|
+
const target = outcomePathLabel(outcome);
|
|
531
|
+
const itemCount = outcome.listed_item_count ?? entries.length;
|
|
532
|
+
if (target) {
|
|
533
|
+
lines.push(target);
|
|
534
|
+
}
|
|
535
|
+
if (itemCount) {
|
|
536
|
+
lines.push(`${itemCount} item(ns) encontrados`);
|
|
537
|
+
}
|
|
538
|
+
if (lines.length) {
|
|
539
|
+
lines.push("");
|
|
540
|
+
}
|
|
541
|
+
const maxEntries = compact ? MAX_RENDERED_LIST_ENTRIES_COMPACT : MAX_RENDERED_LIST_ENTRIES;
|
|
542
|
+
entries.slice(0, maxEntries).forEach((entry) => {
|
|
543
|
+
lines.push(formatDirectoryEntry(entry));
|
|
544
|
+
});
|
|
545
|
+
if (entries.length > maxEntries) {
|
|
546
|
+
lines.push(`... +${entries.length - maxEntries} item(ns)`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (actionType === "read_file") {
|
|
550
|
+
const target = outcomePathLabel(outcome);
|
|
551
|
+
const binaryNotice = normalizeText(outcome.binary_notice);
|
|
552
|
+
const content = collectOutcomeFileContent(outcome);
|
|
553
|
+
const maxChars = compact ? MAX_RENDERED_FILE_CHARS_COMPACT : MAX_RENDERED_FILE_CHARS;
|
|
554
|
+
if (target) {
|
|
555
|
+
lines.push(target);
|
|
556
|
+
lines.push("");
|
|
557
|
+
}
|
|
558
|
+
if (binaryNotice) {
|
|
559
|
+
lines.push(binaryNotice);
|
|
560
|
+
}
|
|
561
|
+
else if (content) {
|
|
562
|
+
const truncatedContent = content.length > maxChars
|
|
563
|
+
? `${content.slice(0, maxChars).trimEnd()}\n\n[... conteúdo truncado ...]`
|
|
564
|
+
: content;
|
|
565
|
+
lines.push(truncatedContent);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return lines.join("\n").trim();
|
|
569
|
+
}
|
|
570
|
+
function printStructuredOutcome(job) {
|
|
571
|
+
const rendered = renderStructuredOutcome(job);
|
|
572
|
+
if (!rendered) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
console.log(rendered);
|
|
576
|
+
}
|
|
577
|
+
function buildConversationSummary(summary, job) {
|
|
578
|
+
const rendered = renderStructuredOutcome(job, { compact: true });
|
|
579
|
+
if (!rendered) {
|
|
580
|
+
return summary;
|
|
581
|
+
}
|
|
582
|
+
if (!summary) {
|
|
583
|
+
return rendered;
|
|
584
|
+
}
|
|
585
|
+
return `${summary}\n${rendered}`.slice(0, 4_000).trim();
|
|
402
586
|
}
|
|
403
587
|
async function printExtensionsOverview(config) {
|
|
404
588
|
printSection("Extensions");
|
|
@@ -497,18 +681,19 @@ async function followConsoleJob(rl, config, jobId) {
|
|
|
497
681
|
? "Execução local falhou."
|
|
498
682
|
: "Execução local cancelada.");
|
|
499
683
|
printAssistantMessage(summary);
|
|
500
|
-
|
|
684
|
+
printStructuredOutcome(job);
|
|
685
|
+
return buildConversationSummary(summary, job);
|
|
501
686
|
}
|
|
502
687
|
await delay(1400);
|
|
503
688
|
}
|
|
504
689
|
}
|
|
505
690
|
async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
506
|
-
|
|
507
|
-
|
|
691
|
+
let activeModel = "fast";
|
|
692
|
+
printConsoleScreen(runtimeSession, activeModel);
|
|
508
693
|
const sessionId = randomUUID();
|
|
509
694
|
const conversation = [];
|
|
510
695
|
const printConsoleHelp = () => {
|
|
511
|
-
|
|
696
|
+
printSoft("Comandos: /help, /model [fast|thinking], /status, /clear, /exit");
|
|
512
697
|
};
|
|
513
698
|
const handlePrompt = async (promptText) => {
|
|
514
699
|
const normalizedPrompt = normalizeText(promptText);
|
|
@@ -521,11 +706,32 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
521
706
|
}
|
|
522
707
|
if (normalizedPrompt === "/clear") {
|
|
523
708
|
conversation.splice(0, conversation.length);
|
|
524
|
-
printConsoleScreen(runtimeSession);
|
|
709
|
+
printConsoleScreen(runtimeSession, activeModel);
|
|
525
710
|
printMuted("Contexto local do console limpo.");
|
|
526
711
|
return;
|
|
527
712
|
}
|
|
713
|
+
if (normalizedPrompt === "/model") {
|
|
714
|
+
printMuted(`Modelo ativo: ${getCliModelLabel(activeModel)}.`);
|
|
715
|
+
printSoft("Use /model fast ou /model thinking para trocar.");
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (normalizedPrompt.startsWith("/model ")) {
|
|
719
|
+
const nextMode = resolveCliModelMode(normalizedPrompt.slice("/model ".length));
|
|
720
|
+
if (!nextMode) {
|
|
721
|
+
printWarning("Modelo inválido. Use /model fast ou /model thinking.");
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (nextMode === activeModel) {
|
|
725
|
+
printMuted(`Modelo já está em ${getCliModelLabel(activeModel)}.`);
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
activeModel = nextMode;
|
|
729
|
+
printConsoleScreen(runtimeSession, activeModel);
|
|
730
|
+
printSuccess(`Modelo ativo: ${getCliModelLabel(activeModel)}.`);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
528
733
|
if (normalizedPrompt === "/status") {
|
|
734
|
+
console.log(`${style("Model", ANSI.brandBlue, supportsAnsi())}: ${getCliModelLabel(activeModel)}`);
|
|
529
735
|
renderStatusOverview(config, runtimeSession).forEach((line) => console.log(line));
|
|
530
736
|
const runtimeFailure = runtimeSession.getLastError();
|
|
531
737
|
if (runtimeFailure) {
|
|
@@ -545,6 +751,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
545
751
|
let handoffPayload = null;
|
|
546
752
|
await streamDeviceCliChat(config, {
|
|
547
753
|
messages: conversation,
|
|
754
|
+
model: getCliModelRequestModel(activeModel),
|
|
548
755
|
session_id: sessionId,
|
|
549
756
|
}, async (event) => {
|
|
550
757
|
const chunkType = normalizeText(event.chunk_type).toLowerCase();
|
|
@@ -569,7 +776,7 @@ async function runOttoConsole(rl, config, runtimeSession, options) {
|
|
|
569
776
|
return;
|
|
570
777
|
}
|
|
571
778
|
if (!assistantPrefixPrinted) {
|
|
572
|
-
output.write(`${style("•", ANSI.
|
|
779
|
+
output.write(`${style("•", ANSI.brandBlue, supportsAnsi())} `);
|
|
573
780
|
assistantPrefixPrinted = true;
|
|
574
781
|
}
|
|
575
782
|
output.write(contentChunk);
|
|
@@ -630,15 +837,15 @@ async function pickHomeChoice(rl, paired) {
|
|
|
630
837
|
printSection("Home");
|
|
631
838
|
const options = paired
|
|
632
839
|
? [
|
|
633
|
-
`${style("1.", ANSI.
|
|
634
|
-
`${style("2.", ANSI.
|
|
635
|
-
`${style("3.", ANSI.
|
|
636
|
-
`${style("4.", ANSI.
|
|
637
|
-
`${style("5.", ANSI.
|
|
840
|
+
`${style("1.", ANSI.brandBlue, supportsAnsi())} Otto Console`,
|
|
841
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} Setup / parear novamente`,
|
|
842
|
+
`${style("3.", ANSI.brandBlue, supportsAnsi())} Status detalhado`,
|
|
843
|
+
`${style("4.", ANSI.brandBlue, supportsAnsi())} Extensões instaladas`,
|
|
844
|
+
`${style("5.", ANSI.brandBlue, supportsAnsi())} Sair`,
|
|
638
845
|
]
|
|
639
846
|
: [
|
|
640
|
-
`${style("1.", ANSI.
|
|
641
|
-
`${style("2.", ANSI.
|
|
847
|
+
`${style("1.", ANSI.brandBlue, supportsAnsi())} Pairing setup`,
|
|
848
|
+
`${style("2.", ANSI.brandBlue, supportsAnsi())} Sair`,
|
|
642
849
|
];
|
|
643
850
|
console.log(options.join("\n"));
|
|
644
851
|
const answer = await ask(rl, "Escolha");
|
|
@@ -661,7 +868,7 @@ export async function launchInteractiveCli(options) {
|
|
|
661
868
|
try {
|
|
662
869
|
let config = await loadBridgeConfig();
|
|
663
870
|
if (!config) {
|
|
664
|
-
printHubScreen(null);
|
|
871
|
+
printHubScreen(null, "fast");
|
|
665
872
|
const setup = await runSetupWizard(rl, options);
|
|
666
873
|
config = setup.config;
|
|
667
874
|
if (config && setup.openConsole) {
|
|
@@ -676,7 +883,7 @@ export async function launchInteractiveCli(options) {
|
|
|
676
883
|
runtimeSession = runtimeSession || new CliRuntimeSession(config);
|
|
677
884
|
await runtimeSession.ensureStarted();
|
|
678
885
|
for (;;) {
|
|
679
|
-
printHubScreen(runtimeSession);
|
|
886
|
+
printHubScreen(runtimeSession, "fast");
|
|
680
887
|
const choice = await pickHomeChoice(rl, true);
|
|
681
888
|
if (choice === "exit") {
|
|
682
889
|
break;
|
|
@@ -726,7 +933,7 @@ export async function runSetupCommand(options) {
|
|
|
726
933
|
const rl = await createPromptInterface();
|
|
727
934
|
let runtimeSession = null;
|
|
728
935
|
try {
|
|
729
|
-
printHubScreen(null);
|
|
936
|
+
printHubScreen(null, "fast");
|
|
730
937
|
const setup = await runSetupWizard(rl, options);
|
|
731
938
|
if (setup.config && setup.openConsole) {
|
|
732
939
|
runtimeSession = new CliRuntimeSession(setup.config);
|
|
@@ -748,7 +955,7 @@ export async function runConsoleCommand(initialPrompt) {
|
|
|
748
955
|
const rl = await createPromptInterface();
|
|
749
956
|
let runtimeSession = null;
|
|
750
957
|
try {
|
|
751
|
-
printHubScreen(null);
|
|
958
|
+
printHubScreen(null, "fast");
|
|
752
959
|
let config = await loadBridgeConfig();
|
|
753
960
|
if (!config) {
|
|
754
961
|
const setup = await runSetupWizard(rl);
|
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.3";
|
|
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
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.3");
|
|
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"], {
|