@luanpdd/kit-mcp 1.5.0 → 1.5.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/CHANGELOG.md +32 -0
- package/bin/ui.js +1 -1
- package/package.json +1 -1
- package/src/cli/index.js +1 -1
- package/src/ui/server.js +1 -1
- package/src/ui/static/index.html +56 -9
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,38 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.5.2] - 2026-05-05
|
|
10
|
+
|
|
11
|
+
Patch de lifecycle: sidecar não desliga mais sozinho por idle.
|
|
12
|
+
|
|
13
|
+
### Corrigido
|
|
14
|
+
|
|
15
|
+
- **Sidecar encerrava sozinho após 30min** mesmo com a aba aberta sem eventos. Default de `idleMs` mudou de `30 * 60 * 1000` (30min) para `0` (nunca encerra). Resolve "abro a sidecar pra acompanhar trabalho longo, saio almoçar, volto e tá morta". Quem quiser o comportamento antigo: `kit ui start --idle-ms 1800000`.
|
|
16
|
+
|
|
17
|
+
### Sem mudanças de API
|
|
18
|
+
|
|
19
|
+
Patch isolado em `src/ui/server.js`. Stable API v1.0+ preservada.
|
|
20
|
+
|
|
21
|
+
### Heads-up
|
|
22
|
+
|
|
23
|
+
Se você tem `@luanpdd/kit-mcp` instalado globalmente (`npm i -g`) e `kit ui start` está dando "unknown command 'ui'", a versão global está stale. Atualize com `npm i -g @luanpdd/kit-mcp@latest`.
|
|
24
|
+
|
|
25
|
+
## [1.5.1] - 2026-05-05
|
|
26
|
+
|
|
27
|
+
Patch da UI sidecar: auto-reconnect quando o server reinicia + bordas com respiro.
|
|
28
|
+
|
|
29
|
+
### Corrigido
|
|
30
|
+
|
|
31
|
+
- **UI fica presa em "desconectado" quando server volta.** O `EventSource` nativo às vezes estagna no estado `CONNECTING` mesmo depois do server voltar — usuário precisava recarregar a aba. Agora um poll do `/healthz` a cada 3s roda em paralelo: ao detectar 200, fecha o `EventSource` antigo, hidrata `/state`, e abre um novo. Funciona pra qualquer cenário (kill -9, `kit ui stop` + `kit ui start`, network blip, máquina suspended). Usuário **não precisa mais recarregar** — basta o server voltar.
|
|
32
|
+
|
|
33
|
+
- **Banner "Sidecar encerrou" persistia mesmo após reconnect.** Race entre o handler de shutdown e o poll de saúde podia deixar o banner visível mesmo com a conexão de volta. Agora `applyConnState("open")` sempre remove o banner — estado saudável significa que o aviso está stale.
|
|
34
|
+
|
|
35
|
+
- **Cropping nas bordas da timeline:** "há 22m" colado na borda esquerda e `runId`/tokens-chip cortados na direita. `.tl-row` ganhou `padding: var(--pad-tight) 12px`. `.tl-time` virou `padding-right: 8px`. `.tl-content` ganhou `padding-right: 4px` + `overflow: hidden`. Tokens-chip e tl-runid agora têm `flex-shrink: 0` explícito pra não encolher quando a mensagem ocupa muita largura.
|
|
36
|
+
|
|
37
|
+
### Sem mudanças de API
|
|
38
|
+
|
|
39
|
+
Patch puro de UI. `src/ui/static/index.html` e `test/integration/ui-static.test.js` apenas. Stable API v1.0+ preservada.
|
|
40
|
+
|
|
9
41
|
## [1.5.0] - 2026-05-05
|
|
10
42
|
|
|
11
43
|
UI sidecar — bug fixes visuais + tokens + histórico de sessão.
|
package/bin/ui.js
CHANGED
|
@@ -33,7 +33,7 @@ if (args.help) {
|
|
|
33
33
|
'Usage: node bin/ui.js [options]',
|
|
34
34
|
' --project-root <path> project root for lockfile keying (default: cwd)',
|
|
35
35
|
' --port <n> bind to a specific port (default: auto-pick 7100-7199)',
|
|
36
|
-
' --idle-ms <ms> idle shutdown timeout (default:
|
|
36
|
+
' --idle-ms <ms> idle shutdown timeout (default: 0 = never; pass e.g. 1800000 for 30min)',
|
|
37
37
|
' --version print version and exit',
|
|
38
38
|
' --help this text',
|
|
39
39
|
'',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luanpdd/kit-mcp",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "Generic infrastructure to ship YOUR personal kit of agents/commands/skills as an MCP server, with cross-IDE sync (Claude Code, Cursor, Codex, Gemini, Windsurf, Antigravity, Copilot, Trae).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/cli/index.js
CHANGED
|
@@ -379,7 +379,7 @@ ui.command('start')
|
|
|
379
379
|
.description('Start the sidecar HTTP server in foreground (Ctrl+C to stop). Prints URL on stderr.')
|
|
380
380
|
.option('--project-root <path>', 'Project root for lockfile keying (default: cwd)')
|
|
381
381
|
.option('--port <n>', 'Bind to a specific port (default: auto-pick 7100-7199)')
|
|
382
|
-
.option('--idle-ms <ms>', 'Idle shutdown timeout (default
|
|
382
|
+
.option('--idle-ms <ms>', 'Idle shutdown timeout (default 0 = never; e.g. 1800000 for 30min)')
|
|
383
383
|
.option('--no-open', 'Skip auto-opening the browser')
|
|
384
384
|
.action(async (opts) => {
|
|
385
385
|
const projectRoot = opts.projectRoot || process.cwd();
|
package/src/ui/server.js
CHANGED
|
@@ -33,7 +33,7 @@ const HOST = '127.0.0.1';
|
|
|
33
33
|
const HEARTBEAT_INTERVAL_MS = 15_000;
|
|
34
34
|
const RING_BUFFER_SIZE = 200;
|
|
35
35
|
const MAX_SSE_SUBSCRIBERS = 32;
|
|
36
|
-
const DEFAULT_IDLE_MS =
|
|
36
|
+
const DEFAULT_IDLE_MS = 0; // never auto-shutdown by default — pass --idle-ms 1800000 to opt back in
|
|
37
37
|
|
|
38
38
|
const SSE_HEADERS = {
|
|
39
39
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
package/src/ui/static/index.html
CHANGED
|
@@ -483,9 +483,9 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
|
|
|
483
483
|
|
|
484
484
|
.tl-row {
|
|
485
485
|
display: grid;
|
|
486
|
-
grid-template-columns: 64px 18px 1fr;
|
|
486
|
+
grid-template-columns: 64px 18px minmax(0, 1fr);
|
|
487
487
|
gap: 0;
|
|
488
|
-
padding: var(--pad-tight)
|
|
488
|
+
padding: var(--pad-tight) 12px;
|
|
489
489
|
position: relative;
|
|
490
490
|
border-radius: 4px;
|
|
491
491
|
transition: background .15s var(--ease);
|
|
@@ -514,8 +514,9 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
|
|
|
514
514
|
font-size: 11px;
|
|
515
515
|
color: var(--text-3);
|
|
516
516
|
font-variant-numeric: tabular-nums;
|
|
517
|
-
padding-left: 4px;
|
|
518
517
|
padding-top: 1px;
|
|
518
|
+
padding-right: 8px;
|
|
519
|
+
text-align: left;
|
|
519
520
|
}
|
|
520
521
|
|
|
521
522
|
/* rail with node */
|
|
@@ -552,14 +553,17 @@ button { font: inherit; color: inherit; background: none; border: 0; cursor: poi
|
|
|
552
553
|
.tl-row[data-type="progress"] .tl-node { width: 5px; height: 5px; margin-top: 6px; }
|
|
553
554
|
|
|
554
555
|
/* group runId connector — visually subtle indent */
|
|
555
|
-
.tl-row[data-grouped="true"] { padding-left: 0; }
|
|
556
556
|
.tl-row[data-grouped="true"] .tl-node { background: var(--text-3); }
|
|
557
557
|
|
|
558
558
|
.tl-content {
|
|
559
559
|
display: flex; align-items: baseline; gap: 8px;
|
|
560
560
|
padding-left: 8px;
|
|
561
|
+
padding-right: 4px;
|
|
561
562
|
min-width: 0;
|
|
563
|
+
overflow: hidden;
|
|
562
564
|
}
|
|
565
|
+
.tl-content > .tokens-chip,
|
|
566
|
+
.tl-content > .tl-runid { flex-shrink: 0; }
|
|
563
567
|
|
|
564
568
|
.tl-badge {
|
|
565
569
|
font-family: var(--mono);
|
|
@@ -1846,6 +1850,12 @@ function applyConnState(s) {
|
|
|
1846
1850
|
els.conn.dataset.state = "on";
|
|
1847
1851
|
els.connLabel.textContent = "conectado";
|
|
1848
1852
|
if (els.srcLabel) els.srcLabel.textContent = "ao vivo";
|
|
1853
|
+
// Healthy => banner stale. Independente de qual caminho colocou ele lá
|
|
1854
|
+
// (shutdown event antigo, race, hydrate trazendo shutdown histórico),
|
|
1855
|
+
// estar conectado significa que o aviso "Reabra com kit ui start" não
|
|
1856
|
+
// serve mais — remova sempre.
|
|
1857
|
+
const banner = document.getElementById("shutdown-banner");
|
|
1858
|
+
if (banner) banner.remove();
|
|
1849
1859
|
} else if (s === "connecting") {
|
|
1850
1860
|
els.conn.dataset.state = "off";
|
|
1851
1861
|
els.connLabel.textContent = "conectando";
|
|
@@ -1893,11 +1903,16 @@ function connectRealSource() {
|
|
|
1893
1903
|
evtSource.addEventListener("open", () => {
|
|
1894
1904
|
lastConnectedAt = Date.now();
|
|
1895
1905
|
applyConnState("open");
|
|
1906
|
+
stopHealthPoll(); // já tá conectado, não precisa pollar
|
|
1896
1907
|
});
|
|
1897
1908
|
|
|
1898
1909
|
evtSource.addEventListener("error", () => {
|
|
1899
|
-
// EventSource auto-retries;
|
|
1910
|
+
// EventSource auto-retries; nós só atualizamos o estado visível e iniciamos
|
|
1911
|
+
// o poll-de-saúde paralelo, que cobre o caso "server reiniciou" — o native
|
|
1912
|
+
// retry às vezes gruda em CONNECTING sem nunca virar OPEN, e o poll detecta
|
|
1913
|
+
// quando o /healthz volta a responder e força um connectRealSource() limpo.
|
|
1900
1914
|
applyConnState("closed");
|
|
1915
|
+
startHealthPoll();
|
|
1901
1916
|
});
|
|
1902
1917
|
|
|
1903
1918
|
// Listen for each typed event the server emits.
|
|
@@ -1907,6 +1922,9 @@ function connectRealSource() {
|
|
|
1907
1922
|
if (data && data.type === "shutdown") {
|
|
1908
1923
|
applyConnState("shutdown");
|
|
1909
1924
|
showShutdownBanner();
|
|
1925
|
+
// Mesmo após shutdown, um novo `kit ui start` faz o server voltar.
|
|
1926
|
+
// Continue tentando reconectar — usuário não precisa recarregar a aba.
|
|
1927
|
+
startHealthPoll();
|
|
1910
1928
|
}
|
|
1911
1929
|
ingest(data);
|
|
1912
1930
|
} catch (_) { /* swallow malformed */ }
|
|
@@ -1917,12 +1935,41 @@ function connectRealSource() {
|
|
|
1917
1935
|
evtSource.onmessage = handler; // fallback for unnamed
|
|
1918
1936
|
}
|
|
1919
1937
|
|
|
1920
|
-
/*
|
|
1921
|
-
|
|
1922
|
-
|
|
1938
|
+
/* ───────────────────── health-poll auto-reconnect ─────────────────────
|
|
1939
|
+
Quando o EventSource cai e fica preso (server reiniciado, sidecar `kit ui
|
|
1940
|
+
stop` seguido de `kit ui start`, network blip), o native retry pode estagnar
|
|
1941
|
+
em CONNECTING. Esse poll roda em segundo plano em paralelo: quando o /healthz
|
|
1942
|
+
volta a responder 200, fechamos o EventSource antigo, hidratamos /state, e
|
|
1943
|
+
abrimos um novo. Para de pollar assim que conectamos com sucesso. */
|
|
1944
|
+
let healthPollTimer = null;
|
|
1945
|
+
function startHealthPoll() {
|
|
1946
|
+
if (healthPollTimer) return;
|
|
1947
|
+
healthPollTimer = setInterval(async () => {
|
|
1948
|
+
if (els.conn.dataset.state === "on") { stopHealthPoll(); return; }
|
|
1949
|
+
try {
|
|
1950
|
+
const res = await fetch("/healthz", { credentials: "omit", cache: "no-store" });
|
|
1951
|
+
if (res.ok) {
|
|
1952
|
+
stopHealthPoll();
|
|
1953
|
+
// Banner de shutdown é stale agora que o server voltou — remova.
|
|
1954
|
+
const banner = document.getElementById("shutdown-banner");
|
|
1955
|
+
if (banner) banner.remove();
|
|
1956
|
+
await hydrateFromState();
|
|
1957
|
+
connectRealSource();
|
|
1958
|
+
}
|
|
1959
|
+
} catch (_) { /* server ainda não voltou — keep tentando */ }
|
|
1960
|
+
}, 3000);
|
|
1961
|
+
}
|
|
1962
|
+
function stopHealthPoll() {
|
|
1963
|
+
if (healthPollTimer) { clearInterval(healthPollTimer); healthPollTimer = null; }
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
/* Background-tab recovery — quando o tab volta a ficar visível e estamos fora
|
|
1967
|
+
do estado conectado, força um reconnect imediato em vez de esperar o próximo
|
|
1968
|
+
tick de health poll. */
|
|
1923
1969
|
document.addEventListener("visibilitychange", () => {
|
|
1924
1970
|
if (document.visibilityState !== "visible") return;
|
|
1925
|
-
if (els.conn.dataset.state
|
|
1971
|
+
if (els.conn.dataset.state !== "on") {
|
|
1972
|
+
stopHealthPoll();
|
|
1926
1973
|
hydrateFromState().then(connectRealSource);
|
|
1927
1974
|
}
|
|
1928
1975
|
});
|