engine-kit 0.2.3 → 0.2.5
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/LICENSE +1 -1
- package/README.md +7 -7
- package/kit/budget.mjs +4 -1
- package/kit/diagnose.mjs +58 -0
- package/kit/doctor.mjs +6 -0
- package/kit/ensure.mjs +1 -0
- package/kit/provision.mjs +34 -59
- package/kit/trust.mjs +19 -0
- package/manifest.json +8 -8
- package/package.json +3 -3
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -27,12 +27,12 @@ vive num lugar só e o consumidor apenas **declara a dependência**.
|
|
|
27
27
|
|---|---|---|---|
|
|
28
28
|
| `embed-house` | 1.0.5 | `daemon` (http) | Casa de embeddings (MiniLM-L6-v2, 384-dim): carrega o modelo uma vez e serve vetores a N consumidores. |
|
|
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
|
-
| `mcp-gateway` | 0.
|
|
30
|
+
| `mcp-gateway` | 0.17.2 | `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.
|
|
33
|
-
| `cerne` | 0.4.
|
|
34
|
-
| `skill-engine` | 0.1
|
|
35
|
-
| `client-harness` | 0.1.
|
|
32
|
+
| `vxk` | 0.21.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
|
+
| `cerne` | 0.4.14 | `daemon`, **self-managed** | Leitura ativa de vídeo (captura, quadros, transcrição, tradução) exposta via MCP. Instalador Inno por usuário, Windows. |
|
|
34
|
+
| `skill-engine` | 0.2.1 | `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.5 | `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. |
|
|
36
36
|
|
|
37
37
|
**Todos** são publicados aqui, assinados com Ed25519 e verificados antes de instalar.
|
|
38
38
|
|
|
@@ -59,7 +59,7 @@ Sem clonar o repositório, com Node 18 ou mais:
|
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
61
|
npx engine-kit ensure vxk # instala ou atualiza; reusa se já estiver em dia
|
|
62
|
-
npx engine-kit ensure vxk --version 0.
|
|
62
|
+
npx engine-kit ensure vxk --version 0.21.0 # pin de versão
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
O `npx` baixa o kit do npm e o kit baixa o motor da release, confere o **sha256**, a **assinatura Ed25519**
|
|
@@ -271,7 +271,7 @@ plataforma, e a release mora aqui, no registry público, com download **sem logi
|
|
|
271
271
|
|
|
272
272
|
| App | Versão | Plataformas | O que faz |
|
|
273
273
|
|---|---|---|---|
|
|
274
|
-
| `agent-launcher` | 1.
|
|
274
|
+
| `agent-launcher` | 1.3.5 | `win32-x64` | Abre Claude Code, Codex, Copilot CLI, VS Code e o Copilot app nos seus projetos por qualquer host de modelos. Leva só o launcher; cada ferramenta se instala pelo cartão dela. |
|
|
275
275
|
|
|
276
276
|
Um app só entra nesta tabela e em `manifest.apps` **depois** de a release pública existir e passar
|
|
277
277
|
em `node kit/verify-release.mjs --app <nome>` (download anônimo de todos os assets, `.sha256` e
|
package/kit/budget.mjs
CHANGED
|
@@ -41,9 +41,12 @@ const ROOT = resolvePath(KIT, "..");
|
|
|
41
41
|
// e update.mjs (`npx engine-kit ensure <motor>` com autoatualização exigida pelo gate de release), +6.3 KB.
|
|
42
42
|
// Escopo novo (comando de instalação), rodado pela mão de quem instala, nunca importado por consumidor:
|
|
43
43
|
// o caminho do `import "engine-kit"` não mudou.
|
|
44
|
+
// 2026-11-09 tooling 31000 → 34000: diagnóstico de vida (pid, porta, idade do runtime) e do ponteiro
|
|
45
|
+
// `cli.json` no doctor (diagnose.mjs), +2.7 KB. Só leitura, rodado na mão de quem diagnostica; o caminho do
|
|
46
|
+
// `import "engine-kit"` não mudou.
|
|
44
47
|
const BUDGETS = {
|
|
45
48
|
runtime: { codeBytes: 27000, baseline: 24519, loc: 900 },
|
|
46
|
-
tooling: { codeBytes:
|
|
49
|
+
tooling: { codeBytes: 34000, baseline: 16420, loc: 950 },
|
|
47
50
|
adapter: { codeBytes: 5000, baseline: 1859, loc: 150 },
|
|
48
51
|
};
|
|
49
52
|
|
package/kit/diagnose.mjs
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// engine-kit/diagnose.mjs — diagnóstico SOMENTE LEITURA de vida e de ponteiro, usado pelo doctor.
|
|
2
|
+
// Não sobe, não derruba e não fala com o motor: lê o runtime que o motor anuncia e o `cli.json`
|
|
3
|
+
// que ele publica (CONTRATO-MOTOR §4). Nunca lança.
|
|
4
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
5
|
+
import { isAbsolute, join, relative } from "node:path";
|
|
6
|
+
import { readRuntime } from "./lifecycle.mjs";
|
|
7
|
+
|
|
8
|
+
const alive = (pid) => {
|
|
9
|
+
try { process.kill(pid, 0); return true; } catch (e) { return e.code === "EPERM"; }
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Vida do motor segundo o arquivo de runtime que ele declara: pid vivo, porta e idade do heartbeat.
|
|
14
|
+
* Estados: SEM-DECLARAÇÃO (o registry não declara arquivo de runtime), PARADO (sem runtime),
|
|
15
|
+
* VIVO (pid existe), ÓRFÃO (runtime aponta para pid que não existe mais).
|
|
16
|
+
* @returns {{state:string, detail:string}}
|
|
17
|
+
*/
|
|
18
|
+
export function lifeOf(engine, now = Date.now()) {
|
|
19
|
+
const rt = engine.runtime;
|
|
20
|
+
if (!rt?.runtimeFile) { return { state: "SEM-DECLARAÇÃO", detail: rt?.transport ? `transporte ${rt.transport}, sem arquivo de runtime` : "motor sem bloco runtime" }; }
|
|
21
|
+
const data = readRuntime(engine);
|
|
22
|
+
if (!data) { return { state: "PARADO", detail: `sem ${rt.runtimeFile}` }; }
|
|
23
|
+
let age = "";
|
|
24
|
+
try {
|
|
25
|
+
const ms = now - statSync(join(engine.homeDir, rt.runtimeFile)).mtimeMs;
|
|
26
|
+
const limit = rt.heartbeatMs ? ` (limite ${Math.round((rt.heartbeatMs * 3) / 1000)}s)` : "";
|
|
27
|
+
age = `, runtime atualizado há ${Math.round(ms / 1000)}s${limit}`;
|
|
28
|
+
} catch { /* o arquivo sumiu entre a leitura e o stat */ }
|
|
29
|
+
const where = `pid ${data.pid ?? "?"}${data.port ? `, porta ${data.port}` : ""}${age}`;
|
|
30
|
+
return data.pid && alive(data.pid)
|
|
31
|
+
? { state: "VIVO", detail: where }
|
|
32
|
+
: { state: "ÓRFÃO", detail: `${where}; o pid não existe mais` };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Ponteiro `<home>/cli.json`: todo caminho absoluto que o motor publica precisa existir, e o kit
|
|
37
|
+
* avisa quando o ponteiro vive fora da instalação que ele provisionou.
|
|
38
|
+
* Estados: AUSENTE, ILEGÍVEL, QUEBRADO (algum caminho não existe), FORA-DA-INSTALAÇÃO, OK.
|
|
39
|
+
* @returns {{state:string, detail:string}}
|
|
40
|
+
*/
|
|
41
|
+
export function pointerOf(engine) {
|
|
42
|
+
const file = join(engine.homeDir, "cli.json");
|
|
43
|
+
if (!existsSync(file)) { return { state: "AUSENTE", detail: "o motor ainda não publicou o ponteiro" }; }
|
|
44
|
+
let data;
|
|
45
|
+
try { data = JSON.parse(readFileSync(file, "utf8")); }
|
|
46
|
+
catch { return { state: "ILEGÍVEL", detail: `${file} não é JSON válido` }; }
|
|
47
|
+
const paths = Object.entries(data).filter(([, v]) => typeof v === "string" && isAbsolute(v));
|
|
48
|
+
const missing = paths.filter(([, v]) => !existsSync(v)).map(([k]) => k);
|
|
49
|
+
if (missing.length) { return { state: "QUEBRADO", detail: `aponta para caminho inexistente: ${missing.join(", ")}` }; }
|
|
50
|
+
const binDir = join(engine.homeDir, engine.install.extractTo ?? "bin");
|
|
51
|
+
const outside = paths.filter(([k, v]) => {
|
|
52
|
+
const rel = relative(binDir, v);
|
|
53
|
+
return k !== "node" && (rel.startsWith("..") || isAbsolute(rel));
|
|
54
|
+
});
|
|
55
|
+
return outside.length
|
|
56
|
+
? { state: "FORA-DA-INSTALAÇÃO", detail: `${outside.map(([k]) => k).join(", ")} fora de ${binDir}` }
|
|
57
|
+
: { state: "OK", detail: `${paths.length} caminho(s) existem` };
|
|
58
|
+
}
|
package/kit/doctor.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import { join } from "node:path";
|
|
|
17
17
|
import { resolve } from "./resolve.mjs";
|
|
18
18
|
import { provision, satisfiesPin } from "./provision.mjs";
|
|
19
19
|
import { shutdown } from "./lifecycle.mjs";
|
|
20
|
+
import { lifeOf, pointerOf } from "./diagnose.mjs";
|
|
20
21
|
|
|
21
22
|
const args = process.argv.slice(2);
|
|
22
23
|
const useLocal = args.includes("--local");
|
|
@@ -85,6 +86,11 @@ const w = Math.max(12, ...rows.map((r) => r.name.length));
|
|
|
85
86
|
for (const r of rows) {
|
|
86
87
|
const mark = (r.state === "OK" || r.state === "SELF-MANAGED") ? " OK " : r.state === "ERRO" ? " ERRO " : " !! ";
|
|
87
88
|
console.log(`${mark} ${pad(r.name, w)} ${pad(r.state, 14)} ${r.detail}`);
|
|
89
|
+
if (r.engine && r.engine.status !== "self-managed") {
|
|
90
|
+
const life = lifeOf(r.engine), ptr = pointerOf(r.engine);
|
|
91
|
+
console.log(` ${pad("", w)} vida: ${life.state} (${life.detail})`);
|
|
92
|
+
console.log(` ${pad("", w)} ponteiro cli.json: ${ptr.state} (${ptr.detail})`);
|
|
93
|
+
}
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
const pending = rows.filter((r) => r.needsWork);
|
package/kit/ensure.mjs
CHANGED
|
@@ -63,6 +63,7 @@ export async function ensureCommand(name, { version, registryUrl, bundled, manif
|
|
|
63
63
|
const p = await provision(r.engine, {
|
|
64
64
|
log,
|
|
65
65
|
trustedKeys: { [name]: publicKey },
|
|
66
|
+
allowDowngrade: Boolean(version),
|
|
66
67
|
...(fetcher ? { fetcher } : {}),
|
|
67
68
|
// Windows: arquivo em uso não se sobrescreve. Derruba o daemon pelo shutdownPath que o registry declara.
|
|
68
69
|
onBeforeReplace: async () => {
|
package/kit/provision.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { join, dirname } from "node:path";
|
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
14
14
|
import { verifyBlob, DEFAULT_ALGORITHM } from "./signature.mjs";
|
|
15
|
-
import { checkTrust, trustPath } from "./trust.mjs";
|
|
15
|
+
import { checkTrust, trustPath, cmpVer, rollbackReason, noteVersion } from "./trust.mjs";
|
|
16
16
|
|
|
17
17
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
18
18
|
|
|
@@ -77,22 +77,29 @@ function claimLock(lock) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const parseVer = (v) => String(v || "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
81
|
-
|
|
82
80
|
/** true se a versão instalada serve o pin: MESMO major e >= (protocolo é atado ao major). */
|
|
83
|
-
export
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
export const satisfiesPin = (installed, pinned) => String(installed).split(".")[0] === String(pinned).split(".")[0] && cmpVer(installed, pinned) >= 0;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Baixa o artefato e confere o sidecar SHA256 (obrigatório, fail-closed) em memória.
|
|
85
|
+
* @returns {Promise<{buf:Buffer} | {reason:string}>}
|
|
86
|
+
*/
|
|
87
|
+
async function downloadChecked(engine, { log, fetcher, timeoutMs }) {
|
|
88
|
+
let expected;
|
|
89
|
+
try {
|
|
90
|
+
expected = String(await fetcher(engine.checksumUrl, 15000)).trim().split(/\s+/)[0].toLowerCase();
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return { reason: `${engine.name}: sha256 sidecar inacessível (${e.status || e.message}) → ABORT (fail-closed)` };
|
|
90
93
|
}
|
|
91
|
-
return
|
|
92
|
-
}
|
|
94
|
+
if (!/^[0-9a-f]{64}$/.test(expected)) { return { reason: `${engine.name}: sha256 sidecar malformado → ABORT` }; }
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
log(`[engine-kit] baixando ${engine.assetUrl} …`);
|
|
97
|
+
let buf;
|
|
98
|
+
try { buf = await fetcher(engine.assetUrl, timeoutMs); }
|
|
99
|
+
catch (e) { return { reason: `${engine.name}: download falhou (${e.status || e.message})` }; }
|
|
100
|
+
|
|
101
|
+
const actual = createHash("sha256").update(buf).digest("hex").toLowerCase();
|
|
102
|
+
return actual === expected ? { buf } : { reason: `${engine.name}: SHA256 mismatch (esperado ${expected.slice(0, 12)}…, obtido ${actual.slice(0, 12)}…) → ABORT` };
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
async function fetchBuf(url, timeoutMs) {
|
|
@@ -183,26 +190,9 @@ export async function fetchArtifact(engine, { log = () => {}, destDir, downloadT
|
|
|
183
190
|
return { ok: false, reason: `${engine.name}: não consegui criar ${dir}: ${e?.message || e}` };
|
|
184
191
|
}
|
|
185
192
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
expected = String(buf).trim().split(/\s+/)[0].toLowerCase();
|
|
190
|
-
} catch (e) {
|
|
191
|
-
return { ok: false, reason: `${engine.name}: sha256 sidecar inacessível (${e.status || e.message}) → ABORT (fail-closed)` };
|
|
192
|
-
}
|
|
193
|
-
if (!/^[0-9a-f]{64}$/.test(expected)) {
|
|
194
|
-
return { ok: false, reason: `${engine.name}: sha256 sidecar malformado → ABORT` };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
log(`[engine-kit] baixando ${engine.assetUrl} …`);
|
|
198
|
-
let buf;
|
|
199
|
-
try { buf = await fetcher(engine.assetUrl, downloadTimeoutMs); }
|
|
200
|
-
catch (e) { return { ok: false, reason: `${engine.name}: download falhou (${e.status || e.message})` }; }
|
|
201
|
-
|
|
202
|
-
const actual = createHash("sha256").update(buf).digest("hex").toLowerCase();
|
|
203
|
-
if (actual !== expected) {
|
|
204
|
-
return { ok: false, reason: `${engine.name}: SHA256 mismatch (esperado ${expected.slice(0, 12)}…, obtido ${actual.slice(0, 12)}…) → ABORT` };
|
|
205
|
-
}
|
|
193
|
+
const dl = await downloadChecked(engine, { log, fetcher, timeoutMs: downloadTimeoutMs });
|
|
194
|
+
if (!dl.buf) { return { ok: false, reason: dl.reason }; }
|
|
195
|
+
const buf = dl.buf;
|
|
206
196
|
|
|
207
197
|
const auth = await verifyAuthenticity(engine, buf, { log, fetcher, trustedKeys });
|
|
208
198
|
if (!auth.ok) { return { ok: false, reason: auth.reason }; }
|
|
@@ -225,7 +215,7 @@ export async function fetchArtifact(engine, { log = () => {}, destDir, downloadT
|
|
|
225
215
|
* adivinha como derrubar um daemon: quem sabe é o descritor/consumidor).
|
|
226
216
|
* @returns {Promise<{ok:true, entryPath:string, version:string, reused?:boolean, installed?:boolean} | {ok:false, reason:string}>}
|
|
227
217
|
*/
|
|
228
|
-
export async function provision(engine, { log = () => {}, allowNetwork = true, onBeforeReplace, downloadTimeoutMs = 120000, fetcher = fetchBuf, trustedKeys } = {}) {
|
|
218
|
+
export async function provision(engine, { log = () => {}, allowNetwork = true, onBeforeReplace, downloadTimeoutMs = 120000, fetcher = fetchBuf, trustedKeys, allowDowngrade } = {}) {
|
|
229
219
|
const home = engine.homeDir;
|
|
230
220
|
const binDir = join(home, engine.install.extractTo ?? "bin");
|
|
231
221
|
const entryPath = join(home, engine.install.entry);
|
|
@@ -239,6 +229,7 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
239
229
|
if (existsSync(entryPath)) {
|
|
240
230
|
const inst = installedVersion(binDir);
|
|
241
231
|
if (inst && satisfiesPin(inst, engine.version)) {
|
|
232
|
+
noteVersion(engine.name, inst);
|
|
242
233
|
return { ok: true, entryPath, version: inst, reused: true };
|
|
243
234
|
}
|
|
244
235
|
if (!inst && engine.status !== "self-managed") {
|
|
@@ -266,6 +257,9 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
266
257
|
return { ok: false, reason: `${engine.name} ainda não publicou release (status: pending-release)` };
|
|
267
258
|
}
|
|
268
259
|
|
|
260
|
+
const rb = !allowDowngrade && rollbackReason(engine.name, engine.version);
|
|
261
|
+
if (rb) { return { ok: false, reason: rb }; }
|
|
262
|
+
|
|
269
263
|
mkdirSync(home, { recursive: true });
|
|
270
264
|
|
|
271
265
|
// LOCK atômico: só um consumidor provisiona; os demais aguardam o resultado dele.
|
|
@@ -284,35 +278,15 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
284
278
|
}
|
|
285
279
|
|
|
286
280
|
try {
|
|
287
|
-
// 1) SHA256
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
expected = String(buf).trim().split(/\s+/)[0].toLowerCase();
|
|
292
|
-
} catch (e) {
|
|
293
|
-
return { ok: false, reason: `${engine.name}: sha256 sidecar inacessível (${e.status || e.message}) → ABORT (fail-closed)` };
|
|
294
|
-
}
|
|
295
|
-
if (!/^[0-9a-f]{64}$/.test(expected)) {
|
|
296
|
-
return { ok: false, reason: `${engine.name}: sha256 sidecar malformado → ABORT` };
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// 2) Download → .part → rename atômico.
|
|
300
|
-
log(`[engine-kit] baixando ${engine.assetUrl} …`);
|
|
301
|
-
let buf;
|
|
302
|
-
try { buf = await fetcher(engine.assetUrl, downloadTimeoutMs); }
|
|
303
|
-
catch (e) { return { ok: false, reason: `${engine.name}: download falhou (${e.status || e.message})` }; }
|
|
281
|
+
// 1-3) Sidecar SHA256 obrigatório, download e integridade: nada toca o disco antes de conferir.
|
|
282
|
+
const dl = await downloadChecked(engine, { log, fetcher, timeoutMs: downloadTimeoutMs });
|
|
283
|
+
if (!dl.buf) { return { ok: false, reason: dl.reason }; }
|
|
284
|
+
const buf = dl.buf;
|
|
304
285
|
const part = join(home, `${engine.assetName}.part`);
|
|
305
286
|
const tgz = join(home, engine.assetName);
|
|
306
287
|
writeFileSync(part, buf);
|
|
307
288
|
renameSync(part, tgz);
|
|
308
289
|
|
|
309
|
-
// 3) Integridade (mismatch = apaga + ABORT).
|
|
310
|
-
const actual = sha256File(tgz);
|
|
311
|
-
if (actual !== expected) {
|
|
312
|
-
dropFile(tgz);
|
|
313
|
-
return { ok: false, reason: `${engine.name}: SHA256 mismatch (esperado ${expected.slice(0, 12)}…, obtido ${actual.slice(0, 12)}…) → ABORT` };
|
|
314
|
-
}
|
|
315
|
-
|
|
316
290
|
// 3.5) AUTENTICIDADE — antes de extrair. Um artefato não confiável não chega a tocar o disco
|
|
317
291
|
// final: se a assinatura não confere, o .tgz é apagado e nada é instalado.
|
|
318
292
|
const auth = await verifyAuthenticity(engine, buf, { log, fetcher, trustedKeys });
|
|
@@ -373,6 +347,7 @@ export async function provision(engine, { log = () => {}, allowNetwork = true, o
|
|
|
373
347
|
if (!existsSync(entryPath)) {
|
|
374
348
|
return { ok: false, reason: `${engine.name}: artefato extraído incompleto (sem ${engine.install.entry})` };
|
|
375
349
|
}
|
|
350
|
+
noteVersion(engine.name, engine.version);
|
|
376
351
|
log(`[engine-kit] ${engine.name} instalado em ${binDir} (v${engine.version})`);
|
|
377
352
|
return { ok: true, entryPath, version: engine.version, installed: true, signed: auth.signed };
|
|
378
353
|
} catch (e) {
|
package/kit/trust.mjs
CHANGED
|
@@ -70,3 +70,22 @@ export function checkTrust(engineName, publicKey, { trustedKeys } = {}) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export const trustPath = () => trustFile();
|
|
73
|
+
|
|
74
|
+
/** Compara versões `x.y.z` numericamente: negativo, zero ou positivo. */
|
|
75
|
+
export const cmpVer = (a, b) => String(a).localeCompare(String(b), "en", { numeric: true });
|
|
76
|
+
|
|
77
|
+
/** Motivo do ABORT se `version` é menor que a maior já instalada do motor (anti-rollback); falsy se pode seguir. */
|
|
78
|
+
export function rollbackReason(name, version) {
|
|
79
|
+
const max = readTrust()[name]?.maxVersion;
|
|
80
|
+
return max && cmpVer(version, max) < 0 && `${name}: v${version} < v${max} já instalada → ABORT (rollback; downgrade só com --version)`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Grava `version` como a maior vista do motor, se superar a registrada. */
|
|
84
|
+
export function noteVersion(name, version) {
|
|
85
|
+
const store = readTrust(), max = store[name]?.maxVersion;
|
|
86
|
+
if (max && cmpVer(version, max) <= 0) return;
|
|
87
|
+
try {
|
|
88
|
+
mkdirSync(trustDir(), { recursive: true });
|
|
89
|
+
writeFileSync(trustFile(), JSON.stringify({ ...store, [name]: { ...store[name], maxVersion: version } }, null, 2));
|
|
90
|
+
} catch { /* sem gravar, a proteção vale a partir da próxima instalação */ }
|
|
91
|
+
}
|
package/manifest.json
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
"registry": "engine-registry",
|
|
4
4
|
"version": 1,
|
|
5
5
|
"owner": {
|
|
6
|
-
"name": "
|
|
6
|
+
"name": "The engine-registry maintainers",
|
|
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
10
|
"apps": [
|
|
11
11
|
{
|
|
12
12
|
"name": "agent-launcher",
|
|
13
|
-
"version": "1.
|
|
14
|
-
"summary": "Abre Claude Code, Codex e Copilot
|
|
13
|
+
"version": "1.3.5",
|
|
14
|
+
"summary": "Abre Claude Code, Codex, Copilot CLI, VS Code e o Copilot app nos seus projetos por qualquer host de modelos. Leva só o launcher; cada ferramenta se instala pelo cartão dela. Windows.",
|
|
15
15
|
"homepage": "https://allansantos-dv.github.io/agent-launcher/",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"platforms": [
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
},
|
|
107
107
|
{
|
|
108
108
|
"name": "mcp-gateway",
|
|
109
|
-
"version": "0.
|
|
109
|
+
"version": "0.17.2",
|
|
110
110
|
"kind": "daemon",
|
|
111
111
|
"summary": "Agregador MCP: conecta UMA vez em cada servidor MCP e reexpõe todas as tools num endpoint Streamable HTTP único, com auth bearer, namespacing condicional, resiliência e auditoria. Mata a multiplicação de processos por sessão.",
|
|
112
112
|
"pagesPendente": "sem página de produto com vídeo; ela nasce do repositório do próprio motor e este registro só aponta para ela",
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
"name": "vxk",
|
|
166
|
-
"version": "0.
|
|
166
|
+
"version": "0.21.0",
|
|
167
167
|
"kind": "cli",
|
|
168
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.",
|
|
169
169
|
"pagesUrl": "https://allansantos-dv.github.io/visual-explainer/",
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
199
|
"name": "cerne",
|
|
200
|
-
"version": "0.4.
|
|
200
|
+
"version": "0.4.14",
|
|
201
201
|
"kind": "daemon",
|
|
202
202
|
"status": "self-managed",
|
|
203
203
|
"summary": "Leitura ativa de vídeo por link ou arquivo: captura, seleção de quadros, transcrição, tradução e consolidação em memória, exposta via servidor MCP por um único daemon local (CerneEngine) compartilhado entre os consumidores.",
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
},
|
|
224
224
|
{
|
|
225
225
|
"name": "skill-engine",
|
|
226
|
-
"version": "0.1
|
|
226
|
+
"version": "0.2.1",
|
|
227
227
|
"kind": "cli",
|
|
228
228
|
"summary": "Skill Manager v2: motor portátil que gerencia skills de agentes fora de qualquer IDE — busca repositórios de skills em quarentena, varre com regras determinísticas e só ativa o que sai limpo, nas pastas que Claude Code, Codex, Copilot e OpenCode leem. CLI `skill-engine`, não é daemon.",
|
|
229
229
|
"pagesPendente": "sem página de produto com vídeo; ela nasce do repositório do próprio motor e este registro só aponta para ela (a sessão do skill-manager a constrói)",
|
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
},
|
|
248
248
|
{
|
|
249
249
|
"name": "client-harness",
|
|
250
|
-
"version": "0.1.
|
|
250
|
+
"version": "0.1.5",
|
|
251
251
|
"kind": "cli",
|
|
252
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
253
|
"pagesUrl": "https://allansantos-dv.github.io/harness-padroes-cliente/",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engine-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
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-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",
|
|
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-rollback.mjs && node tests/test-diagnose.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 && node tests/test-regras-escopo.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",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"shared-infra"
|
|
42
42
|
],
|
|
43
43
|
"author": {
|
|
44
|
-
"name": "
|
|
44
|
+
"name": "The engine-registry maintainers",
|
|
45
45
|
"url": "https://github.com/AllanSantos-DV"
|
|
46
46
|
},
|
|
47
47
|
"repository": {
|