@orkastery/cli 0.5.1 → 0.5.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/adapters/claude-code/README.md +158 -0
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/ork.md +9 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/codex/skills/ork/SKILL.md +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
- package/adapters/openclaw/README.md +26 -5
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +46 -11
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +49 -11
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +116 -18
- package/dist/adapters/codex.js +14 -4
- package/dist/agents-md.js +24 -14
- package/dist/auditrun.js +22 -3
- package/dist/board.js +20 -17
- package/dist/branch-de-estado.js +88 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +21 -8
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +41 -1
- package/dist/company-brain-client.js +36 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +36 -1
- package/dist/conducao-sentinela.js +388 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +122 -30
- package/dist/conhecimento.js +490 -0
- package/dist/docs.js +70 -17
- package/dist/doctor-pulse-cron.js +349 -0
- package/dist/doctor-rede.js +150 -0
- package/dist/doctor.js +195 -111
- package/dist/entrega-pr.js +42 -29
- package/dist/escopo-escrita.js +10 -1
- package/dist/esgotamentos.js +181 -0
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +52 -14
- package/dist/fabrica-publicar.js +9 -1
- package/dist/fechamento.js +20 -66
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +31 -7
- package/dist/gates.js +3 -24
- package/dist/handoff.js +10 -6
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +38 -3
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/hitl.js +29 -33
- package/dist/horario.js +41 -7
- package/dist/hosts.js +104 -11
- package/dist/impedimento.js +299 -0
- package/dist/index.js +961 -542
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +9 -1
- package/dist/init.js +56 -43
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +4 -0
- package/dist/intelligence-graph-cli.js +170 -16
- package/dist/intelligence-graph-contexto.js +254 -0
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/intelligence-graph-query.js +66 -19
- package/dist/leases.js +664 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +18 -1
- package/dist/licoes.js +1 -0
- package/dist/liveness.js +6 -3
- package/dist/locale-ajuda.js +761 -0
- package/dist/locale-gerados.js +160 -0
- package/dist/locale-master.js +99 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-pulse.js +211 -0
- package/dist/locale-retry.js +385 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-ship.js +494 -0
- package/dist/locale-thread.js +277 -0
- package/dist/locale-verify.js +125 -0
- package/dist/locale.js +714 -0
- package/dist/maestro-runtime.js +4 -1
- package/dist/manifest.js +149 -4
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +18 -11
- package/dist/master.js +145 -31
- package/dist/mcp-artifacts.js +43 -3
- package/dist/mcp-git.js +42 -8
- package/dist/mcp-grafo-worker.js +5 -3
- package/dist/mcp-grafo.js +55 -6
- package/dist/mcp-server.js +22 -3
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/memoria.js +48 -6
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +196 -18
- package/dist/noctum-arco.js +911 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-envelope.js +810 -0
- package/dist/noctum-execucao.js +304 -0
- package/dist/noctum-fila.js +280 -0
- package/dist/ocupacao.js +40 -5
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +99 -7
- package/dist/orquestracao.js +24 -21
- package/dist/parado-no-condutor.js +219 -38
- package/dist/phase.js +160 -52
- package/dist/policies.js +56 -5
- package/dist/portfolio.js +8 -3
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +132 -5
- package/dist/prompts.js +40 -1
- package/dist/prova-ativacao.js +75 -6
- package/dist/pulse-delivery.js +47 -4
- package/dist/pulse.js +98 -19
- package/dist/ratelimit.js +24 -6
- package/dist/recall.js +3 -1
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +337 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +291 -140
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +150 -34
- package/dist/runtime-context.js +181 -3
- package/dist/runtime-profiles.js +22 -1
- package/dist/runtimes.js +2 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +120 -24
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -18
- package/dist/sessoes.js +83 -2
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +157 -88
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +299 -49
- package/dist/util.js +8 -1
- package/dist/verify.js +141 -52
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +165 -0
- package/dist/worktree.js +25 -2
- package/dist/yaml.js +27 -8
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
- package/skills/README.md +1 -1
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RM-039 (B6): o `ork doctor` acusa o pulse que bate devagar demais.
|
|
4
|
+
*
|
|
5
|
+
* A cadencia por tag (#OrkPulseOn-15m e as outras) so vale inteira com a varredura do cron de 15
|
|
6
|
+
* em 15 minutos. A instalacao antiga fica em `0 * * * *`, e nada avisava. Com o transporte do
|
|
7
|
+
* pulse configurado (`.orkastery/monitor/pulse-host.json`), o doctor le o `crontab -l` e avisa,
|
|
8
|
+
* sem bloquear, quando a linha da varredura falta ou bate mais devagar que o template
|
|
9
|
+
* `monitor/pulse.cron`. O doctor nunca edita o crontab: a correcao traz a linha pronta.
|
|
10
|
+
*
|
|
11
|
+
* RM-063 (F1): com thread aberta no projeto, a falta da batida REPROVA, com ou sem `pulse-host.json`.
|
|
12
|
+
* Sem transporte o doctor calava, e o maquina-b ficou de 03/10 a 12/10/2026 sem varredura nenhuma. A
|
|
13
|
+
* correcao e o `ork fabrica batida instalar`, que instala a linha do template de forma idempotente.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.COMANDO_DE_INSTALAR_A_BATIDA = exports.ATRASO_DA_BATIDA_MIN = exports.COMANDO_DO_TEMPLATE = exports.SCRIPT_DA_VARREDURA = exports.BATIDA_DO_TEMPLATE = void 0;
|
|
50
|
+
exports.lerCrontabDoSistema = lerCrontabDoSistema;
|
|
51
|
+
exports.intervaloEmMinutos = intervaloEmMinutos;
|
|
52
|
+
exports.valorNoShell = valorNoShell;
|
|
53
|
+
exports.projetoDaLinha = projetoDaLinha;
|
|
54
|
+
exports.scriptsDaLinha = scriptsDaLinha;
|
|
55
|
+
exports.entradasDaVarredura = entradasDaVarredura;
|
|
56
|
+
exports.entradaDoProjeto = entradaDoProjeto;
|
|
57
|
+
exports.mesmoCaminho = mesmoCaminho;
|
|
58
|
+
exports.entradaNaoEDeOutro = entradaNaoEDeOutro;
|
|
59
|
+
exports.ultimaRodadaDaVarredura = ultimaRodadaDaVarredura;
|
|
60
|
+
exports.threadsAbertasNoEstado = threadsAbertasNoEstado;
|
|
61
|
+
exports.citarNoShell = citarNoShell;
|
|
62
|
+
exports.scriptDaVarreduraDoOrk = scriptDaVarreduraDoOrk;
|
|
63
|
+
exports.comandoDaVarredura = comandoDaVarredura;
|
|
64
|
+
exports.checarCronDoPulse = checarCronDoPulse;
|
|
65
|
+
const fs = __importStar(require("node:fs"));
|
|
66
|
+
const os = __importStar(require("node:os"));
|
|
67
|
+
const path = __importStar(require("node:path"));
|
|
68
|
+
const util_1 = require("./util");
|
|
69
|
+
const locale_1 = require("./locale");
|
|
70
|
+
/** A batida do template `monitor/pulse.cron` (I-50): a menor cadencia das tags. */
|
|
71
|
+
exports.BATIDA_DO_TEMPLATE = '*/15 * * * *';
|
|
72
|
+
/** O script que o template agenda; a linha do crontab e achada por ele. */
|
|
73
|
+
exports.SCRIPT_DA_VARREDURA = 'varredura-pulse.sh';
|
|
74
|
+
/** Comando do template antes da troca do caminho pelo checkout de quem instala. */
|
|
75
|
+
exports.COMANDO_DO_TEMPLATE = '/CAMINHO/orkastery/monitor/varredura-pulse.sh';
|
|
76
|
+
/** Le o crontab do usuario do processo, sem shell e com prazo. */
|
|
77
|
+
function lerCrontabDoSistema() {
|
|
78
|
+
const r = (0, util_1.exec)('crontab', ['-l'], undefined, 5000);
|
|
79
|
+
if (r.ok)
|
|
80
|
+
return { ok: true, texto: r.stdout };
|
|
81
|
+
if (r.error === 'ENOENT')
|
|
82
|
+
return { ok: false, motivo: (0, locale_1.msg)().doctor.cronSemComando };
|
|
83
|
+
// RM-063 (F1): o usuario sem crontab tem um crontab vazio, nao uma leitura que falhou; e o caso da maquina nova.
|
|
84
|
+
if (/no crontab for /i.test(`${r.stderr}${r.stdout}`))
|
|
85
|
+
return { ok: true, texto: '' };
|
|
86
|
+
const primeira = (r.stderr || r.stdout).trim().split('\n')[0] ?? '';
|
|
87
|
+
return { ok: false, motivo: primeira ? (0, locale_1.msg)().doctor.cronFalhou(primeira) : (0, locale_1.msg)().doctor.cronSaiu(r.code) };
|
|
88
|
+
}
|
|
89
|
+
const MACROS = { '@hourly': 60, '@daily': 1440, '@midnight': 1440, '@weekly': 10080,
|
|
90
|
+
'@monthly': 43200, '@yearly': 525600, '@annually': 525600 };
|
|
91
|
+
/**
|
|
92
|
+
* Maior intervalo, em minutos, entre duas batidas da linha (so os campos de minuto e hora; dia,
|
|
93
|
+
* mes e dia da semana restritos contam como mais devagar que uma hora). `null` quando a expressao
|
|
94
|
+
* nao e reconhecida: o doctor entao avisa pela linha, sem adivinhar.
|
|
95
|
+
*/
|
|
96
|
+
function intervaloEmMinutos(expressao) {
|
|
97
|
+
const campos = expressao.trim().split(/\s+/);
|
|
98
|
+
if (campos.length === 1 && campos[0].startsWith('@'))
|
|
99
|
+
return MACROS[campos[0]] ?? null;
|
|
100
|
+
if (campos.length !== 5)
|
|
101
|
+
return null;
|
|
102
|
+
const [minuto, hora, dia, mes, semana] = campos;
|
|
103
|
+
const minutos = valoresDoCampo(minuto, 0, 59);
|
|
104
|
+
if (!minutos)
|
|
105
|
+
return null;
|
|
106
|
+
if (hora !== '*' || dia !== '*' || mes !== '*' || semana !== '*')
|
|
107
|
+
return Math.max(60, maiorLacuna(minutos, 60));
|
|
108
|
+
return maiorLacuna(minutos, 60);
|
|
109
|
+
}
|
|
110
|
+
function valoresDoCampo(campo, min, max) {
|
|
111
|
+
const valores = new Set();
|
|
112
|
+
for (const parte of campo.split(',')) {
|
|
113
|
+
const m = /^(\*|(\d+)(?:-(\d+))?)(?:\/(\d+))?$/.exec(parte);
|
|
114
|
+
if (!m)
|
|
115
|
+
return null;
|
|
116
|
+
const inicio = m[1] === '*' ? min : Number(m[2]);
|
|
117
|
+
const fim = m[1] === '*' ? max : m[3] !== undefined ? Number(m[3]) : m[4] !== undefined ? max : inicio;
|
|
118
|
+
const passo = m[4] !== undefined ? Number(m[4]) : 1;
|
|
119
|
+
if (passo < 1 || inicio < min || fim > max || inicio > fim)
|
|
120
|
+
return null;
|
|
121
|
+
for (let v = inicio; v <= fim; v += passo)
|
|
122
|
+
valores.add(v);
|
|
123
|
+
}
|
|
124
|
+
return [...valores].sort((a, b) => a - b);
|
|
125
|
+
}
|
|
126
|
+
/** Maior distancia entre dois valores consecutivos, contando a volta do ciclo. */
|
|
127
|
+
function maiorLacuna(valores, ciclo) {
|
|
128
|
+
let maior = 0;
|
|
129
|
+
for (let i = 0; i < valores.length; i++) {
|
|
130
|
+
const proximo = i + 1 < valores.length ? valores[i + 1] : valores[0] + ciclo;
|
|
131
|
+
maior = Math.max(maior, proximo - valores[i]);
|
|
132
|
+
}
|
|
133
|
+
return maior;
|
|
134
|
+
}
|
|
135
|
+
/** Separa a expressao de horario do comando numa linha do crontab. */
|
|
136
|
+
function dividirLinha(linha) {
|
|
137
|
+
const t = linha.trim();
|
|
138
|
+
if (t.startsWith('@')) {
|
|
139
|
+
const m = /^(@\S+)\s+(.+)$/.exec(t);
|
|
140
|
+
return m ? { quando: m[1], comando: m[2] } : null;
|
|
141
|
+
}
|
|
142
|
+
const m = /^(\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(.+)$/.exec(t);
|
|
143
|
+
return m ? { quando: m[1], comando: m[2] } : null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* RM-063 (F1, GO-FIX): um valor como o `sh` do cron o le: aspas simples literais; aspas duplas com `$HOME`; sem aspas,
|
|
147
|
+
* com o `~` do comeco e `$HOME`. So o que a linha da varredura usa; o resto fica como esta.
|
|
148
|
+
*/
|
|
149
|
+
function valorNoShell(bruto, home = os.homedir()) {
|
|
150
|
+
const comHome = (v) => v.replace(/\$\{HOME\}|\$HOME(?![A-Za-z0-9_])/g, home);
|
|
151
|
+
if (bruto.length >= 2 && bruto.startsWith(`'`) && bruto.endsWith(`'`))
|
|
152
|
+
return bruto.slice(1, -1).replace(/'\\''/g, `'`);
|
|
153
|
+
if (bruto.length >= 2 && bruto.startsWith('"') && bruto.endsWith('"'))
|
|
154
|
+
return comHome(bruto.slice(1, -1).replace(/\\(["\\$`])/g, '$1'));
|
|
155
|
+
return comHome(bruto === '~' ? home : bruto.startsWith('~/') ? home + bruto.slice(1) : bruto);
|
|
156
|
+
}
|
|
157
|
+
/** Um valor de atribuicao ou um caminho: entre aspas simples, entre aspas duplas, ou ate o espaco. */
|
|
158
|
+
const VALOR = String.raw `(?:'(?:[^']|'\\'')*'|"(?:[^"\\]|\\.)*"|[^\s;&|<>()]+)`;
|
|
159
|
+
/** O valor de `ORK_PULSE_PROJECT=` na linha, como o `sh` o le; `null` quando a linha nao o diz. */
|
|
160
|
+
function projetoDaLinha(linha, home = os.homedir()) {
|
|
161
|
+
const m = new RegExp(`(?:^|\\s)ORK_PULSE_PROJECT=(${VALOR})`).exec(linha);
|
|
162
|
+
return m ? valorNoShell(m[1], home) : null;
|
|
163
|
+
}
|
|
164
|
+
/** Os scripts da varredura citados na linha, como o `sh` os le; `varredura-pulse.sh.bak` nao e o script. */
|
|
165
|
+
function scriptsDaLinha(linha, home = os.homedir()) {
|
|
166
|
+
const nome = exports.SCRIPT_DA_VARREDURA.replace(/\./g, '\\.');
|
|
167
|
+
const re = new RegExp(`(?:^|[\\s=])('[^']*${nome}'|"[^"]*${nome}"|[^\\s'";&|<>()]*${nome})(?=$|[\\s;&|<>)])`, 'g');
|
|
168
|
+
return [...linha.matchAll(re)].map((m) => valorNoShell(m[1], home));
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* RM-063 (F1, GO-FIX): as entradas da varredura, na ordem. A linha de ambiente do crontab (`NOME=valor`) vale para as
|
|
172
|
+
* entradas que vem depois dela: o template sem `ORK_PULSE_PROJECT` abaixo de `ORK_PULSE_PROJECT=/outro` varre o outro.
|
|
173
|
+
*/
|
|
174
|
+
function entradasDaVarredura(crontab, home = os.homedir()) {
|
|
175
|
+
const ambiente = {};
|
|
176
|
+
const entradas = [];
|
|
177
|
+
for (const linha of crontab.split('\n')) {
|
|
178
|
+
const t = linha.trim();
|
|
179
|
+
if (!t || t.startsWith('#'))
|
|
180
|
+
continue;
|
|
181
|
+
const env = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(t);
|
|
182
|
+
if (env) {
|
|
183
|
+
ambiente[env[1]] = valorNoShell(env[2].trim(), ambiente.HOME ?? home);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const casa = ambiente.HOME ?? home;
|
|
187
|
+
const scripts = scriptsDaLinha(t, casa);
|
|
188
|
+
if (!scripts.length)
|
|
189
|
+
continue;
|
|
190
|
+
entradas.push({ linha, projeto: projetoDaLinha(t, casa) ?? ambiente.ORK_PULSE_PROJECT ?? null, scripts });
|
|
191
|
+
}
|
|
192
|
+
return entradas;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* RM-063 (F1): a entrada roda, com certeza, a varredura DESTE projeto: o `ORK_PULSE_PROJECT` efetivo e ele, ou nao ha
|
|
196
|
+
* projeto nenhum e o script e o do proprio checkout (que entao e o alvo, como no template).
|
|
197
|
+
*/
|
|
198
|
+
function entradaDoProjeto(e, raiz) {
|
|
199
|
+
if (e.projeto !== null)
|
|
200
|
+
return mesmoCaminho(e.projeto, raiz);
|
|
201
|
+
const doCheckout = path.join(raiz, 'monitor', exports.SCRIPT_DA_VARREDURA);
|
|
202
|
+
return e.scripts.some((s) => mesmoCaminho(s, doCheckout));
|
|
203
|
+
}
|
|
204
|
+
/** O mesmo caminho, escrito igual ou pelo mesmo destino real. */
|
|
205
|
+
function mesmoCaminho(a, b) {
|
|
206
|
+
if (path.resolve(a) === path.resolve(b))
|
|
207
|
+
return true;
|
|
208
|
+
try {
|
|
209
|
+
return fs.realpathSync(a) === fs.realpathSync(b);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* A entrada vale para o aviso de cadencia (sem thread aberta) quando nao aponta OUTRO projeto: a linha antiga do
|
|
217
|
+
* template, sem projeto, continua valendo como na RM-039.
|
|
218
|
+
*/
|
|
219
|
+
function entradaNaoEDeOutro(e, raiz) {
|
|
220
|
+
return e.projeto === null || mesmoCaminho(e.projeto, raiz);
|
|
221
|
+
}
|
|
222
|
+
/** Acima disto sem rodada nova no `hitl.log`, a batida esta atrasada: duas batidas do template perdidas. */
|
|
223
|
+
exports.ATRASO_DA_BATIDA_MIN = 30;
|
|
224
|
+
function ultimaRodadaDaVarredura(dirMonitor) {
|
|
225
|
+
const arquivo = path.join(dirMonitor, 'hitl.log');
|
|
226
|
+
let texto;
|
|
227
|
+
try {
|
|
228
|
+
const fd = fs.openSync(arquivo, 'r');
|
|
229
|
+
try {
|
|
230
|
+
const tamanho = fs.fstatSync(fd).size, lido = Math.min(tamanho, 64 * 1024);
|
|
231
|
+
const buffer = Buffer.alloc(lido);
|
|
232
|
+
fs.readSync(fd, buffer, 0, lido, tamanho - lido);
|
|
233
|
+
texto = buffer.toString('utf8');
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
fs.closeSync(fd);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
for (const linha of texto.split('\n').reverse()) {
|
|
243
|
+
try {
|
|
244
|
+
const e = JSON.parse(linha);
|
|
245
|
+
if (e.tipo !== 'pulse_scan' || typeof e.ts !== 'string' || !Number.isFinite(Date.parse(e.ts)))
|
|
246
|
+
continue;
|
|
247
|
+
return { em: e.ts, code: typeof e.code === 'number' ? e.code : null, detalhe: typeof e.detalhe === 'string' ? e.detalhe : '' };
|
|
248
|
+
}
|
|
249
|
+
catch { /* linha cortada no inicio do trecho, ou ilegivel */ }
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
/** RM-063 (F1): threads nao fechadas no estado do projeto (`.orkastery/threads/<id>/thread.json`). */
|
|
254
|
+
function threadsAbertasNoEstado(dirEstado) {
|
|
255
|
+
const dir = path.join(dirEstado, 'threads');
|
|
256
|
+
let nomes;
|
|
257
|
+
try {
|
|
258
|
+
nomes = fs.readdirSync(dir);
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
let abertas = 0;
|
|
264
|
+
for (const nome of nomes) {
|
|
265
|
+
try {
|
|
266
|
+
const t = JSON.parse(fs.readFileSync(path.join(dir, nome, 'thread.json'), 'utf8'));
|
|
267
|
+
// O registro de adocao nao tem trabalho a conduzir; a thread pausada espera o dono, e a batida e quem o lembra.
|
|
268
|
+
if (t.status !== 'fechada' && t.origem !== 'adocao')
|
|
269
|
+
abertas++;
|
|
270
|
+
}
|
|
271
|
+
catch { /* pasta sem thread.json, ou ilegivel: nao e thread a vigiar */ }
|
|
272
|
+
}
|
|
273
|
+
return abertas;
|
|
274
|
+
}
|
|
275
|
+
/** O comando que instala a batida; e a correcao do doctor quando ela falta (RM-063, F1). */
|
|
276
|
+
exports.COMANDO_DE_INSTALAR_A_BATIDA = 'ork fabrica batida instalar';
|
|
277
|
+
/** Aspas de shell so quando o caminho precisa (a linha vai colada no crontab, que roda por `sh`). */
|
|
278
|
+
function citarNoShell(caminho) {
|
|
279
|
+
return /^[A-Za-z0-9_./:@+-]+$/.test(caminho) ? caminho : `'${caminho.replace(/'/g, `'\\''`)}'`;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* O script da varredura que este `ork` traz: no pacote do npm, `monitor/` fica ao lado de `dist/`; no
|
|
283
|
+
* checkout, na raiz do repositorio, dois niveis acima de `core/dist/`. `null` quando nenhum existe.
|
|
284
|
+
*/
|
|
285
|
+
function scriptDaVarreduraDoOrk(dirDoDist = __dirname) {
|
|
286
|
+
for (const dir of [path.join(dirDoDist, '..', 'monitor'), path.join(dirDoDist, '..', '..', 'monitor')]) {
|
|
287
|
+
const script = path.join(dir, exports.SCRIPT_DA_VARREDURA);
|
|
288
|
+
if (fs.existsSync(script))
|
|
289
|
+
return path.resolve(script);
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* O comando que a linha nova do crontab roda. Ensaio de 03/10/2026 (RM-049): a correcao apontava
|
|
295
|
+
* `<projeto>/monitor/varredura-pulse.sh`, que so existe no checkout do proprio Orkastery; quem instalou
|
|
296
|
+
* pelo npm recebia um caminho que nao existe. Quando o projeto nao traz o script, a linha usa o do `ork`
|
|
297
|
+
* instalado e diz o projeto por `ORK_PULSE_PROJECT` (o cron nao roda no diretorio do projeto).
|
|
298
|
+
*/
|
|
299
|
+
function comandoDaVarredura(raizDoProjeto, scriptDoOrk = scriptDaVarreduraDoOrk()) {
|
|
300
|
+
const doProjeto = path.join(raizDoProjeto, 'monitor', exports.SCRIPT_DA_VARREDURA);
|
|
301
|
+
if (fs.existsSync(doProjeto) || !scriptDoOrk)
|
|
302
|
+
return citarNoShell(doProjeto);
|
|
303
|
+
return `ORK_PULSE_PROJECT=${citarNoShell(raizDoProjeto)} ${citarNoShell(scriptDoOrk)}`;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Check "cadencia do pulse no cron". `null` quando o projeto nao tem transporte do pulse nem thread
|
|
307
|
+
* aberta: sem `pulse-host.json` e sem thread a vigiar, o doctor nao fala de cron.
|
|
308
|
+
*
|
|
309
|
+
* RM-063 (F1): com thread aberta (`abertas`, contadas no estado ao lado de `dirMonitor`), a falta da
|
|
310
|
+
* batida reprova, com ou sem transporte, e a correcao e o `ork fabrica batida instalar`.
|
|
311
|
+
*/
|
|
312
|
+
function checarCronDoPulse(dirMonitor, raizDoCheckout, ler = lerCrontabDoSistema, scriptDoOrk = scriptDaVarreduraDoOrk(), abertas = threadsAbertasNoEstado(path.dirname(dirMonitor))) {
|
|
313
|
+
const comHost = fs.existsSync(path.join(dirMonitor, 'pulse-host.json'));
|
|
314
|
+
if (!comHost && abertas === 0)
|
|
315
|
+
return null;
|
|
316
|
+
const nome = 'cadencia do pulse no cron';
|
|
317
|
+
const comandoSugerido = comandoDaVarredura(raizDoCheckout, scriptDoOrk);
|
|
318
|
+
const linhaNova = (comando) => `${exports.BATIDA_DO_TEMPLATE} ${comando}`;
|
|
319
|
+
const ausente = (detalhe) => abertas > 0
|
|
320
|
+
? { nome, nivel: 'fail', detalhe: (0, locale_1.msg)().doctor.batidaAusente(abertas, detalhe),
|
|
321
|
+
correcao: (0, locale_1.msg)().doctor.batidaInstale(exports.COMANDO_DE_INSTALAR_A_BATIDA) }
|
|
322
|
+
: { nome, nivel: 'warn', detalhe, correcao: (0, locale_1.msg)().doctor.cronAdicione(linhaNova(comandoSugerido)) };
|
|
323
|
+
// RM-063 (F1, GO-FIX): a varredura que bate por outro agendador (timer do systemd, batida do host) se prova pelo hitl.log.
|
|
324
|
+
const rodada = ultimaRodadaDaVarredura(dirMonitor);
|
|
325
|
+
const haMin = rodada ? Math.floor((Date.now() - Date.parse(rodada.em)) / 60000) : null;
|
|
326
|
+
const semCron = (detalhe) => haMin !== null && haMin >= 0 && haMin <= exports.ATRASO_DA_BATIDA_MIN
|
|
327
|
+
? { nome, nivel: 'ok', detalhe: (0, locale_1.msg)().doctor.batidaPeloLog(haMin) } : ausente(detalhe);
|
|
328
|
+
const leitura = ler();
|
|
329
|
+
if (!leitura.ok)
|
|
330
|
+
return semCron(comHost ? (0, locale_1.msg)().doctor.cronSemLeitura(leitura.motivo) : (0, locale_1.msg)().doctor.batidaSemLeitura(leitura.motivo));
|
|
331
|
+
// Com thread aberta, so a batida DESTE projeto; sem thread, o aviso de cadencia aceita a linha antiga sem projeto.
|
|
332
|
+
const linhas = entradasDaVarredura(leitura.texto)
|
|
333
|
+
.filter(e => abertas > 0 ? entradaDoProjeto(e, raizDoCheckout) : entradaNaoEDeOutro(e, raizDoCheckout)).map(e => e.linha.trim());
|
|
334
|
+
if (linhas.length === 0)
|
|
335
|
+
return semCron(comHost ? (0, locale_1.msg)().doctor.cronSemLinha(exports.SCRIPT_DA_VARREDURA) : (0, locale_1.msg)().doctor.batidaSemLinha(exports.SCRIPT_DA_VARREDURA));
|
|
336
|
+
const lentas = [];
|
|
337
|
+
for (const linha of linhas) {
|
|
338
|
+
const partes = dividirLinha(linha);
|
|
339
|
+
const intervalo = partes ? intervaloEmMinutos(partes.quando) : null;
|
|
340
|
+
if (intervalo === null || intervalo > 15)
|
|
341
|
+
lentas.push({ linha, comando: partes?.comando ?? comandoSugerido, intervalo });
|
|
342
|
+
}
|
|
343
|
+
if (lentas.length < linhas.length) {
|
|
344
|
+
return { nome, nivel: 'ok', detalhe: (0, locale_1.msg)().doctor.cronOk(linhas.length) };
|
|
345
|
+
}
|
|
346
|
+
const l = lentas[0];
|
|
347
|
+
return { nome, nivel: 'warn', detalhe: (0, locale_1.msg)().doctor.cronLento((0, locale_1.msg)().doctor.cronRitmo(l.intervalo), l.linha),
|
|
348
|
+
correcao: (0, locale_1.msg)().doctor.cronTroque(linhaNova(l.comando)) };
|
|
349
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CAUDA_DO_LOG_BYTES = void 0;
|
|
37
|
+
exports.caudaDoLogDaRede = caudaDoLogDaRede;
|
|
38
|
+
exports.correcaoDaFalha = correcaoDaFalha;
|
|
39
|
+
exports.checarRede = checarRede;
|
|
40
|
+
/**
|
|
41
|
+
* RM-053 (fatia 2): o check "rede" do `ork doctor`.
|
|
42
|
+
*
|
|
43
|
+
* A rede pessoal publica sozinha (batida do pulse e eventos de thread) e so deixava rastro em
|
|
44
|
+
* `~/.orkastery/rede/rede.log`: a maquina-b ficou de 02/10 em diante como membro herdado da fabrica,
|
|
45
|
+
* com toda publicacao caindo em `rede.sem-repositorio`, e o doctor dizia PRONTO sem uma palavra.
|
|
46
|
+
*
|
|
47
|
+
* O check diz a adesao, a casa, a ultima batida publicada e a ultima falha do log. Ele so le
|
|
48
|
+
* arquivos locais (`rede.json`, `maquina.json`, `rede/publicada.json`, `rede/rede.log`): nao chama
|
|
49
|
+
* a forja, nao cria estado e roda de qualquer diretorio. Nunca reprova: a rede e opcional e o
|
|
50
|
+
* doctor e gate de despacho; o pior nivel e `warn`, com a correcao.
|
|
51
|
+
*/
|
|
52
|
+
const fs = __importStar(require("node:fs"));
|
|
53
|
+
const path = __importStar(require("node:path"));
|
|
54
|
+
const horario_1 = require("./horario");
|
|
55
|
+
const rede_1 = require("./rede");
|
|
56
|
+
const rede_adesao_1 = require("./rede-adesao");
|
|
57
|
+
const rede_status_1 = require("./rede-status");
|
|
58
|
+
const saida_segura_1 = require("./saida-segura");
|
|
59
|
+
const locale_1 = require("./locale");
|
|
60
|
+
/** O fim do `rede.log` que o doctor le: o log so cresce, e o que importa e o mais recente. */
|
|
61
|
+
exports.CAUDA_DO_LOG_BYTES = 64 * 1024;
|
|
62
|
+
/** As linhas legiveis do fim do `rede.log`, da mais antiga para a mais nova. Sem log, lista vazia. */
|
|
63
|
+
function caudaDoLogDaRede(arquivo = path.join((0, rede_adesao_1.pastaDaRede)(), 'rede.log')) {
|
|
64
|
+
let texto;
|
|
65
|
+
try {
|
|
66
|
+
const fd = fs.openSync(arquivo, 'r');
|
|
67
|
+
try {
|
|
68
|
+
const tamanho = fs.fstatSync(fd).size;
|
|
69
|
+
const inicio = Math.max(0, tamanho - exports.CAUDA_DO_LOG_BYTES);
|
|
70
|
+
const buf = Buffer.alloc(tamanho - inicio);
|
|
71
|
+
fs.readSync(fd, buf, 0, buf.length, inicio);
|
|
72
|
+
texto = buf.toString('utf8');
|
|
73
|
+
// A primeira linha de uma cauda cortada vem pela metade.
|
|
74
|
+
if (inicio > 0)
|
|
75
|
+
texto = texto.slice(texto.indexOf('\n') + 1);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
fs.closeSync(fd);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const registros = [];
|
|
85
|
+
for (const linha of texto.split('\n')) {
|
|
86
|
+
if (!linha.trim())
|
|
87
|
+
continue;
|
|
88
|
+
try {
|
|
89
|
+
const r = JSON.parse(linha);
|
|
90
|
+
if (typeof r.ts !== 'string' || !Number.isFinite(Date.parse(r.ts)) || typeof r.acao !== 'string')
|
|
91
|
+
continue;
|
|
92
|
+
registros.push({ ts: r.ts, acao: r.acao, origem: typeof r.origem === 'string' ? r.origem : null,
|
|
93
|
+
erro: typeof r.erro === 'string' ? r.erro : null });
|
|
94
|
+
}
|
|
95
|
+
catch { /* linha ilegivel: o log e informativo */ }
|
|
96
|
+
}
|
|
97
|
+
return registros;
|
|
98
|
+
}
|
|
99
|
+
const curto = (s, teto = 200) => {
|
|
100
|
+
const limpo = (0, saida_segura_1.emUmaLinha)(s).trim();
|
|
101
|
+
return limpo.length > teto ? `${limpo.slice(0, teto - 3)}...` : limpo;
|
|
102
|
+
};
|
|
103
|
+
/** A correcao pela familia do erro da ultima falha (o prefixo tipado, como `rede.sem-repositorio`). */
|
|
104
|
+
function correcaoDaFalha(erro) {
|
|
105
|
+
const codigo = /^([a-z]+\.[a-z-]+):/.exec(erro)?.[1] ?? '';
|
|
106
|
+
const m = (0, locale_1.msg)().doctor;
|
|
107
|
+
if (codigo === 'rede.sem-repositorio' || codigo === 'rede.repositorio-alheio')
|
|
108
|
+
return m.redeSemCasaCorrecao;
|
|
109
|
+
if (codigo === 'rede.sem-forja')
|
|
110
|
+
return m.redeSemForjaCorrecao;
|
|
111
|
+
if (codigo === 'rede.repositorio-publico')
|
|
112
|
+
return m.redePublicaCorrecao;
|
|
113
|
+
if (codigo === 'rede.nome-em-uso')
|
|
114
|
+
return m.redeNomeCorrecao;
|
|
115
|
+
return m.redeOutraCorrecao;
|
|
116
|
+
}
|
|
117
|
+
/** O check "rede". `agoraMs` e o log ficam injetaveis para o teste. */
|
|
118
|
+
function checarRede(opcoes = {}) {
|
|
119
|
+
const nome = 'rede';
|
|
120
|
+
const agora = opcoes.agoraMs ?? Date.now();
|
|
121
|
+
const adesao = (0, rede_adesao_1.adesaoDaRede)();
|
|
122
|
+
if (!adesao.membro) {
|
|
123
|
+
return { nome, nivel: 'ok', detalhe: adesao.config && !adesao.config.membro
|
|
124
|
+
? (0, locale_1.msg)().doctor.redeSaiu
|
|
125
|
+
: (0, locale_1.msg)().doctor.foraDaRede };
|
|
126
|
+
}
|
|
127
|
+
const marca = (0, rede_1.lerMarcaDaRede)();
|
|
128
|
+
const config = adesao.config;
|
|
129
|
+
const casa = config?.host && config.dono && config.repositorio ? `${config.host}/${config.dono}/${config.repositorio}`
|
|
130
|
+
: typeof marca?.casa === 'string' ? marca.casa : null;
|
|
131
|
+
const registros = caudaDoLogDaRede(opcoes.log);
|
|
132
|
+
const falha = [...registros].reverse().find((r) => r.acao === 'falhou') ?? null;
|
|
133
|
+
const emBatida = marca && typeof marca.em === 'string' && Number.isFinite(Date.parse(marca.em)) ? marca.em : null;
|
|
134
|
+
const idade = emBatida ? agora - Date.parse(emBatida) : NaN;
|
|
135
|
+
const m = (0, locale_1.msg)().doctor;
|
|
136
|
+
const partes = [
|
|
137
|
+
adesao.adesao === 'fabrica' ? m.redeMembroHerdado : m.redeMembro,
|
|
138
|
+
casa ? m.redeCasa(curto(casa, 120)) : m.redeSemCasa,
|
|
139
|
+
emBatida ? m.redeBatida((0, horario_1.formatarDataHora)(emBatida), (0, rede_status_1.duracao)(idade)) : m.redeSemBatida,
|
|
140
|
+
falha ? m.redeFalha((0, horario_1.formatarDataHora)(falha.ts), falha.origem ? curto(falha.origem, 20) : null, curto(falha.erro ?? m.redeSemDetalhe))
|
|
141
|
+
: m.redeSemFalha,
|
|
142
|
+
];
|
|
143
|
+
// A falha mais nova que a ultima batida e o estado de agora; a de antes ja passou.
|
|
144
|
+
const falhaAtual = !!falha && (!emBatida || Date.parse(falha.ts) > Date.parse(emBatida));
|
|
145
|
+
const parada = !emBatida || !(idade <= rede_status_1.SEM_BATIDA_MS);
|
|
146
|
+
if (!falhaAtual && !parada)
|
|
147
|
+
return { nome, nivel: 'ok', detalhe: partes.join('; ') };
|
|
148
|
+
const correcao = falhaAtual ? correcaoDaFalha(falha.erro ?? '') : m.redeParadaCorrecao;
|
|
149
|
+
return { nome, nivel: 'warn', detalhe: partes.join('; '), correcao };
|
|
150
|
+
}
|