@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.
Files changed (260) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/ork.md +9 -0
  5. package/adapters/claude-code/mods/autoridade.json +56 -0
  6. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  8. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  9. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  10. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  11. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  12. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  13. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  16. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  17. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  18. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  19. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  20. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  21. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  25. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  27. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  28. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  29. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  30. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  33. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  35. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  36. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  37. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  38. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  44. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  45. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  46. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  47. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  48. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  49. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  52. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  53. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  54. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  55. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  56. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  57. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  59. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  61. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  62. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  63. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  64. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  67. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  68. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  69. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  70. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  71. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  72. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  77. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  78. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  79. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  80. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  82. package/adapters/codex/skills/ork/SKILL.md +18 -3
  83. package/adapters/hermes/README.md +1 -0
  84. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  85. package/adapters/hermes/hermes.plugin.json +1 -0
  86. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  87. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
  88. package/adapters/openclaw/README.md +26 -5
  89. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  90. package/adapters/openclaw/dist/index.js +46 -11
  91. package/adapters/openclaw/openclaw.plugin.json +5 -1
  92. package/adapters/openclaw/package.json +2 -2
  93. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  94. package/adapters/openclaw/src/index.ts +49 -11
  95. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  96. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  97. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  98. package/assets/docs-en/produto/README.md +14 -0
  99. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  100. package/assets/docs-en/roadmap/README.md +14 -0
  101. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  102. package/assets/orkmind_bridge.py +117 -9
  103. package/dist/adapters/claude-bg.js +116 -18
  104. package/dist/adapters/codex.js +14 -4
  105. package/dist/agents-md.js +24 -14
  106. package/dist/auditrun.js +22 -3
  107. package/dist/board.js +20 -17
  108. package/dist/branch-de-estado.js +88 -10
  109. package/dist/busca-semantica.js +52 -12
  110. package/dist/canarios-rm057.js +149 -0
  111. package/dist/canarios.js +2 -0
  112. package/dist/catalogo.js +33 -0
  113. package/dist/ci.js +21 -8
  114. package/dist/claims-conferir.js +342 -0
  115. package/dist/claims.js +41 -1
  116. package/dist/company-brain-client.js +36 -1
  117. package/dist/company-brain-context.js +158 -22
  118. package/dist/company-brain-journal.js +2 -1
  119. package/dist/company-brain-migration.js +36 -1
  120. package/dist/conducao-sentinela.js +388 -0
  121. package/dist/conducao-texto.js +12 -18
  122. package/dist/conducao.js +122 -30
  123. package/dist/conhecimento.js +490 -0
  124. package/dist/docs.js +70 -17
  125. package/dist/doctor-pulse-cron.js +349 -0
  126. package/dist/doctor-rede.js +150 -0
  127. package/dist/doctor.js +195 -111
  128. package/dist/entrega-pr.js +42 -29
  129. package/dist/escopo-escrita.js +10 -1
  130. package/dist/esgotamentos.js +181 -0
  131. package/dist/estado-thread.js +23 -1
  132. package/dist/experiencia-instalacao.js +45 -25
  133. package/dist/experiencia.js +12 -2
  134. package/dist/fabrica-batida.js +310 -0
  135. package/dist/fabrica-estado.js +52 -14
  136. package/dist/fabrica-publicar.js +9 -1
  137. package/dist/fechamento.js +20 -66
  138. package/dist/fila-pesada.js +444 -0
  139. package/dist/fix.js +3 -2
  140. package/dist/forja.js +31 -7
  141. package/dist/gates.js +3 -24
  142. package/dist/handoff.js +10 -6
  143. package/dist/hitl-contract.js +59 -4
  144. package/dist/hitl-curto.js +11 -6
  145. package/dist/hitl-gates.js +5 -0
  146. package/dist/hitl-local-receipt.js +2 -0
  147. package/dist/hitl-lote.js +5 -2
  148. package/dist/hitl-resumo.js +38 -3
  149. package/dist/hitl-tempo-parado.js +201 -0
  150. package/dist/hitl-texto-livre.js +5 -5
  151. package/dist/hitl.js +29 -33
  152. package/dist/horario.js +41 -7
  153. package/dist/hosts.js +104 -11
  154. package/dist/impedimento.js +299 -0
  155. package/dist/index.js +961 -542
  156. package/dist/indice-vetorial.js +87 -10
  157. package/dist/indice.js +9 -1
  158. package/dist/init.js +56 -43
  159. package/dist/instabilidade.js +144 -0
  160. package/dist/integracoes-locais.js +4 -0
  161. package/dist/intelligence-graph-cli.js +170 -16
  162. package/dist/intelligence-graph-contexto.js +254 -0
  163. package/dist/intelligence-graph-contract.js +2 -1
  164. package/dist/intelligence-graph-extract-md.js +56 -4
  165. package/dist/intelligence-graph-extract-ts.js +71 -3
  166. package/dist/intelligence-graph-parsers.js +6 -4
  167. package/dist/intelligence-graph-query.js +66 -19
  168. package/dist/leases.js +664 -87
  169. package/dist/ledger-stats.js +15 -1
  170. package/dist/ledger.js +18 -1
  171. package/dist/licoes.js +1 -0
  172. package/dist/liveness.js +6 -3
  173. package/dist/locale-ajuda.js +761 -0
  174. package/dist/locale-gerados.js +160 -0
  175. package/dist/locale-master.js +99 -0
  176. package/dist/locale-onboarding.js +92 -0
  177. package/dist/locale-phase.js +151 -0
  178. package/dist/locale-pulse.js +211 -0
  179. package/dist/locale-retry.js +385 -0
  180. package/dist/locale-roadmap.js +102 -0
  181. package/dist/locale-ship.js +494 -0
  182. package/dist/locale-thread.js +277 -0
  183. package/dist/locale-verify.js +125 -0
  184. package/dist/locale.js +714 -0
  185. package/dist/maestro-runtime.js +4 -1
  186. package/dist/manifest.js +149 -4
  187. package/dist/maquina.js +23 -4
  188. package/dist/master-digest.js +6 -1
  189. package/dist/master-nota.js +18 -11
  190. package/dist/master.js +145 -31
  191. package/dist/mcp-artifacts.js +43 -3
  192. package/dist/mcp-git.js +42 -8
  193. package/dist/mcp-grafo-worker.js +5 -3
  194. package/dist/mcp-grafo.js +55 -6
  195. package/dist/mcp-server.js +22 -3
  196. package/dist/mcp-ship.js +3 -1
  197. package/dist/mcp-verify.js +6 -1
  198. package/dist/memoria.js +48 -6
  199. package/dist/modos.js +3 -6
  200. package/dist/network-roadmap.js +196 -18
  201. package/dist/noctum-arco.js +911 -0
  202. package/dist/noctum-capacidade.js +141 -0
  203. package/dist/noctum-envelope.js +810 -0
  204. package/dist/noctum-execucao.js +304 -0
  205. package/dist/noctum-fila.js +280 -0
  206. package/dist/ocupacao.js +40 -5
  207. package/dist/onboarding.js +40 -21
  208. package/dist/orkmind.js +99 -7
  209. package/dist/orquestracao.js +24 -21
  210. package/dist/parado-no-condutor.js +219 -38
  211. package/dist/phase.js +160 -52
  212. package/dist/policies.js +56 -5
  213. package/dist/portfolio.js +8 -3
  214. package/dist/postura-local.js +234 -0
  215. package/dist/preflight.js +11 -10
  216. package/dist/procedencia.js +225 -0
  217. package/dist/projeto-alvo.js +132 -5
  218. package/dist/prompts.js +40 -1
  219. package/dist/prova-ativacao.js +75 -6
  220. package/dist/pulse-delivery.js +47 -4
  221. package/dist/pulse.js +98 -19
  222. package/dist/ratelimit.js +24 -6
  223. package/dist/recall.js +3 -1
  224. package/dist/redacao-saida.js +63 -0
  225. package/dist/rede-adesao.js +399 -0
  226. package/dist/rede-forja.js +289 -0
  227. package/dist/rede-projetos.js +239 -0
  228. package/dist/rede-status.js +337 -0
  229. package/dist/rede.js +980 -0
  230. package/dist/retry.js +291 -140
  231. package/dist/roadmap-reservas.js +20 -3
  232. package/dist/roadmap-status.js +150 -34
  233. package/dist/runtime-context.js +181 -3
  234. package/dist/runtime-profiles.js +22 -1
  235. package/dist/runtimes.js +2 -1
  236. package/dist/saida-segura.js +58 -0
  237. package/dist/session-events.js +7 -3
  238. package/dist/session-watcher-claude.js +120 -24
  239. package/dist/session-watcher.js +185 -33
  240. package/dist/sessoes-contas.js +187 -0
  241. package/dist/sessoes-inventario.js +6 -18
  242. package/dist/sessoes.js +83 -2
  243. package/dist/ship-publicar.js +800 -0
  244. package/dist/ship.js +157 -88
  245. package/dist/steal.js +77 -0
  246. package/dist/thread-close.js +3 -2
  247. package/dist/thread.js +299 -49
  248. package/dist/util.js +8 -1
  249. package/dist/verify.js +141 -52
  250. package/dist/vida-da-thread.js +254 -0
  251. package/dist/worktree-local.js +165 -0
  252. package/dist/worktree.js +25 -2
  253. package/dist/yaml.js +27 -8
  254. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  255. package/package.json +7 -3
  256. package/references/definition-of-done.md +1 -1
  257. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  258. package/skills/README.md +1 -1
  259. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  260. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -0,0 +1,310 @@
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.CRONTAB_DO_SISTEMA = exports.MARCA_DA_BATIDA = exports.ultimaRodadaDaVarredura = exports.ATRASO_DA_BATIDA_MIN = void 0;
37
+ exports.gravarCrontabDoSistema = gravarCrontabDoSistema;
38
+ exports.caminhoDaBatida = caminhoDaBatida;
39
+ exports.scriptDaBatida = scriptDaBatida;
40
+ exports.linhaDaBatida = linhaDaBatida;
41
+ exports.linhaGerenciada = linhaGerenciada;
42
+ exports.estadoDaBatida = estadoDaBatida;
43
+ exports.instalarBatida = instalarBatida;
44
+ exports.removerBatida = removerBatida;
45
+ exports.textoDoEstadoDaBatida = textoDoEstadoDaBatida;
46
+ exports.textoDoResultadoDaBatida = textoDoResultadoDaBatida;
47
+ /**
48
+ * RM-063 (F1): a batida da fabrica, instalada e vigiada pelo proprio `ork`.
49
+ *
50
+ * Incidente do maquina-b, de 03/10 a 12/10/2026 (P1 e P7 da pagina da RM-063): o cron da varredura do
51
+ * pulse nunca foi instalado, o `ork doctor` so conferia o crontab com `pulse-host.json` e, sem
52
+ * transporte, calava; a correcao (editar o crontab) foi barrada como persistencia nao autorizada. A
53
+ * maquina ficou nove dias sem batida e ninguem soube.
54
+ *
55
+ * `ork fabrica batida instalar|status|remover` e o comando que o dono autoriza uma vez: instala a linha
56
+ * do template `monitor/pulse.cron` com o checkout canonico e o PATH que acha o `node`, o `claude` e o
57
+ * transporte, preserva as outras entradas do crontab e grava o evento no ledger da maquina
58
+ * (`~/.orkastery/ledger.jsonl`). A linha e marcada (`# ork-batida`) e diz o projeto
59
+ * (`ORK_PULSE_PROJECT`): e por elas que o `remover` tira so a linha dela.
60
+ *
61
+ * O crontab nunca e gravado sem ter sido lido, e a gravacao so vale depois de relida. O executor e
62
+ * injetado: os testes usam um crontab falso e nunca tocam no da maquina.
63
+ */
64
+ const fs = __importStar(require("node:fs"));
65
+ const path = __importStar(require("node:path"));
66
+ const node_child_process_1 = require("node:child_process");
67
+ const doctor_pulse_cron_1 = require("./doctor-pulse-cron");
68
+ var doctor_pulse_cron_2 = require("./doctor-pulse-cron");
69
+ Object.defineProperty(exports, "ATRASO_DA_BATIDA_MIN", { enumerable: true, get: function () { return doctor_pulse_cron_2.ATRASO_DA_BATIDA_MIN; } });
70
+ Object.defineProperty(exports, "ultimaRodadaDaVarredura", { enumerable: true, get: function () { return doctor_pulse_cron_2.ultimaRodadaDaVarredura; } });
71
+ const estado_thread_1 = require("./estado-thread");
72
+ const ledger_1 = require("./ledger");
73
+ const maquina_1 = require("./maquina");
74
+ const horario_1 = require("./horario");
75
+ const util_1 = require("./util");
76
+ const procedencia_1 = require("./procedencia");
77
+ /** O comentario no fim da linha que o `ork` instalou; o `sh` do cron o ignora. */
78
+ exports.MARCA_DA_BATIDA = '# ork-batida';
79
+ /** Os binarios que a varredura chama, alem do `node`: o runtime, a forja e o git. */
80
+ const BINARIOS_DA_VARREDURA = ['claude', 'codex', 'gh', 'git'];
81
+ const PATH_DO_SISTEMA = ['/usr/local/bin', '/usr/bin', '/bin'];
82
+ /** `crontab -` com o texto no stdin, sem shell e com prazo. */
83
+ function gravarCrontabDoSistema(texto) {
84
+ const r = (0, node_child_process_1.spawnSync)('crontab', ['-'], { input: texto, encoding: 'utf8', timeout: 5000 });
85
+ if (r.status === 0)
86
+ return { ok: true };
87
+ const erro = r.error ? r.error.code ?? r.error.message : null;
88
+ if (erro === 'ENOENT')
89
+ return { ok: false, motivo: 'comando crontab ausente nesta maquina' };
90
+ const primeira = `${r.stderr ?? ''}${r.stdout ?? ''}`.trim().split('\n')[0] ?? '';
91
+ return { ok: false, motivo: primeira ? `crontab - falhou: ${primeira}` : `crontab - saiu ${r.status ?? erro ?? 'sem codigo'}` };
92
+ }
93
+ exports.CRONTAB_DO_SISTEMA = { ler: doctor_pulse_cron_1.lerCrontabDoSistema, gravar: gravarCrontabDoSistema };
94
+ /** Caminho que entra na linha do cron: absoluto, sem `%` (o cron o troca por quebra de linha) e sem controle. */
95
+ function caminhoAceito(caminho) {
96
+ return path.isAbsolute(caminho) && !/[%\p{Cc}]/u.test(caminho);
97
+ }
98
+ /** Diretorio aceito no PATH da linha: alem do caminho aceito, sem `:`, o separador do PATH. */
99
+ function diretorioAceito(dir) {
100
+ return caminhoAceito(dir) && !dir.includes(':');
101
+ }
102
+ /**
103
+ * O executavel do transporte, so do `pulse-host.json` local desta maquina (RM-047, P2): o versionado no git, ou o link,
104
+ * nao poe diretorio nenhum no PATH da batida.
105
+ */
106
+ function transporteDoProjeto(raiz, dirMonitor) {
107
+ const arquivo = path.join(dirMonitor, 'pulse-host.json');
108
+ try {
109
+ (0, procedencia_1.exigirHostLocal)(raiz, arquivo);
110
+ const config = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
111
+ return typeof config.executavel === 'string' && config.executavel.trim() ? config.executavel.trim() : null;
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
117
+ /**
118
+ * O PATH da linha: o diretorio do `node` que roda este `ork`, os do `claude`, `codex`, `gh` e `git` achados
119
+ * agora, o do executavel do transporte (`pulse-host.json`) e o PATH minimo do sistema. O cron nao carrega o
120
+ * perfil do shell; sem isto a varredura nao acha o runtime nem o transporte (o template avisa).
121
+ */
122
+ function caminhoDaBatida(opcoes = {}) {
123
+ const achar = opcoes.achar ?? ((bin) => (0, util_1.noPath)(bin));
124
+ const dirs = [];
125
+ const somar = (arquivo) => {
126
+ if (!arquivo || !path.isAbsolute(arquivo))
127
+ return;
128
+ const dir = path.dirname(arquivo);
129
+ if (diretorioAceito(dir) && !dirs.includes(dir))
130
+ dirs.push(dir);
131
+ };
132
+ somar(opcoes.node ?? process.execPath);
133
+ for (const bin of BINARIOS_DA_VARREDURA)
134
+ somar(achar(bin));
135
+ if (opcoes.transporte)
136
+ somar(path.isAbsolute(opcoes.transporte) ? opcoes.transporte : achar(opcoes.transporte));
137
+ for (const dir of PATH_DO_SISTEMA)
138
+ if (!dirs.includes(dir))
139
+ dirs.push(dir);
140
+ return dirs.join(':');
141
+ }
142
+ /** O script da varredura que a linha roda: o do checkout do projeto, senao o do `ork` instalado. */
143
+ function scriptDaBatida(raiz, scriptDoOrk = (0, doctor_pulse_cron_1.scriptDaVarreduraDoOrk)()) {
144
+ const doProjeto = path.join(raiz, 'monitor', doctor_pulse_cron_1.SCRIPT_DA_VARREDURA);
145
+ return fs.existsSync(doProjeto) ? doProjeto : scriptDoOrk;
146
+ }
147
+ /**
148
+ * A linha que o `ork` instala: a batida do template (`*\/15`), o projeto alvo, o PATH e o script, com a
149
+ * marca no fim. `raiz` e a raiz canonica (o checkout principal, nunca a worktree de uma thread).
150
+ */
151
+ function linhaDaBatida(raiz, opcoes = {}) {
152
+ const script = scriptDaBatida(raiz, opcoes.scriptDoOrk === undefined ? (0, doctor_pulse_cron_1.scriptDaVarreduraDoOrk)() : opcoes.scriptDoOrk);
153
+ if (!script) {
154
+ throw new Error(`fabrica.batida.sem-script: nem ${path.join(raiz, 'monitor', doctor_pulse_cron_1.SCRIPT_DA_VARREDURA)} nem o ` +
155
+ `${doctor_pulse_cron_1.SCRIPT_DA_VARREDURA} do ork instalado existem; reinstale o ork (npm i -g @orkastery/cli)`);
156
+ }
157
+ for (const caminho of [raiz, script]) {
158
+ if (!caminhoAceito(caminho)) {
159
+ throw new Error(`fabrica.batida.caminho-invalido: ${JSON.stringify(caminho)} nao cabe numa linha do cron ` +
160
+ '(precisa ser absoluto, sem % e sem caractere de controle)');
161
+ }
162
+ }
163
+ const PATH = caminhoDaBatida({ node: opcoes.node, achar: opcoes.achar,
164
+ transporte: transporteDoProjeto(raiz, path.join(raiz, '.orkastery', 'monitor')) });
165
+ return `${doctor_pulse_cron_1.BATIDA_DO_TEMPLATE} ORK_PULSE_PROJECT=${(0, doctor_pulse_cron_1.citarNoShell)(raiz)} PATH=${(0, doctor_pulse_cron_1.citarNoShell)(PATH)} ` +
166
+ `${(0, doctor_pulse_cron_1.citarNoShell)(script)} ${exports.MARCA_DA_BATIDA}`;
167
+ }
168
+ /** A linha foi instalada pelo `ork fabrica batida instalar` (traz a marca no fim)? */
169
+ function linhaGerenciada(linha) {
170
+ return linha.trimEnd().endsWith(exports.MARCA_DA_BATIDA);
171
+ }
172
+ /**
173
+ * As entradas da varredura DESTE projeto, separadas pela marca. O projeto efetivo considera a linha de ambiente do
174
+ * crontab acima da entrada: o template abaixo de `ORK_PULSE_PROJECT=/outro` e a batida do outro, e fica.
175
+ */
176
+ function separar(texto, raiz) {
177
+ const doProjeto = (0, doctor_pulse_cron_1.entradasDaVarredura)(texto).filter((e) => (0, doctor_pulse_cron_1.entradaDoProjeto)(e, raiz)).map((e) => e.linha);
178
+ return { gerenciadas: doProjeto.filter(linhaGerenciada), manuais: doProjeto.filter((l) => !linhaGerenciada(l)) };
179
+ }
180
+ function ritmoDa(linha) {
181
+ const t = linha.trim();
182
+ const m = t.startsWith('@') ? /^(@\S+)\s/.exec(t) : /^(\S+\s+\S+\s+\S+\s+\S+\s+\S+)\s/.exec(t);
183
+ return m ? (0, doctor_pulse_cron_1.intervaloEmMinutos)(m[1]) : null;
184
+ }
185
+ /** `ork fabrica batida status`: o que o crontab desta maquina diz da batida deste projeto, e a ultima rodada. */
186
+ function estadoDaBatida(raizDoProjeto, opcoes = {}) {
187
+ const raiz = (0, estado_thread_1.raizDoEstado)(raizDoProjeto);
188
+ const executor = opcoes.executor ?? exports.CRONTAB_DO_SISTEMA;
189
+ const quando = opcoes.quando ?? new Date().toISOString();
190
+ const leitura = executor.ler();
191
+ const { gerenciadas, manuais } = leitura.ok ? separar(leitura.texto, raiz) : { gerenciadas: [], manuais: [] };
192
+ const ritmos = [...gerenciadas, ...manuais].map(ritmoDa).filter((r) => r !== null);
193
+ const rodada = (0, doctor_pulse_cron_1.ultimaRodadaDaVarredura)(path.join(raiz, '.orkastery', 'monitor'));
194
+ return {
195
+ projeto: raiz, maquina: (0, maquina_1.nomeDaMaquina)(),
196
+ leitura: leitura.ok ? { ok: true } : { ok: false, motivo: leitura.motivo },
197
+ gerenciada: gerenciadas[0]?.trim() ?? null, manuais: manuais.map((l) => l.trim()),
198
+ instalada: gerenciadas.length + manuais.length > 0,
199
+ intervaloMin: ritmos.length ? Math.min(...ritmos) : null,
200
+ ultimaRodada: rodada ? { ...rodada, haMin: Math.max(0, Math.floor((Date.parse(quando) - Date.parse(rodada.em)) / 60000)) } : null,
201
+ };
202
+ }
203
+ function lerParaEditar(executor) {
204
+ const leitura = executor.ler();
205
+ // Nunca gravar sem ter lido: um crontab que nao foi lido seria apagado pela gravacao.
206
+ if (!leitura.ok)
207
+ throw new Error(`fabrica.batida.sem-leitura: ${leitura.motivo}; nada foi gravado`);
208
+ return leitura.texto;
209
+ }
210
+ function gravarEConferir(executor, lido, texto, confere) {
211
+ // O crontab que mudou entre a leitura e a gravacao (um `crontab -e`, outro `instalar`) nao e sobrescrito.
212
+ const antes = executor.ler();
213
+ if (!antes.ok || antes.texto !== lido) {
214
+ throw new Error('fabrica.batida.concorrencia: o crontab mudou durante a operacao; nada foi gravado, rode de novo');
215
+ }
216
+ const gravacao = executor.gravar(texto);
217
+ if (!gravacao.ok)
218
+ throw new Error(`fabrica.batida.sem-gravacao: ${gravacao.motivo}`);
219
+ const relido = executor.ler();
220
+ if (!relido.ok || !confere(relido.texto)) {
221
+ throw new Error(`fabrica.batida.nao-confirmada: o crontab relido nao traz o que foi gravado` +
222
+ (relido.ok ? '' : ` (${relido.motivo})`) + '; confira com crontab -l');
223
+ }
224
+ }
225
+ /** O texto do crontab sem as linhas pedidas, com o fim de linha que o cron exige. */
226
+ function semAsLinhas(texto, sair) {
227
+ const ficam = texto.split('\n').filter((l) => !sair.has(l));
228
+ while (ficam.length && ficam[ficam.length - 1].trim() === '')
229
+ ficam.pop();
230
+ return ficam.length ? `${ficam.join('\n')}\n` : '';
231
+ }
232
+ function noLedgerDaMaquina(tipo, dados) {
233
+ return (0, ledger_1.registrar)((0, maquina_1.pastaDoUsuario)(), 'maquina', tipo, { maquina: (0, maquina_1.nomeDaMaquina)(), ...dados }).eventId;
234
+ }
235
+ /**
236
+ * `ork fabrica batida instalar`: idempotente. A linha igual ja instalada nao muda nada; a linha antiga do
237
+ * `ork` e a linha posta a mao da varredura deste projeto saem, e a linha do template entra no lugar. As
238
+ * outras entradas do crontab ficam como estavam.
239
+ */
240
+ function instalarBatida(raizDoProjeto, opcoes = {}) {
241
+ const raiz = (0, estado_thread_1.raizDoEstado)(raizDoProjeto);
242
+ const executor = opcoes.executor ?? exports.CRONTAB_DO_SISTEMA;
243
+ const linha = linhaDaBatida(raiz, opcoes);
244
+ const texto = lerParaEditar(executor);
245
+ const { gerenciadas, manuais } = separar(texto, raiz);
246
+ if (manuais.length === 0 && gerenciadas.length === 1 && gerenciadas[0].trim() === linha) {
247
+ return { acao: 'ja-instalada', projeto: raiz, linha, retiradas: [], manuais: [], eventId: null };
248
+ }
249
+ const retiradas = [...gerenciadas, ...manuais];
250
+ const base = semAsLinhas(texto, new Set(retiradas));
251
+ gravarEConferir(executor, texto, `${base}${linha}\n`, (relido) => relido.split('\n').some((l) => l.trim() === linha));
252
+ const eventId = noLedgerDaMaquina(ledger_1.TIPOS_DE_EVENTO.batidaInstalada, {
253
+ projeto: raiz, linha, retiradas: retiradas.map((l) => l.trim()), por: opcoes.por ?? null,
254
+ evidencia: 'crontab -l relido depois da gravacao traz a linha',
255
+ });
256
+ return { acao: 'instalou', projeto: raiz, linha, retiradas: retiradas.map((l) => l.trim()), manuais: [], eventId };
257
+ }
258
+ /** `ork fabrica batida remover`: tira so a linha que o `ork` instalou para este projeto. */
259
+ function removerBatida(raizDoProjeto, opcoes = {}) {
260
+ const raiz = (0, estado_thread_1.raizDoEstado)(raizDoProjeto);
261
+ const executor = opcoes.executor ?? exports.CRONTAB_DO_SISTEMA;
262
+ const texto = lerParaEditar(executor);
263
+ const { gerenciadas, manuais } = separar(texto, raiz);
264
+ const ficam = manuais.map((l) => l.trim());
265
+ if (gerenciadas.length === 0)
266
+ return { acao: 'nao-instalada', projeto: raiz, linha: null, retiradas: [], manuais: ficam, eventId: null };
267
+ const sair = new Set(gerenciadas);
268
+ gravarEConferir(executor, texto, semAsLinhas(texto, sair), (relido) => separar(relido, raiz).gerenciadas.length === 0);
269
+ const eventId = noLedgerDaMaquina(ledger_1.TIPOS_DE_EVENTO.batidaRemovida, {
270
+ projeto: raiz, retiradas: gerenciadas.map((l) => l.trim()), manuaisQueFicaram: ficam, por: opcoes.por ?? null,
271
+ evidencia: 'crontab -l relido depois da gravacao nao traz a linha do ork',
272
+ });
273
+ return { acao: 'removeu', projeto: raiz, linha: null, retiradas: gerenciadas.map((l) => l.trim()), manuais: ficam, eventId };
274
+ }
275
+ function textoDoEstadoDaBatida(e, quando = new Date().toISOString()) {
276
+ const linhas = [`Batida da fábrica em ${e.maquina}, projeto ${e.projeto}`];
277
+ if (!e.leitura.ok)
278
+ linhas.push(` crontab: não lido (${e.leitura.motivo}); não dá para dizer se a batida está instalada`);
279
+ else if (!e.instalada)
280
+ linhas.push(' instalada: não; instale com: ork fabrica batida instalar');
281
+ else {
282
+ const ritmo = e.intervaloMin === null ? 'com horário que o ork não lê' : `a cada ${e.intervaloMin} min`;
283
+ linhas.push(` instalada: sim, ${ritmo}${e.gerenciada ? ', pelo ork fabrica batida instalar' : ', por linha posta à mão'}`);
284
+ for (const l of [...(e.gerenciada ? [e.gerenciada] : []), ...e.manuais])
285
+ linhas.push(` linha: ${l}`);
286
+ }
287
+ const r = e.ultimaRodada;
288
+ if (!r)
289
+ linhas.push(' última rodada: nenhuma registrada (.orkastery/monitor/hitl.log)');
290
+ else {
291
+ linhas.push(` última rodada: ${(0, horario_1.formatarHora)(r.em, { agora: quando })} (há ${r.haMin} min), ` +
292
+ `${r.code === 0 ? 'ok' : `código ${r.code ?? '?'}`}: ${r.detalhe || 'sem detalhe'}`);
293
+ if (e.instalada && r.haMin > doctor_pulse_cron_1.ATRASO_DA_BATIDA_MIN) {
294
+ linhas.push(` atrasada: nenhuma rodada há ${r.haMin} min com a batida instalada; confira crontab -l e o hitl.log`);
295
+ }
296
+ }
297
+ linhas.push((0, horario_1.legendaDoFuso)());
298
+ return linhas.join('\n');
299
+ }
300
+ function textoDoResultadoDaBatida(r) {
301
+ switch (r.acao) {
302
+ case 'ja-instalada': return `Batida já instalada para ${r.projeto}; nada mudou no crontab.\n linha: ${r.linha}`;
303
+ case 'instalou': return [`Batida instalada para ${r.projeto} (evento ${r.eventId} no ledger da máquina).`, ` linha: ${r.linha}`,
304
+ ...r.retiradas.map((l) => ` saiu: ${l}`), ' confira com: ork fabrica batida status'].join('\n');
305
+ case 'removeu': return [`Batida removida de ${r.projeto} (evento ${r.eventId} no ledger da máquina).`,
306
+ ...r.retiradas.map((l) => ` saiu: ${l}`), ...r.manuais.map((l) => ` ficou (posta à mão, não é do ork): ${l}`)].join('\n');
307
+ case 'nao-instalada': return [`Nenhuma batida do ork instalada para ${r.projeto}; nada mudou no crontab.`,
308
+ ...r.manuais.map((l) => ` ficou (posta à mão, não é do ork): ${l}`)].join('\n');
309
+ }
310
+ }
@@ -57,8 +57,9 @@ exports.painelDaFabricaEmMarkdown = painelDaFabricaEmMarkdown;
57
57
  * GitHub. `ork board`, `ork fabrica` e o resumo do pulse leem as outras maquinas dali. Cada
58
58
  * maquina so escreve o proprio arquivo: push recusado se resolve relendo a ponta.
59
59
  *
60
- * Publicar e empurrar para o remoto do projeto, entao so acontece sozinho com
61
- * `fabrica.compartilhada: true` no manifesto; `ork fabrica publicar` e o caminho explicito. Nada de
60
+ * Publicar e empurrar para o remoto do projeto, entao so acontece sozinho com a adesao da maquina
61
+ * (`ork fabrica entrar`); o `fabrica.compartilhada` do manifesto so pede (RM-047, P5), e
62
+ * `ork fabrica publicar` e o caminho explicito. Nada de
62
63
  * credencial, prompt, log ou caminho local sai daqui: so identificadores, fase, status e o assunto
63
64
  * da pausa que espera o dono.
64
65
  *
@@ -81,6 +82,8 @@ const roadmap_reservas_1 = require("./roadmap-reservas");
81
82
  const thread_1 = require("./thread");
82
83
  const util_1 = require("./util");
83
84
  const versao_1 = require("./versao");
85
+ const saida_segura_1 = require("./saida-segura");
86
+ const vida_da_thread_1 = require("./vida-da-thread");
84
87
  exports.CONTRATO_MAQUINA = 'ork.fabrica-maquina/v1';
85
88
  exports.BRANCH_DA_FABRICA = 'ork/fabrica-estado';
86
89
  /** O diretorio dos retratos na branch; a leitura sem clone (RM-054) le o mesmo. */
@@ -161,17 +164,37 @@ function retratoDaMaquina(carregado, opcoes = {}) {
161
164
  const linhas = new Map(monitor.linhas.map((l) => [l.thread, l]));
162
165
  const entregas = entregasNaBase(raiz, carregado.manifesto.worktree.base_branch, remoto);
163
166
  const itemDaThread = new Map((0, roadmap_reservas_1.reservasLocais)(raiz, remoto).filter((r) => r.thread).map((r) => [r.thread, r.item]));
167
+ let leitura;
168
+ const lerUmaVez = () => (leitura ??= (opcoes.runtime ?? (() => (0, vida_da_thread_1.lerRuntime)(raiz)))());
164
169
  const threads = itens.map(({ thread: t, raiz: raizDoPerfil }) => {
165
170
  const entregue = entregas.get(t.id) ?? null;
166
171
  // RM-037 (fatia 4): o fim de turno sem pergunta e do condutor, e as outras maquinas nao o leem como espera
167
172
  // do dono. Sai so a pausa que o observador abriu nele; outra pausa da thread continua contando.
168
173
  let espera = null;
174
+ let eventos = [];
169
175
  try {
170
- espera = (0, parado_no_condutor_1.esperaDoCondutor)(t, (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raizDoPerfil, t.id)), quando);
176
+ eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raizDoPerfil, t.id));
177
+ }
178
+ catch {
179
+ eventos = [];
180
+ }
181
+ try {
182
+ espera = (0, parado_no_condutor_1.esperaDoCondutor)(t, eventos, quando);
171
183
  }
172
184
  catch {
173
185
  espera = null;
174
186
  }
187
+ // RM-063 (F2): a fabrica so mostra a thread como andando com a sessao viva verificada no runtime.
188
+ let sessao = null;
189
+ if (!entregue) {
190
+ try {
191
+ const v = (0, vida_da_thread_1.vidaDaThread)(t, eventos, lerUmaVez, Date.parse(quando));
192
+ sessao = { viva: v.viva, desdeEm: v.desdeEm, motivo: v.motivo };
193
+ }
194
+ catch {
195
+ sessao = null;
196
+ }
197
+ }
175
198
  const pausas = (linhas.get(t.id)?.pausas ?? []).filter((p) => !(espera && p.motivo === 'human.pending' && p.fonte === 'ledger' &&
176
199
  p.fase === (espera.fase ?? t.faseAtual)));
177
200
  const pausa = pausas[0];
@@ -183,6 +206,7 @@ function retratoDaMaquina(carregado, opcoes = {}) {
183
206
  esperaVoce, pergunta: esperaVoce && pausa ? curto(pausa.pausaSobre || pausa.detalhe) : null,
184
207
  paradaDesde: esperaVoce && pausa ? pausa.desdeEm : null,
185
208
  ...despachoDaThread((0, thread_1.dirThread)(raizDoPerfil, t.id)),
209
+ sessao,
186
210
  };
187
211
  }).sort((a, b) => a.id.localeCompare(b.id));
188
212
  const eu = (0, roadmap_reservas_1.quemSouEu)(raiz, { por: opcoes.por, maquina: opcoes.maquina });
@@ -234,7 +258,8 @@ function gravarMarca(raiz, marca) {
234
258
  */
235
259
  function publicarMaquina(carregado, opcoes = {}) {
236
260
  const raiz = carregado.raiz;
237
- const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
261
+ // RM-047: antes do retrato e da marca, para o remoto invalido recusar mesmo sem mudanca a enviar.
262
+ const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
238
263
  const estado = retratoDaMaquina(carregado, { ...opcoes, remoto });
239
264
  const assinatura = assinaturaDoRetrato(estado);
240
265
  const marca = lerMarca(raiz);
@@ -274,7 +299,7 @@ function publicarMaquina(carregado, opcoes = {}) {
274
299
  /** `ork fabrica sair`: tira o retrato desta maquina da branch. `null` quando nao havia retrato. */
275
300
  function removerMaquina(carregado, opcoes = {}) {
276
301
  const raiz = carregado.raiz;
277
- const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
302
+ const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
278
303
  const maquina = (0, roadmap_reservas_1.quemSouEu)(raiz, { maquina: opcoes.maquina }).maquina;
279
304
  for (let tentativa = 1; tentativa <= TENTATIVAS; tentativa++) {
280
305
  const { ponta, atualizado } = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, PREFIXO, 30000);
@@ -321,18 +346,29 @@ function tabela(cabecalho, linhas) {
321
346
  const linha = (l) => ' ' + l.map((c, i) => c.padEnd(largura[i])).join(' ').trimEnd();
322
347
  return [linha(cabecalho), linha(largura.map((n) => '-'.repeat(n))), ...linhas.map(linha)];
323
348
  }
349
+ /** RM-063 (F2): a coluna da sessao: `viva`, `parada desde HH:MM: <motivo>`, ou `-` no retrato que nao a traz. */
350
+ function sessaoNaFabrica(t, teto) {
351
+ const v = (0, vida_da_thread_1.vidaDoRetrato)(t.sessao);
352
+ return v.viva ? 'viva' : v.motivo === null ? '-' : curto((0, vida_da_thread_1.paradaDesde)(v), teto);
353
+ }
324
354
  function blocoDaMaquina(m, eu) {
325
355
  const vivas = ativas(m), entregues = m.threads.length - vivas.length;
326
356
  const cabeca = `${m.maquina}${m.maquina === eu ? ' (esta maquina)' : ''}, ${m.por}, publicado ${(0, horario_1.formatarDataHora)(m.publicadoEm)}: ` +
327
357
  `${vivas.length} thread(s) ativa(s)` + (entregues ? `, ${entregues} entregue(s) sem MASTER` : '');
328
358
  if (vivas.length === 0)
329
359
  return [cabeca];
330
- return [cabeca, ...tabela(['THREAD', 'MODO', 'FASE', 'RUNTIME', 'ITEM', 'ESPERA VOCE'], vivas.map((t) => [
360
+ return [cabeca, ...tabela(['THREAD', 'MODO', 'FASE', 'RUNTIME', 'ITEM', 'ESPERA VOCE', 'SESSAO'], vivas.map((t) => [
331
361
  t.id, t.modo, t.fase, runtimeDaThread(t), t.roadmap ?? '-', t.esperaVoce ? `sim: ${curto(t.pergunta ?? 'veredito', 40)}` : '-',
362
+ sessaoNaFabrica(t, 70),
332
363
  ]))];
333
364
  }
334
- /** `ork fabrica`: todas as maquinas. */
335
- function textoDaFabrica(p, eu) {
365
+ /**
366
+ * `ork fabrica`: todas as maquinas. RM-053 (fatia 2): o painel vem do remoto, onde outras pessoas
367
+ * escrevem; cada valor dele (o `por`, o `projeto`, a pergunta) chega a tela numa linha so, sem nada
368
+ * que o terminal execute.
369
+ */
370
+ function textoDaFabrica(painel, eu) {
371
+ const p = (0, saida_segura_1.valoresEmUmaLinha)(painel);
336
372
  const linhas = [];
337
373
  if (!p.atualizado)
338
374
  linhas.push('AVISO: sem leitura nova do remoto; esta e a ultima copia lida nesta maquina.', '');
@@ -345,8 +381,9 @@ function textoDaFabrica(p, eu) {
345
381
  linhas.push((0, horario_1.legendaDoFuso)());
346
382
  return linhas.join('\n');
347
383
  }
348
- /** A secao de `ork board` com as outras maquinas. */
349
- function textoDasOutrasMaquinas(p, eu) {
384
+ /** A secao de `ork board` com as outras maquinas, saneada como o `ork fabrica`. */
385
+ function textoDasOutrasMaquinas(painel, eu) {
386
+ const p = (0, saida_segura_1.valoresEmUmaLinha)(painel);
350
387
  const outras = p.maquinas.filter((m) => m.maquina !== eu);
351
388
  const origem = p.atualizado ? 'lido agora' : 'ultima copia local';
352
389
  if (outras.length === 0)
@@ -364,17 +401,18 @@ function painelDaFabricaEmMarkdown(maquinas) {
364
401
  'O que cada máquina está conduzindo agora. Gerado pelo `ork fabrica publicar`; não edite à mão.',
365
402
  'Antes de pegar um item do roadmap: `ork roadmap reservas`.',
366
403
  '',
367
- '| Máquina | Thread | Modo | Fase | Runtime | Item | Espera você | Publicado |',
368
- '| --- | --- | --- | --- | --- | --- | --- | --- |',
404
+ '| Máquina | Thread | Modo | Fase | Runtime | Item | Espera você | Publicado | Sessão |',
405
+ '| --- | --- | --- | --- | --- | --- | --- | --- | --- |',
369
406
  ];
370
407
  const vivas = maquinas.flatMap((m) => ativas(m).map((t) => ({ m, t })));
371
408
  if (vivas.length === 0)
372
- linhas.push('| — | nenhuma thread ativa | — | — | | — | — | — |');
409
+ linhas.push('| — | nenhuma thread ativa | — | — | | — | — | — | — |');
373
410
  for (const { m, t } of vivas) {
374
411
  const pergunta = t.esperaVoce ? `sim: ${curto(t.pergunta ?? 'veredito', 60).replace(/\|/g, '/')}` : '—';
375
412
  // RM-037 (sugestao 6 do CHECK 1): a celula sem despacho diz isso, sem travessao novo.
376
413
  const runtime = t.runtime || t.modelo ? runtimeDaThread(t).replace(/\|/g, '/') : 'sem despacho';
377
- linhas.push(`| ${m.maquina} | ${t.id} | ${t.modo} | ${t.fase} | ${runtime} | ${t.roadmap ?? '—'} | ${pergunta} | ${(0, horario_1.formatarDataHora)(m.publicadoEm)} |`);
414
+ const sessao = sessaoNaFabrica(t, 80).replace(/\|/g, '/').replace(/^-$/, '—');
415
+ linhas.push(`| ${m.maquina} | ${t.id} | ${t.modo} | ${t.fase} | ${runtime} | ${t.roadmap ?? '—'} | ${pergunta} | ${(0, horario_1.formatarDataHora)(m.publicadoEm)} | ${sessao} |`);
378
416
  }
379
417
  return [...linhas, '', (0, horario_1.legendaDoFuso)(), ''].join('\n');
380
418
  }
@@ -39,18 +39,25 @@ exports.publicarEmSegundoPlano = publicarEmSegundoPlano;
39
39
  *
40
40
  * Modulo a parte, com o minimo de dependencias, porque quem chama (despacho de fase, entrega,
41
41
  * thread nova) nao pode esperar rede nem arrastar o retrato inteiro para dentro de si.
42
+ *
43
+ * RM-053 (D9): o mesmo evento tambem dispara o retrato desta maquina na rede da pessoa, pela
44
+ * `rede-adesao` (tao leve quanto este modulo): so membro, no maximo uma vez a cada 14 minutos.
42
45
  */
43
46
  const node_child_process_1 = require("node:child_process");
44
47
  const fs = __importStar(require("node:fs"));
45
48
  const path = __importStar(require("node:path"));
46
49
  const maquina_1 = require("./maquina");
47
50
  const manifest_1 = require("./manifest");
51
+ const projeto_alvo_1 = require("./projeto-alvo");
52
+ const rede_adesao_1 = require("./rede-adesao");
48
53
  /**
49
54
  * Publica sem segurar quem chamou (thread nova, despacho de fase, entrega). So com a fabrica
50
55
  * compartilhada nesta maquina (`ork fabrica entrar`) ou no projeto; `ORK_FABRICA_PUBLICAR=0`
51
56
  * desliga (testes e `ork eval`).
52
57
  */
53
58
  function publicarEmSegundoPlano(raiz) {
59
+ // A rede decide por si (adesao, teto e ambiente); o retorno continua sendo o da fabrica.
60
+ (0, rede_adesao_1.publicarRedeEmSegundoPlano)({ diretorio: raiz });
54
61
  if (process.env.ORK_FABRICA_PUBLICAR === '0')
55
62
  return false;
56
63
  let carregado;
@@ -66,7 +73,8 @@ function publicarEmSegundoPlano(raiz) {
66
73
  if (!fs.existsSync(cli))
67
74
  return false;
68
75
  try {
69
- const filho = (0, node_child_process_1.spawn)(process.execPath, [cli, 'fabrica', 'publicar', '--silencioso'], { cwd: carregado.raiz, detached: true, stdio: 'ignore', env: process.env });
76
+ // RM-052 (fatia 2, D2): o filho publica o projeto do proprio cwd; o modo host de quem chamou nao o segue.
77
+ const filho = (0, node_child_process_1.spawn)(process.execPath, [cli, 'fabrica', 'publicar', '--silencioso'], { cwd: carregado.raiz, detached: true, stdio: 'ignore', env: { ...process.env, ...projeto_alvo_1.AMBIENTE_SEM_PROJETO_HERDADO } });
70
78
  filho.unref();
71
79
  return true;
72
80
  }
@@ -1,37 +1,4 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.liberarAoFechar = liberarAoFechar;
37
4
  /**
@@ -43,61 +10,48 @@ exports.liberarAoFechar = liberarAoFechar;
43
10
  * derruba. Cada parte roda isolada (achado A1 do CHECK 1): erro de E/S numa nao impede a outra, e o
44
11
  * que nao saiu agora sai na poda do proximo pedido de regiao ou no `ork roadmap reservas --soltar-orfas`.
45
12
  */
46
- const fs = __importStar(require("node:fs"));
47
- const path = __importStar(require("node:path"));
48
- const estado_thread_1 = require("./estado-thread");
49
13
  const ledger_1 = require("./ledger");
50
14
  const leases_1 = require("./leases");
51
15
  const roadmap_reservas_1 = require("./roadmap-reservas");
16
+ const sessoes_1 = require("./sessoes");
52
17
  const thread_1 = require("./thread");
53
18
  function liberarAoFechar(raiz, threadId) {
54
- const saida = { leases: [], fila: [], reservas: [], falhas: [] };
19
+ const saida = { leases: [], fila: [], reservas: [], sessoes: [], falhas: [] };
55
20
  const falhou = (parte, e) => saida.falhas.push(`${parte}: ${e?.message ?? String(e)}`);
56
- // Os leases de regiao sao por checkout: o de quem fecha, a raiz do projeto e, quando a thread tem
57
- // worktree, os pegos de la (sugestao 6 do CHECK 2: fechar da worktree tambem solta os da raiz).
58
- const real = (p) => { try {
59
- return fs.realpathSync(p);
60
- }
61
- catch {
62
- return path.resolve(p);
63
- } };
64
- const candidatos = [raiz];
21
+ // RM-036 (D6): os leases de regiao moram todos no estado canonico, chamado de qualquer checkout, e a
22
+ // soltura de la ja alcanca o legado que a versao anterior gravou nas worktrees. Uma soltura basta.
65
23
  try {
66
- candidatos.push((0, estado_thread_1.raizDoEstado)(raiz));
24
+ const solto = (0, leases_1.soltarThreadFechada)(raiz, threadId, 'fechamento');
25
+ saida.leases.push(...solto.leases);
26
+ saida.fila.push(...solto.fila);
67
27
  }
68
28
  catch (e) {
69
- falhou('raiz', e);
29
+ falhou(`leases em ${raiz}`, e);
70
30
  }
71
31
  try {
72
- const wt = (0, thread_1.lerThread)(raiz, threadId).worktree;
73
- if (wt && fs.existsSync(wt))
74
- candidatos.push(wt);
32
+ saida.reservas = (0, roadmap_reservas_1.soltarReservaDaThread)(raiz, threadId);
75
33
  }
76
34
  catch (e) {
77
- falhou('thread', e);
78
- }
79
- const checkouts = candidatos.filter((c, i) => candidatos.findIndex((x) => real(x) === real(c)) === i);
80
- for (const checkout of checkouts) {
81
- try {
82
- const solto = (0, leases_1.soltarThreadFechada)(checkout, threadId, 'fechamento');
83
- saida.leases.push(...solto.leases);
84
- saida.fila.push(...solto.fila);
85
- }
86
- catch (e) {
87
- falhou(`leases em ${checkout}`, e);
88
- }
35
+ falhou('reserva', e);
89
36
  }
37
+ // RM-056 (ao fechar): a sessao fantasma presa a thread fechada seguia no inventario e no doctor ate
38
+ // alguem rodar `ork sessions limpar-fantasmas` (624f65db e d38ae1d4, 03/10). So consulta o runtime
39
+ // quando o ledger tem sessao sem fim; nunca para nem remove nada no runtime, e sessao viva fica.
90
40
  try {
91
- saida.reservas = (0, roadmap_reservas_1.soltarReservaDaThread)(raiz, threadId);
41
+ if ((0, sessoes_1.sessoesSemFim)(raiz, threadId).length > 0) {
42
+ const r = (0, sessoes_1.limparFantasmas)(raiz, { thread: threadId, origem: 'fechamento' });
43
+ saida.sessoes = r.itens.filter((i) => i.acao === 'solto');
44
+ }
92
45
  }
93
46
  catch (e) {
94
- falhou('reserva', e);
47
+ falhou('sessoes', e);
95
48
  }
96
49
  // Sugestao 2 do CHECK 2: a falha tambem vai ao ledger da thread, de melhor esforco.
97
50
  if (saida.falhas.length > 0) {
98
51
  try {
99
52
  (0, ledger_1.registrarSeExiste)((0, thread_1.dirThread)(raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.solturaFalhou, { falhas: saida.falhas,
100
- correcao: 'a poda do proximo pedido de regiao solta o que ficou; reserva: ork roadmap reservas --soltar-orfas' });
53
+ correcao: 'a poda do proximo pedido de regiao solta o que ficou; reserva: ork roadmap reservas --soltar-orfas; ' +
54
+ 'sessao fantasma: ork sessions limpar-fantasmas' });
101
55
  }
102
56
  catch { /* o ledger que nao grava ja esta nas falhas */ }
103
57
  }