@orkastery/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +86 -65
- package/adapters/README.md +30 -15
- package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
- package/adapters/claude-code/README.md +110 -8
- package/adapters/claude-code/commands/onboarding.md +21 -0
- package/adapters/claude-code/commands/ork.md +99 -27
- package/adapters/claude-code/hooks/hooks.json +6 -1
- package/adapters/claude-code/hooks/ork-sensor.js +66 -0
- package/adapters/codex/skills/ork/SKILL.md +57 -0
- package/adapters/hermes/README.md +163 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
- package/adapters/hermes/bin/ork-brain.sh +4 -0
- package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
- package/adapters/hermes/bin/ork-maestro.sh +6 -0
- package/adapters/hermes/bin/ork-master-enviar.py +84 -0
- package/adapters/hermes/bin/ork-objective-message.sh +17 -0
- package/adapters/hermes/bin/ork-objective-status.sh +12 -0
- package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
- package/adapters/hermes/hermes.plugin.json +26 -4
- package/adapters/hermes/hitl-ingress/__init__.py +356 -0
- package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
- package/adapters/openclaw/README.md +135 -26
- package/adapters/openclaw/bin/ork-brain.sh +4 -0
- package/adapters/openclaw/construir.sh +19 -0
- package/adapters/openclaw/dist/hitl-ingress.js +199 -0
- package/adapters/openclaw/dist/index.js +437 -0
- package/adapters/openclaw/openclaw.plugin.json +39 -134
- package/adapters/openclaw/package.json +26 -0
- package/adapters/openclaw/src/hitl-ingress.ts +180 -0
- package/adapters/openclaw/src/index.ts +458 -0
- package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
- package/adapters/openclaw/tsconfig.json +15 -0
- package/assets/docs/markdownlint-cli2.jsonc +22 -0
- package/assets/docs/padroes/documentacao-de-produto.md +133 -0
- package/assets/docs/padroes/roadmap-de-produto.md +98 -0
- package/assets/docs/produto/README.md +14 -0
- package/assets/docs/produto/_modelo-feature.md +59 -0
- package/assets/docs/roadmap/README.md +14 -0
- package/assets/docs/roadmap/_modelo-item.md +73 -0
- package/assets/orkmind-native-schema.json +32 -0
- package/assets/orkmind_bridge.py +357 -0
- package/assets/orkmind_fixture.py +67 -0
- package/assets/orkmind_prospective.py +138 -0
- package/assets/reference-tariffs-i07.json +23 -0
- package/dist/adapters/claude-bg.js +488 -39
- package/dist/adapters/codex-controller-sensor.js +449 -0
- package/dist/adapters/codex-controller-worker.js +361 -0
- package/dist/adapters/codex-controller.js +252 -0
- package/dist/adapters/codex-events.js +205 -0
- package/dist/adapters/codex-question.js +26 -0
- package/dist/adapters/codex-runner.js +133 -0
- package/dist/adapters/codex.js +394 -0
- package/dist/agents-md.js +84 -0
- package/dist/auditoria.js +11 -8
- package/dist/auditrun.js +5 -4
- package/dist/board.js +173 -22
- package/dist/branch-de-estado.js +136 -0
- package/dist/canarios-hitl.js +177 -0
- package/dist/canarios-i43.js +543 -0
- package/dist/canarios-pulse.js +147 -0
- package/dist/canarios-sensores.js +129 -0
- package/dist/canarios.js +111 -2
- package/dist/catalogo.js +9 -0
- package/dist/ci.js +216 -0
- package/dist/ciclos.js +2 -1
- package/dist/claim-lint.js +64 -0
- package/dist/claims.js +60 -0
- package/dist/company-brain-capture.js +195 -0
- package/dist/company-brain-cli.js +123 -0
- package/dist/company-brain-client.js +61 -0
- package/dist/company-brain-contract.js +166 -0
- package/dist/company-brain-journal.js +192 -0
- package/dist/company-brain-mcp.js +33 -0
- package/dist/company-brain-migration.js +60 -0
- package/dist/company-brain-source.js +177 -0
- package/dist/company-brain-worker.js +18 -0
- package/dist/conducao-texto.js +61 -0
- package/dist/conducao.js +876 -0
- package/dist/contrato-publico.js +39 -0
- package/dist/creation-operation-store.js +251 -0
- package/dist/creation-operation.js +148 -0
- package/dist/decisao-autonoma.js +183 -0
- package/dist/delegation.js +79 -0
- package/dist/demo.js +120 -0
- package/dist/docs.js +828 -0
- package/dist/doctor.js +183 -24
- package/dist/entrega-pr.js +117 -0
- package/dist/escopo-escrita.js +71 -0
- package/dist/estado-thread.js +222 -0
- package/dist/evalrunner.js +12 -0
- package/dist/fabrica-estado.js +329 -0
- package/dist/fabrica-publicar.js +76 -0
- package/dist/fix.js +21 -0
- package/dist/gates.js +55 -9
- package/dist/handoff.js +39 -23
- package/dist/hitl-canais.js +333 -0
- package/dist/hitl-classificacao.js +131 -0
- package/dist/hitl-contract.js +465 -0
- package/dist/hitl-estado.js +136 -0
- package/dist/hitl-gates.js +554 -0
- package/dist/hitl-ingress-receipt.js +382 -0
- package/dist/hitl-local-atestado.js +57 -0
- package/dist/hitl-local-receipt.js +328 -0
- package/dist/hitl-local.js +143 -0
- package/dist/hitl-lock.js +139 -0
- package/dist/hitl-lote.js +223 -0
- package/dist/hitl-native-offer.js +97 -0
- package/dist/hitl-native.js +65 -0
- package/dist/hitl-presentation.js +209 -0
- package/dist/hitl-public-receipt.js +176 -0
- package/dist/hitl-resumo.js +209 -0
- package/dist/hitl-sessions.js +475 -0
- package/dist/hitl.js +711 -0
- package/dist/horario.js +269 -0
- package/dist/hosts.js +198 -22
- package/dist/index.js +1736 -93
- package/dist/indice.js +99 -0
- package/dist/init.js +28 -3
- package/dist/integracoes-locais.js +17 -0
- package/dist/leases.js +65 -21
- package/dist/ledger-stats.js +272 -0
- package/dist/ledger.js +107 -2
- package/dist/licoes.js +215 -0
- package/dist/liveness.js +218 -0
- package/dist/maestro-actions.js +51 -0
- package/dist/maestro-authority.js +152 -0
- package/dist/maestro-cli.js +97 -0
- package/dist/maestro-contract.js +55 -0
- package/dist/maestro-discovery.js +132 -0
- package/dist/maestro-runtime.js +268 -0
- package/dist/maestro-snapshot.js +86 -0
- package/dist/maestro-sources.js +224 -0
- package/dist/manifest.js +165 -6
- package/dist/maquina.js +102 -0
- package/dist/master-audit.js +101 -0
- package/dist/master-batch.js +43 -0
- package/dist/master-digest.js +149 -0
- package/dist/master-migracao.js +155 -0
- package/dist/master.js +361 -77
- package/dist/mcp-artifacts.js +234 -0
- package/dist/mcp-git.js +433 -0
- package/dist/mcp-install.js +310 -0
- package/dist/mcp-maestro.js +25 -0
- package/dist/mcp-server.js +469 -0
- package/dist/mcp-ship.js +436 -0
- package/dist/mcp-verify.js +173 -0
- package/dist/memoria-humana.js +207 -0
- package/dist/memoria.js +314 -78
- package/dist/memory-migration.js +279 -0
- package/dist/memory-prospective.js +148 -0
- package/dist/modos-migracao.js +191 -0
- package/dist/modos.js +151 -15
- package/dist/monitor-lock.js +110 -0
- package/dist/objective.js +449 -0
- package/dist/ocupacao.js +267 -0
- package/dist/onboarding.js +303 -0
- package/dist/orkmind.js +389 -90
- package/dist/orquestracao.js +96 -69
- package/dist/phase.js +596 -150
- package/dist/playbook-capabilities.js +150 -0
- package/dist/playbook-contracts.js +188 -0
- package/dist/portfolio-context.js +61 -0
- package/dist/portfolio.js +180 -0
- package/dist/preflight.js +207 -0
- package/dist/process-audit.js +112 -0
- package/dist/project-state.js +93 -0
- package/dist/prompts.js +25 -5
- package/dist/prova-minima.js +112 -0
- package/dist/pulse-cadencia.js +163 -0
- package/dist/pulse-consentimento.js +260 -0
- package/dist/pulse-delivery.js +323 -0
- package/dist/pulse-resposta.js +542 -0
- package/dist/pulse.js +262 -0
- package/dist/ratelimit.js +35 -2
- package/dist/recall.js +80 -3
- package/dist/redacao-saida.js +45 -0
- package/dist/redacao-url.js +66 -0
- package/dist/retry.js +637 -115
- package/dist/roadmap-reservas.js +243 -0
- package/dist/runtime-ambiente.js +38 -0
- package/dist/runtime-context.js +215 -0
- package/dist/runtime-profiles.js +869 -0
- package/dist/runtimes.js +103 -0
- package/dist/sandbox.js +9 -0
- package/dist/session-events.js +203 -0
- package/dist/session-watcher-claude.js +637 -0
- package/dist/session-watcher.js +683 -0
- package/dist/sessoes-adopt.js +154 -0
- package/dist/sessoes-inventario.js +159 -0
- package/dist/sessoes.js +16 -35
- package/dist/setup.js +691 -0
- package/dist/ship.js +150 -15
- package/dist/slug.js +1 -1
- package/dist/thread-close.js +115 -0
- package/dist/thread.js +142 -28
- package/dist/tokens.js +1 -1
- package/dist/util.js +4 -2
- package/dist/verify-sandbox.js +265 -0
- package/dist/verify.js +270 -25
- package/dist/versao.js +60 -0
- package/dist/worktree.js +31 -2
- package/dist/write-activation.js +291 -0
- package/dist/yaml.js +2 -0
- package/eval/casos/onboarding.json +102 -0
- package/eval/casos/orkastery-bootstrap.json +35 -1
- package/eval/casos/scope-check-capability-map.json +45 -7
- package/eval/casos/ship-release.json +2 -2
- package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
- package/eval/fixtures/b2-master-log/caso.json +1 -1
- package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
- package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
- package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
- package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
- package/eval/fixtures/fx-codex-dry/caso.json +18 -0
- package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
- package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
- package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
- package/eval/fixtures/fx-happy/caso.json +6 -4
- package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
- package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
- package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
- package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
- package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
- package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
- package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
- package/eval/fixtures/fx-omnicanal/caso.json +17 -0
- package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
- package/monitor/company-brain.cjs +17 -0
- package/monitor/pulse-scope.cjs +45 -0
- package/monitor/pulse.cron +18 -0
- package/monitor/varredura-pulse.sh +27 -0
- package/package.json +25 -4
- package/schemas/claims.schema.json +25 -0
- package/schemas/company-brain.schema.json +1158 -0
- package/schemas/creation-operation.schema.json +365 -0
- package/schemas/maestro-snapshot.schema.json +2716 -0
- package/skills/core/onboarding/SKILL.md +50 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
- package/skills/core/thread-state/SKILL.md +3 -1
- package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
package/dist/horario.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* I-35: horário do dono em toda superfície humana (domicílio único).
|
|
4
|
+
*
|
|
5
|
+
* Ordem do dono em 19/09/2026: nunca mais horário UTC para se comunicar com ele. Todo
|
|
6
|
+
* horário que o Orkastery mostra a uma pessoa passa por aqui. O fuso sai do manifesto
|
|
7
|
+
* (`owner.timezone`, nome IANA), com o fuso do sistema como default e UTC como piso; o
|
|
8
|
+
* formato é o brasileiro (`19/09 15:16`) e o fuso aparece uma vez por mensagem.
|
|
9
|
+
*
|
|
10
|
+
* Dado de máquina (ledger, JSON, recibos, hashes) continua em UTC ISO: este módulo só
|
|
11
|
+
* formata na saída, nunca reescreve o que é gravado. Ele também não importa o manifesto
|
|
12
|
+
* (sem ciclo): quem conhece o projeto registra a fonte com `registrarFonteDoFuso`.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.FUSO_DE_BRASILIA = exports.CHAVE_DO_FUSO = void 0;
|
|
16
|
+
exports.normalizarFuso = normalizarFuso;
|
|
17
|
+
exports.fusoDoSistema = fusoDoSistema;
|
|
18
|
+
exports.lerFusoDoDono = lerFusoDoDono;
|
|
19
|
+
exports.fusoDoManifesto = fusoDoManifesto;
|
|
20
|
+
exports.registrarFonteDoFuso = registrarFonteDoFuso;
|
|
21
|
+
exports.definirFusoDoDono = definirFusoDoDono;
|
|
22
|
+
exports.fusoDoDono = fusoDoDono;
|
|
23
|
+
exports.partesLocais = partesLocais;
|
|
24
|
+
exports.dataLocal = dataLocal;
|
|
25
|
+
exports.duracaoCurta = duracaoCurta;
|
|
26
|
+
exports.duracaoRelativa = duracaoRelativa;
|
|
27
|
+
exports.rotuloDoFuso = rotuloDoFuso;
|
|
28
|
+
exports.legendaDoFuso = legendaDoFuso;
|
|
29
|
+
exports.formatarDataHora = formatarDataHora;
|
|
30
|
+
exports.formatarHora = formatarHora;
|
|
31
|
+
exports.formatarDataHoraRotulada = formatarDataHoraRotulada;
|
|
32
|
+
exports.formatarPrazo = formatarPrazo;
|
|
33
|
+
exports.formatarDesde = formatarDesde;
|
|
34
|
+
exports.localizarTexto = localizarTexto;
|
|
35
|
+
exports.localizarTextoRotulado = localizarTextoRotulado;
|
|
36
|
+
exports.CHAVE_DO_FUSO = 'owner.timezone';
|
|
37
|
+
/** Único fuso com nome próprio no rótulo; exemplo da documentação e do `ork init`. */
|
|
38
|
+
exports.FUSO_DE_BRASILIA = 'America/Sao_Paulo';
|
|
39
|
+
/** Forma canônica do Intl (`america/sao_paulo` vira `America/Sao_Paulo`) ou undefined. */
|
|
40
|
+
function normalizarFuso(bruto) {
|
|
41
|
+
if (typeof bruto !== 'string' || bruto.trim() === '')
|
|
42
|
+
return undefined;
|
|
43
|
+
try {
|
|
44
|
+
return new Intl.DateTimeFormat('en-US', { timeZone: bruto.trim() }).resolvedOptions().timeZone || undefined;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Fuso do sistema (`TZ` ou configuração do SO), com piso UTC quando nada resolve. */
|
|
51
|
+
function fusoDoSistema() {
|
|
52
|
+
try {
|
|
53
|
+
return normalizarFuso(Intl.DateTimeFormat().resolvedOptions().timeZone) ?? 'UTC';
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return 'UTC';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Valor bruto de `owner.timezone`: válido vale; ausente cai no sistema; inválido avisa. */
|
|
60
|
+
function lerFusoDoDono(bruto) {
|
|
61
|
+
const fuso = normalizarFuso(bruto);
|
|
62
|
+
if (fuso)
|
|
63
|
+
return { fuso, origem: 'manifesto' };
|
|
64
|
+
const sistema = fusoDoSistema();
|
|
65
|
+
if (bruto === undefined || bruto === null || bruto === '')
|
|
66
|
+
return { fuso: sistema, origem: 'sistema' };
|
|
67
|
+
return {
|
|
68
|
+
fuso: sistema,
|
|
69
|
+
origem: 'sistema',
|
|
70
|
+
aviso: `${exports.CHAVE_DO_FUSO} invalido (${JSON.stringify(bruto).slice(0, 80)}): usando o fuso do sistema ` +
|
|
71
|
+
`(${sistema}); informe um nome IANA, ex.: ${exports.FUSO_DE_BRASILIA}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Fuso de um manifesto já carregado; o aviso de valor inválido vem de `avisos`. */
|
|
75
|
+
function fusoDoManifesto(carregado) {
|
|
76
|
+
const configurado = carregado?.manifesto.owner?.timezone;
|
|
77
|
+
if (configurado)
|
|
78
|
+
return lerFusoDoDono(configurado);
|
|
79
|
+
const aviso = carregado?.avisos?.find(a => a.startsWith(`${exports.CHAVE_DO_FUSO} `));
|
|
80
|
+
return { fuso: fusoDoSistema(), origem: 'sistema', ...(aviso ? { aviso } : {}) };
|
|
81
|
+
}
|
|
82
|
+
let fonte;
|
|
83
|
+
let resolvido;
|
|
84
|
+
let fixo = false;
|
|
85
|
+
let avisado = false;
|
|
86
|
+
/**
|
|
87
|
+
* O ponto de entrada (CLI no cwd, MCP no `--project`) diz de onde vem o fuso. A leitura é
|
|
88
|
+
* preguiçosa: comando que não mostra horário não carrega nada a mais. Um fuso fixado por
|
|
89
|
+
* `definirFusoDoDono` prevalece.
|
|
90
|
+
*/
|
|
91
|
+
function registrarFonteDoFuso(f) {
|
|
92
|
+
if (fixo)
|
|
93
|
+
return;
|
|
94
|
+
fonte = f;
|
|
95
|
+
resolvido = undefined;
|
|
96
|
+
avisado = false;
|
|
97
|
+
}
|
|
98
|
+
/** Fixa o fuso do processo (testes, host que já resolveu); undefined volta ao default. */
|
|
99
|
+
function definirFusoDoDono(fuso) {
|
|
100
|
+
fonte = undefined;
|
|
101
|
+
avisado = false;
|
|
102
|
+
fixo = fuso !== undefined;
|
|
103
|
+
resolvido = fuso === undefined ? undefined : lerFusoDoDono(fuso);
|
|
104
|
+
}
|
|
105
|
+
/** Fuso do dono deste processo; o aviso de valor inválido sai no stderr uma única vez. */
|
|
106
|
+
function fusoDoDono() {
|
|
107
|
+
if (!resolvido) {
|
|
108
|
+
try {
|
|
109
|
+
resolvido = fonte ? fonte() : { fuso: fusoDoSistema(), origem: 'sistema' };
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
resolvido = { fuso: fusoDoSistema(), origem: 'sistema' };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (resolvido.aviso && !avisado) {
|
|
116
|
+
avisado = true;
|
|
117
|
+
process.stderr.write(`aviso: ${resolvido.aviso}\n`);
|
|
118
|
+
}
|
|
119
|
+
return resolvido;
|
|
120
|
+
}
|
|
121
|
+
const formatadores = new Map();
|
|
122
|
+
const ISO_SEM_FUSO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?$/;
|
|
123
|
+
/** Milissegundos do instante; data-hora sem fuso é ambígua e vira NaN (sem chute). */
|
|
124
|
+
function instante(v) {
|
|
125
|
+
if (v === undefined)
|
|
126
|
+
return Date.now();
|
|
127
|
+
if (v === null)
|
|
128
|
+
return NaN;
|
|
129
|
+
if (v instanceof Date)
|
|
130
|
+
return v.getTime();
|
|
131
|
+
if (typeof v === 'number')
|
|
132
|
+
return v;
|
|
133
|
+
if (typeof v !== 'string' || ISO_SEM_FUSO.test(v.trim()))
|
|
134
|
+
return NaN;
|
|
135
|
+
return Date.parse(v);
|
|
136
|
+
}
|
|
137
|
+
/** Ano, mês, dia, hora, minuto e segundo do instante no fuso pedido (relógio de 24 h). */
|
|
138
|
+
function partesLocais(quando, fuso = fusoDoDono().fuso) {
|
|
139
|
+
const ms = instante(quando);
|
|
140
|
+
if (!Number.isFinite(ms))
|
|
141
|
+
throw new Error('data inválida');
|
|
142
|
+
let f = formatadores.get(fuso);
|
|
143
|
+
if (!f) {
|
|
144
|
+
f = new Intl.DateTimeFormat('en-US', { timeZone: fuso, year: 'numeric', month: '2-digit', day: '2-digit',
|
|
145
|
+
hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h23' });
|
|
146
|
+
formatadores.set(fuso, f);
|
|
147
|
+
}
|
|
148
|
+
const p = Object.fromEntries(f.formatToParts(new Date(ms)).map(x => [x.type, x.value]));
|
|
149
|
+
return { ano: p.year, mes: p.month, dia: p.day, hora: p.hour, minuto: p.minute, segundo: p.second };
|
|
150
|
+
}
|
|
151
|
+
/** Data local `AAAA-MM-DD` do instante no fuso (chave de dia, não texto para pessoa). */
|
|
152
|
+
function dataLocal(quando, fuso = fusoDoDono().fuso) {
|
|
153
|
+
const p = partesLocais(quando, fuso);
|
|
154
|
+
return `${p.ano}-${p.mes}-${p.dia}`;
|
|
155
|
+
}
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Texto para pessoa
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
/** Duração curta de minutos inteiros: `45min`, `1h00`, `2d 16h`. */
|
|
160
|
+
function duracaoCurta(minutos) {
|
|
161
|
+
if (minutos < 60)
|
|
162
|
+
return `${minutos}min`;
|
|
163
|
+
const horas = Math.floor(minutos / 60);
|
|
164
|
+
if (horas < 24)
|
|
165
|
+
return `${horas}h${String(minutos % 60).padStart(2, '0')}`;
|
|
166
|
+
return `${Math.floor(horas / 24)}d ${String(horas % 24).padStart(2, '0')}h`;
|
|
167
|
+
}
|
|
168
|
+
/** Duração até ou desde agora: abaixo de 1 min diz `menos de 1 min`, nunca `0min`. */
|
|
169
|
+
function duracaoRelativa(minutos) {
|
|
170
|
+
return minutos < 1 ? 'menos de 1 min' : duracaoCurta(minutos);
|
|
171
|
+
}
|
|
172
|
+
function sigla(fuso, ms) {
|
|
173
|
+
try {
|
|
174
|
+
return new Intl.DateTimeFormat('pt-BR', { timeZone: fuso, timeZoneName: 'short' })
|
|
175
|
+
.formatToParts(new Date(ms)).find(p => p.type === 'timeZoneName')?.value ?? '';
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return '';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** Como o fuso aparece entre parênteses: `horário de Brasília`, `UTC`, `Europe/Lisbon, GMT+1`. */
|
|
182
|
+
function rotuloDoFuso(fuso = fusoDoDono().fuso, quando) {
|
|
183
|
+
if (fuso === exports.FUSO_DE_BRASILIA)
|
|
184
|
+
return 'horário de Brasília';
|
|
185
|
+
if (fuso === 'UTC')
|
|
186
|
+
return 'UTC';
|
|
187
|
+
const ms = instante(quando);
|
|
188
|
+
const s = sigla(fuso, Number.isFinite(ms) ? ms : Date.now());
|
|
189
|
+
return s && s !== fuso ? `${fuso}, ${s}` : fuso;
|
|
190
|
+
}
|
|
191
|
+
/** Legenda de mensagem com vários horários: `Horários de Brasília.` */
|
|
192
|
+
function legendaDoFuso(fuso = fusoDoDono().fuso, quando) {
|
|
193
|
+
if (fuso === exports.FUSO_DE_BRASILIA)
|
|
194
|
+
return 'Horários de Brasília.';
|
|
195
|
+
return `Horários em ${rotuloDoFuso(fuso, quando)}.`;
|
|
196
|
+
}
|
|
197
|
+
/** `19/09 15:16`; `19/09/2025 15:16` quando o ano não é o corrente do dono. Inválido volta como veio. */
|
|
198
|
+
function formatarDataHora(quando, opcoes = {}) {
|
|
199
|
+
const ms = instante(quando ?? null);
|
|
200
|
+
if (!Number.isFinite(ms))
|
|
201
|
+
return quando === null || quando === undefined ? '' : String(quando);
|
|
202
|
+
const fuso = opcoes.fuso ?? fusoDoDono().fuso;
|
|
203
|
+
const p = partesLocais(ms, fuso), a = partesLocais(instante(opcoes.agora), fuso);
|
|
204
|
+
return `${p.dia}/${p.mes}${p.ano !== a.ano ? `/${p.ano}` : ''} ${p.hora}:${p.minuto}` +
|
|
205
|
+
(opcoes.segundos ? `:${p.segundo}` : '');
|
|
206
|
+
}
|
|
207
|
+
/** Só `15:16` quando é hoje no fuso do dono; senão a data-hora completa. */
|
|
208
|
+
function formatarHora(quando, opcoes = {}) {
|
|
209
|
+
const ms = instante(quando ?? null);
|
|
210
|
+
if (!Number.isFinite(ms))
|
|
211
|
+
return formatarDataHora(quando, opcoes);
|
|
212
|
+
const fuso = opcoes.fuso ?? fusoDoDono().fuso;
|
|
213
|
+
const p = partesLocais(ms, fuso), a = partesLocais(instante(opcoes.agora), fuso);
|
|
214
|
+
if (p.ano !== a.ano || p.mes !== a.mes || p.dia !== a.dia)
|
|
215
|
+
return formatarDataHora(ms, { ...opcoes, fuso });
|
|
216
|
+
return `${p.hora}:${p.minuto}${opcoes.segundos ? `:${p.segundo}` : ''}`;
|
|
217
|
+
}
|
|
218
|
+
/** Data-hora absoluta com o fuso entre parênteses: estável para JSON de exibição. */
|
|
219
|
+
function formatarDataHoraRotulada(quando, opcoes = {}) {
|
|
220
|
+
const ms = instante(quando ?? null);
|
|
221
|
+
if (!Number.isFinite(ms))
|
|
222
|
+
return formatarDataHora(quando, opcoes);
|
|
223
|
+
const fuso = opcoes.fuso ?? fusoDoDono().fuso;
|
|
224
|
+
return `${formatarDataHora(ms, { ...opcoes, fuso })} (${rotuloDoFuso(fuso, ms)})`;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Prazo: `19/09 16:16, em 1h00` ou `19/09 14:00, venceu há 5min`; a menos de 1 min, `em menos de
|
|
228
|
+
* 1 min` ou `venceu há menos de 1 min`. `rotulo` põe o fuso.
|
|
229
|
+
*/
|
|
230
|
+
function formatarPrazo(quando, opcoes = {}) {
|
|
231
|
+
const ms = instante(quando ?? null);
|
|
232
|
+
if (!Number.isFinite(ms))
|
|
233
|
+
return formatarDataHora(quando, opcoes);
|
|
234
|
+
const fuso = opcoes.fuso ?? fusoDoDono().fuso;
|
|
235
|
+
const base = opcoes.rotulo ? formatarDataHoraRotulada(ms, { ...opcoes, fuso }) : formatarDataHora(ms, { ...opcoes, fuso });
|
|
236
|
+
const diferenca = ms - instante(opcoes.agora);
|
|
237
|
+
return diferenca >= 0
|
|
238
|
+
? `${base}, em ${duracaoRelativa(diferenca < 60000 ? 0 : Math.ceil(diferenca / 60000))}`
|
|
239
|
+
: `${base}, venceu há ${duracaoRelativa(Math.floor(-diferenca / 60000))}`;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Desde: `11:00 (há 4h16)` hoje, `16/09 22:53 (há 2d 16h)` em outro dia, `(há menos de 1 min)`
|
|
243
|
+
* no limite. Instante 1 min ou mais à frente do relógio diz `relógio adiantado`. `rotulo` põe o fuso.
|
|
244
|
+
*/
|
|
245
|
+
function formatarDesde(quando, opcoes = {}) {
|
|
246
|
+
const ms = instante(quando ?? null);
|
|
247
|
+
if (!Number.isFinite(ms))
|
|
248
|
+
return formatarDataHora(quando, opcoes);
|
|
249
|
+
const fuso = opcoes.fuso ?? fusoDoDono().fuso;
|
|
250
|
+
const diferenca = instante(opcoes.agora) - ms;
|
|
251
|
+
const prefixo = `${formatarHora(ms, { ...opcoes, fuso })} (${opcoes.rotulo ? `${rotuloDoFuso(fuso, ms)}, ` : ''}`;
|
|
252
|
+
if (diferenca <= -60000)
|
|
253
|
+
return `${prefixo}relógio adiantado: ${duracaoCurta(Math.floor(-diferenca / 60000))} no futuro)`;
|
|
254
|
+
return `${prefixo}há ${duracaoRelativa(Math.max(0, Math.floor(diferenca / 60000)))})`;
|
|
255
|
+
}
|
|
256
|
+
/** ISO completo com `Z` ou offset; sem fuso fica como está (ambíguo, sem chute). */
|
|
257
|
+
const ISO_EM_TEXTO = /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:?\d{2})\b/g;
|
|
258
|
+
/**
|
|
259
|
+
* Localiza os instantes ISO de um texto já composto (detalhe, evidência, correção) na hora
|
|
260
|
+
* de exibir. O dado gravado continua com o ISO; quem exibe põe a legenda uma vez.
|
|
261
|
+
*/
|
|
262
|
+
function localizarTexto(texto, opcoes = {}) {
|
|
263
|
+
return texto.replace(ISO_EM_TEXTO, iso => formatarDataHora(iso, opcoes));
|
|
264
|
+
}
|
|
265
|
+
/** Como `localizarTexto`, e rotula o fuso no fim quando algum horário foi trocado (campo de uma linha). */
|
|
266
|
+
function localizarTextoRotulado(texto, opcoes = {}) {
|
|
267
|
+
const local = localizarTexto(texto, opcoes);
|
|
268
|
+
return local === texto ? texto : `${local} (${rotuloDoFuso(opcoes.fuso ?? fusoDoDono().fuso, opcoes.agora)})`;
|
|
269
|
+
}
|
package/dist/hosts.js
CHANGED
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
* - **Uma copia so do catalogo.** O instalador copia de `skills/` e `references/`, sempre da
|
|
13
13
|
* mesma raiz, e grava a origem e o sha256 de cada arquivo em `INSTALADO.json`. Duas copias
|
|
14
14
|
* do catalogo divergem, e a divergencia so aparece quando ja custou uma entrega.
|
|
15
|
-
* - **Sobrescrever e decisao, nao efeito colateral.** Arquivo que ja existe diferente do
|
|
16
|
-
* seria escrito
|
|
17
|
-
*
|
|
15
|
+
* - **Sobrescrever e decisao, nao efeito colateral.** Arquivo que ja existe diferente do
|
|
16
|
+
* que seria escrito e CLASSIFICADO pelo recibo (I-43, D5): o que so mudou no catalogo a
|
|
17
|
+
* instalacao resolve sozinha, porque sobrescrever copia intacta nao perde trabalho de
|
|
18
|
+
* ninguem; o que a pessoa editou barra, com o diff na tela e as duas saidas por arquivo
|
|
19
|
+
* (`--aceitar-catalogo`, `--manter-copia`). Instalador que apaga edicao alheia em
|
|
20
|
+
* silencio e o mesmo defeito que o guard `git add -A` bloqueia.
|
|
18
21
|
*/
|
|
19
22
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
23
|
if (k2 === undefined) k2 = k;
|
|
@@ -50,8 +53,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
50
53
|
};
|
|
51
54
|
})();
|
|
52
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
-
exports.ORDEM_DOS_HOSTS = exports.HOSTS = void 0;
|
|
56
|
+
exports.ORDEM_DOS_HOSTS = exports.HOSTS = exports.MAESTRO_HOST_SURFACES = exports.BRAIN_HOST_SURFACES = void 0;
|
|
54
57
|
exports.parseHost = parseHost;
|
|
58
|
+
exports.lerRecibo = lerRecibo;
|
|
59
|
+
exports.classificarDivergencia = classificarDivergencia;
|
|
55
60
|
exports.renderizarArquivo = renderizarArquivo;
|
|
56
61
|
exports.instalarAdaptador = instalarAdaptador;
|
|
57
62
|
exports.tabelaDeHosts = tabelaDeHosts;
|
|
@@ -59,13 +64,24 @@ exports.textoDosPitfalls = textoDosPitfalls;
|
|
|
59
64
|
exports.textoDaInstalacao = textoDaInstalacao;
|
|
60
65
|
const node_crypto_1 = require("node:crypto");
|
|
61
66
|
const fs = __importStar(require("node:fs"));
|
|
67
|
+
exports.BRAIN_HOST_SURFACES = {
|
|
68
|
+
hermes: 'cli', openclaw: 'cli', 'claude-code': 'mcp', codex: 'mcp',
|
|
69
|
+
};
|
|
70
|
+
/** Superfícies instaladas; a presença do arquivo não comprova ativação live. */
|
|
71
|
+
exports.MAESTRO_HOST_SURFACES = {
|
|
72
|
+
codex: { entry: 'skills/ork/SKILL.md', transport: 'mcp', operation: 'ork_maestro' },
|
|
73
|
+
'claude-code': { entry: 'commands/ork.md', transport: 'mcp', operation: 'ork_maestro' },
|
|
74
|
+
hermes: { entry: 'bin/ork-maestro.sh', transport: 'argv', operation: 'maestro' },
|
|
75
|
+
openclaw: { entry: 'dist/index.js', transport: 'argv', operation: 'maestro' },
|
|
76
|
+
};
|
|
62
77
|
const path = __importStar(require("node:path"));
|
|
63
78
|
const catalogo_1 = require("./catalogo");
|
|
64
79
|
const util_1 = require("./util");
|
|
80
|
+
const versao_1 = require("./versao");
|
|
65
81
|
exports.HOSTS = {
|
|
66
82
|
'claude-code': {
|
|
67
83
|
host: 'claude-code',
|
|
68
|
-
descricao: 'Plugin com as
|
|
84
|
+
descricao: 'Plugin com as skills do catalogo, subagentes de fase, entrada /orkastery:ork e hooks do Orkastery',
|
|
69
85
|
destinoPadrao: '.claude',
|
|
70
86
|
subdir: path.join('plugins', 'orkastery'),
|
|
71
87
|
levaCatalogo: true,
|
|
@@ -77,7 +93,7 @@ exports.HOSTS = {
|
|
|
77
93
|
},
|
|
78
94
|
{
|
|
79
95
|
titulo: 'duas copias do catalogo divergem',
|
|
80
|
-
detalhe: 'a fonte unica e skills/ na raiz do produto; o INSTALADO.json guarda a origem e o sha256 de cada arquivo, e reinstalar
|
|
96
|
+
detalhe: 'a fonte unica e skills/ na raiz do produto; o INSTALADO.json guarda a origem e o sha256 de cada arquivo, e reinstalar CLASSIFICA cada divergencia por ele: o que so mudou no catalogo ressincroniza sozinho, o que voce editou barra e pede decisao por arquivo (--aceitar-catalogo ou --manter-copia). Editar a copia instalada e bandeira vermelha',
|
|
81
97
|
prova: 'ork adapter install claude-code --dry-run # mostra o que divergiu do catalogo',
|
|
82
98
|
},
|
|
83
99
|
{
|
|
@@ -87,6 +103,30 @@ exports.HOSTS = {
|
|
|
87
103
|
},
|
|
88
104
|
],
|
|
89
105
|
},
|
|
106
|
+
codex: {
|
|
107
|
+
host: 'codex',
|
|
108
|
+
descricao: 'Entrada $ork e catalogo de conducao em skills locais do projeto, sem configuracao global',
|
|
109
|
+
destinoPadrao: '.agents',
|
|
110
|
+
subdir: path.join('skills', 'orkastery'),
|
|
111
|
+
levaCatalogo: true,
|
|
112
|
+
pitfalls: [
|
|
113
|
+
{
|
|
114
|
+
titulo: 'skills instaladas fora do projeto da sessao',
|
|
115
|
+
detalhe: 'Codex descobre .agents/skills a partir do diretorio da sessao ate a raiz do repositorio; outra worktree precisa da sua propria instalacao e comprovacao de descoberta',
|
|
116
|
+
prova: 'No Codex aberto no projeto instalado: $ork',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
titulo: 'copia instalada diferente do catalogo',
|
|
120
|
+
detalhe: 'o recibo INSTALADO.json registra os arquivos e hashes dentro da pasta propria orkastery; uma edicao divergente barra a reinstalacao sem substituir arquivos de outras skills',
|
|
121
|
+
prova: 'ork adapter install codex --dry-run',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
titulo: 'skill confundida com autorizacao de runtime',
|
|
125
|
+
detalhe: 'instalar instrucoes nao habilita permissoes, hooks ou sandbox nem altera AGENTS.md; gates e preflight continuam no nucleo, e descoberta nao prova a jornada conversacional',
|
|
126
|
+
prova: 'ork doctor',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
90
130
|
hermes: {
|
|
91
131
|
host: 'hermes',
|
|
92
132
|
descricao: 'Skill roteadora fina, plugin que a declara e script de abertura de thread',
|
|
@@ -113,19 +153,19 @@ exports.HOSTS = {
|
|
|
113
153
|
},
|
|
114
154
|
openclaw: {
|
|
115
155
|
host: 'openclaw',
|
|
116
|
-
descricao: '
|
|
156
|
+
descricao: 'extensao do OpenClaw 2026.7.1 (package.json + dist/index.js) com as tools ork_*, cada uma uma chamada de CLI',
|
|
117
157
|
destinoPadrao: '.openclaw',
|
|
118
|
-
subdir: '',
|
|
158
|
+
subdir: 'extensions/orkastery',
|
|
119
159
|
levaCatalogo: false,
|
|
120
160
|
pitfalls: [
|
|
121
161
|
{
|
|
122
162
|
titulo: 'placeholder {{ork_bin}} que fica no arquivo',
|
|
123
|
-
detalhe: 'o
|
|
124
|
-
prova: 'grep -c "{{" <destino>/
|
|
163
|
+
detalhe: 'o entry executa o binario como ele esta: um placeholder nao renderizado cai no fallback (ORK_BIN ou o ork do PATH), e sem o fallback vira "comando nao encontrado" em toda tool, que a sessao so descobre depois de ja ter aberto thread na cabeca do agente',
|
|
164
|
+
prova: 'grep -c "{{" <destino>/extensions/orkastery/dist/index.js # precisa devolver 0',
|
|
125
165
|
},
|
|
126
166
|
{
|
|
127
167
|
titulo: 'argumento com espaco quebrado em varios argumentos',
|
|
128
|
-
detalhe: 'cada tool
|
|
168
|
+
detalhe: 'cada tool monta o comando como VETOR de argv, nunca como string de shell, para o pedido do builder chegar inteiro em --prompt; string de shell aqui vira injecao de comando com o texto do builder dentro',
|
|
129
169
|
prova: 'ork_phase_run com um prompt de varias linhas, conferido em ork phase list <thread>',
|
|
130
170
|
},
|
|
131
171
|
{
|
|
@@ -136,12 +176,14 @@ exports.HOSTS = {
|
|
|
136
176
|
],
|
|
137
177
|
},
|
|
138
178
|
};
|
|
139
|
-
exports.ORDEM_DOS_HOSTS = ['claude-code', 'hermes', 'openclaw'];
|
|
179
|
+
exports.ORDEM_DOS_HOSTS = ['claude-code', 'codex', 'hermes', 'openclaw'];
|
|
140
180
|
/** Reconhece o nome do host, com os apelidos que o builder costuma digitar. */
|
|
141
181
|
function parseHost(bruto) {
|
|
142
182
|
const limpo = (bruto ?? '').trim().toLowerCase();
|
|
143
183
|
if (limpo === 'claude-code' || limpo === 'claude' || limpo === 'claudecode')
|
|
144
184
|
return 'claude-code';
|
|
185
|
+
if (limpo === 'codex')
|
|
186
|
+
return 'codex';
|
|
145
187
|
if (limpo === 'hermes')
|
|
146
188
|
return 'hermes';
|
|
147
189
|
if (limpo === 'openclaw' || limpo === 'open-claw')
|
|
@@ -152,6 +194,37 @@ function parseHost(bruto) {
|
|
|
152
194
|
const RENDERIZAVEIS = new Set(['.md', '.json', '.sh', '.js', '.yaml', '.yml', '.txt']);
|
|
153
195
|
/** Extensoes que sao instaladas com bit de execucao. */
|
|
154
196
|
const EXECUTAVEIS = new Set(['.sh', '.js']);
|
|
197
|
+
/** Le o recibo do destino. Ausente ou ilegivel devolve null: nao se adivinha recibo. */
|
|
198
|
+
function lerRecibo(destino) {
|
|
199
|
+
const caminho = path.join(destino, 'INSTALADO.json');
|
|
200
|
+
if (!fs.existsSync(caminho))
|
|
201
|
+
return null;
|
|
202
|
+
try {
|
|
203
|
+
const bruto = JSON.parse(fs.readFileSync(caminho, 'utf8'));
|
|
204
|
+
return Array.isArray(bruto?.arquivos) ? bruto : null;
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Classifica UMA divergencia usando o recibo (I-43, D5).
|
|
212
|
+
*
|
|
213
|
+
* As tres comparacoes sao entre tres shas: o do catalogo AGORA, o da copia AGORA, e o
|
|
214
|
+
* que o recibo gravou QUANDO instalou. Sem o terceiro nao ha classificacao possivel, e
|
|
215
|
+
* e por isso que `sem-recibo` pergunta em vez de chutar.
|
|
216
|
+
*/
|
|
217
|
+
function classificarDivergencia(shaDoCatalogo, shaDaCopia, shaDoRecibo) {
|
|
218
|
+
if (!shaDoRecibo)
|
|
219
|
+
return 'sem-recibo';
|
|
220
|
+
const copiaIntacta = shaDaCopia === shaDoRecibo;
|
|
221
|
+
const catalogoIntacto = shaDoCatalogo === shaDoRecibo;
|
|
222
|
+
if (copiaIntacta && !catalogoIntacto)
|
|
223
|
+
return 'catalogo-andou';
|
|
224
|
+
if (catalogoIntacto && !copiaIntacta)
|
|
225
|
+
return 'copia-editada';
|
|
226
|
+
return 'ambos-andaram';
|
|
227
|
+
}
|
|
155
228
|
function sha256(conteudo) {
|
|
156
229
|
return (0, node_crypto_1.createHash)('sha256').update(conteudo).digest('hex');
|
|
157
230
|
}
|
|
@@ -190,13 +263,14 @@ function instalarAdaptador(host, opcoes = {}) {
|
|
|
190
263
|
? path.resolve(projeto, opcoes.dir)
|
|
191
264
|
: path.join(projeto, def.destinoPadrao);
|
|
192
265
|
const destino = def.subdir ? path.join(base, def.subdir) : base;
|
|
193
|
-
const orkBin = opcoes.orkBin ?? (0, util_1.noPath)('ork') ??
|
|
194
|
-
const versao = opcoes.versao ??
|
|
266
|
+
const orkBin = opcoes.orkBin ?? (0, util_1.noPath)('ork') ?? (0, catalogo_1.cliDoCatalogo)(catalogo);
|
|
267
|
+
const versao = opcoes.versao ?? versao_1.VERSAO_DO_ORK;
|
|
195
268
|
const dirDoHost = path.join(catalogo, 'adapters', host);
|
|
196
269
|
if (!fs.existsSync(dirDoHost)) {
|
|
197
270
|
throw new Error(`adaptador do host ${host} nao existe em ${dirDoHost}`);
|
|
198
271
|
}
|
|
199
|
-
const fontes = listarArquivos(dirDoHost)
|
|
272
|
+
const fontes = listarArquivos(dirDoHost).map(fonte => host === 'hermes' && fonte.relativo.startsWith('hitl-ingress/')
|
|
273
|
+
? { ...fonte, relativo: fonte.relativo.replace(/^hitl-ingress\//, 'plugins/orkastery-hitl/') } : fonte);
|
|
200
274
|
if (def.levaCatalogo) {
|
|
201
275
|
for (const skill of (0, catalogo_1.skillsDoCatalogo)(catalogo)) {
|
|
202
276
|
fontes.push({
|
|
@@ -208,6 +282,24 @@ function instalarAdaptador(host, opcoes = {}) {
|
|
|
208
282
|
fontes.push({ origem: ref, relativo: path.posix.join('references', path.basename(ref)) });
|
|
209
283
|
}
|
|
210
284
|
}
|
|
285
|
+
// I-43 (D5): o recibo e lido UMA vez, antes de classificar, e e ele que transforma
|
|
286
|
+
// "divergente" em um dos quatro motivos.
|
|
287
|
+
const recibo = lerRecibo(destino);
|
|
288
|
+
/*
|
|
289
|
+
* I-43 (D5), segundo defeito medido em M3: a DETECCAO falhava em silencio quando o
|
|
290
|
+
* `--dir` apontava para a raiz.
|
|
291
|
+
*
|
|
292
|
+
* `--dir` e o destino COMPLETO, nao a raiz do projeto. Como o destino padrao do
|
|
293
|
+
* hermes e `.hermes`, passar `--dir $HOME` e o erro natural, e ele devolvia
|
|
294
|
+
* "22 novos, 0 divergentes" com saida 0: a copia editada passava despercebida, e a
|
|
295
|
+
* unica coisa que o recibo existe para pegar nao era pega.
|
|
296
|
+
*
|
|
297
|
+
* Agora o comando DIZ que encontrou o recibo um nivel abaixo, em vez de reportar
|
|
298
|
+
* tudo novo. Ele nao corrige sozinho: adivinhar o destino do operador seria trocar
|
|
299
|
+
* um silencio por outro.
|
|
300
|
+
*/
|
|
301
|
+
const caminhoVizinho = def.destinoPadrao ? path.join(destino, def.destinoPadrao) : null;
|
|
302
|
+
const reciboVizinho = !recibo && caminhoVizinho && lerRecibo(caminhoVizinho) ? caminhoVizinho : null;
|
|
211
303
|
const arquivos = [];
|
|
212
304
|
for (const fonte of fontes) {
|
|
213
305
|
const ext = path.extname(fonte.origem);
|
|
@@ -217,13 +309,21 @@ function instalarAdaptador(host, opcoes = {}) {
|
|
|
217
309
|
: bruto;
|
|
218
310
|
const alvo = path.join(destino, fonte.relativo);
|
|
219
311
|
let estado = 'novo';
|
|
312
|
+
let divergencia;
|
|
220
313
|
if (fs.existsSync(alvo)) {
|
|
221
|
-
|
|
314
|
+
const shaDaCopia = sha256(fs.readFileSync(alvo));
|
|
315
|
+
const shaDoCatalogo = sha256(conteudo);
|
|
316
|
+
estado = shaDaCopia === shaDoCatalogo ? 'igual' : 'sobrescrito';
|
|
317
|
+
if (estado === 'sobrescrito') {
|
|
318
|
+
// I-43 (D5): o recibo responde POR QUE divergiu, em vez de so dizer que divergiu.
|
|
319
|
+
divergencia = classificarDivergencia(shaDoCatalogo, shaDaCopia, recibo?.arquivos.find((x) => x.arquivo === fonte.relativo)?.sha256);
|
|
320
|
+
}
|
|
222
321
|
}
|
|
223
322
|
arquivos.push({
|
|
224
323
|
origem: fonte.origem,
|
|
225
324
|
destino: alvo,
|
|
226
325
|
relativo: fonte.relativo,
|
|
326
|
+
...(divergencia ? { divergencia } : {}),
|
|
227
327
|
sha256: sha256(conteudo),
|
|
228
328
|
bytes: conteudo.length,
|
|
229
329
|
executavel: EXECUTAVEIS.has(ext),
|
|
@@ -231,14 +331,35 @@ function instalarAdaptador(host, opcoes = {}) {
|
|
|
231
331
|
});
|
|
232
332
|
}
|
|
233
333
|
const conflitos = arquivos.filter((a) => a.estado === 'sobrescrito');
|
|
234
|
-
|
|
334
|
+
/**
|
|
335
|
+
* I-43 (D5): a divergencia continua sendo DETECTADA; ela deixa de BARRAR quando e
|
|
336
|
+
* segura.
|
|
337
|
+
*
|
|
338
|
+
* `catalogo-andou` significa que a copia esta exatamente como foi instalada e quem
|
|
339
|
+
* mudou foi o catalogo. Sobrescrever isso nao perde trabalho de ninguem, e e a maior
|
|
340
|
+
* parte do que aparece num destino real: o regime antigo barrava esses junto com os
|
|
341
|
+
* demais e mandava o operador descobrir sozinho qual importava, que e a arqueologia de
|
|
342
|
+
* madrugada que o GOAL mediu.
|
|
343
|
+
*
|
|
344
|
+
* O resto PERGUNTA, e e o que R1 exige: resolver `ambos-andaram` sozinho descartaria
|
|
345
|
+
* a edicao manual sem ninguem ver, que e o defeito silencioso com outro nome.
|
|
346
|
+
*/
|
|
347
|
+
// I-43 (D5): a decisao do operador por ARQUIVO, que e o que faz `--force` deixar de
|
|
348
|
+
// ser o unico caminho. `--force` continua existindo como instrumento cego.
|
|
349
|
+
const decididos = new Set([...(opcoes.aceitarCatalogo ?? []), ...(opcoes.manterCopia ?? [])]);
|
|
350
|
+
const manter = new Set(opcoes.manterCopia ?? []);
|
|
351
|
+
const precisamDeDecisao = conflitos.filter((a) => a.divergencia !== 'catalogo-andou' && !decididos.has(a.relativo));
|
|
352
|
+
const barrado = precisamDeDecisao.length > 0 && opcoes.force !== true;
|
|
235
353
|
const resultado = {
|
|
236
354
|
host,
|
|
355
|
+
projeto,
|
|
237
356
|
catalogo,
|
|
238
357
|
destino,
|
|
239
358
|
dryRun: opcoes.dryRun === true,
|
|
240
359
|
arquivos,
|
|
241
360
|
conflitos,
|
|
361
|
+
precisamDeDecisao,
|
|
362
|
+
...(reciboVizinho ? { reciboVizinho } : {}),
|
|
242
363
|
ok: !barrado,
|
|
243
364
|
orkBin,
|
|
244
365
|
pitfalls: def.pitfalls,
|
|
@@ -246,6 +367,11 @@ function instalarAdaptador(host, opcoes = {}) {
|
|
|
246
367
|
if (opcoes.dryRun === true || barrado)
|
|
247
368
|
return resultado;
|
|
248
369
|
for (const arquivo of arquivos) {
|
|
370
|
+
// `--manter-copia`: a copia local fica como esta. Ela permanece DETECTADA como
|
|
371
|
+
// divergente na proxima rodada, porque esconder a divergencia seria o defeito
|
|
372
|
+
// silencioso de R1 voltando por outra porta.
|
|
373
|
+
if (manter.has(arquivo.relativo))
|
|
374
|
+
continue;
|
|
249
375
|
const ext = path.extname(arquivo.origem);
|
|
250
376
|
const bruto = fs.readFileSync(arquivo.origem);
|
|
251
377
|
const conteudo = RENDERIZAVEIS.has(ext)
|
|
@@ -306,24 +432,74 @@ function textoDaInstalacao(r) {
|
|
|
306
432
|
linhas.push(r.dryRun
|
|
307
433
|
? `Simulacao (--dry-run) da instalacao do adaptador ${r.host}: nada foi escrito.`
|
|
308
434
|
: r.ok
|
|
309
|
-
? `
|
|
435
|
+
? `Arquivos do adaptador ${r.host} preparados.`
|
|
310
436
|
: `Instalacao do adaptador ${r.host} BARRADA: ha arquivo diferente no destino.`);
|
|
437
|
+
if (r.ok && !r.dryRun && r.host === 'claude-code') {
|
|
438
|
+
const destino = "'" + r.destino.replace(/'/g, "'\"'\"'") + "'";
|
|
439
|
+
linhas.push('Uso no Claude Code ainda nao verificado por esta copia.');
|
|
440
|
+
linhas.push('No diretorio deste projeto, valide e ative o plugin:');
|
|
441
|
+
linhas.push(` claude plugin validate ${destino}`);
|
|
442
|
+
linhas.push(` claude plugin marketplace add ${destino} --scope project`);
|
|
443
|
+
linhas.push(' claude plugin install orkastery@orkastery --scope project');
|
|
444
|
+
linhas.push(' claude plugin list --json');
|
|
445
|
+
linhas.push('Depois, na sessao deste projeto: /orkastery:ork');
|
|
446
|
+
}
|
|
447
|
+
else if (r.ok && !r.dryRun && r.host === 'codex') {
|
|
448
|
+
linhas.push('Abra o Codex neste projeto e use $ork.');
|
|
449
|
+
linhas.push('Descoberta e uso nativo ainda nao verificados por esta copia.');
|
|
450
|
+
}
|
|
451
|
+
if (r.ok && !r.dryRun && (r.host === 'codex' || r.host === 'claude-code')) {
|
|
452
|
+
const projeto = "'" + r.projeto.replace(/'/g, "'\"'\"'") + "'";
|
|
453
|
+
linhas.push(`Prepare tambem as ferramentas do projeto: ork mcp install --project ${projeto} --host ${r.host}`);
|
|
454
|
+
linhas.push('A configuracao MCP exige descoberta e consentimento no cliente; a copia nao concede aprovacoes.');
|
|
455
|
+
}
|
|
311
456
|
linhas.push(` catalogo: ${r.catalogo}`);
|
|
312
457
|
linhas.push(` destino: ${r.destino}`);
|
|
313
458
|
linhas.push(` ork: ${r.orkBin}`);
|
|
314
459
|
linhas.push('');
|
|
315
460
|
const novos = r.arquivos.filter((a) => a.estado === 'novo').length;
|
|
316
461
|
const iguais = r.arquivos.filter((a) => a.estado === 'igual').length;
|
|
462
|
+
const catalogoAndou = r.conflitos.filter((a) => a.divergencia === 'catalogo-andou').length;
|
|
317
463
|
linhas.push(` arquivos: ${r.arquivos.length} (${novos} novo(s), ${iguais} ja identico(s), ${r.conflitos.length} divergente(s))`);
|
|
318
464
|
for (const a of r.arquivos) {
|
|
319
465
|
const marca = a.estado === 'novo' ? '+' : a.estado === 'igual' ? '=' : '!';
|
|
320
|
-
|
|
466
|
+
const porque = a.divergencia ? ` (${a.divergencia})` : '';
|
|
467
|
+
linhas.push(` ${marca} ${a.relativo}${porque}`);
|
|
468
|
+
}
|
|
469
|
+
// I-43 (D5): o `--dir` na raiz avisa, em vez de reportar "tudo novo" com saida 0.
|
|
470
|
+
if (r.reciboVizinho) {
|
|
471
|
+
linhas.push('');
|
|
472
|
+
linhas.push(` ATENCAO: nao ha recibo em ${r.destino}, mas ha um em`);
|
|
473
|
+
linhas.push(` ${r.reciboVizinho}`);
|
|
474
|
+
linhas.push(' `--dir` e o destino COMPLETO, nao a raiz do projeto. Do jeito que esta, esta');
|
|
475
|
+
linhas.push(' instalacao criaria uma copia nova ao lado da que ja existe, e a que existe');
|
|
476
|
+
linhas.push(' ficaria sem conferencia nenhuma.');
|
|
477
|
+
}
|
|
478
|
+
// I-43 (D5): o que o recibo garante seguro se resolve sozinho, e sai como uma linha
|
|
479
|
+
// de resumo em vez de virar decisao do operador.
|
|
480
|
+
if (catalogoAndou > 0) {
|
|
481
|
+
linhas.push('');
|
|
482
|
+
linhas.push(` ${catalogoAndou} divergente(s) sao "catalogo-andou": a copia esta como foi instalada`);
|
|
483
|
+
linhas.push(' e quem mudou foi o catalogo. Sobrescrever nao perde trabalho de ninguem.');
|
|
321
484
|
}
|
|
322
485
|
if (!r.ok) {
|
|
486
|
+
// A pergunta de UMA linha, com o arquivo NOMEADO e as duas saidas reais. O `--dir`
|
|
487
|
+
// em uso e repetido de proposito: a linha antiga mandava rodar sem ele, e rodar o
|
|
488
|
+
// que estava na tela instalaria em `<catalogo>/<destinoPadrao>`, nao na copia real.
|
|
489
|
+
const dirEmUso = r.destino.startsWith(r.projeto + '/')
|
|
490
|
+
? r.destino.slice(r.projeto.length + 1)
|
|
491
|
+
: r.destino;
|
|
492
|
+
linhas.push('');
|
|
493
|
+
linhas.push(` ${r.precisamDeDecisao.length} arquivo(s) precisam de uma decisao sua:`);
|
|
494
|
+
for (const a of r.precisamDeDecisao) {
|
|
495
|
+
linhas.push(` ${a.relativo} (${a.divergencia})`);
|
|
496
|
+
linhas.push(` $ diff -u ${a.origem} ${a.destino}`);
|
|
497
|
+
}
|
|
323
498
|
linhas.push('');
|
|
324
|
-
linhas.push('
|
|
325
|
-
|
|
326
|
-
linhas.push(` ork adapter install ${r.host} --
|
|
499
|
+
linhas.push(' Resolva em UM comando, por arquivo:');
|
|
500
|
+
const exemplo = r.precisamDeDecisao[0]?.relativo ?? '<arquivo>';
|
|
501
|
+
linhas.push(` ork adapter install ${r.host} --dir ${dirEmUso} --aceitar-catalogo ${exemplo}`);
|
|
502
|
+
linhas.push(` ork adapter install ${r.host} --dir ${dirEmUso} --manter-copia ${exemplo}`);
|
|
327
503
|
}
|
|
328
504
|
linhas.push('');
|
|
329
505
|
linhas.push(textoDosPitfalls(r.host));
|