paris-immersion 0.1.4 → 0.1.6
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/dist/index.js +1 -1
- package/dist/lib/relay-agent.js +40 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { serveCommand } from './commands/serve.js';
|
|
|
12
12
|
const program = new Command()
|
|
13
13
|
.name('paris')
|
|
14
14
|
.description('Paris Immersion — CLI do aluno (login, workspace, tarefas)')
|
|
15
|
-
.version('0.1.
|
|
15
|
+
.version('0.1.6');
|
|
16
16
|
program
|
|
17
17
|
.command('login')
|
|
18
18
|
.description('Autenticar com sua conta Paris (device-code OR --token direto)')
|
package/dist/lib/relay-agent.js
CHANGED
|
@@ -72,12 +72,42 @@ export async function startRelayAgent(opts) {
|
|
|
72
72
|
// "no-op" (reconexão com mesma dimensão) e forçar o Claude a repintar.
|
|
73
73
|
let ptyCols = 80;
|
|
74
74
|
let ptyRows = 24;
|
|
75
|
+
// Coalescing da saída do pty: o Claude faz streaming token-a-token (~20-30ms
|
|
76
|
+
// entre tokens). Com janela curta, cada token vira um frame (1 AES-GCM + 1
|
|
77
|
+
// frame WS + 1 round-trip + 1 render) → escrita picotada/lenta. Uma janela
|
|
78
|
+
// maior junta VÁRIOS tokens por frame → escrita lisa, muito menos overhead.
|
|
79
|
+
// O custo é ~Xms a mais de latência — imperceptível pra leitura do curso.
|
|
80
|
+
// Ajustável por env (PARIS_RELAY_COALESCE_MS) pra afinar sem republicar.
|
|
81
|
+
const COALESCE_MS = Math.min(250, Math.max(0, Number(process.env.PARIS_RELAY_COALESCE_MS) || 55));
|
|
82
|
+
const MAX_OUT_BUF = 256 * 1024;
|
|
83
|
+
let outBuf = '';
|
|
84
|
+
let flushTimer;
|
|
85
|
+
const flushOut = () => {
|
|
86
|
+
flushTimer = undefined;
|
|
87
|
+
if (!outBuf)
|
|
88
|
+
return;
|
|
89
|
+
const data = outBuf;
|
|
90
|
+
outBuf = '';
|
|
91
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
92
|
+
try {
|
|
93
|
+
ws.send(seal(key, TYPE_DATA, Buffer.from(data, 'utf8')), { binary: true });
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* ws caiu no meio */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
75
100
|
// Mantém o Claude vivo por uma janela após o viewer sair, pra que uma
|
|
76
101
|
// reconexão (ex.: Safari derruba o ws periodicamente) REATACHE na MESMA sessão
|
|
77
102
|
// em vez de reiniciar o curso. O resize que o viewer manda ao reconectar faz a
|
|
78
103
|
// TUI do Claude repintar a tela atual.
|
|
79
104
|
const PTY_GRACE_MS = 600_000; // 10min — aluno que sai no meio e volta reatacha na MESMA sessão
|
|
80
105
|
const killPty = () => {
|
|
106
|
+
if (flushTimer) {
|
|
107
|
+
clearTimeout(flushTimer);
|
|
108
|
+
flushTimer = undefined;
|
|
109
|
+
}
|
|
110
|
+
outBuf = '';
|
|
81
111
|
if (pty) {
|
|
82
112
|
try {
|
|
83
113
|
pty.kill();
|
|
@@ -99,13 +129,17 @@ export async function startRelayAgent(opts) {
|
|
|
99
129
|
return;
|
|
100
130
|
}
|
|
101
131
|
pty.onData((d) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
outBuf += d;
|
|
133
|
+
// Flush imediato se acumulou muito (evita um frame gigante); senão coalesce.
|
|
134
|
+
if (outBuf.length >= MAX_OUT_BUF) {
|
|
135
|
+
if (flushTimer) {
|
|
136
|
+
clearTimeout(flushTimer);
|
|
137
|
+
flushTimer = undefined;
|
|
108
138
|
}
|
|
139
|
+
flushOut();
|
|
140
|
+
}
|
|
141
|
+
else if (!flushTimer) {
|
|
142
|
+
flushTimer = setTimeout(flushOut, COALESCE_MS);
|
|
109
143
|
}
|
|
110
144
|
});
|
|
111
145
|
pty.onExit?.(() => killPty());
|
package/package.json
CHANGED