chocolatito-code 1.0.0 → 1.2.0
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 +155 -0
- package/dist/agent/compaction.d.ts +65 -0
- package/dist/agent/compaction.js +134 -0
- package/dist/agent/context.d.ts +3 -0
- package/dist/agent/context.js +35 -0
- package/dist/agent/loop.d.ts +91 -2
- package/dist/agent/loop.js +642 -91
- package/dist/agent/loopDetector.d.ts +57 -0
- package/dist/agent/loopDetector.js +0 -0
- package/dist/agent/mentions.d.ts +13 -0
- package/dist/agent/mentions.js +49 -0
- package/dist/agent/repoMap.d.ts +117 -0
- package/dist/agent/repoMap.js +560 -0
- package/dist/agent/result.d.ts +64 -0
- package/dist/agent/result.js +48 -0
- package/dist/agent/retry.d.ts +35 -0
- package/dist/agent/retry.js +123 -4
- package/dist/agent/salidaDelAgente.d.ts +43 -0
- package/dist/agent/salidaDelAgente.js +53 -0
- package/dist/agent/subagent.js +62 -15
- package/dist/agent/syntaxValidator.js +288 -52
- package/dist/agent/toolGate.d.ts +58 -0
- package/dist/agent/toolGate.js +111 -0
- package/dist/agent/tracker.js +4 -7
- package/dist/agent/undoManager.d.ts +44 -11
- package/dist/agent/undoManager.js +95 -24
- package/dist/agent/usageMeter.d.ts +56 -0
- package/dist/agent/usageMeter.js +51 -0
- package/dist/agent/verifier.d.ts +49 -0
- package/dist/agent/verifier.js +158 -0
- package/dist/config/constants.js +14 -0
- package/dist/config/engine.d.ts +19 -0
- package/dist/config/engine.js +35 -3
- package/dist/config/license.d.ts +5 -0
- package/dist/config/license.js +107 -9
- package/dist/config/limits.d.ts +28 -0
- package/dist/config/limits.js +51 -0
- package/dist/config/nodeVersion.d.ts +13 -0
- package/dist/config/nodeVersion.js +26 -0
- package/dist/config/permissions.d.ts +76 -1
- package/dist/config/permissions.js +379 -23
- package/dist/config/quota.js +24 -1
- package/dist/config/updater.d.ts +68 -0
- package/dist/config/updater.js +215 -0
- package/dist/hooks/manager.js +159 -14
- package/dist/index.js +240 -37
- package/dist/mcp/client.d.ts +50 -0
- package/dist/mcp/client.js +155 -19
- package/dist/mcp/ide.d.ts +76 -0
- package/dist/mcp/ide.js +160 -0
- package/dist/mcp/manager.js +9 -0
- package/dist/prompts/systemPrompt.js +12 -1
- package/dist/sessions/manager.d.ts +12 -0
- package/dist/sessions/manager.js +33 -5
- package/dist/sessions/resume.d.ts +36 -0
- package/dist/sessions/resume.js +43 -0
- package/dist/skills/manager.d.ts +1 -0
- package/dist/skills/manager.js +9 -0
- package/dist/tools/binary.d.ts +46 -0
- package/dist/tools/binary.js +100 -0
- package/dist/tools/browserCdp.js +13 -1
- package/dist/tools/browserExtension.d.ts +5 -0
- package/dist/tools/browserExtension.js +237 -22
- package/dist/tools/browserSession.d.ts +23 -0
- package/dist/tools/browserSession.js +28 -0
- package/dist/tools/computerUse.js +33 -1
- package/dist/tools/createImage.d.ts +11 -0
- package/dist/tools/createImage.js +13 -1
- package/dist/tools/definitions.js +17 -4
- package/dist/tools/editDiagnosis.d.ts +54 -0
- package/dist/tools/editDiagnosis.js +142 -0
- package/dist/tools/editFile.d.ts +1 -0
- package/dist/tools/editFile.js +118 -62
- package/dist/tools/findFiles.d.ts +21 -0
- package/dist/tools/findFiles.js +75 -6
- package/dist/tools/getSystemInfo.js +15 -7
- package/dist/tools/gitTools.d.ts +24 -0
- package/dist/tools/gitTools.js +87 -26
- package/dist/tools/grepSearch.js +111 -23
- package/dist/tools/moveCopyFile.d.ts +18 -1
- package/dist/tools/moveCopyFile.js +74 -4
- package/dist/tools/patchCascade.d.ts +57 -0
- package/dist/tools/patchCascade.js +338 -0
- package/dist/tools/runCommand.js +213 -25
- package/dist/tools/runner.js +58 -5
- package/dist/tools/toolDefsComputer.js +12 -2
- package/dist/tools/truncator.d.ts +27 -1
- package/dist/tools/truncator.js +117 -11
- package/dist/tools/viewFile.js +15 -11
- package/dist/tools/visionBridge.js +7 -0
- package/dist/tools/webSearch.d.ts +21 -0
- package/dist/tools/webSearch.js +125 -14
- package/dist/tools/writeFile.d.ts +16 -1
- package/dist/tools/writeFile.js +40 -2
- package/dist/ui/interrupt.d.ts +4 -1
- package/dist/ui/interrupt.js +59 -7
- package/dist/ui/keyboardGuard.d.ts +76 -0
- package/dist/ui/keyboardGuard.js +94 -0
- package/dist/ui/loopPrompt.d.ts +17 -0
- package/dist/ui/loopPrompt.js +59 -0
- package/dist/ui/pegados.d.ts +50 -0
- package/dist/ui/pegados.js +83 -0
- package/dist/ui/permissionPrompt.js +84 -2
- package/dist/ui/prompt.d.ts +40 -0
- package/dist/ui/prompt.js +436 -131
- package/dist/ui/reasoningStream.d.ts +8 -0
- package/dist/ui/reasoningStream.js +99 -7
- package/dist/ui/salida.d.ts +10 -0
- package/dist/ui/salida.js +50 -0
- package/extension/README.md +95 -74
- package/extension/background.js +772 -181
- package/extension/content.js +527 -46
- package/extension/fuentes/Chocolatito-Marca.ttf +0 -0
- package/extension/iconos/128.png +0 -0
- package/extension/iconos/16.png +0 -0
- package/extension/iconos/32.png +0 -0
- package/extension/iconos/48.png +0 -0
- package/extension/iconos/logotipo.png +0 -0
- package/extension/manifest.json +28 -4
- package/extension/popup.html +16 -6
- package/extension/popup.js +28 -12
- package/package.json +3 -2
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export declare class ReasoningStream {
|
|
2
2
|
private hasStarted;
|
|
3
3
|
private atLineStart;
|
|
4
|
+
private inicio;
|
|
5
|
+
private caracteres;
|
|
6
|
+
private ultimoRefresco;
|
|
7
|
+
private cola;
|
|
8
|
+
private lineaPintada;
|
|
4
9
|
push(chunk: string): void;
|
|
10
|
+
/** El volcado literal, para quien lo pida. Una escritura por trozo, no por letra. */
|
|
11
|
+
private volcar;
|
|
12
|
+
private refrescar;
|
|
5
13
|
flush(): void;
|
|
6
14
|
}
|
|
@@ -1,38 +1,130 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import process from "node:process";
|
|
3
|
+
/**
|
|
4
|
+
* Lo que se ve mientras el modelo piensa.
|
|
5
|
+
*
|
|
6
|
+
* Antes se volcaba el razonamiento entero, letra a letra:
|
|
7
|
+
*
|
|
8
|
+
* for (let i = 0; i < chunk.length; i++)
|
|
9
|
+
* process.stdout.write(chalk.gray(char));
|
|
10
|
+
*
|
|
11
|
+
* Dos problemas, y el segundo es el grave.
|
|
12
|
+
*
|
|
13
|
+
* **Es ilegible.** Un razonamiento de una tarea mediana son quince mil
|
|
14
|
+
* caracteres de deliberacion en voz alta —"mejor 12x16... no, mejor por
|
|
15
|
+
* partes... cuento: 2+2+6+2=12"— que llenan la pantalla y entierran lo unico
|
|
16
|
+
* que importa: que esta haciendo y si va bien.
|
|
17
|
+
*
|
|
18
|
+
* **Y bloquea el terminal.** Quince mil escrituras sincronas, cada una
|
|
19
|
+
* envuelta por chalk en codigos de color (once bytes para pintar una letra),
|
|
20
|
+
* son quince mil llamadas al sistema. En Windows eso deja el bucle de eventos
|
|
21
|
+
* parado el rato que dura, y las teclas que el usuario pulsa mientras tanto se
|
|
22
|
+
* quedan en la cola del sistema operativo. De ahi el "escribo y no sale nada",
|
|
23
|
+
* que aparecia justo en las tareas largas -las que mas razonan- y nunca al
|
|
24
|
+
* saludar.
|
|
25
|
+
*
|
|
26
|
+
* Ahora se resume: una sola linea que se reescribe sobre si misma diciendo
|
|
27
|
+
* cuanto lleva pensando y en que anda. Cero avance de scroll, una escritura por
|
|
28
|
+
* trozo recibido en vez de una por letra.
|
|
29
|
+
*
|
|
30
|
+
* Quien quiera verlo entero: CHOCOLATITO_RAZONAMIENTO=completo.
|
|
31
|
+
*/
|
|
32
|
+
const COMPLETO = process.env.CHOCOLATITO_RAZONAMIENTO === "completo";
|
|
33
|
+
/** Cada cuanto se refresca la linea. Mas a menudo es parpadeo, no informacion. */
|
|
34
|
+
const MS_ENTRE_REFRESCOS = 400;
|
|
35
|
+
/** Se recorta para que quepa siempre en una linea, sin envolver. */
|
|
36
|
+
function unaLinea(texto, ancho) {
|
|
37
|
+
const limpio = texto.replace(/\s+/g, " ").trim();
|
|
38
|
+
if (limpio.length <= ancho)
|
|
39
|
+
return limpio;
|
|
40
|
+
return limpio.slice(0, Math.max(0, ancho - 1)) + "…";
|
|
41
|
+
}
|
|
3
42
|
export class ReasoningStream {
|
|
4
43
|
hasStarted = false;
|
|
5
44
|
atLineStart = true;
|
|
45
|
+
inicio = 0;
|
|
46
|
+
caracteres = 0;
|
|
47
|
+
ultimoRefresco = 0;
|
|
48
|
+
cola = "";
|
|
49
|
+
lineaPintada = false;
|
|
6
50
|
push(chunk) {
|
|
7
51
|
if (!chunk)
|
|
8
52
|
return;
|
|
9
53
|
if (!this.hasStarted) {
|
|
10
54
|
this.hasStarted = true;
|
|
11
|
-
|
|
55
|
+
this.inicio = Date.now();
|
|
56
|
+
this.caracteres = 0;
|
|
57
|
+
this.cola = "";
|
|
58
|
+
if (COMPLETO) {
|
|
59
|
+
process.stdout.write(`\n ${chalk.bold.hex("#8B5CF6")("💭 Razonando:")}\n`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (COMPLETO) {
|
|
63
|
+
this.volcar(chunk);
|
|
64
|
+
return;
|
|
12
65
|
}
|
|
13
|
-
|
|
14
|
-
|
|
66
|
+
this.caracteres += chunk.length;
|
|
67
|
+
// Solo se guarda la cola: es de donde sale el "en que anda ahora".
|
|
68
|
+
this.cola = (this.cola + chunk).slice(-300);
|
|
69
|
+
this.refrescar(false);
|
|
70
|
+
}
|
|
71
|
+
/** El volcado literal, para quien lo pida. Una escritura por trozo, no por letra. */
|
|
72
|
+
volcar(chunk) {
|
|
73
|
+
const prefijo = ` ${chalk.hex("#8B5CF6")("│")} `;
|
|
74
|
+
let salida = "";
|
|
75
|
+
for (const char of chunk) {
|
|
15
76
|
if (this.atLineStart) {
|
|
16
|
-
|
|
77
|
+
salida += prefijo;
|
|
17
78
|
this.atLineStart = false;
|
|
18
79
|
}
|
|
19
80
|
if (char === "\n") {
|
|
20
|
-
|
|
81
|
+
salida += "\n";
|
|
21
82
|
this.atLineStart = true;
|
|
22
83
|
}
|
|
23
84
|
else {
|
|
24
|
-
|
|
85
|
+
salida += char;
|
|
25
86
|
}
|
|
26
87
|
}
|
|
88
|
+
// Un solo write, y el color se aplica una vez al bloque entero en vez de a
|
|
89
|
+
// cada letra: mismo resultado en pantalla, una fraccion de los bytes.
|
|
90
|
+
process.stdout.write(chalk.gray(salida));
|
|
91
|
+
}
|
|
92
|
+
refrescar(forzar) {
|
|
93
|
+
const ahora = Date.now();
|
|
94
|
+
if (!forzar && ahora - this.ultimoRefresco < MS_ENTRE_REFRESCOS)
|
|
95
|
+
return;
|
|
96
|
+
this.ultimoRefresco = ahora;
|
|
97
|
+
if (!process.stdout.isTTY)
|
|
98
|
+
return;
|
|
99
|
+
const segundos = Math.round((ahora - this.inicio) / 1000);
|
|
100
|
+
const ancho = Math.max(20, (process.stdout.columns || 80) - 4);
|
|
101
|
+
const cabeza = ` ${chalk.hex("#8B5CF6")("💭")} ${chalk.gray(`pensando… ${segundos}s`)}`;
|
|
102
|
+
// La ultima frase a medias da una pista de por donde va sin contarlo todo.
|
|
103
|
+
const pista = unaLinea(this.cola, Math.max(0, ancho - 24));
|
|
104
|
+
const linea = pista ? `${cabeza} ${chalk.dim("·")} ${chalk.dim(pista)}` : cabeza;
|
|
105
|
+
process.stdout.write(`\r\x1b[2K${unaLinea(linea, ancho + 40)}`);
|
|
106
|
+
this.lineaPintada = true;
|
|
27
107
|
}
|
|
28
108
|
flush() {
|
|
29
|
-
if (this.hasStarted)
|
|
109
|
+
if (!this.hasStarted)
|
|
110
|
+
return;
|
|
111
|
+
if (COMPLETO) {
|
|
30
112
|
if (!this.atLineStart) {
|
|
31
113
|
process.stdout.write("\n");
|
|
32
114
|
this.atLineStart = true;
|
|
33
115
|
}
|
|
34
116
|
process.stdout.write("\n");
|
|
35
117
|
this.hasStarted = false;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const segundos = Math.max(1, Math.round((Date.now() - this.inicio) / 1000));
|
|
121
|
+
if (this.lineaPintada) {
|
|
122
|
+
// Se deja una sola linea de resumen donde estaba la que iba cambiando.
|
|
123
|
+
process.stdout.write(`\r\x1b[2K ${chalk.hex("#8B5CF6")("💭")} ${chalk.gray(`pensó ${segundos}s`)}\n`);
|
|
36
124
|
}
|
|
125
|
+
this.lineaPintada = false;
|
|
126
|
+
this.hasStarted = false;
|
|
127
|
+
this.cola = "";
|
|
128
|
+
this.caracteres = 0;
|
|
37
129
|
}
|
|
38
130
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Pinta encima del terminal. En produccion es `process.stdout.write` y nada mas. */
|
|
2
|
+
export declare function dibujar(texto: string): void;
|
|
3
|
+
/**
|
|
4
|
+
* Desvia el dibujo. Con `null` vuelve al terminal.
|
|
5
|
+
*
|
|
6
|
+
* Solo lo usan las pruebas, y siempre devolviendolo en un `finally`: si se
|
|
7
|
+
* queda desviado, las pruebas que vengan detras dibujan en el vacio sin
|
|
8
|
+
* enterarse.
|
|
9
|
+
*/
|
|
10
|
+
export declare function redirigirDibujo(destino: ((texto: string) => void) | null): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
/**
|
|
3
|
+
* Por donde sale lo que se pinta encima del terminal.
|
|
4
|
+
*
|
|
5
|
+
* Aqui va SOLO el dibujo que ocupa el terminal a la fuerza: el marco del
|
|
6
|
+
* prompt, los avisos que se escriben pisando la linea actual con \r y \x1b[2K.
|
|
7
|
+
* Un `console.log` normal no pasa por aqui y no tiene por que.
|
|
8
|
+
*
|
|
9
|
+
* POR QUE EXISTE
|
|
10
|
+
*
|
|
11
|
+
* `npm publish` corre `prepublishOnly` -> `npm test`, y las pruebas del teclado
|
|
12
|
+
* abren el prompt de verdad. Con un terminal delante, el prompt mueve el
|
|
13
|
+
* cursor, borra lineas y repinta su marco encima de la sesion de quien esta
|
|
14
|
+
* publicando. En un `npm publish` real se vio esto:
|
|
15
|
+
*
|
|
16
|
+
* ❯ hola mundo
|
|
17
|
+
* ↳ en cola: y ahora los tests
|
|
18
|
+
*
|
|
19
|
+
* El terminal dejaba de ser suyo y la publicacion no arrancaba. Hubo que
|
|
20
|
+
* publicar con --ignore-scripts, saltandose las 488 pruebas; una red de
|
|
21
|
+
* seguridad que hay que apagar para trabajar no es una red.
|
|
22
|
+
*
|
|
23
|
+
* POR QUE NO SE SILENCIA `process.stdout` DESDE FUERA
|
|
24
|
+
*
|
|
25
|
+
* Se intento de las dos formas y las dos rompen algo peor. `node --test`
|
|
26
|
+
* instrumenta process.stdout para asociar cada linea a la prueba que la
|
|
27
|
+
* produjo: al sustituir el objeto -o solo su `write`- las pruebas se ejecutan
|
|
28
|
+
* pero desaparecen del informe, sin fallar y sin dejar rastro. Medido: de 6
|
|
29
|
+
* pruebas pasaban a contarse 2, en verde. Una prueba que se evapora es peor que
|
|
30
|
+
* una que falla.
|
|
31
|
+
*
|
|
32
|
+
* Con un conducto propio, process.stdout no se toca nunca.
|
|
33
|
+
*/
|
|
34
|
+
let conducto = (texto) => {
|
|
35
|
+
process.stdout.write(texto);
|
|
36
|
+
};
|
|
37
|
+
/** Pinta encima del terminal. En produccion es `process.stdout.write` y nada mas. */
|
|
38
|
+
export function dibujar(texto) {
|
|
39
|
+
conducto(texto);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Desvia el dibujo. Con `null` vuelve al terminal.
|
|
43
|
+
*
|
|
44
|
+
* Solo lo usan las pruebas, y siempre devolviendolo en un `finally`: si se
|
|
45
|
+
* queda desviado, las pruebas que vengan detras dibujan en el vacio sin
|
|
46
|
+
* enterarse.
|
|
47
|
+
*/
|
|
48
|
+
export function redirigirDibujo(destino) {
|
|
49
|
+
conducto = destino ?? ((texto) => { process.stdout.write(texto); });
|
|
50
|
+
}
|
package/extension/README.md
CHANGED
|
@@ -1,74 +1,95 @@
|
|
|
1
|
-
# Extensión de Chocolatito Code para Chrome
|
|
2
|
-
|
|
3
|
-
Es lo que permite que el agente trabaje **dentro de tu Chrome**, con tus cuentas ya
|
|
4
|
-
iniciadas y **sin quitarte la pantalla**. Puedes seguir jugando o trabajando mientras
|
|
5
|
-
opera en una pestaña que ni siquiera está visible.
|
|
6
|
-
|
|
7
|
-
## Instalación (una sola vez)
|
|
8
|
-
|
|
9
|
-
1. Abre `chrome://extensions`
|
|
10
|
-
2. Activa **Modo de desarrollador** (interruptor arriba a la derecha)
|
|
11
|
-
3. Pulsa **Cargar descomprimida**
|
|
12
|
-
4. Selecciona esta carpeta:
|
|
13
|
-
`C:\Users\Ryzen\Documents\tomas\chocolatito-code\extension`
|
|
14
|
-
|
|
15
|
-
Listo. El icono mostrará el distintivo `on` cuando la CLI esté abierta.
|
|
16
|
-
|
|
17
|
-
> **No uses el flag `--load-extension`.** Chrome 137 lo eliminó por abuso de malware;
|
|
18
|
-
> hoy no hace nada. Y si añades `--disable-extensions-except`, Chrome desactiva todas
|
|
19
|
-
> las extensiones, incluida esta.
|
|
20
|
-
|
|
21
|
-
## Cómo saber que está funcionando
|
|
22
|
-
|
|
23
|
-
- El icono de la extensión muestra
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
│
|
|
47
|
-
│
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
│
|
|
54
|
-
│ ·
|
|
55
|
-
│ ·
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
# Extensión de Chocolatito Code para Chrome
|
|
2
|
+
|
|
3
|
+
Es lo que permite que el agente trabaje **dentro de tu Chrome**, con tus cuentas ya
|
|
4
|
+
iniciadas y **sin quitarte la pantalla**. Puedes seguir jugando o trabajando mientras
|
|
5
|
+
opera en una pestaña que ni siquiera está visible.
|
|
6
|
+
|
|
7
|
+
## Instalación (una sola vez)
|
|
8
|
+
|
|
9
|
+
1. Abre `chrome://extensions`
|
|
10
|
+
2. Activa **Modo de desarrollador** (interruptor arriba a la derecha)
|
|
11
|
+
3. Pulsa **Cargar descomprimida**
|
|
12
|
+
4. Selecciona esta carpeta:
|
|
13
|
+
`C:\Users\Ryzen\Documents\tomas\chocolatito-code\extension`
|
|
14
|
+
|
|
15
|
+
Listo. El icono mostrará el distintivo `on` cuando la CLI esté abierta.
|
|
16
|
+
|
|
17
|
+
> **No uses el flag `--load-extension`.** Chrome 137 lo eliminó por abuso de malware;
|
|
18
|
+
> hoy no hace nada. Y si añades `--disable-extensions-except`, Chrome desactiva todas
|
|
19
|
+
> las extensiones, incluida esta.
|
|
20
|
+
|
|
21
|
+
## Cómo saber que está funcionando
|
|
22
|
+
|
|
23
|
+
- El icono de la extensión muestra el **número de sesiones** conectadas a la CLI, y al
|
|
24
|
+
pulsarlo el panel lo dice con palabras.
|
|
25
|
+
- La pestaña en la que trabaja entra en un **grupo naranja "Chocolatito Code"**.
|
|
26
|
+
- En esa página aparece un **aviso flotante** abajo a la derecha con lo que está
|
|
27
|
+
haciendo en cada momento ("leyendo la pagina", "escribiendo", …).
|
|
28
|
+
- Al terminar, `chrome(done)` deshace el grupo y retira el aviso.
|
|
29
|
+
|
|
30
|
+
## Por qué hace falta una extensión
|
|
31
|
+
|
|
32
|
+
| Vía | ¿Tus sesiones? | ¿Segundo plano real? |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| CDP con puerto de depuración (`browser`) | No — Chrome 136+ lo bloquea en el perfil por defecto | Sí, pero en un perfil vacío |
|
|
35
|
+
| UI Automation + ratón y teclado (`computer_use`) | Sí | No — para escribir hay que traer la ventana al frente |
|
|
36
|
+
| **Extensión (`chrome`)** | **Sí** | **Sí** |
|
|
37
|
+
|
|
38
|
+
Las dos primeras fallan justo en la casilla que importa. La extensión corre dentro del
|
|
39
|
+
navegador, así que no necesita foco ni ratón, y hereda tu sesión porque *es* tu Chrome.
|
|
40
|
+
|
|
41
|
+
## Arquitectura
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
CLI (chocolatito) Chrome
|
|
45
|
+
┌──────────────────┐ ┌─────────────────────────────┐
|
|
46
|
+
│ browserExtension │ WebSocket │ background.js │
|
|
47
|
+
│ servidor local │◄──────────────►│ service worker │
|
|
48
|
+
│ 127.0.0.1:8787 │ │ · pestañas y grupos │
|
|
49
|
+
└──────────────────┘ │ · captura por CDP │
|
|
50
|
+
└───────────┬─────────────────┘
|
|
51
|
+
│ chrome.tabs.sendMessage
|
|
52
|
+
┌───────────▼─────────────────┐
|
|
53
|
+
│ content.js (en la página) │
|
|
54
|
+
│ · snapshot con refs │
|
|
55
|
+
│ · clic y escritura │
|
|
56
|
+
│ · aviso flotante │
|
|
57
|
+
└─────────────────────────────┘
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
El servidor escucha **solo en 127.0.0.1** y rechaza cualquier conexión cuyo origen no
|
|
61
|
+
sea `chrome-extension://`, para que una página web no pueda hablar con él.
|
|
62
|
+
|
|
63
|
+
## Detalles que costó descubrir
|
|
64
|
+
|
|
65
|
+
- **Escribir con `el.value = ...` no funciona en React.** El texto se ve pero el estado
|
|
66
|
+
interno del framework sigue vacío, así que el botón de enviar queda deshabilitado.
|
|
67
|
+
Hay que llamar al *setter nativo* del prototipo y emitir `input`. En campos
|
|
68
|
+
`contenteditable`, `document.execCommand("insertText")`.
|
|
69
|
+
- **El snapshot atraviesa shadow DOM.** Sin eso, en las apps de Google se ve media
|
|
70
|
+
interfaz.
|
|
71
|
+
- **Las capturas van por `chrome.debugger`**, no por `chrome.tabs.captureVisibleTab`:
|
|
72
|
+
esta última exige que la pestaña esté activa y la ventana enfocada, justo lo que
|
|
73
|
+
queremos evitar.
|
|
74
|
+
- **El service worker se duerme.** Un ping cada 20 s mantiene viva la conexión, pero
|
|
75
|
+
Chrome puede apagarlo igual. Cuando eso pasa desaparecen la pestaña de trabajo, el
|
|
76
|
+
grupo y el índice de referencias, y todo empieza a fallar con "el ref 3 no existe"
|
|
77
|
+
aunque en pantalla siga igual. Por eso el estado se apunta en `storage.session` y la
|
|
78
|
+
CLI se identifica con un `sessionId` al conectar: mismo id, se recupera lo suyo; id
|
|
79
|
+
distinto, se empieza limpio (heredar la pestaña de otra sesión es como se acaba
|
|
80
|
+
actuando sobre una pestaña ajena).
|
|
81
|
+
- **Esperar `status === "complete"` no basta.** Justo después de pedir una navegación, la
|
|
82
|
+
pestaña todavía informa del estado de la página *anterior*: hay que ver primero la
|
|
83
|
+
transición (estado `loading` o cambio de URL). Sin eso, `navigate` volvía al instante y
|
|
84
|
+
el snapshot siguiente leía la página que se acababa de abandonar, sin dar ningún error.
|
|
85
|
+
- **`chrome.debugger.attach` es exclusivo por pestaña.** Con varias sesiones hablando por
|
|
86
|
+
el mismo service worker, dos escrituras a la vez se pisaban. Los adjuntos van en cola
|
|
87
|
+
por pestaña, y las coordenadas del elemento se miden **dentro** del turno: medirlas
|
|
88
|
+
antes significa medirlas antes de que la acción anterior mueva la página.
|
|
89
|
+
- **Hay páginas donde ninguna extensión puede entrar** (`chrome://`, la Web Store,
|
|
90
|
+
`view-source:`). Se dice con esas palabras en vez de devolver el "Cannot access
|
|
91
|
+
contents of the page" de Chrome, que no explica ni que es culpa de la URL ni que no
|
|
92
|
+
hay que reintentarlo.
|
|
93
|
+
- **Solo se cierra lo que se abrió.** Cada sesión lleva la lista de las pestañas que
|
|
94
|
+
abrió con `open` o eligió con `select`; mirar una pestaña (`snapshot`, `getText`) nunca
|
|
95
|
+
da derecho a cerrarla.
|