@orkastery/cli 0.5.3 → 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.
Files changed (212) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/mods/autoridade.json +56 -0
  3. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  4. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  5. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  6. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  8. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  9. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  10. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  11. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  12. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  13. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  16. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  17. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  18. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  19. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  20. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  21. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  25. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  26. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  27. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  28. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  29. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  30. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  33. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  34. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  35. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  36. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  37. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  38. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  44. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  45. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  46. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  47. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  48. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  49. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  52. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  53. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  54. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  55. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  56. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  57. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  59. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  60. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  61. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  62. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  63. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  64. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  67. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  68. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  69. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  70. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  71. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  72. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  77. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  78. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  79. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +1 -1
  80. package/adapters/openclaw/README.md +2 -1
  81. package/adapters/openclaw/openclaw.plugin.json +1 -1
  82. package/adapters/openclaw/package.json +1 -1
  83. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  84. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  85. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  86. package/assets/docs-en/produto/README.md +14 -0
  87. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  88. package/assets/docs-en/roadmap/README.md +14 -0
  89. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  90. package/dist/adapters/claude-bg.js +77 -13
  91. package/dist/adapters/codex.js +10 -1
  92. package/dist/agents-md.js +24 -15
  93. package/dist/auditrun.js +17 -2
  94. package/dist/board.js +4 -3
  95. package/dist/branch-de-estado.js +23 -1
  96. package/dist/catalogo.js +33 -0
  97. package/dist/ci.js +13 -7
  98. package/dist/claims-conferir.js +342 -0
  99. package/dist/claims.js +3 -1
  100. package/dist/company-brain-client.js +36 -1
  101. package/dist/company-brain-journal.js +2 -1
  102. package/dist/company-brain-migration.js +36 -1
  103. package/dist/conducao-sentinela.js +388 -0
  104. package/dist/conducao-texto.js +12 -18
  105. package/dist/conducao.js +122 -30
  106. package/dist/conhecimento.js +490 -0
  107. package/dist/docs.js +22 -8
  108. package/dist/doctor-pulse-cron.js +182 -27
  109. package/dist/doctor-rede.js +18 -17
  110. package/dist/doctor.js +115 -111
  111. package/dist/entrega-pr.js +22 -21
  112. package/dist/escopo-escrita.js +10 -1
  113. package/dist/estado-thread.js +23 -1
  114. package/dist/experiencia-instalacao.js +45 -25
  115. package/dist/fabrica-batida.js +310 -0
  116. package/dist/fabrica-estado.js +38 -8
  117. package/dist/fabrica-publicar.js +3 -1
  118. package/dist/fila-pesada.js +444 -0
  119. package/dist/fix.js +3 -2
  120. package/dist/forja.js +1 -1
  121. package/dist/gates.js +3 -28
  122. package/dist/handoff.js +10 -6
  123. package/dist/hitl-local-receipt.js +2 -0
  124. package/dist/hitl-resumo.js +29 -3
  125. package/dist/hitl-tempo-parado.js +4 -4
  126. package/dist/hitl.js +29 -33
  127. package/dist/horario.js +14 -7
  128. package/dist/hosts.js +52 -1
  129. package/dist/impedimento.js +108 -14
  130. package/dist/index.js +623 -521
  131. package/dist/indice.js +7 -1
  132. package/dist/init.js +43 -47
  133. package/dist/instabilidade.js +144 -0
  134. package/dist/integracoes-locais.js +2 -0
  135. package/dist/intelligence-graph-cli.js +92 -21
  136. package/dist/intelligence-graph-contexto.js +56 -17
  137. package/dist/intelligence-graph-contract.js +2 -1
  138. package/dist/intelligence-graph-extract-md.js +56 -4
  139. package/dist/intelligence-graph-extract-ts.js +71 -3
  140. package/dist/intelligence-graph-query.js +66 -19
  141. package/dist/ledger.js +15 -1
  142. package/dist/liveness.js +6 -3
  143. package/dist/locale-ajuda.js +761 -0
  144. package/dist/locale-gerados.js +160 -0
  145. package/dist/locale-master.js +99 -0
  146. package/dist/locale-onboarding.js +92 -0
  147. package/dist/locale-phase.js +151 -0
  148. package/dist/locale-pulse.js +211 -0
  149. package/dist/locale-retry.js +385 -0
  150. package/dist/locale-roadmap.js +102 -0
  151. package/dist/locale-ship.js +494 -0
  152. package/dist/locale-thread.js +277 -0
  153. package/dist/locale-verify.js +125 -0
  154. package/dist/locale.js +714 -0
  155. package/dist/manifest.js +129 -3
  156. package/dist/maquina.js +23 -4
  157. package/dist/master-digest.js +6 -1
  158. package/dist/master-nota.js +4 -6
  159. package/dist/master.js +21 -24
  160. package/dist/mcp-artifacts.js +11 -2
  161. package/dist/mcp-git.js +23 -6
  162. package/dist/mcp-grafo.js +7 -4
  163. package/dist/mcp-server.js +8 -2
  164. package/dist/mcp-ship.js +3 -1
  165. package/dist/mcp-verify.js +6 -1
  166. package/dist/modos.js +3 -6
  167. package/dist/network-roadmap.js +5 -2
  168. package/dist/noctum-arco.js +911 -0
  169. package/dist/noctum-capacidade.js +141 -0
  170. package/dist/noctum-envelope.js +810 -0
  171. package/dist/noctum-execucao.js +304 -0
  172. package/dist/noctum-fila.js +280 -0
  173. package/dist/ocupacao.js +29 -2
  174. package/dist/onboarding.js +40 -21
  175. package/dist/orkmind.js +9 -2
  176. package/dist/orquestracao.js +13 -12
  177. package/dist/parado-no-condutor.js +34 -9
  178. package/dist/phase.js +94 -39
  179. package/dist/policies.js +4 -4
  180. package/dist/portfolio.js +4 -2
  181. package/dist/postura-local.js +234 -0
  182. package/dist/preflight.js +11 -10
  183. package/dist/procedencia.js +225 -0
  184. package/dist/projeto-alvo.js +127 -4
  185. package/dist/prompts.js +6 -2
  186. package/dist/pulse-delivery.js +13 -2
  187. package/dist/pulse.js +52 -19
  188. package/dist/ratelimit.js +4 -2
  189. package/dist/redacao-saida.js +63 -0
  190. package/dist/rede-status.js +3 -2
  191. package/dist/retry.js +232 -148
  192. package/dist/roadmap-reservas.js +20 -3
  193. package/dist/roadmap-status.js +129 -33
  194. package/dist/runtime-context.js +181 -3
  195. package/dist/runtimes.js +2 -1
  196. package/dist/session-events.js +7 -3
  197. package/dist/session-watcher-claude.js +7 -1
  198. package/dist/session-watcher.js +185 -33
  199. package/dist/sessoes-contas.js +1 -1
  200. package/dist/ship-publicar.js +800 -0
  201. package/dist/ship.js +84 -81
  202. package/dist/steal.js +77 -0
  203. package/dist/thread-close.js +3 -2
  204. package/dist/thread.js +153 -82
  205. package/dist/util.js +8 -1
  206. package/dist/verify.js +141 -52
  207. package/dist/vida-da-thread.js +254 -0
  208. package/dist/worktree-local.js +165 -0
  209. package/dist/worktree.js +20 -0
  210. package/dist/yaml.js +27 -8
  211. package/package.json +1 -1
  212. package/schemas/code-artifact-graph.v1.schema.json +2 -1
@@ -7,6 +7,10 @@
7
7
  * pulse configurado (`.orkastery/monitor/pulse-host.json`), o doctor le o `crontab -l` e avisa,
8
8
  * sem bloquear, quando a linha da varredura falta ou bate mais devagar que o template
9
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.
10
14
  */
11
15
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
16
  if (k2 === undefined) k2 = k;
@@ -42,16 +46,27 @@ var __importStar = (this && this.__importStar) || (function () {
42
46
  };
43
47
  })();
44
48
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.COMANDO_DO_TEMPLATE = exports.SCRIPT_DA_VARREDURA = exports.BATIDA_DO_TEMPLATE = void 0;
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;
46
50
  exports.lerCrontabDoSistema = lerCrontabDoSistema;
47
51
  exports.intervaloEmMinutos = intervaloEmMinutos;
48
- exports.linhasDaVarredura = linhasDaVarredura;
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;
49
62
  exports.scriptDaVarreduraDoOrk = scriptDaVarreduraDoOrk;
50
63
  exports.comandoDaVarredura = comandoDaVarredura;
51
64
  exports.checarCronDoPulse = checarCronDoPulse;
52
65
  const fs = __importStar(require("node:fs"));
66
+ const os = __importStar(require("node:os"));
53
67
  const path = __importStar(require("node:path"));
54
68
  const util_1 = require("./util");
69
+ const locale_1 = require("./locale");
55
70
  /** A batida do template `monitor/pulse.cron` (I-50): a menor cadencia das tags. */
56
71
  exports.BATIDA_DO_TEMPLATE = '*/15 * * * *';
57
72
  /** O script que o template agenda; a linha do crontab e achada por ele. */
@@ -64,9 +79,12 @@ function lerCrontabDoSistema() {
64
79
  if (r.ok)
65
80
  return { ok: true, texto: r.stdout };
66
81
  if (r.error === 'ENOENT')
67
- return { ok: false, motivo: 'comando crontab ausente nesta maquina' };
68
- const msg = (r.stderr || r.stdout).trim().split('\n')[0] ?? '';
69
- return { ok: false, motivo: msg ? `crontab -l falhou: ${msg}` : `crontab -l saiu ${r.code}` };
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) };
70
88
  }
71
89
  const MACROS = { '@hourly': 60, '@daily': 1440, '@midnight': 1440, '@weekly': 10080,
72
90
  '@monthly': 43200, '@yearly': 525600, '@annually': 525600 };
@@ -124,13 +142,140 @@ function dividirLinha(linha) {
124
142
  const m = /^(\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s+(.+)$/.exec(t);
125
143
  return m ? { quando: m[1], comando: m[2] } : null;
126
144
  }
127
- /** Linhas ativas (nao comentario, nao variavel) que agendam a varredura do pulse. */
128
- function linhasDaVarredura(crontab) {
129
- return crontab.split('\n').map(l => l.trim())
130
- .filter(l => l && !l.startsWith('#') && !/^[A-Za-z_][A-Za-z0-9_]*\s*=/.test(l) && l.includes(exports.SCRIPT_DA_VARREDURA));
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
+ }
131
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';
132
277
  /** Aspas de shell so quando o caminho precisa (a linha vai colada no crontab, que roda por `sh`). */
133
- function citar(caminho) {
278
+ function citarNoShell(caminho) {
134
279
  return /^[A-Za-z0-9_./:@+-]+$/.test(caminho) ? caminho : `'${caminho.replace(/'/g, `'\\''`)}'`;
135
280
  }
136
281
  /**
@@ -154,27 +299,40 @@ function scriptDaVarreduraDoOrk(dirDoDist = __dirname) {
154
299
  function comandoDaVarredura(raizDoProjeto, scriptDoOrk = scriptDaVarreduraDoOrk()) {
155
300
  const doProjeto = path.join(raizDoProjeto, 'monitor', exports.SCRIPT_DA_VARREDURA);
156
301
  if (fs.existsSync(doProjeto) || !scriptDoOrk)
157
- return citar(doProjeto);
158
- return `ORK_PULSE_PROJECT=${citar(raizDoProjeto)} ${citar(scriptDoOrk)}`;
302
+ return citarNoShell(doProjeto);
303
+ return `ORK_PULSE_PROJECT=${citarNoShell(raizDoProjeto)} ${citarNoShell(scriptDoOrk)}`;
159
304
  }
160
305
  /**
161
- * Check "cadencia do pulse no cron". `null` quando o projeto nao tem transporte do pulse: sem
162
- * `pulse-host.json` nao ha resumo para entregar, e o doctor nao fala de cron.
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`.
163
311
  */
164
- function checarCronDoPulse(dirMonitor, raizDoCheckout, ler = lerCrontabDoSistema, scriptDoOrk = scriptDaVarreduraDoOrk()) {
165
- if (!fs.existsSync(path.join(dirMonitor, 'pulse-host.json')))
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)
166
315
  return null;
167
316
  const nome = 'cadencia do pulse no cron';
168
317
  const comandoSugerido = comandoDaVarredura(raizDoCheckout, scriptDoOrk);
169
318
  const linhaNova = (comando) => `${exports.BATIDA_DO_TEMPLATE} ${comando}`;
170
- const ausente = (detalhe) => ({ nome, nivel: 'warn', detalhe,
171
- correcao: `adicione ao crontab (crontab -e), como no template monitor/pulse.cron: ${linhaNova(comandoSugerido)}` });
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);
172
328
  const leitura = ler();
173
329
  if (!leitura.ok)
174
- return ausente(`pulse-host.json presente, mas sem crontab para ler (${leitura.motivo}): a varredura nao bate`);
175
- const linhas = linhasDaVarredura(leitura.texto);
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());
176
334
  if (linhas.length === 0)
177
- return ausente(`pulse-host.json presente, e nenhuma linha do crontab roda ${exports.SCRIPT_DA_VARREDURA}: o resumo nao sai`);
335
+ return semCron(comHost ? (0, locale_1.msg)().doctor.cronSemLinha(exports.SCRIPT_DA_VARREDURA) : (0, locale_1.msg)().doctor.batidaSemLinha(exports.SCRIPT_DA_VARREDURA));
178
336
  const lentas = [];
179
337
  for (const linha of linhas) {
180
338
  const partes = dividirLinha(linha);
@@ -183,12 +341,9 @@ function checarCronDoPulse(dirMonitor, raizDoCheckout, ler = lerCrontabDoSistema
183
341
  lentas.push({ linha, comando: partes?.comando ?? comandoSugerido, intervalo });
184
342
  }
185
343
  if (lentas.length < linhas.length) {
186
- return { nome, nivel: 'ok', detalhe: `varredura no cron a cada 15 minutos ou menos (${linhas.length} linha(s))` };
344
+ return { nome, nivel: 'ok', detalhe: (0, locale_1.msg)().doctor.cronOk(linhas.length) };
187
345
  }
188
346
  const l = lentas[0];
189
- const ritmo = l.intervalo === null ? 'com horario que o doctor nao le' : l.intervalo === 60 ? 'de hora em hora'
190
- : `a cada ${l.intervalo} minutos`;
191
- return { nome, nivel: 'warn',
192
- detalhe: `a varredura do pulse bate ${ritmo} (\`${l.linha}\`): a cadencia por tag (RM-039) pede 15 minutos`,
193
- correcao: `troque a linha no crontab (crontab -e) por: ${linhaNova(l.comando)}` };
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)) };
194
349
  }
@@ -41,7 +41,7 @@ exports.checarRede = checarRede;
41
41
  * RM-053 (fatia 2): o check "rede" do `ork doctor`.
42
42
  *
43
43
  * A rede pessoal publica sozinha (batida do pulse e eventos de thread) e so deixava rastro em
44
- * `~/.orkastery/rede/rede.log`: a srvjcp86 ficou de 02/10 em diante como membro herdado da fabrica,
44
+ * `~/.orkastery/rede/rede.log`: a maquina-b ficou de 02/10 em diante como membro herdado da fabrica,
45
45
  * com toda publicacao caindo em `rede.sem-repositorio`, e o doctor dizia PRONTO sem uma palavra.
46
46
  *
47
47
  * O check diz a adesao, a casa, a ultima batida publicada e a ultima falha do log. Ele so le
@@ -56,6 +56,7 @@ const rede_1 = require("./rede");
56
56
  const rede_adesao_1 = require("./rede-adesao");
57
57
  const rede_status_1 = require("./rede-status");
58
58
  const saida_segura_1 = require("./saida-segura");
59
+ const locale_1 = require("./locale");
59
60
  /** O fim do `rede.log` que o doctor le: o log so cresce, e o que importa e o mais recente. */
60
61
  exports.CAUDA_DO_LOG_BYTES = 64 * 1024;
61
62
  /** As linhas legiveis do fim do `rede.log`, da mais antiga para a mais nova. Sem log, lista vazia. */
@@ -102,16 +103,16 @@ const curto = (s, teto = 200) => {
102
103
  /** A correcao pela familia do erro da ultima falha (o prefixo tipado, como `rede.sem-repositorio`). */
103
104
  function correcaoDaFalha(erro) {
104
105
  const codigo = /^([a-z]+\.[a-z-]+):/.exec(erro)?.[1] ?? '';
105
- if (codigo === 'rede.sem-repositorio' || codigo === 'rede.repositorio-alheio') {
106
- return 'a casa da rede ainda nao existe: o dono roda ork network entrar, que cria o repositorio privado; ou ork network sair, para tirar esta maquina da rede';
107
- }
106
+ const m = (0, locale_1.msg)().doctor;
107
+ if (codigo === 'rede.sem-repositorio' || codigo === 'rede.repositorio-alheio')
108
+ return m.redeSemCasaCorrecao;
108
109
  if (codigo === 'rede.sem-forja')
109
- return 'faca login na forja (gh auth login ou glab auth login) e rode ork network publicar';
110
+ return m.redeSemForjaCorrecao;
110
111
  if (codigo === 'rede.repositorio-publico')
111
- return 'torne a casa da rede privada na forja; nada e publicado num repositorio publico';
112
+ return m.redePublicaCorrecao;
112
113
  if (codigo === 'rede.nome-em-uso')
113
- return 'escolha outro nome com ork network entrar --maquina NOME, ou tome este com --forcar';
114
- return 'ork network status mostra as lacunas; ork network publicar tenta de novo';
114
+ return m.redeNomeCorrecao;
115
+ return m.redeOutraCorrecao;
115
116
  }
116
117
  /** O check "rede". `agoraMs` e o log ficam injetaveis para o teste. */
117
118
  function checarRede(opcoes = {}) {
@@ -120,8 +121,8 @@ function checarRede(opcoes = {}) {
120
121
  const adesao = (0, rede_adesao_1.adesaoDaRede)();
121
122
  if (!adesao.membro) {
122
123
  return { nome, nivel: 'ok', detalhe: adesao.config && !adesao.config.membro
123
- ? 'fora da rede (saiu com ork network sair)'
124
- : 'fora da rede; ork network entrar poe esta maquina na rede da pessoa' };
124
+ ? (0, locale_1.msg)().doctor.redeSaiu
125
+ : (0, locale_1.msg)().doctor.foraDaRede };
125
126
  }
126
127
  const marca = (0, rede_1.lerMarcaDaRede)();
127
128
  const config = adesao.config;
@@ -131,19 +132,19 @@ function checarRede(opcoes = {}) {
131
132
  const falha = [...registros].reverse().find((r) => r.acao === 'falhou') ?? null;
132
133
  const emBatida = marca && typeof marca.em === 'string' && Number.isFinite(Date.parse(marca.em)) ? marca.em : null;
133
134
  const idade = emBatida ? agora - Date.parse(emBatida) : NaN;
135
+ const m = (0, locale_1.msg)().doctor;
134
136
  const partes = [
135
- adesao.adesao === 'fabrica' ? 'membro herdado da fabrica' : 'membro',
136
- casa ? `casa ${curto(casa, 120)}` : 'casa ainda nao gravada',
137
- emBatida ? `ultima batida ${(0, horario_1.formatarDataHora)(emBatida)} (ha ${(0, rede_status_1.duracao)(idade)})` : 'nenhuma batida publicada',
138
- falha ? `ultima falha ${(0, horario_1.formatarDataHora)(falha.ts)}${falha.origem ? ` (${curto(falha.origem, 20)})` : ''}: ${curto(falha.erro ?? 'sem detalhe')}`
139
- : 'nenhuma falha no rede.log',
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,
140
142
  ];
141
143
  // A falha mais nova que a ultima batida e o estado de agora; a de antes ja passou.
142
144
  const falhaAtual = !!falha && (!emBatida || Date.parse(falha.ts) > Date.parse(emBatida));
143
145
  const parada = !emBatida || !(idade <= rede_status_1.SEM_BATIDA_MS);
144
146
  if (!falhaAtual && !parada)
145
147
  return { nome, nivel: 'ok', detalhe: partes.join('; ') };
146
- const correcao = falhaAtual ? correcaoDaFalha(falha.erro ?? '')
147
- : 'a batida sai do pulse: confira o cron do pulse e rode ork network publicar';
148
+ const correcao = falhaAtual ? correcaoDaFalha(falha.erro ?? '') : m.redeParadaCorrecao;
148
149
  return { nome, nivel: 'warn', detalhe: partes.join('; '), correcao };
149
150
  }