engine-kit 0.2.2 → 0.2.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/CONTRATO-MOTOR.md +4 -0
- package/README.md +8 -4
- package/kit/lifecycle.mjs +20 -4
- package/kit/provision.mjs +26 -22
- package/manifest.json +71 -4
- package/package.json +2 -2
package/CONTRATO-MOTOR.md
CHANGED
|
@@ -157,6 +157,10 @@ polui o histórico e obriga todo consumidor a decidir se atualiza — para desco
|
|
|
157
157
|
| `embed-house` | daemon | `embed-house` | `engine-registry` ✅ | ✅ | `copilot-marketplace` |
|
|
158
158
|
| `neural-link` | **cli** | `neural-link` | `engine-registry` ✅ | ✅ | `neural-link-runtime` (redirecionado) |
|
|
159
159
|
| `vox-engine` | daemon (`pipe`), **self-managed** | `vox-engine` | `engine-registry` ✅ | ✅ | `copilot-marketplace` |
|
|
160
|
+
| `vxk` | **cli** (shim stdio; daemon único próprio, desligado pelo `ensure` via `runtime.shutdownPath`) | `visual-explainer` | `engine-registry` ✅ | ✅ | — (nunca esteve lá) |
|
|
161
|
+
| `cerne` | daemon, **self-managed** | `cerne` | `engine-registry` ✅ | ✅ | — (nunca esteve lá) |
|
|
162
|
+
| `skill-engine` | **cli** | `skill-engine` | `engine-registry` ✅ | ✅ | — (nunca esteve lá) |
|
|
163
|
+
| `client-harness` | **cli** (daemon único próprio, desligado pelo `ensure` via `runtime.shutdownPath`) | `harness-padroes-cliente` | `engine-registry` ✅ | ✅ | — (nunca esteve lá) |
|
|
160
164
|
|
|
161
165
|
Cada repo de motor tem o próprio `publish.ps1`, que **builda** o que é específico dele e **delega**
|
|
162
166
|
ao `kit/publish-base.ps1` a parte comum (identidade do `gh`, assinatura, release, manifest, prova).
|
package/README.md
CHANGED
|
@@ -29,9 +29,10 @@ vive num lugar só e o consumidor apenas **declara a dependência**.
|
|
|
29
29
|
| `vox-engine` | 0.28.7 | `daemon` (pipe), **self-managed** | Motor de voz (STT/TTS) compartilhado, com auto-unload quando ocioso. Traz o próprio instalador e updater. |
|
|
30
30
|
| `mcp-gateway` | 0.16.4 | `daemon` (http) | Agregador MCP: conecta uma vez em cada servidor MCP e reexpõe tudo num endpoint único. |
|
|
31
31
|
| `neural-link` | 0.7.15 | `cli` | Dispatcher adaptativo de hooks: recebe o evento por stdin, pontua os handlers e devolve uma decisão composta. |
|
|
32
|
-
| `vxk` | 0.
|
|
32
|
+
| `vxk` | 0.19.0 | `cli` | Explicações visuais animadas e narradas como servidor MCP (stdio): devolve UM HTML autocontido e offline, ou o vídeo MP4 (preset, aplicativo real, Motion) e o PDF. Um daemon único por máquina atende os agentes. A narração vem do `vox-engine`. |
|
|
33
33
|
| `cerne` | 0.4.10 | `daemon`, **self-managed** | Leitura ativa de vídeo (captura, quadros, transcrição, tradução) exposta via MCP. Instalador Inno por usuário, Windows. |
|
|
34
34
|
| `skill-engine` | 0.1.0 | `cli` | Gerencia skills de agentes fora de qualquer IDE: busca repositórios em quarentena, varre com regras determinísticas e só ativa o que sai limpo. |
|
|
35
|
+
| `client-harness` | 0.1.1 | `cli` | Daemon local que faz o Claude Code e o Codex respeitarem os padrões de cada cliente: confere cada ação do agente contra as regras da pasta e recusa antes de gravar, com o motivo. |
|
|
35
36
|
|
|
36
37
|
**Todos** são publicados aqui, assinados com Ed25519 e verificados antes de instalar.
|
|
37
38
|
|
|
@@ -42,7 +43,10 @@ Um motor pode ser de dois tipos (`kind`):
|
|
|
42
43
|
- **`cli`** — executável invocado **por evento** (ex.: um dispatcher de hooks). Não há processo
|
|
43
44
|
para manter vivo: provisionar já é entregar. O `lifecycle` devolve o caminho do binário sem
|
|
44
45
|
spawn, sem health e sem runtime — forçar um daemon aqui seria inventar um ciclo de vida que o
|
|
45
|
-
motor não tem.
|
|
46
|
+
motor não tem. Exceção: `vxk` (shim stdio por agente) e `client-harness` são `cli` mas delegam a
|
|
47
|
+
um daemon único por máquina. O kit não sobe nem espera esse daemon, mas o manifest declara `runtime`
|
|
48
|
+
(`run/daemon.json`, `shutdownPath`, token) para o `ensure` pedir o desligamento antes de trocar a
|
|
49
|
+
instalação — no Windows um daemon vivo segura a pasta e o swap falharia com EPERM.
|
|
46
50
|
|
|
47
51
|
E pode declarar `status: "self-managed"`: o motor traz o **próprio instalador/updater** (o caso do
|
|
48
52
|
`vox-engine`, um app Python). A entrada aqui é o **descritor de registro** — onde está a release,
|
|
@@ -55,7 +59,7 @@ Sem clonar o repositório, com Node 18 ou mais:
|
|
|
55
59
|
|
|
56
60
|
```sh
|
|
57
61
|
npx engine-kit ensure vxk # instala ou atualiza; reusa se já estiver em dia
|
|
58
|
-
npx engine-kit ensure vxk --version 0.
|
|
62
|
+
npx engine-kit ensure vxk --version 0.19.0 # pin de versão
|
|
59
63
|
```
|
|
60
64
|
|
|
61
65
|
O `npx` baixa o kit do npm e o kit baixa o motor da release, confere o **sha256**, a **assinatura Ed25519**
|
|
@@ -267,7 +271,7 @@ plataforma, e a release mora aqui, no registry público, com download **sem logi
|
|
|
267
271
|
|
|
268
272
|
| App | Versão | Plataformas | O que faz |
|
|
269
273
|
|---|---|---|---|
|
|
270
|
-
|
|
|
274
|
+
| `agent-launcher` | 1.2.11 | `win32-x64` | Abre Claude Code, Codex e Copilot CLI nos seus projetos por qualquer host de modelos. |
|
|
271
275
|
|
|
272
276
|
Um app só entra nesta tabela e em `manifest.apps` **depois** de a release pública existir e passar
|
|
273
277
|
em `node kit/verify-release.mjs --app <nome>` (download anônimo de todos os assets, `.sha256` e
|
package/kit/lifecycle.mjs
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
// • daemon — processo longo que anuncia `runtime.json` e responde health. É o fluxo abaixo.
|
|
9
9
|
// • cli — executável invocado POR EVENTO (ex.: o dispatcher de hooks): não há processo para
|
|
10
10
|
// manter vivo, nem runtime, nem health. Provisionar já é entregar. Forçar um daemon
|
|
11
|
-
// aqui seria inventar um ciclo de vida que o motor não tem.
|
|
11
|
+
// aqui seria inventar um ciclo de vida que o motor não tem. Exceção: o vxk é cli mas
|
|
12
|
+
// delega a um daemon próprio; o kit não o sobe, só o derruba (`shutdown`, via
|
|
13
|
+
// `runtime.shutdownPath`) antes de trocar a instalação.
|
|
12
14
|
import { spawn } from "node:child_process";
|
|
13
15
|
import { readFileSync, statSync } from "node:fs";
|
|
14
16
|
import { join } from "node:path";
|
|
@@ -63,6 +65,14 @@ async function findAlive(engine, healthCheck) {
|
|
|
63
65
|
return health ? { runtime: rt, health } : null;
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
/** Espera o processo sair (até `ms`). true = saiu. */
|
|
69
|
+
async function waitExit(pid, ms) {
|
|
70
|
+
for (const fim = Date.now() + ms; Date.now() < fim; await sleep(500)) {
|
|
71
|
+
try { process.kill(pid, 0); } catch { return true; }
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
/**
|
|
67
77
|
* Pede ao motor que se encerre, usando SÓ o que o registry declara.
|
|
68
78
|
*
|
|
@@ -74,7 +84,7 @@ async function findAlive(engine, healthCheck) {
|
|
|
74
84
|
*
|
|
75
85
|
* @returns {Promise<{ok:true, stopped:boolean} | {ok:false, reason:string}>} nunca lança.
|
|
76
86
|
*/
|
|
77
|
-
export async function shutdown(engine, { log = () => {}, timeoutMs = 5000 } = {}) {
|
|
87
|
+
export async function shutdown(engine, { log = () => {}, timeoutMs = 5000, waitBusyMs = 120000 } = {}) {
|
|
78
88
|
const path = engine.runtime?.shutdownPath;
|
|
79
89
|
if (!path) { return { ok: true, stopped: false }; }
|
|
80
90
|
|
|
@@ -93,10 +103,16 @@ export async function shutdown(engine, { log = () => {}, timeoutMs = 5000 } = {}
|
|
|
93
103
|
const res = await fetch(`http://127.0.0.1:${rt.port}${path}`, {
|
|
94
104
|
method: "POST", headers, signal: AbortSignal.timeout(timeoutMs),
|
|
95
105
|
});
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
// 202 = aceito, mas o motor está ocupado e só fecha quando o trabalho em curso acabar (vxk com build rodando);
|
|
107
|
+
// 503 = já está fechando. Nos dois o swap só é seguro depois que o processo sair.
|
|
108
|
+
const adiado = res.status === 202 || res.status === 503;
|
|
109
|
+
if (!res.ok && !adiado) { return { ok: false, reason: `${engine.name}: shutdown respondeu HTTP ${res.status}` }; }
|
|
110
|
+
log(`[engine-kit] ${engine.name}: shutdown pedido (pid ${rt.pid ?? "?"}, HTTP ${res.status})`);
|
|
98
111
|
// O motor responde ANTES de sair. Sem esta espera, o swap ainda encontra o arquivo em uso.
|
|
99
112
|
await sleep(1500);
|
|
113
|
+
if (adiado && rt.pid && !(await waitExit(rt.pid, waitBusyMs))) {
|
|
114
|
+
return { ok: false, reason: `${engine.name}: ainda ocupado após ${waitBusyMs / 1000} s (fecha ao fim do trabalho em curso); rode de novo depois` };
|
|
115
|
+
}
|
|
100
116
|
return { ok: true, stopped: true };
|
|
101
117
|
} catch (e) {
|
|
102
118
|
return { ok: false, reason: `${engine.name}: shutdown falhou (${e?.message || e})` };
|
package/kit/provision.mjs
CHANGED
|
@@ -16,6 +16,16 @@ import { checkTrust, trustPath } from "./trust.mjs";
|
|
|
16
16
|
|
|
17
17
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
18
18
|
|
|
19
|
+
// No Windows o handle de quem acabou de sair solta um instante depois: rename ocupado repete com backoff, depois lança.
|
|
20
|
+
export async function renameRetry(a, b, tries = 8) {
|
|
21
|
+
for (let i = 1; ; await sleep(100 * i++)) {
|
|
22
|
+
try { return renameSync(a, b); } catch (e) { if (i >= tries || !/^(EBUSY|EPERM|EACCES)$/.test(e.code)) { throw e; } }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const dropFile = (p) => { try { unlinkSync(p); } catch { /* ignore */ } };
|
|
27
|
+
const rmTree = (p) => rmSync(p, { recursive: true, force: true });
|
|
28
|
+
|
|
19
29
|
/** Lock mais velho que isto é considerado ÓRFÃO (processo morreu antes do finally). */
|
|
20
30
|
const LOCK_STALE_MS = 120000;
|
|
21
31
|
|
|
@@ -56,19 +66,13 @@ function extractTgz(tgz, dest) {
|
|
|
56
66
|
* `openSync` e o `finally` travaria todos os consumidores para sempre.
|
|
57
67
|
*/
|
|
58
68
|
function claimLock(lock) {
|
|
59
|
-
|
|
69
|
+
const open = () => {
|
|
60
70
|
const fd = openSync(lock, "wx");
|
|
61
71
|
try { writeFileSync(lock, String(process.pid)); } catch { /* diagnóstico é best-effort */ }
|
|
62
72
|
return fd;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
unlinkSync(lock);
|
|
67
|
-
const fd = openSync(lock, "wx");
|
|
68
|
-
try { writeFileSync(lock, String(process.pid)); } catch { /* ignore */ }
|
|
69
|
-
return fd;
|
|
70
|
-
}
|
|
71
|
-
} catch { /* outro consumidor ganhou a corrida */ }
|
|
73
|
+
};
|
|
74
|
+
try { return open(); } catch {
|
|
75
|
+
try { if (Date.now() - statSync(lock).mtimeMs > LOCK_STALE_MS) { unlinkSync(lock); return open(); } } catch { /* outro consumidor ganhou a corrida */ }
|
|
72
76
|
return null;
|
|
73
77
|
}
|
|
74
78
|
}
|
|
@@ -305,7 +309,7 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
305
309
|
// 3) Integridade (mismatch = apaga + ABORT).
|
|
306
310
|
const actual = sha256File(tgz);
|
|
307
311
|
if (actual !== expected) {
|
|
308
|
-
|
|
312
|
+
dropFile(tgz);
|
|
309
313
|
return { ok: false, reason: `${engine.name}: SHA256 mismatch (esperado ${expected.slice(0, 12)}…, obtido ${actual.slice(0, 12)}…) → ABORT` };
|
|
310
314
|
}
|
|
311
315
|
|
|
@@ -313,13 +317,13 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
313
317
|
// final: se a assinatura não confere, o .tgz é apagado e nada é instalado.
|
|
314
318
|
const auth = await verifyAuthenticity(engine, buf, { log, fetcher, trustedKeys });
|
|
315
319
|
if (!auth.ok) {
|
|
316
|
-
|
|
320
|
+
dropFile(tgz);
|
|
317
321
|
return { ok: false, reason: auth.reason };
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
// 4) Extrai em staging e faz o swap (com shutdown-first se já existe).
|
|
321
325
|
const staging = join(home, `stage-${engine.version}`);
|
|
322
|
-
|
|
326
|
+
rmTree(staging);
|
|
323
327
|
mkdirSync(staging, { recursive: true });
|
|
324
328
|
const ex = extractTgz(tgz, staging);
|
|
325
329
|
if (!ex.ok) { return { ok: false, reason: `${engine.name}: ${ex.reason}` }; }
|
|
@@ -329,8 +333,8 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
329
333
|
// sha256 e pela Ed25519. O package.json do artefato tem de dizer a versão que o registry pede.
|
|
330
334
|
const got = installedVersion(staging);
|
|
331
335
|
if (got !== engine.version) {
|
|
332
|
-
|
|
333
|
-
|
|
336
|
+
rmTree(staging);
|
|
337
|
+
dropFile(tgz);
|
|
334
338
|
return { ok: false, reason: `${engine.name}: o artefato diz v${got ?? "? (package.json ausente/ilegível)"} e o registry pede v${engine.version} → ABORT` };
|
|
335
339
|
}
|
|
336
340
|
|
|
@@ -341,12 +345,12 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
341
345
|
if (existsSync(binDir)) {
|
|
342
346
|
if (onBeforeReplace) { try { await onBeforeReplace(); } catch { /* best-effort */ } }
|
|
343
347
|
aside = `${binDir}.old-${process.pid}`;
|
|
344
|
-
|
|
348
|
+
rmTree(aside);
|
|
345
349
|
try {
|
|
346
|
-
|
|
350
|
+
await renameRetry(binDir, aside);
|
|
347
351
|
} catch (e) {
|
|
348
|
-
|
|
349
|
-
|
|
352
|
+
rmTree(staging);
|
|
353
|
+
dropFile(tgz);
|
|
350
354
|
return { ok: false, reason: `${engine.name}: não consegui trocar ${binDir} (${e.code || e.message}): arquivos em uso — feche quem usa o motor e rode de novo; a instalação atual ficou intacta` };
|
|
351
355
|
}
|
|
352
356
|
}
|
|
@@ -355,16 +359,16 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
355
359
|
// isto, funciona na máquina que já tem a pasta e falha justamente na máquina limpa.
|
|
356
360
|
mkdirSync(dirname(binDir), { recursive: true });
|
|
357
361
|
try {
|
|
358
|
-
|
|
362
|
+
await renameRetry(staging, binDir);
|
|
359
363
|
} catch (e) {
|
|
360
364
|
if (aside) { try { renameSync(aside, binDir); } catch { /* o reason abaixo diz onde ficou */ } }
|
|
361
365
|
return { ok: false, reason: `${engine.name}: swap falhou (${e.code || e.message})${aside && existsSync(aside) ? `; a instalação anterior está em ${aside}` : ""}` };
|
|
362
366
|
}
|
|
363
367
|
if (aside) {
|
|
364
|
-
try {
|
|
368
|
+
try { rmTree(aside); }
|
|
365
369
|
catch (e) { log(`[engine-kit] ${engine.name}: instalado, mas a versão anterior ficou em ${aside} (${e.code || e.message})`); }
|
|
366
370
|
}
|
|
367
|
-
|
|
371
|
+
dropFile(tgz);
|
|
368
372
|
|
|
369
373
|
if (!existsSync(entryPath)) {
|
|
370
374
|
return { ok: false, reason: `${engine.name}: artefato extraído incompleto (sem ${engine.install.entry})` };
|
package/manifest.json
CHANGED
|
@@ -7,7 +7,33 @@
|
|
|
7
7
|
"url": "https://github.com/AllanSantos-DV"
|
|
8
8
|
},
|
|
9
9
|
"description": "Catálogo público de motores compartilhados e aplicativos distribuídos. engine-kit consome apenas engines; apps declara versões e downloads sem instalar aplicativos automaticamente.",
|
|
10
|
-
"apps": [
|
|
10
|
+
"apps": [
|
|
11
|
+
{
|
|
12
|
+
"name": "agent-launcher",
|
|
13
|
+
"version": "1.2.11",
|
|
14
|
+
"summary": "Abre Claude Code, Codex e Copilot CLI nos seus projetos por qualquer host de modelos. Windows.",
|
|
15
|
+
"homepage": "https://allansantos-dv.github.io/agent-launcher/",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"platforms": [
|
|
18
|
+
"win32-x64"
|
|
19
|
+
],
|
|
20
|
+
"release": {
|
|
21
|
+
"repo": "AllanSantos-DV/engine-registry",
|
|
22
|
+
"tag": "agent-launcher-v{version}",
|
|
23
|
+
"assets": [
|
|
24
|
+
{
|
|
25
|
+
"name": "agent-launcher-{version}-win32-x64.tgz",
|
|
26
|
+
"platform": "win32-x64",
|
|
27
|
+
"checksum": "{asset}.sha256",
|
|
28
|
+
"signature": "{asset}.sig"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"publicKey": "bbfc8c1a639872d4e5fc44ac4172443886153b4579b4f3e0466220c02d09407a",
|
|
32
|
+
"signatureAlgorithm": "ed25519-sha256-raw",
|
|
33
|
+
"signatureRequired": true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
],
|
|
11
37
|
"engines": [
|
|
12
38
|
{
|
|
13
39
|
"name": "embed-house",
|
|
@@ -137,9 +163,9 @@
|
|
|
137
163
|
},
|
|
138
164
|
{
|
|
139
165
|
"name": "vxk",
|
|
140
|
-
"version": "0.
|
|
166
|
+
"version": "0.19.0",
|
|
141
167
|
"kind": "cli",
|
|
142
|
-
"summary": "VXK — explicações visuais animadas e narradas como servidor MCP (stdio): qualquer cliente MCP (Claude Code, Codex, Copilot) compõe a biblioteca de componentes, ícones e o controle único de reprodução e recebe UM HTML autocontido e offline. A narração é assada pelo motor vox-engine.",
|
|
168
|
+
"summary": "VXK — explicações visuais animadas e narradas como servidor MCP (stdio): qualquer cliente MCP (Claude Code, Codex, Copilot) compõe a biblioteca de componentes, ícones e o controle único de reprodução e recebe UM HTML autocontido e offline, ou o vídeo MP4 da mesma explicação (por preset, do aplicativo real ou em Motion) e o PDF. A narração é assada pelo motor vox-engine.",
|
|
143
169
|
"pagesUrl": "https://allansantos-dv.github.io/visual-explainer/",
|
|
144
170
|
"pagesPendente": "a página no ar não oferece o caminho Ver o que o produto faz antes do vídeo",
|
|
145
171
|
"home": "~/.vxk",
|
|
@@ -158,7 +184,16 @@
|
|
|
158
184
|
"platforms": [
|
|
159
185
|
"any"
|
|
160
186
|
],
|
|
161
|
-
"notes": "kind=cli: o host MCP sobe `node ~/.vxk/bin/vxk-mcp.mjs` por stdio (direto ou como backend do mcp-gateway); o build é job assíncrono porque o gateway corta chamadas em 30 s. O motor publica ~/.vxk/cli.json (mcp, builder, version) a cada arranque. Traz também o builder CLI (bin/build-artifact.mjs). Narração requer o vox-engine (Windows); sem ele o build falha alto em vez de gerar mudo."
|
|
187
|
+
"notes": "kind=cli: o host MCP sobe `node ~/.vxk/bin/vxk-mcp.mjs` por stdio (direto ou como backend do mcp-gateway); o build é job assíncrono porque o gateway corta chamadas em 30 s. Desde a 0.19.0 esse arquivo é um shim fino: quem trabalha é UM daemon por máquina e por usuário (bin/vxk-daemon.mjs, 127.0.0.1, porta derivada do usuário), que o shim sobe se não houver e compartilha entre os agentes; ele anuncia porta, pid e shutdownToken em ~/.vxk/run/daemon.json. O bloco `runtime` declara só o que o kit usa para trocar a instalação: o kit NÃO sobe nem espera esse daemon (o shim cuida disso), mas ao atualizar pede POST /shutdown com o x-shutdown-token antes do swap (ocupado, o daemon responde 202 e fecha quando o trabalho em curso termina: o kit espera o processo sair), porque no Windows o daemon vivo segura a pasta bin e a troca falharia com EPERM. O motor publica ~/.vxk/cli.json (mcp, builder, version) a cada arranque. Traz também o builder CLI (bin/build-artifact.mjs). Entrega HTML, MP4 (vxk_build target=mp4, vxk_video por preset, vxk_app_video do aplicativo real, vxk_motion) e PDF. Narração requer o vox-engine (Windows); sem ele o build falha alto em vez de gerar mudo.",
|
|
188
|
+
"runtime": {
|
|
189
|
+
"transport": "http",
|
|
190
|
+
"runtimeFile": "run/daemon.json",
|
|
191
|
+
"healthPath": "/health",
|
|
192
|
+
"shutdownPath": "/shutdown",
|
|
193
|
+
"heartbeatMs": 30000,
|
|
194
|
+
"shutdownTokenField": "shutdownToken",
|
|
195
|
+
"shutdownTokenHeader": "x-shutdown-token"
|
|
196
|
+
}
|
|
162
197
|
},
|
|
163
198
|
{
|
|
164
199
|
"name": "cerne",
|
|
@@ -209,6 +244,38 @@
|
|
|
209
244
|
"any"
|
|
210
245
|
],
|
|
211
246
|
"notes": "kind=cli: o lifecycle entrega o binário e NÃO sobe processo. `extractTo` é aninhado como no neural-link: o home guarda ESTADO que sobrevive ao update, e só `runtimes/skill-engine` é trocado. Código-fonte no repo privado AllanSantos-DV/skill-engine; a release pública mora aqui."
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"name": "client-harness",
|
|
250
|
+
"version": "0.1.1",
|
|
251
|
+
"kind": "cli",
|
|
252
|
+
"summary": "Daemon local que faz o Claude Code e o Codex respeitarem os padrões de cada cliente: confere cada ação do agente (hooks) contra as regras do cliente da pasta e recusa antes de gravar, em cerca de 1 ms, com o motivo e o que usar no lugar.",
|
|
253
|
+
"pagesUrl": "https://allansantos-dv.github.io/harness-padroes-cliente/",
|
|
254
|
+
"home": "~/.client-harness",
|
|
255
|
+
"install": {
|
|
256
|
+
"type": "github-release",
|
|
257
|
+
"repo": "AllanSantos-DV/engine-registry",
|
|
258
|
+
"tag": "client-harness-v{version}",
|
|
259
|
+
"asset": "client-harness-any.tgz",
|
|
260
|
+
"checksum": "{asset}.sha256",
|
|
261
|
+
"extractTo": "runtimes/client-harness",
|
|
262
|
+
"entry": "runtimes/client-harness/src/cli.mjs",
|
|
263
|
+
"publicKey": "b3624d289f9e026388ee0b2c1aba0b1e2328b546b607c7f283548e85972052c4",
|
|
264
|
+
"signatureAlgorithm": "ed25519-sha256-raw",
|
|
265
|
+
"signatureRequired": true
|
|
266
|
+
},
|
|
267
|
+
"platforms": [
|
|
268
|
+
"any"
|
|
269
|
+
],
|
|
270
|
+
"notes": "kind=cli: o lifecycle entrega o binário e NÃO sobe processo (`client-harness start` sobe o daemon, que os hooks dos agentes consultam; o hook de SessionStart gravado por `client-harness install` o sobe a cada sessão). `extractTo` é aninhado como no neural-link e no skill-engine: o home guarda ESTADO (token, eventos, run/) que sobrevive ao update, e só `runtimes/client-harness` é trocado. Daemon único por máquina e usuário: bind numa porta derivada do usuário (47000-47999, CLIENT_HARNESS_PORT troca), run/daemon.json com porta, pid e shutdownToken. O bloco `runtime` declara só o que o kit usa para trocar a instalação: POST /shutdown?relaunch=1 com x-shutdown-token antes do swap (no Windows o daemon vivo segura a pasta); com relaunch=1 o daemon deixa um runner que sobe o daemon novo depois da troca, então um update não deixa a máquina sem checagem. O motor publica ~/.client-harness/cli.json em toda invocação do CLI sem --data. Artefato cross-platform (Node puro, sem dependências). Código-fonte no repo AllanSantos-DV/harness-padroes-cliente; a release pública mora aqui.",
|
|
271
|
+
"runtime": {
|
|
272
|
+
"transport": "http",
|
|
273
|
+
"runtimeFile": "run/daemon.json",
|
|
274
|
+
"healthPath": "/health",
|
|
275
|
+
"shutdownPath": "/shutdown?relaunch=1",
|
|
276
|
+
"shutdownTokenField": "shutdownToken",
|
|
277
|
+
"shutdownTokenHeader": "x-shutdown-token"
|
|
278
|
+
}
|
|
212
279
|
}
|
|
213
280
|
]
|
|
214
281
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engine-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Cliente das dependências compartilhadas (motores) do engine-registry: resolve, provisiona com SHA256 e assinatura Ed25519 fail-closed, e mantém o motor vivo — uma instalação por máquina, N consumidores.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"node": ">=18"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
-
"test": "node kit/budget.mjs && node tests/test-silencio.mjs && node tests/smoke.mjs && node tests/test-edge.mjs && node tests/test-signature.mjs && node tests/test-ensure.mjs && node tests/test-update.mjs && node tests/test-trust.mjs && node tests/test-tar.mjs && node tests/test-apps.mjs && node tests/test-resolve-cache.mjs && node tests/test-doctor-exit.mjs && node tests/test-mcp-gateway-token.mjs && node tests/test-gateway-provision.mjs && node tests/test-neural-link-alignment.mjs && node tests/test-site.mjs && node tests/test-pages-branch.mjs && node tests/test-readme-versions.mjs && node tests/test-pages-gate.mjs",
|
|
27
|
+
"test": "node kit/budget.mjs && node tests/test-silencio.mjs && node tests/smoke.mjs && node tests/test-edge.mjs && node tests/test-signature.mjs && node tests/test-ensure.mjs && node tests/test-ensure-daemon.mjs && node tests/test-update.mjs && node tests/test-trust.mjs && node tests/test-tar.mjs && node tests/test-apps.mjs && node tests/test-resolve-cache.mjs && node tests/test-doctor-exit.mjs && node tests/test-mcp-gateway-token.mjs && node tests/test-gateway-provision.mjs && node tests/test-neural-link-alignment.mjs && node tests/test-site.mjs && node tests/test-pages-branch.mjs && node tests/test-readme-versions.mjs && node tests/test-pages-gate.mjs",
|
|
28
28
|
"test:pages-live": "node tests/test-pages-gate.mjs --live",
|
|
29
29
|
"test:network": "node tests/smoke.mjs --network && node tests/test-signature.mjs --network",
|
|
30
30
|
"test:e2e": "node tests/e2e-install.mjs",
|