@orkastery/cli 0.2.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 +21 -0
- package/README.md +87 -0
- package/adapters/README.md +22 -0
- package/adapters/claude-code/.claude-plugin/marketplace.json +15 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +31 -0
- package/adapters/claude-code/README.md +102 -0
- package/adapters/claude-code/agents/ork-check.md +32 -0
- package/adapters/claude-code/agents/ork-go.md +32 -0
- package/adapters/claude-code/agents/ork-goal.md +33 -0
- package/adapters/claude-code/agents/ork-master.md +32 -0
- package/adapters/claude-code/agents/ork-plan.md +32 -0
- package/adapters/claude-code/agents/ork-ship.md +32 -0
- package/adapters/claude-code/commands/check.md +45 -0
- package/adapters/claude-code/commands/go.md +46 -0
- package/adapters/claude-code/commands/goal.md +54 -0
- package/adapters/claude-code/commands/master.md +42 -0
- package/adapters/claude-code/commands/ork.md +37 -0
- package/adapters/claude-code/commands/plan.md +47 -0
- package/adapters/claude-code/commands/ship.md +45 -0
- package/adapters/claude-code/hooks/hooks.json +17 -0
- package/adapters/claude-code/hooks/ork-guard.js +130 -0
- package/adapters/hermes/README.md +45 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +25 -0
- package/adapters/hermes/hermes.plugin.json +21 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +116 -0
- package/adapters/openclaw/README.md +63 -0
- package/adapters/openclaw/bin/ork-abrir-thread.sh +24 -0
- package/adapters/openclaw/openclaw.plugin.json +138 -0
- package/dist/adapters/claude-bg.js +308 -0
- package/dist/auditoria.js +848 -0
- package/dist/auditrun.js +976 -0
- package/dist/board.js +314 -0
- package/dist/canarios.js +271 -0
- package/dist/catalogo.js +124 -0
- package/dist/ciclos.js +156 -0
- package/dist/claims.js +226 -0
- package/dist/divida.js +374 -0
- package/dist/doctor.js +246 -0
- package/dist/evalrunner.js +458 -0
- package/dist/fix.js +450 -0
- package/dist/gates.js +92 -0
- package/dist/handoff.js +521 -0
- package/dist/hosts.js +331 -0
- package/dist/index.js +1910 -0
- package/dist/init.js +231 -0
- package/dist/leases.js +508 -0
- package/dist/ledger.js +138 -0
- package/dist/manifest.js +255 -0
- package/dist/master.js +453 -0
- package/dist/memoria.js +773 -0
- package/dist/modos.js +220 -0
- package/dist/orkmind.js +487 -0
- package/dist/orquestracao.js +559 -0
- package/dist/phase.js +572 -0
- package/dist/policies.js +176 -0
- package/dist/prompts.js +406 -0
- package/dist/ratelimit.js +179 -0
- package/dist/recall.js +252 -0
- package/dist/retry.js +917 -0
- package/dist/sandbox.js +94 -0
- package/dist/sessoes.js +104 -0
- package/dist/ship.js +551 -0
- package/dist/slug.js +100 -0
- package/dist/superficie.js +1347 -0
- package/dist/thread.js +333 -0
- package/dist/tokens.js +228 -0
- package/dist/types.js +20 -0
- package/dist/util.js +156 -0
- package/dist/verify.js +262 -0
- package/dist/worktree.js +576 -0
- package/dist/yaml.js +112 -0
- package/eval/README.md +85 -0
- package/eval/casos/check-quality.json +103 -0
- package/eval/casos/code-reviewer.json +102 -0
- package/eval/casos/decision-triage.json +103 -0
- package/eval/casos/go-implementation.json +103 -0
- package/eval/casos/goal-definition.json +103 -0
- package/eval/casos/master-metrics.json +103 -0
- package/eval/casos/narrative-guardian.json +163 -0
- package/eval/casos/orkastery-bootstrap.json +102 -0
- package/eval/casos/plan-specification.json +102 -0
- package/eval/casos/roadmap-keeper.json +103 -0
- package/eval/casos/scope-check-capability-map.json +83 -0
- package/eval/casos/security-auditor.json +102 -0
- package/eval/casos/ship-release.json +122 -0
- package/eval/casos/test-engineer.json +122 -0
- package/eval/casos/thread-state.json +82 -0
- package/eval/casos/thread-tracing.json +83 -0
- package/eval/casos/web-performance-auditor.json +102 -0
- package/eval/fixtures/b0-slug-e-modos/caso.json +56 -0
- package/eval/fixtures/b2-master-log/caso.json +91 -0
- package/eval/fixtures/fx-concurrency/caso.json +15 -0
- package/eval/fixtures/fx-hallucination/caso.json +12 -0
- package/eval/fixtures/fx-happy/caso.json +16 -0
- package/eval/fixtures/fx-schema-drift/caso.json +15 -0
- package/eval/fixtures/fx-stale-base/caso.json +12 -0
- package/eval/fixtures/fx-wiki-destroy/caso.json +16 -0
- package/eval/fixtures/superficie-de-rede/README.md +22 -0
- package/eval/fixtures/superficie-de-rede/api-express.js +30 -0
- package/eval/fixtures/superficie-de-rede/api_fastapi.py +17 -0
- package/eval/fixtures/superficie-de-rede/api_gin.go +18 -0
- package/package.json +56 -0
- package/references/README.md +25 -0
- package/references/code-review-axes.md +82 -0
- package/references/definition-of-done.md +74 -0
- package/references/performance-checklist.md +53 -0
- package/references/security-checklist.md +101 -0
- package/references/testing-patterns.md +51 -0
- package/skills/README.md +43 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +89 -0
- package/skills/core/thread-state/SKILL.md +86 -0
- package/skills/governance/decision-triage/SKILL.md +65 -0
- package/skills/governance/narrative-guardian/SKILL.md +66 -0
- package/skills/governance/roadmap-keeper/SKILL.md +60 -0
- package/skills/governance/scope-check-capability-map/SKILL.md +61 -0
- package/skills/observability/thread-tracing/SKILL.md +63 -0
- package/skills/phases/check-quality/SKILL.md +87 -0
- package/skills/phases/go-implementation/SKILL.md +70 -0
- package/skills/phases/goal-definition/SKILL.md +69 -0
- package/skills/phases/master-metrics/SKILL.md +65 -0
- package/skills/phases/plan-specification/SKILL.md +67 -0
- package/skills/phases/ship-release/SKILL.md +68 -0
- package/skills/reviewers/code-reviewer/SKILL.md +61 -0
- package/skills/reviewers/security-auditor/SKILL.md +68 -0
- package/skills/reviewers/test-engineer/SKILL.md +66 -0
- package/skills/reviewers/web-performance-auditor/SKILL.md +63 -0
package/dist/hosts.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `ork adapter list|show|install <host>`: instalacao dos adaptadores de Camada 1 (bloco B4).
|
|
4
|
+
*
|
|
5
|
+
* O que este modulo faz e deliberadamente pouco: ele COPIA arquivos do produto para onde o
|
|
6
|
+
* host os procura, renderiza tres placeholders e escreve um recibo. Nenhuma regra de negocio
|
|
7
|
+
* do Orkastery mora aqui, e nenhum adaptador ganha comportamento na instalacao que ele nao
|
|
8
|
+
* tenha em disco: o que o builder le em `adapters/<host>/` e o que roda.
|
|
9
|
+
*
|
|
10
|
+
* Duas decisoes que carregam peso:
|
|
11
|
+
*
|
|
12
|
+
* - **Uma copia so do catalogo.** O instalador copia de `skills/` e `references/`, sempre da
|
|
13
|
+
* mesma raiz, e grava a origem e o sha256 de cada arquivo em `INSTALADO.json`. Duas copias
|
|
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 que
|
|
16
|
+
* seria escrito faz a instalacao parar e pedir `--force`, listando os arquivos. Instalador
|
|
17
|
+
* que apaga edicao alheia em silencio e o mesmo defeito que o guard `git add -A` bloqueia.
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.ORDEM_DOS_HOSTS = exports.HOSTS = void 0;
|
|
54
|
+
exports.parseHost = parseHost;
|
|
55
|
+
exports.renderizarArquivo = renderizarArquivo;
|
|
56
|
+
exports.instalarAdaptador = instalarAdaptador;
|
|
57
|
+
exports.tabelaDeHosts = tabelaDeHosts;
|
|
58
|
+
exports.textoDosPitfalls = textoDosPitfalls;
|
|
59
|
+
exports.textoDaInstalacao = textoDaInstalacao;
|
|
60
|
+
const node_crypto_1 = require("node:crypto");
|
|
61
|
+
const fs = __importStar(require("node:fs"));
|
|
62
|
+
const path = __importStar(require("node:path"));
|
|
63
|
+
const catalogo_1 = require("./catalogo");
|
|
64
|
+
const util_1 = require("./util");
|
|
65
|
+
exports.HOSTS = {
|
|
66
|
+
'claude-code': {
|
|
67
|
+
host: 'claude-code',
|
|
68
|
+
descricao: 'Plugin com as 17 skills, 6 subagentes de fase, os comandos /goal ... /master e o guard PreToolUse',
|
|
69
|
+
destinoPadrao: '.claude',
|
|
70
|
+
subdir: path.join('plugins', 'orkastery'),
|
|
71
|
+
levaCatalogo: true,
|
|
72
|
+
pitfalls: [
|
|
73
|
+
{
|
|
74
|
+
titulo: 'manifesto sem os caminhos das skills instala limpo e nao expoe nada',
|
|
75
|
+
detalhe: 'o plugin auto-descobre skills/<nome>/SKILL.md e para ai: ele nao desce nos buckets (core/, phases/, reviewers/, ...) que este catalogo usa, entao o plugin.json declara os 17 caminhos um a um',
|
|
76
|
+
prova: 'claude plugin details orkastery # a contagem precisa bater com os SKILL.md em disco',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
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 com --force ressincroniza. Editar a copia instalada e bandeira vermelha',
|
|
81
|
+
prova: 'ork adapter install claude-code --dry-run # mostra o que divergiu do catalogo',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
titulo: 'hook com caminho relativo nao roda, e o guard vira decoracao',
|
|
85
|
+
detalhe: 'o hooks.json chama node "${CLAUDE_PLUGIN_ROOT}/hooks/ork-guard.js", nunca um caminho relativo ao diretorio de trabalho, que muda a cada worktree de thread',
|
|
86
|
+
prova: 'echo \'{"tool_name":"Bash","tool_input":{"command":"git add -A"}}\' | node <destino>/hooks/ork-guard.js ; echo "codigo: $?" # 2 = guard vivo',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
hermes: {
|
|
91
|
+
host: 'hermes',
|
|
92
|
+
descricao: 'Skill roteadora fina, plugin que a declara e script de abertura de thread',
|
|
93
|
+
destinoPadrao: '.hermes',
|
|
94
|
+
subdir: '',
|
|
95
|
+
levaCatalogo: false,
|
|
96
|
+
pitfalls: [
|
|
97
|
+
{
|
|
98
|
+
titulo: 'ork fora do PATH do host',
|
|
99
|
+
detalhe: 'o host roda com ambiente mais enxuto que o terminal do builder e ~/.local/bin pode nao estar la; o plugin grava o caminho absoluto e o script honra ORK_BIN',
|
|
100
|
+
prova: 'ORK_BIN=$(command -v ork) ; "$ORK_BIN" doctor',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
titulo: 'reimplementar o parse da #TAG no host',
|
|
104
|
+
detalhe: 'duas implementacoes divergem na primeira tag nova, e a divergencia aparece como thread aberta no modo errado, o que ninguem percebe ate a pausa que nao veio',
|
|
105
|
+
prova: 'ork modos --do-pedido "teste #Maestro" # a unica fonte do modo',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
titulo: 'validar allowed_modes no host',
|
|
109
|
+
detalhe: 'um host que recusa o modo antes de chamar o ork recusa com a regra que ele tem em cache, nao com a do manifesto do projeto; deixe o ork thread new recusar, com erro tipado',
|
|
110
|
+
prova: 'ork thread new "teste" --mode look --dry-run',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
openclaw: {
|
|
115
|
+
host: 'openclaw',
|
|
116
|
+
descricao: 'openclaw.plugin.json com as tools ork_*, cada uma uma chamada de CLI',
|
|
117
|
+
destinoPadrao: '.openclaw',
|
|
118
|
+
subdir: '',
|
|
119
|
+
levaCatalogo: false,
|
|
120
|
+
pitfalls: [
|
|
121
|
+
{
|
|
122
|
+
titulo: 'placeholder {{ork_bin}} que fica no arquivo',
|
|
123
|
+
detalhe: 'o host executa o vetor de comando como ele esta: um placeholder nao renderizado vira "comando nao encontrado" em toda tool, e a sessao so descobre depois de ja ter aberto thread na cabeca do agente',
|
|
124
|
+
prova: 'grep -c "{{" <destino>/openclaw.plugin.json # precisa devolver 0',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
titulo: 'argumento com espaco quebrado em varios argumentos',
|
|
128
|
+
detalhe: 'cada tool declara o comando como VETOR, 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
|
+
prova: 'ork_phase_run com um prompt de varias linhas, conferido em ork phase list <thread>',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
titulo: 'tool que reimplementa regra do nucleo',
|
|
133
|
+
detalhe: 'toda tool deste plugin e uma chamada de CLI e nada mais; tool nova que precisa de logica e sinal de que a logica pertence ao ork, com teste',
|
|
134
|
+
prova: 'ork eval # o corpus reprova host com regra de negocio duplicada',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
exports.ORDEM_DOS_HOSTS = ['claude-code', 'hermes', 'openclaw'];
|
|
140
|
+
/** Reconhece o nome do host, com os apelidos que o builder costuma digitar. */
|
|
141
|
+
function parseHost(bruto) {
|
|
142
|
+
const limpo = (bruto ?? '').trim().toLowerCase();
|
|
143
|
+
if (limpo === 'claude-code' || limpo === 'claude' || limpo === 'claudecode')
|
|
144
|
+
return 'claude-code';
|
|
145
|
+
if (limpo === 'hermes')
|
|
146
|
+
return 'hermes';
|
|
147
|
+
if (limpo === 'openclaw' || limpo === 'open-claw')
|
|
148
|
+
return 'openclaw';
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
/** Extensoes cujo conteudo passa pela renderizacao de placeholders. */
|
|
152
|
+
const RENDERIZAVEIS = new Set(['.md', '.json', '.sh', '.js', '.yaml', '.yml', '.txt']);
|
|
153
|
+
/** Extensoes que sao instaladas com bit de execucao. */
|
|
154
|
+
const EXECUTAVEIS = new Set(['.sh', '.js']);
|
|
155
|
+
function sha256(conteudo) {
|
|
156
|
+
return (0, node_crypto_1.createHash)('sha256').update(conteudo).digest('hex');
|
|
157
|
+
}
|
|
158
|
+
function listarArquivos(dir, prefixo = '') {
|
|
159
|
+
const achados = [];
|
|
160
|
+
for (const entrada of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
161
|
+
const origem = path.join(dir, entrada.name);
|
|
162
|
+
const relativo = path.posix.join(prefixo, entrada.name);
|
|
163
|
+
if (entrada.isDirectory()) {
|
|
164
|
+
achados.push(...listarArquivos(origem, relativo));
|
|
165
|
+
}
|
|
166
|
+
else if (entrada.isFile()) {
|
|
167
|
+
achados.push({ origem, relativo });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return achados;
|
|
171
|
+
}
|
|
172
|
+
/** Substitui os tres placeholders. Nao ha template engine aqui de proposito. */
|
|
173
|
+
function renderizarArquivo(conteudo, valores) {
|
|
174
|
+
return conteudo
|
|
175
|
+
.replace(/\{\{ork_bin\}\}/g, valores.orkBin)
|
|
176
|
+
.replace(/\{\{catalogo\}\}/g, valores.catalogo)
|
|
177
|
+
.replace(/\{\{versao\}\}/g, valores.versao);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Instala o adaptador do host. Com `dryRun`, nada e escrito e o resultado diz o que seria.
|
|
181
|
+
*
|
|
182
|
+
* O retorno e tipado de proposito: o teste do bloco instala em diretorio temporario e confere
|
|
183
|
+
* o que foi escrito, em vez de conferir o que a funcao disse que escreveu.
|
|
184
|
+
*/
|
|
185
|
+
function instalarAdaptador(host, opcoes = {}) {
|
|
186
|
+
const def = exports.HOSTS[host];
|
|
187
|
+
const projeto = path.resolve(opcoes.projeto ?? process.cwd());
|
|
188
|
+
const catalogo = opcoes.catalogo ? path.resolve(opcoes.catalogo) : (0, catalogo_1.exigirCatalogo)(projeto);
|
|
189
|
+
const base = opcoes.dir
|
|
190
|
+
? path.resolve(projeto, opcoes.dir)
|
|
191
|
+
: path.join(projeto, def.destinoPadrao);
|
|
192
|
+
const destino = def.subdir ? path.join(base, def.subdir) : base;
|
|
193
|
+
const orkBin = opcoes.orkBin ?? (0, util_1.noPath)('ork') ?? path.join(catalogo, 'core', 'dist', 'index.js');
|
|
194
|
+
const versao = opcoes.versao ?? '0.2.0';
|
|
195
|
+
const dirDoHost = path.join(catalogo, 'adapters', host);
|
|
196
|
+
if (!fs.existsSync(dirDoHost)) {
|
|
197
|
+
throw new Error(`adaptador do host ${host} nao existe em ${dirDoHost}`);
|
|
198
|
+
}
|
|
199
|
+
const fontes = listarArquivos(dirDoHost);
|
|
200
|
+
if (def.levaCatalogo) {
|
|
201
|
+
for (const skill of (0, catalogo_1.skillsDoCatalogo)(catalogo)) {
|
|
202
|
+
fontes.push({
|
|
203
|
+
origem: skill.caminho,
|
|
204
|
+
relativo: path.posix.join('skills', skill.bucket, skill.nome, 'SKILL.md'),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
for (const ref of (0, catalogo_1.referenciasDoCatalogo)(catalogo)) {
|
|
208
|
+
fontes.push({ origem: ref, relativo: path.posix.join('references', path.basename(ref)) });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const arquivos = [];
|
|
212
|
+
for (const fonte of fontes) {
|
|
213
|
+
const ext = path.extname(fonte.origem);
|
|
214
|
+
const bruto = fs.readFileSync(fonte.origem);
|
|
215
|
+
const conteudo = RENDERIZAVEIS.has(ext)
|
|
216
|
+
? Buffer.from(renderizarArquivo(bruto.toString('utf8'), { orkBin, catalogo, versao }), 'utf8')
|
|
217
|
+
: bruto;
|
|
218
|
+
const alvo = path.join(destino, fonte.relativo);
|
|
219
|
+
let estado = 'novo';
|
|
220
|
+
if (fs.existsSync(alvo)) {
|
|
221
|
+
estado = sha256(fs.readFileSync(alvo)) === sha256(conteudo) ? 'igual' : 'sobrescrito';
|
|
222
|
+
}
|
|
223
|
+
arquivos.push({
|
|
224
|
+
origem: fonte.origem,
|
|
225
|
+
destino: alvo,
|
|
226
|
+
relativo: fonte.relativo,
|
|
227
|
+
sha256: sha256(conteudo),
|
|
228
|
+
bytes: conteudo.length,
|
|
229
|
+
executavel: EXECUTAVEIS.has(ext),
|
|
230
|
+
estado,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const conflitos = arquivos.filter((a) => a.estado === 'sobrescrito');
|
|
234
|
+
const barrado = conflitos.length > 0 && opcoes.force !== true;
|
|
235
|
+
const resultado = {
|
|
236
|
+
host,
|
|
237
|
+
catalogo,
|
|
238
|
+
destino,
|
|
239
|
+
dryRun: opcoes.dryRun === true,
|
|
240
|
+
arquivos,
|
|
241
|
+
conflitos,
|
|
242
|
+
ok: !barrado,
|
|
243
|
+
orkBin,
|
|
244
|
+
pitfalls: def.pitfalls,
|
|
245
|
+
};
|
|
246
|
+
if (opcoes.dryRun === true || barrado)
|
|
247
|
+
return resultado;
|
|
248
|
+
for (const arquivo of arquivos) {
|
|
249
|
+
const ext = path.extname(arquivo.origem);
|
|
250
|
+
const bruto = fs.readFileSync(arquivo.origem);
|
|
251
|
+
const conteudo = RENDERIZAVEIS.has(ext)
|
|
252
|
+
? Buffer.from(renderizarArquivo(bruto.toString('utf8'), { orkBin, catalogo, versao }), 'utf8')
|
|
253
|
+
: bruto;
|
|
254
|
+
fs.mkdirSync(path.dirname(arquivo.destino), { recursive: true });
|
|
255
|
+
fs.writeFileSync(arquivo.destino, conteudo);
|
|
256
|
+
if (arquivo.executavel)
|
|
257
|
+
fs.chmodSync(arquivo.destino, 0o755);
|
|
258
|
+
}
|
|
259
|
+
// O recibo: com ele da para saber, depois, se alguem editou a copia instalada em vez do
|
|
260
|
+
// catalogo. Sem ele a instalacao e um monte de arquivo sem procedencia.
|
|
261
|
+
fs.writeFileSync(path.join(destino, 'INSTALADO.json'), JSON.stringify({
|
|
262
|
+
contrato: 'ork.adapter-install/v1',
|
|
263
|
+
host,
|
|
264
|
+
versao,
|
|
265
|
+
catalogo,
|
|
266
|
+
orkBin,
|
|
267
|
+
instaladoEm: new Date().toISOString(),
|
|
268
|
+
arquivos: arquivos.map((a) => ({
|
|
269
|
+
arquivo: a.relativo,
|
|
270
|
+
origem: path.relative(catalogo, a.origem),
|
|
271
|
+
sha256: a.sha256,
|
|
272
|
+
bytes: a.bytes,
|
|
273
|
+
})),
|
|
274
|
+
}, null, 2) + '\n', 'utf8');
|
|
275
|
+
return resultado;
|
|
276
|
+
}
|
|
277
|
+
/** Texto de `ork adapter list`. */
|
|
278
|
+
function tabelaDeHosts() {
|
|
279
|
+
const linhas = ['Adaptadores de host (Camada 1, o podio)', ''];
|
|
280
|
+
for (const host of exports.ORDEM_DOS_HOSTS) {
|
|
281
|
+
const def = exports.HOSTS[host];
|
|
282
|
+
linhas.push(` ${host.padEnd(13)} ${def.descricao}`);
|
|
283
|
+
linhas.push(` ${' '.repeat(13)} destino padrao: <projeto>/${def.destinoPadrao}${def.subdir ? '/' + def.subdir : ''}`);
|
|
284
|
+
linhas.push(` ${' '.repeat(13)} catalogo de skills junto: ${def.levaCatalogo ? 'sim' : 'nao'}`);
|
|
285
|
+
}
|
|
286
|
+
linhas.push('');
|
|
287
|
+
linhas.push('Instale com: ork adapter install <host> [--dir DIR] [--dry-run] [--force]');
|
|
288
|
+
linhas.push('Zero regra de negocio no host: a #TAG vira `--mode` e a validacao e do nucleo.');
|
|
289
|
+
return linhas.join('\n');
|
|
290
|
+
}
|
|
291
|
+
/** Texto dos pitfalls de um host, impresso no fim de toda instalacao. */
|
|
292
|
+
function textoDosPitfalls(host) {
|
|
293
|
+
const def = exports.HOSTS[host];
|
|
294
|
+
const linhas = [`Os 3 pitfalls de instalacao do ${host} (confira, nao suponha):`, ''];
|
|
295
|
+
def.pitfalls.forEach((p, i) => {
|
|
296
|
+
linhas.push(` ${i + 1}. ${p.titulo}`);
|
|
297
|
+
linhas.push(` ${p.detalhe}`);
|
|
298
|
+
linhas.push(` prova: ${p.prova}`);
|
|
299
|
+
linhas.push('');
|
|
300
|
+
});
|
|
301
|
+
return linhas.join('\n').replace(/\n$/, '');
|
|
302
|
+
}
|
|
303
|
+
/** Texto de `ork adapter install`. */
|
|
304
|
+
function textoDaInstalacao(r) {
|
|
305
|
+
const linhas = [];
|
|
306
|
+
linhas.push(r.dryRun
|
|
307
|
+
? `Simulacao (--dry-run) da instalacao do adaptador ${r.host}: nada foi escrito.`
|
|
308
|
+
: r.ok
|
|
309
|
+
? `Adaptador ${r.host} instalado.`
|
|
310
|
+
: `Instalacao do adaptador ${r.host} BARRADA: ha arquivo diferente no destino.`);
|
|
311
|
+
linhas.push(` catalogo: ${r.catalogo}`);
|
|
312
|
+
linhas.push(` destino: ${r.destino}`);
|
|
313
|
+
linhas.push(` ork: ${r.orkBin}`);
|
|
314
|
+
linhas.push('');
|
|
315
|
+
const novos = r.arquivos.filter((a) => a.estado === 'novo').length;
|
|
316
|
+
const iguais = r.arquivos.filter((a) => a.estado === 'igual').length;
|
|
317
|
+
linhas.push(` arquivos: ${r.arquivos.length} (${novos} novo(s), ${iguais} ja identico(s), ${r.conflitos.length} divergente(s))`);
|
|
318
|
+
for (const a of r.arquivos) {
|
|
319
|
+
const marca = a.estado === 'novo' ? '+' : a.estado === 'igual' ? '=' : '!';
|
|
320
|
+
linhas.push(` ${marca} ${a.relativo}`);
|
|
321
|
+
}
|
|
322
|
+
if (!r.ok) {
|
|
323
|
+
linhas.push('');
|
|
324
|
+
linhas.push(' O destino tem arquivo diferente do catalogo. Ou a copia instalada foi editada');
|
|
325
|
+
linhas.push(' (e a correcao pertence ao catalogo), ou o catalogo andou. Para ressincronizar:');
|
|
326
|
+
linhas.push(` ork adapter install ${r.host} --force`);
|
|
327
|
+
}
|
|
328
|
+
linhas.push('');
|
|
329
|
+
linhas.push(textoDosPitfalls(r.host));
|
|
330
|
+
return linhas.join('\n');
|
|
331
|
+
}
|