@orkastery/cli 0.4.3 → 0.5.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/adapters/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/agents/ork-check.md +1 -1
- package/adapters/claude-code/agents/ork-go.md +2 -2
- package/adapters/claude-code/agents/ork-goal.md +1 -1
- package/adapters/claude-code/agents/ork-master.md +1 -1
- package/adapters/claude-code/agents/ork-plan.md +1 -1
- package/adapters/claude-code/agents/ork-ship.md +1 -1
- package/adapters/claude-code/commands/master.md +11 -8
- package/adapters/claude-code/commands/ork.md +8 -0
- package/adapters/claude-code/commands/ship.md +2 -2
- package/adapters/codex/skills/ork/SKILL.md +12 -1
- package/adapters/hermes/README.md +3 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
- package/adapters/hermes/bin/ork-brain.sh +3 -0
- package/adapters/hermes/bin/ork-maestro.sh +4 -1
- package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
- package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
- package/adapters/hermes/hermes.plugin.json +2 -0
- package/adapters/hermes/hitl-ingress/__init__.py +36 -3
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
- package/adapters/openclaw/README.md +34 -2
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +92 -20
- package/adapters/openclaw/openclaw.plugin.json +10 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +32 -3
- package/adapters/openclaw/src/index.ts +108 -20
- package/assets/docs/padroes/documentacao-de-produto.md +2 -2
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs/produto/_modelo-feature.md +2 -0
- package/assets/orkmind_bridge.py +214 -1
- package/dist/adapters/claude-bg.js +6 -3
- package/dist/board.js +74 -0
- package/dist/branch-de-estado.js +5 -4
- package/dist/busca-semantica.js +161 -0
- package/dist/ci.js +141 -10
- package/dist/company-brain-cli.js +27 -2
- package/dist/company-brain-context.js +132 -0
- package/dist/company-brain-dossie.js +376 -0
- package/dist/company-brain-mcp.js +4 -1
- package/dist/conducao.js +71 -18
- package/dist/decisao-autonoma.js +42 -0
- package/dist/docs.js +64 -22
- package/dist/doctor.js +33 -0
- package/dist/entrega-pr.js +109 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +43 -10
- package/dist/fechamento.js +105 -0
- package/dist/forja.js +435 -0
- package/dist/gates.js +9 -1
- package/dist/hitl-classificacao.js +11 -1
- package/dist/hitl-contract.js +50 -7
- package/dist/hitl-curto.js +158 -0
- package/dist/hitl-gates.js +67 -1
- package/dist/hitl-lote.js +30 -15
- package/dist/hitl-presentation.js +10 -7
- package/dist/hitl-resumo.js +33 -12
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +86 -1
- package/dist/index.js +602 -38
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +13 -0
- package/dist/integracoes-locais.js +1 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +430 -0
- package/dist/intelligence-graph-contract.js +577 -0
- package/dist/intelligence-graph-extract-md.js +469 -0
- package/dist/intelligence-graph-extract-ts.js +810 -0
- package/dist/intelligence-graph-extract.js +369 -0
- package/dist/intelligence-graph-index.js +512 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +187 -0
- package/dist/leases.js +84 -0
- package/dist/ledger.js +14 -0
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +142 -5
- package/dist/master-batch.js +3 -2
- package/dist/master-digest.js +2 -2
- package/dist/master-nota.js +203 -0
- package/dist/master.js +4 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +4 -0
- package/dist/mcp-install.js +3 -1
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +84 -5
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +38 -2
- package/dist/onboarding.js +113 -7
- package/dist/orkmind.js +165 -9
- package/dist/phase.js +166 -13
- package/dist/policies.js +2 -0
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +5 -0
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +21 -0
- package/dist/retry.js +51 -8
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +213 -0
- package/dist/thread-close.js +3 -0
- package/dist/verify.js +4 -3
- package/eval/casos/orchestration-experience-pt-br.json +48 -0
- package/eval/casos/orchestration-experience.json +48 -0
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +581 -0
- package/schemas/graph-benchmark.v1.schema.json +2240 -0
- package/schemas/maestro-snapshot.schema.json +23 -0
- package/skills/README.md +7 -5
- package/skills/core/onboarding/SKILL.md +7 -0
- package/skills/core/orchestration-experience/SKILL.md +113 -0
- package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/go-implementation/SKILL.md +2 -0
|
@@ -0,0 +1,187 @@
|
|
|
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.TENANT_PADRAO = void 0;
|
|
37
|
+
exports.identidadeDaLeitura = identidadeDaLeitura;
|
|
38
|
+
exports.revisaoDaArvore = revisaoDaArvore;
|
|
39
|
+
exports.headsDasArvores = headsDasArvores;
|
|
40
|
+
exports.lerRepositorio = lerRepositorio;
|
|
41
|
+
/**
|
|
42
|
+
* RM-031 KG2 (D8): leitura do repositorio Git local para o extrator. E a unica borda de E/S do
|
|
43
|
+
* KG2: lista os arquivos rastreados, le os bytes da arvore de trabalho e fixa a revisao. Nao
|
|
44
|
+
* executa shell nem texto vindo do repositorio; o Git roda com argumentos fixos. O KG3 usa daqui a
|
|
45
|
+
* identidade da leitura e a revisao da arvore limpa, sem ler os arquivos.
|
|
46
|
+
*
|
|
47
|
+
* Fica fora e declarado: link simbolico, submodulo, arquivo em conflito, arquivo rastreado que
|
|
48
|
+
* sumiu da arvore, arquivo cujo caminho real sai da raiz e caminho que nao e UTF-8. A revisao so e
|
|
49
|
+
* o HEAD quando nada rastreado mudou e os bytes lidos sao os blobs do indice (sem filtro do Git).
|
|
50
|
+
*/
|
|
51
|
+
const node_crypto_1 = require("node:crypto");
|
|
52
|
+
const fs = __importStar(require("node:fs"));
|
|
53
|
+
const path = __importStar(require("node:path"));
|
|
54
|
+
const node_child_process_1 = require("node:child_process");
|
|
55
|
+
const yaml_1 = require("./yaml");
|
|
56
|
+
exports.TENANT_PADRAO = 'local';
|
|
57
|
+
const ID_DE_REPOSITORIO = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
58
|
+
/** Git com argumentos fixos; o fsmonitor do repositorio nao roda (seria comando externo configurado nele). */
|
|
59
|
+
function git(raiz, args) {
|
|
60
|
+
const r = (0, node_child_process_1.spawnSync)('git', ['-c', 'core.fsmonitor=false', ...args], {
|
|
61
|
+
cwd: raiz, maxBuffer: 512 * 1024 * 1024, env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' },
|
|
62
|
+
});
|
|
63
|
+
return { ok: r.status === 0, saida: r.stdout ?? Buffer.alloc(0) };
|
|
64
|
+
}
|
|
65
|
+
function obrigatorio(raiz, args) {
|
|
66
|
+
const r = git(raiz, args);
|
|
67
|
+
if (!r.ok)
|
|
68
|
+
throw new Error(`extracao.repositorio.git-falhou em git ${args[0]}`);
|
|
69
|
+
return r.saida;
|
|
70
|
+
}
|
|
71
|
+
const utf8 = (b) => {
|
|
72
|
+
try {
|
|
73
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(b);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/** Nome do projeto no `orkastery.yaml`, quando ha um valido na raiz. */
|
|
80
|
+
function nomeDoProjeto(raiz) {
|
|
81
|
+
const arquivo = path.join(raiz, 'orkastery.yaml');
|
|
82
|
+
if (!fs.existsSync(arquivo))
|
|
83
|
+
return null;
|
|
84
|
+
try {
|
|
85
|
+
const dados = (0, yaml_1.lerYaml)(fs.readFileSync(arquivo, 'utf8'));
|
|
86
|
+
const projeto = dados && typeof dados === 'object' && !Array.isArray(dados) ? dados.project : null;
|
|
87
|
+
const nome = projeto && typeof projeto === 'object' && !Array.isArray(projeto) ? projeto.name : null;
|
|
88
|
+
return typeof nome === 'string' && ID_DE_REPOSITORIO.test(nome) ? nome : null;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Repositorio, tenant e ACL da leitura: os informados, ou os padroes (D8). */
|
|
95
|
+
function identidadeDaLeitura(raiz, opcoes = {}) {
|
|
96
|
+
const repositorio = opcoes.repository_id ?? nomeDoProjeto(raiz);
|
|
97
|
+
if (!repositorio || !ID_DE_REPOSITORIO.test(repositorio))
|
|
98
|
+
throw new Error('extracao.repositorio.sem-id');
|
|
99
|
+
return { repository_id: repositorio, tenant_id: opcoes.tenant_id ?? exports.TENANT_PADRAO, acl_refs: [...(opcoes.acl_refs ?? [`repo:${repositorio}:leitura`])] };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* RM-031 KG3 (D3): o HEAD e se a arvore esta limpa nos rastreados, sem ler arquivo nenhum. O filtro
|
|
103
|
+
* do Git (eol, LFS) so aparece lendo os bytes: quem indexa confere a revisao do `lerRepositorio`.
|
|
104
|
+
*/
|
|
105
|
+
function revisaoDaArvore(diretorio) {
|
|
106
|
+
const raiz = obrigatorio(diretorio, ['rev-parse', '--show-toplevel']).toString('utf8').trim();
|
|
107
|
+
const head = git(raiz, ['rev-parse', '--verify', '-q', 'HEAD']);
|
|
108
|
+
if (!head.ok)
|
|
109
|
+
return { raiz, head: null, motivo: 'sem-commit' };
|
|
110
|
+
const sujo = obrigatorio(raiz, ['status', '--porcelain=v1', '-z', '--untracked-files=no']).length > 0;
|
|
111
|
+
return { raiz, head: head.saida.toString('utf8').trim(), motivo: sujo ? 'working-tree-modified' : null };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* RM-031 KG3 (CHECK rodada 1, A6): o HEAD de cada arvore do repositorio (a principal e as worktrees),
|
|
115
|
+
* pelo `git worktree list --porcelain`. O estado do indice e compartilhado entre elas.
|
|
116
|
+
*/
|
|
117
|
+
function headsDasArvores(diretorio) {
|
|
118
|
+
const saida = obrigatorio(diretorio, ['worktree', 'list', '--porcelain']).toString('utf8');
|
|
119
|
+
return [...new Set(saida.split('\n').filter((l) => /^HEAD [0-9a-f]{40,64}$/.test(l)).map((l) => l.slice(5)))].sort();
|
|
120
|
+
}
|
|
121
|
+
/** Le o repositorio que contem `diretorio` e devolve a entrada do extrator. */
|
|
122
|
+
function lerRepositorio(diretorio, opcoes = {}) {
|
|
123
|
+
const raiz = obrigatorio(diretorio, ['rev-parse', '--show-toplevel']).toString('utf8').trim();
|
|
124
|
+
const { repository_id: repositorio, tenant_id, acl_refs } = identidadeDaLeitura(raiz, opcoes);
|
|
125
|
+
// `-s` traz modo e estagio: 120000 e link simbolico, 160000 e submodulo, estagio > 0 e conflito.
|
|
126
|
+
const registros = obrigatorio(raiz, ['ls-files', '-z', '-s', '--full-name']);
|
|
127
|
+
const porCaminho = new Map(), excluidas = [];
|
|
128
|
+
let inicio = 0;
|
|
129
|
+
for (let i = 0; i < registros.length; i++) {
|
|
130
|
+
if (registros[i] !== 0)
|
|
131
|
+
continue;
|
|
132
|
+
const registro = registros.subarray(inicio, i);
|
|
133
|
+
inicio = i + 1;
|
|
134
|
+
const tab = registro.indexOf(0x09);
|
|
135
|
+
if (tab < 0)
|
|
136
|
+
continue;
|
|
137
|
+
const [modo, objeto, estagio] = registro.subarray(0, tab).toString('latin1').split(' ');
|
|
138
|
+
const caminho = utf8(registro.subarray(tab + 1));
|
|
139
|
+
if (caminho === null) {
|
|
140
|
+
excluidas.push({ path: registro.subarray(tab + 1).toString('latin1'), motivo: 'caminho-nao-utf8' });
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const atual = porCaminho.get(caminho);
|
|
144
|
+
porCaminho.set(caminho, { modo, objeto, estagios: (atual?.estagios ?? 0) + (estagio === '0' ? 0 : 1) });
|
|
145
|
+
}
|
|
146
|
+
const fontes = [], raizReal = fs.realpathSync(raiz);
|
|
147
|
+
let filtrado = false;
|
|
148
|
+
for (const [caminho, { modo, objeto, estagios }] of porCaminho) {
|
|
149
|
+
if (estagios > 0)
|
|
150
|
+
excluidas.push({ path: caminho, motivo: 'conflito-de-merge' });
|
|
151
|
+
else if (modo === '120000')
|
|
152
|
+
excluidas.push({ path: caminho, motivo: 'link-simbolico' });
|
|
153
|
+
else if (modo === '160000')
|
|
154
|
+
excluidas.push({ path: caminho, motivo: 'submodulo' });
|
|
155
|
+
else {
|
|
156
|
+
const absoluto = path.join(raiz, caminho);
|
|
157
|
+
const st = fs.lstatSync(absoluto, { throwIfNoEntry: false });
|
|
158
|
+
if (!st)
|
|
159
|
+
excluidas.push({ path: caminho, motivo: 'ausente-na-arvore' });
|
|
160
|
+
else if (!st.isFile())
|
|
161
|
+
excluidas.push({ path: caminho, motivo: 'nao-e-arquivo' });
|
|
162
|
+
// Pasta do caminho trocada por link simbolico levaria a leitura para fora do repositorio.
|
|
163
|
+
else if (!fs.realpathSync(absoluto).startsWith(raizReal + path.sep))
|
|
164
|
+
excluidas.push({ path: caminho, motivo: 'fora-do-repositorio' });
|
|
165
|
+
else {
|
|
166
|
+
const bytes = fs.readFileSync(absoluto);
|
|
167
|
+
if ((0, node_crypto_1.createHash)('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex') !== objeto)
|
|
168
|
+
filtrado = true;
|
|
169
|
+
fontes.push({ path: caminho, bytes });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const head = git(raiz, ['rev-parse', '--verify', '-q', 'HEAD']);
|
|
174
|
+
const sujo = obrigatorio(raiz, ['status', '--porcelain=v1', '-z', '--untracked-files=no']).length > 0;
|
|
175
|
+
const revisao = head.ok ? head.saida.toString('utf8').trim() : null;
|
|
176
|
+
// A5: filtro do Git (eol, LFS) deixa o status limpo com bytes que nao sao o blob da revisao.
|
|
177
|
+
const motivo = !revisao ? 'sem-commit' : sujo ? 'working-tree-modified' : filtrado ? 'filtro-do-git' : null;
|
|
178
|
+
return {
|
|
179
|
+
tenant_id,
|
|
180
|
+
repository_id: repositorio,
|
|
181
|
+
revision: motivo === null ? revisao : null,
|
|
182
|
+
revision_unavailable_reason: motivo,
|
|
183
|
+
acl_refs,
|
|
184
|
+
fontes,
|
|
185
|
+
excluidas,
|
|
186
|
+
};
|
|
187
|
+
}
|
package/dist/leases.js
CHANGED
|
@@ -76,6 +76,8 @@ exports.posicaoNaFila = posicaoNaFila;
|
|
|
76
76
|
exports.proximoDaFila = proximoDaFila;
|
|
77
77
|
exports.adquirirRegiao = adquirirRegiao;
|
|
78
78
|
exports.tabelaDeLeases = tabelaDeLeases;
|
|
79
|
+
exports.soltarDaThread = soltarDaThread;
|
|
80
|
+
exports.soltarThreadFechada = soltarThreadFechada;
|
|
79
81
|
const fs = __importStar(require("node:fs"));
|
|
80
82
|
const path = __importStar(require("node:path"));
|
|
81
83
|
const manifest_1 = require("./manifest");
|
|
@@ -83,6 +85,8 @@ const estado_thread_1 = require("./estado-thread");
|
|
|
83
85
|
const util_1 = require("./util");
|
|
84
86
|
const horario_1 = require("./horario");
|
|
85
87
|
const conducao_texto_1 = require("./conducao-texto");
|
|
88
|
+
const ledger_1 = require("./ledger");
|
|
89
|
+
const thread_1 = require("./thread");
|
|
86
90
|
/** Nome do lease que serializa o merge na base (visao, paridade 5). */
|
|
87
91
|
exports.LEASE_MAIN_TREE = 'main-tree';
|
|
88
92
|
/** TTL padrao de 30 minutos: um SHIP que passe disso deixou de existir. */
|
|
@@ -433,6 +437,12 @@ function proximoDaFila(raiz, nome) {
|
|
|
433
437
|
function adquirirRegiao(raiz, nome, opcoes) {
|
|
434
438
|
const tipo = tipoDoLease(nome);
|
|
435
439
|
const base = { nome, tipo, lease: null, ocupadoPor: null, tomadoDeVencido: false };
|
|
440
|
+
// A poda e limpeza de estado alheio: erro de E/S nela nunca impede a thread viva de pedir a regiao
|
|
441
|
+
// (achado A1 do CHECK 1); o que nao saiu agora sai no proximo pedido.
|
|
442
|
+
try {
|
|
443
|
+
podarThreadsFechadas(raiz, nome, opcoes.thread);
|
|
444
|
+
}
|
|
445
|
+
catch { /* a regra de colisao abaixo decide */ }
|
|
436
446
|
const colidentes = leasesColidentes(raiz, nome, opcoes.thread);
|
|
437
447
|
if (colidentes.length > 0) {
|
|
438
448
|
const dono = colidentes[0];
|
|
@@ -550,3 +560,77 @@ function tabelaDeLeases(raiz) {
|
|
|
550
560
|
linhas.push((0, horario_1.legendaDoFuso)());
|
|
551
561
|
return linhas.join('\n');
|
|
552
562
|
}
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
// RM-037 (rm037noite, defeito 2): thread fechada nao segura regiao nem fila.
|
|
565
|
+
// ---------------------------------------------------------------------------
|
|
566
|
+
/**
|
|
567
|
+
* Thread fechada nesta maquina? So `status: fechada` conta: thread que nao existe aqui (outro
|
|
568
|
+
* perfil, estado apagado) segue contando, porque ninguem prova que ela acabou.
|
|
569
|
+
*/
|
|
570
|
+
function threadFechada(raiz, thread) {
|
|
571
|
+
try {
|
|
572
|
+
return (0, thread_1.lerThread)(raiz, thread).status === 'fechada';
|
|
573
|
+
}
|
|
574
|
+
catch {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Tira da thread os leases de escrita e as entradas dela na fila. O `exec:` fica de fora: a
|
|
580
|
+
* conducao tem ciclo proprio e ja trata thread fechada como sem conducao.
|
|
581
|
+
*/
|
|
582
|
+
function soltarDaThread(raiz, thread) {
|
|
583
|
+
const leases = [];
|
|
584
|
+
for (const lease of listarLeases(raiz)) {
|
|
585
|
+
if (lease.thread !== thread || tipoDoLease(lease.nome) === 'exec')
|
|
586
|
+
continue;
|
|
587
|
+
// Achado A2 do CHECK 1: rele logo antes de apagar. Outra poda pode ter soltado este lease e uma
|
|
588
|
+
// thread viva pode ter adquirido o mesmo nome no meio; apagar pelo caminho da listagem levaria o
|
|
589
|
+
// lease dela. So sai o arquivo que ainda e o da thread fechada, com o mesmo carimbo.
|
|
590
|
+
const atual = lerLease(raiz, lease.nome);
|
|
591
|
+
if (!atual || atual.thread !== thread || atual.adquiridoEm !== lease.adquiridoEm)
|
|
592
|
+
continue;
|
|
593
|
+
try {
|
|
594
|
+
fs.unlinkSync(caminhoLease(raiz, lease.nome));
|
|
595
|
+
leases.push(lease.nome);
|
|
596
|
+
}
|
|
597
|
+
catch { /* outra limpeza chegou antes */ }
|
|
598
|
+
}
|
|
599
|
+
const fila = lerFila(raiz);
|
|
600
|
+
const dela = fila.filter((p) => p.thread === thread);
|
|
601
|
+
if (dela.length > 0)
|
|
602
|
+
gravarFila(raiz, fila.filter((p) => p.thread !== thread));
|
|
603
|
+
return { leases, fila: dela.map((p) => p.nome) };
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Solta o que a thread fechada deixou e registra no ledger dela: `lease_released` por lease e
|
|
607
|
+
* `lease_dequeued` por entrada de fila, com a origem (o fechamento ou a poda de quem pediu a regiao).
|
|
608
|
+
*/
|
|
609
|
+
function soltarThreadFechada(raiz, thread, origem, pedidaPor) {
|
|
610
|
+
const solto = soltarDaThread(raiz, thread);
|
|
611
|
+
const dir = (0, thread_1.dirThread)(raiz, thread);
|
|
612
|
+
const quem = pedidaPor ? { pedidaPor } : {};
|
|
613
|
+
for (const lease of solto.leases) {
|
|
614
|
+
(0, ledger_1.registrarSeExiste)(dir, thread, ledger_1.TIPOS_DE_EVENTO.leaseLiberado, { lease, ok: true, detalhe: 'thread fechada: lease solto', origem, ...quem });
|
|
615
|
+
}
|
|
616
|
+
for (const lease of solto.fila) {
|
|
617
|
+
(0, ledger_1.registrarSeExiste)(dir, thread, ledger_1.TIPOS_DE_EVENTO.leaseDesenfileirado, { lease, detalhe: 'thread fechada: saiu da fila', origem, ...quem });
|
|
618
|
+
}
|
|
619
|
+
return solto;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Antes de decidir a vez, quem pede a regiao tira da frente o que e de thread ja fechada: o lease
|
|
623
|
+
* colidente e a espera na fila. E a cura do que ficou preso antes de o fechamento soltar sozinho
|
|
624
|
+
* (a ork-companybrai3, fechada, na frente da fila de `path:docs/roadmap/README.md`).
|
|
625
|
+
*/
|
|
626
|
+
function podarThreadsFechadas(raiz, nome, quem) {
|
|
627
|
+
const suspeitas = new Set([
|
|
628
|
+
...listarLeases(raiz).filter((l) => leasesColidem(nome, l.nome)).map((l) => l.thread),
|
|
629
|
+
...esperandoPor(raiz, nome).map((p) => p.thread),
|
|
630
|
+
]);
|
|
631
|
+
suspeitas.delete(quem);
|
|
632
|
+
for (const thread of suspeitas) {
|
|
633
|
+
if (threadFechada(raiz, thread))
|
|
634
|
+
soltarThreadFechada(raiz, thread, 'poda', quem);
|
|
635
|
+
}
|
|
636
|
+
}
|
package/dist/ledger.js
CHANGED
|
@@ -99,8 +99,18 @@ exports.TIPOS_DE_EVENTO = {
|
|
|
99
99
|
worktreeAuditada: 'worktree_audited',
|
|
100
100
|
worktreeLiberada: 'worktree_released',
|
|
101
101
|
leaseEnfileirado: 'lease_queued',
|
|
102
|
+
// RM-037 (rm037noite, defeito 2): a entrada da fila que saiu porque a thread fechou.
|
|
103
|
+
leaseDesenfileirado: 'lease_dequeued',
|
|
104
|
+
// RM-037 (rm037noite, defeito 3): a reserva do roadmap que a thread fechada soltou (ou reapontou),
|
|
105
|
+
// e a que ficou para depois porque nao deu para soltar agora.
|
|
106
|
+
reservaLiberada: 'roadmap_reserva_liberada',
|
|
107
|
+
reservaPendente: 'roadmap_reserva_pendente',
|
|
108
|
+
// A parte do fechamento que falhou (E/S), com o erro; o fechamento segue valendo.
|
|
109
|
+
solturaFalhou: 'soltura_ao_fechar_falhou',
|
|
102
110
|
postmortemGravado: 'postmortem_recorded',
|
|
103
111
|
masterConcluido: 'master_done',
|
|
112
|
+
// RM-048 (item 8): o pedido de nota ao dono, com codigo curto; a nota volta pelo ingresso.
|
|
113
|
+
notaPedida: 'master_nota_pedida',
|
|
104
114
|
scoreProposto: 'score_proposto',
|
|
105
115
|
masterMigrado: 'master_migrated',
|
|
106
116
|
threadFechadaAdmin: 'thread_closed_admin',
|
|
@@ -138,6 +148,8 @@ exports.TIPOS_DE_EVENTO = {
|
|
|
138
148
|
onboardingRegistrado: 'onboarding_recorded',
|
|
139
149
|
// Correcao do escalonador: vaga devolvida por thread sem procura ativa (`ork board reap`)
|
|
140
150
|
vagaLiberada: 'slot_released',
|
|
151
|
+
// RM-037 (rm037defeito, defeito 3): o `phase run` recusou o despacho por falta de vaga no projeto.
|
|
152
|
+
vagaRecusada: 'slot_refused',
|
|
141
153
|
// I-07: estimativa humana no PLAN para comparacao economica posterior.
|
|
142
154
|
estimativaPlano: 'plan_estimate',
|
|
143
155
|
// I-42 (D5): ciclo sem CHECK que nao achou teste nem comando honesto declara a ausencia de
|
|
@@ -156,6 +168,8 @@ exports.TIPOS_DE_EVENTO = {
|
|
|
156
168
|
conducaoLiberada: 'conducao_liberada',
|
|
157
169
|
conducaoOrfaLiberada: 'conducao_orfa_liberada',
|
|
158
170
|
conducaoAssumida: 'conducao_assumida',
|
|
171
|
+
// RM-037 (rm037defeito, A-1 do CHECK 3): o despacho recusado antes da sessao devolveu o lease que a tomada consumiu.
|
|
172
|
+
conducaoDevolvida: 'conducao_devolvida',
|
|
159
173
|
conducaoRenovada: 'conducao_renovada',
|
|
160
174
|
};
|
|
161
175
|
/**
|
package/dist/maestro-cli.js
CHANGED
|
@@ -31,10 +31,19 @@ function readMaestro(context, options) {
|
|
|
31
31
|
return sources;
|
|
32
32
|
} });
|
|
33
33
|
}
|
|
34
|
+
/** RM-052: de onde veio o projeto do panorama, na lingua do dono. */
|
|
35
|
+
const ORIGEM_DO_PANORAMA = Object.freeze({
|
|
36
|
+
installation: 'fixado na instalação', cwd: 'pelo diretório atual', worktree: 'pela worktree da thread',
|
|
37
|
+
selection: 'pedido explicitamente (--projeto ou ORK_PROJETO)',
|
|
38
|
+
});
|
|
34
39
|
function maestroText(snapshot) {
|
|
35
|
-
|
|
40
|
+
const p = snapshot.project;
|
|
41
|
+
return [`${p.name} · panorama Maestro`,
|
|
36
42
|
// I-35: o snapshot guarda ISO; o texto ao dono diz quando consultou, no fuso dele.
|
|
37
43
|
`• Consulta: ${(0, horario_1.formatarDataHoraRotulada)(snapshot.observedAt, { agora: snapshot.observedAt })}`,
|
|
44
|
+
// RM-052: qual cópia foi lida e o que o panorama não lê, antes de qualquer seção.
|
|
45
|
+
`• Projeto consultado: ${p.name}${p.root ? ` · ${p.root}` : ''}${p.remote === undefined ? '' : ` · ${p.remote ?? 'sem remoto'}`} · ${ORIGEM_DO_PANORAMA[p.origin]}`,
|
|
46
|
+
...(snapshot.notConsulted?.length ? [`• Não lido: ${snapshot.notConsulted.join(' · ')}`] : []),
|
|
38
47
|
...maestro_contract_1.SECTION_NAMES.map(name => {
|
|
39
48
|
const s = snapshot.sections[name];
|
|
40
49
|
return `• ${name}: ${s.state} · ${s.coverage.returned}/${s.coverage.total ?? '?'}${s.coverage.omitted ? ' · mais itens disponíveis' : ''}`;
|
package/dist/maestro-contract.js
CHANGED
|
@@ -31,10 +31,14 @@ exports.maestroSectionSchema = zod_1.z.object({
|
|
|
31
31
|
}).strict();
|
|
32
32
|
exports.maestroSnapshotSchema = zod_1.z.object({
|
|
33
33
|
schema: zod_1.z.literal(exports.MAESTRO_SCHEMA), observedAt: zod_1.z.string().datetime(), fingerprint,
|
|
34
|
-
|
|
34
|
+
// RM-052: a raiz exibida (com ~) e o remoto sem credencial dizem QUAL copia foi lida.
|
|
35
|
+
project: zod_1.z.object({ id: identity, name: text, origin: zod_1.z.enum(['installation', 'cwd', 'worktree', 'selection']), fingerprint,
|
|
36
|
+
root: text.optional(), remote: text.nullable().optional() }).strict(),
|
|
35
37
|
limits: zod_1.z.object({ items: zod_1.z.literal(50), bytes: zod_1.z.literal(65536), sourceMs: zod_1.z.literal(2000), totalMs: zod_1.z.literal(10000) }).strict(),
|
|
36
38
|
sections: zod_1.z.object(Object.fromEntries(exports.SECTION_NAMES.map(name => [name, exports.maestroSectionSchema]))).strict(),
|
|
37
39
|
gaps: zod_1.z.array(text).max(100), conflicts: zod_1.z.array(text).max(100),
|
|
40
|
+
// RM-052: o que o panorama NAO le (roadmap, reservas, outras maquinas): lacuna declarada nunca vira "vazio".
|
|
41
|
+
notConsulted: zod_1.z.array(text).max(20).optional(),
|
|
38
42
|
}).strict();
|
|
39
43
|
function validateMaestroSnapshot(input) {
|
|
40
44
|
const value = exports.maestroSnapshotSchema.parse(input);
|
|
@@ -42,6 +42,20 @@ const node_crypto_1 = require("node:crypto");
|
|
|
42
42
|
const manifest_1 = require("./manifest");
|
|
43
43
|
const estado_thread_1 = require("./estado-thread");
|
|
44
44
|
const thread_1 = require("./thread");
|
|
45
|
+
const projeto_alvo_1 = require("./projeto-alvo");
|
|
46
|
+
/**
|
|
47
|
+
* RM-052: o panorama le threads, sessoes e filas deste projeto nesta maquina. Roadmap, reservas e
|
|
48
|
+
* as outras maquinas NAO entram nele; quem le o panorama precisa saber disso para nao concluir
|
|
49
|
+
* "roadmap vazio" a partir de zero threads, que foi o incidente de 29/09.
|
|
50
|
+
*/
|
|
51
|
+
function consultaDoMaestro(value, countOtherProjects) {
|
|
52
|
+
const loaded = (0, manifest_1.carregarManifesto)(value.root);
|
|
53
|
+
const others = countOtherProjects ? (0, projeto_alvo_1.outrosProjetosConhecidos)(value.root) : 0;
|
|
54
|
+
return { ...value, project: { ...value.project, root: (0, projeto_alvo_1.raizParaExibir)(value.root),
|
|
55
|
+
remote: (0, projeto_alvo_1.remotoDoProjeto)(value.root, loaded?.manifesto.fabrica?.remoto ?? 'origin') },
|
|
56
|
+
notConsulted: [projeto_alvo_1.FORA_DA_CONSULTA.roadmap, projeto_alvo_1.FORA_DA_CONSULTA.reservas, projeto_alvo_1.FORA_DA_CONSULTA.outrasMaquinas,
|
|
57
|
+
...(others > 0 ? [`outros projetos desta máquina: ${others} (ork projetos)`] : [])] };
|
|
58
|
+
}
|
|
45
59
|
const inside = (root, file) => file === root || file.startsWith(root + path.sep);
|
|
46
60
|
function physical(file) {
|
|
47
61
|
const absolute = path.resolve(file);
|
|
@@ -121,9 +135,9 @@ function discoverMaestro(options) {
|
|
|
121
135
|
if (fs.lstatSync(file).isSymbolicLink() && fs.realpathSync(file) !== target)
|
|
122
136
|
throw Error('maestro.project.scope');
|
|
123
137
|
}
|
|
124
|
-
return context(canonical, 'worktree');
|
|
138
|
+
return consultaDoMaestro(context(canonical, 'worktree'), options.countOtherProjects !== false);
|
|
125
139
|
}
|
|
126
|
-
return context(chosen, origin);
|
|
140
|
+
return consultaDoMaestro(context(chosen, origin), options.countOtherProjects !== false);
|
|
127
141
|
}
|
|
128
142
|
function revalidateMaestroContext(value) {
|
|
129
143
|
const current = context(value.root, value.project.origin);
|
package/dist/maestro-snapshot.js
CHANGED
|
@@ -51,9 +51,13 @@ function maestroSnapshot(context, options = {}) {
|
|
|
51
51
|
coverage: { total: unavailable ? null : all.length, offset, returned: items.length, omitted: unavailable ? null : omitted,
|
|
52
52
|
nextOffset: omitted ? offset + items.length : null, limit: 50 }, items, gaps: publicMessages(src?.gaps ?? ['maestro.source.unavailable'], 4) };
|
|
53
53
|
}
|
|
54
|
+
// RM-052: raiz e remoto passam pela mesma redacao dos demais campos publicos.
|
|
55
|
+
const { root, remote } = context.project;
|
|
54
56
|
const result = { schema: maestro_contract_1.MAESTRO_SCHEMA, observedAt, fingerprint: (0, maestro_sources_1.digest)([context.fingerprint, sources.fingerprint]),
|
|
55
|
-
project: { ...context.project, name: (0, maestro_sources_1.safeText)(context.project.name) }
|
|
56
|
-
|
|
57
|
+
project: { ...context.project, name: (0, maestro_sources_1.safeText)(context.project.name), ...(root === undefined ? {} : { root: (0, maestro_sources_1.safeText)(root) }),
|
|
58
|
+
...(remote === undefined ? {} : { remote: remote === null ? null : (0, maestro_sources_1.safeText)(remote) }) }, limits: maestro_contract_1.MAESTRO_LIMITS, sections, gaps: publicMessages(sources.gaps, 12),
|
|
59
|
+
conflicts: publicMessages([...sources.conflicts, ...(!stable ? ['maestro.snapshot.stale'] : [])], 12),
|
|
60
|
+
...(context.notConsulted ? { notConsulted: publicMessages(context.notConsulted, 19) } : {}) };
|
|
57
61
|
// Orçamento incremental; mantém um item por seção para nextOffset avançar.
|
|
58
62
|
const sizes = new Map(Object.values(sections).map(s => [s, Buffer.byteLength(JSON.stringify(s))]));
|
|
59
63
|
let bytes = Buffer.byteLength(JSON.stringify(result));
|
package/dist/manifest.js
CHANGED
|
@@ -37,10 +37,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
};
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.LIMITE_MANIFESTO_BYTES = exports.DIR_ESTADO = exports.NOME_MANIFESTO_LEGADO = exports.NOME_MANIFESTO = void 0;
|
|
40
|
+
exports.EMBEDDING_PADRAO = exports.LIMITE_MANIFESTO_BYTES = exports.DIR_ESTADO = exports.NOME_MANIFESTO_LEGADO = exports.NOME_MANIFESTO = void 0;
|
|
41
|
+
exports.fixarDiretorioDoProjeto = fixarDiretorioDoProjeto;
|
|
42
|
+
exports.diretorioDoProjeto = diretorioDoProjeto;
|
|
41
43
|
exports.acharRaiz = acharRaiz;
|
|
42
44
|
exports.dirEstado = dirEstado;
|
|
43
45
|
exports.validarOptInPlaybook = validarOptInPlaybook;
|
|
46
|
+
exports.configDeEmbedding = configDeEmbedding;
|
|
44
47
|
exports.carregarManifesto = carregarManifesto;
|
|
45
48
|
exports.exigirManifesto = exigirManifesto;
|
|
46
49
|
const fs = __importStar(require("node:fs"));
|
|
@@ -53,13 +56,28 @@ const slug_1 = require("./slug");
|
|
|
53
56
|
const util_1 = require("./util");
|
|
54
57
|
const delegation_1 = require("./delegation");
|
|
55
58
|
const horario_1 = require("./horario");
|
|
59
|
+
const experiencia_1 = require("./experiencia");
|
|
60
|
+
const runtime_ambiente_1 = require("./runtime-ambiente");
|
|
61
|
+
const orkmind_1 = require("./orkmind");
|
|
56
62
|
exports.NOME_MANIFESTO = 'orkastery.yaml';
|
|
57
63
|
exports.NOME_MANIFESTO_LEGADO = 'devmaster.yaml';
|
|
58
64
|
exports.DIR_ESTADO = '.orkastery';
|
|
59
65
|
/** Limite duro: o manifesto nao pode virar o novo arquivo de 100 KB. */
|
|
60
66
|
exports.LIMITE_MANIFESTO_BYTES = 16384;
|
|
67
|
+
let diretorioFixado = null;
|
|
68
|
+
/**
|
|
69
|
+
* RM-052: o projeto-alvo fixado pelo CLI (`--projeto`, `ORK_PROJETO`, host sem cwd) vira o ponto
|
|
70
|
+
* de partida padrao das raizes. Sem ele, vale o cwd, como sempre. Quem fixa e `fixarProjetoAlvo`.
|
|
71
|
+
*/
|
|
72
|
+
function fixarDiretorioDoProjeto(dir) {
|
|
73
|
+
diretorioFixado = dir;
|
|
74
|
+
}
|
|
75
|
+
/** De onde as raizes padrao partem: o projeto-alvo fixado ou, sem ele, o cwd do processo. */
|
|
76
|
+
function diretorioDoProjeto() {
|
|
77
|
+
return diretorioFixado ?? process.cwd();
|
|
78
|
+
}
|
|
61
79
|
/** Raiz do projeto: o diretorio que contem o manifesto; senao, a raiz do repositorio git. */
|
|
62
|
-
function acharRaiz(dirInicial =
|
|
80
|
+
function acharRaiz(dirInicial = diretorioDoProjeto()) {
|
|
63
81
|
const porManifesto = (0, util_1.subirAte)(dirInicial, exports.NOME_MANIFESTO);
|
|
64
82
|
if (porManifesto)
|
|
65
83
|
return porManifesto;
|
|
@@ -116,6 +134,31 @@ function prazosDoVerify(verify, erros) {
|
|
|
116
134
|
saida.timeout_ms_por_comando = porComando;
|
|
117
135
|
return saida;
|
|
118
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* RM-037 (rm037defeito, defeito 5): `ci.external_repositories`, mapa `dono/nome` para o check exigido no
|
|
139
|
+
* head do PR (texto vazio ou nulo: repositorio sem CI). Forma invalida e erro do manifesto, nunca adivinhada.
|
|
140
|
+
*/
|
|
141
|
+
function repositoriosExternos(v, erros) {
|
|
142
|
+
if (v === undefined || v === null)
|
|
143
|
+
return {};
|
|
144
|
+
if (typeof v !== 'object' || Array.isArray(v)) {
|
|
145
|
+
erros.push('ci.external_repositories deve ser um mapa "dono/nome": "check exigido" (vazio para repositorio sem CI)');
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
const r = {};
|
|
149
|
+
for (const [repo, check] of Object.entries(v)) {
|
|
150
|
+
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo)) {
|
|
151
|
+
erros.push(`ci.external_repositories: "${repo}" nao e dono/nome do GitHub`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (check !== null && typeof check !== 'string') {
|
|
155
|
+
erros.push(`ci.external_repositories.${repo}: o check exigido e texto (vazio para sem CI)`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
r[repo] = (check ?? '').trim();
|
|
159
|
+
}
|
|
160
|
+
return r;
|
|
161
|
+
}
|
|
119
162
|
function booleano(v, padrao) {
|
|
120
163
|
return typeof v === 'boolean' ? v : padrao;
|
|
121
164
|
}
|
|
@@ -133,6 +176,81 @@ function validarOptInPlaybook(valor) {
|
|
|
133
176
|
}
|
|
134
177
|
return valor.ativo;
|
|
135
178
|
}
|
|
179
|
+
/** I-38 (D5): sem o bloco `memory.embedding`, os embeddings ficam desligados. */
|
|
180
|
+
exports.EMBEDDING_PADRAO = Object.freeze({
|
|
181
|
+
provider: 'none', model: '', dim: 1024, api_key_env: '', fallback_model: '', max_tokens_por_execucao: 1_000_000,
|
|
182
|
+
});
|
|
183
|
+
const CHAVES_DE_EMBEDDING = ['provider', 'model', 'dim', 'api_key_env', 'fallback_model', 'max_tokens_por_execucao'];
|
|
184
|
+
/** Valor que parece segredo (URL, prefixo de chave, sequencia longa) nunca e repetido num erro. */
|
|
185
|
+
function exibir(v) {
|
|
186
|
+
const t = typeof v === 'string' ? v : JSON.stringify(v);
|
|
187
|
+
return /:\/\/|\bsk-|[A-Za-z0-9_-]{32,}/.test(t) ? '(valor omitido: parece segredo)' : JSON.stringify(v);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* I-38 (D5): le e valida `memory.embedding`. A chave so entra pelo NOME da variavel: valor com
|
|
191
|
+
* cara de chave ou DSN reprova o manifesto, e nome da lista de provider pago tambem, porque a
|
|
192
|
+
* entrada do `ork` apagaria a variavel sob `subscription-only` e o preflight reprovaria o codex.
|
|
193
|
+
*/
|
|
194
|
+
function lerEmbedding(bruto, variavelDaDsn, erros) {
|
|
195
|
+
if (bruto === undefined || bruto === null)
|
|
196
|
+
return undefined;
|
|
197
|
+
if (typeof bruto !== 'object' || Array.isArray(bruto)) {
|
|
198
|
+
erros.push('memory.embedding precisa ser um bloco com provider, model, dim, api_key_env, fallback_model e max_tokens_por_execucao');
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
const e = bruto;
|
|
202
|
+
for (const chave of Object.keys(e)) {
|
|
203
|
+
if (!CHAVES_DE_EMBEDDING.includes(chave)) {
|
|
204
|
+
erros.push(`memory.embedding.${chave} nao existe (a chave entra pelo ambiente: declare so o NOME em api_key_env)`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const provider = texto(e.provider, exports.EMBEDDING_PADRAO.provider).trim();
|
|
208
|
+
if (provider !== 'none' && provider !== 'openrouter') {
|
|
209
|
+
erros.push(`memory.embedding.provider invalido: ${exibir(provider)} (aceitos: none, openrouter)`);
|
|
210
|
+
}
|
|
211
|
+
const model = texto(e.model, '').trim();
|
|
212
|
+
const apiKeyEnv = texto(e.api_key_env, '').trim();
|
|
213
|
+
const fallback = texto(e.fallback_model, '').trim();
|
|
214
|
+
const inteiro = (chave, v, padrao, min, max) => {
|
|
215
|
+
if (v === undefined || v === null)
|
|
216
|
+
return padrao;
|
|
217
|
+
const n = typeof v === 'number' ? v : typeof v === 'string' && v.trim() !== '' ? Number(v) : NaN;
|
|
218
|
+
if (!Number.isInteger(n) || n < min || n > max) {
|
|
219
|
+
erros.push(`memory.embedding.${chave} precisa ser inteiro entre ${min} e ${max} (recebido ${exibir(v)})`);
|
|
220
|
+
return padrao;
|
|
221
|
+
}
|
|
222
|
+
return n;
|
|
223
|
+
};
|
|
224
|
+
const dim = inteiro('dim', e.dim, exports.EMBEDDING_PADRAO.dim, 32, 4096);
|
|
225
|
+
const teto = inteiro('max_tokens_por_execucao', e.max_tokens_por_execucao, exports.EMBEDDING_PADRAO.max_tokens_por_execucao, 1, 10_000_000);
|
|
226
|
+
if (apiKeyEnv.includes('://') || /^sk-/i.test(apiKeyEnv)) {
|
|
227
|
+
erros.push('memory.embedding.api_key_env recebe o NOME da variavel de ambiente com a chave, nunca o valor da chave nem uma DSN');
|
|
228
|
+
}
|
|
229
|
+
else if (apiKeyEnv !== '' && !/^[A-Z][A-Z0-9_]*$/.test(apiKeyEnv)) {
|
|
230
|
+
erros.push('memory.embedding.api_key_env invalido: esperado nome de variavel de ambiente, [A-Z][A-Z0-9_]* (o valor recebido nao e repetido)');
|
|
231
|
+
}
|
|
232
|
+
else if (runtime_ambiente_1.ENVS_DE_PROVIDER_PAGO.includes(apiKeyEnv)) {
|
|
233
|
+
erros.push(`memory.embedding.api_key_env nao pode ser ${apiKeyEnv}: nomes de provider pago sao removidos sob subscription-only; use uma chave dedicada com nome proprio`);
|
|
234
|
+
}
|
|
235
|
+
else if (apiKeyEnv !== '' && apiKeyEnv === variavelDaDsn) {
|
|
236
|
+
erros.push('memory.embedding.api_key_env nao pode repetir memory.database_url_env: a DSN nunca vai ao provider');
|
|
237
|
+
}
|
|
238
|
+
if (provider === 'openrouter') {
|
|
239
|
+
if (!orkmind_1.MODELO_DE_EMBEDDING.test(model))
|
|
240
|
+
erros.push(`memory.embedding.model invalido: ${exibir(model)} (esperado org/nome, por exemplo qwen/qwen3-embedding-8b)`);
|
|
241
|
+
if (apiKeyEnv === '')
|
|
242
|
+
erros.push('memory.embedding.api_key_env e obrigatorio com provider openrouter (o NOME da variavel com a chave dedicada)');
|
|
243
|
+
}
|
|
244
|
+
if (fallback !== '' && !orkmind_1.MODELO_DE_EMBEDDING.test(fallback)) {
|
|
245
|
+
erros.push(`memory.embedding.fallback_model invalido: ${exibir(fallback)} (esperado org/nome de um modelo do Hugging Face)`);
|
|
246
|
+
}
|
|
247
|
+
return { provider: provider === 'openrouter' ? 'openrouter' : 'none', model, dim, api_key_env: apiKeyEnv,
|
|
248
|
+
fallback_model: fallback, max_tokens_por_execucao: teto };
|
|
249
|
+
}
|
|
250
|
+
/** I-38 (D5): leitura unica da configuracao de embedding, com o padrao `none`. */
|
|
251
|
+
function configDeEmbedding(manifesto) {
|
|
252
|
+
return { ...exports.EMBEDDING_PADRAO, ...(manifesto.memory.embedding ?? {}) };
|
|
253
|
+
}
|
|
136
254
|
/** I-33 (N2): texto e numero que dizem sim ou nao sem ambiguidade; o resto nao e reconhecido. */
|
|
137
255
|
const BOOLEANOS_RECONHECIVEIS = new Map([
|
|
138
256
|
['true', true], ['yes', true], ['on', true], ['sim', true], ['1', true],
|
|
@@ -146,7 +264,7 @@ function booleanoReconhecivel(v) {
|
|
|
146
264
|
return BOOLEANOS_RECONHECIVEIS.get(v.trim().toLowerCase()) ?? null;
|
|
147
265
|
}
|
|
148
266
|
/** Le e valida o manifesto. Nunca lanca: erros e avisos voltam na estrutura. */
|
|
149
|
-
function carregarManifesto(dirInicial =
|
|
267
|
+
function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
150
268
|
const raiz = acharRaiz(dirInicial);
|
|
151
269
|
const candidatos = [
|
|
152
270
|
{ caminho: path.join(raiz, exports.NOME_MANIFESTO), legado: false },
|
|
@@ -171,6 +289,22 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
171
289
|
const fusoDoDono = (0, horario_1.lerFusoDoDono)(mapa(dados.owner).timezone);
|
|
172
290
|
if (fusoDoDono.aviso)
|
|
173
291
|
avisos.push(fusoDoDono.aviso);
|
|
292
|
+
// Preferência inválida segue o fuso: avisa e cai no padrão, sem parar os comandos (e o próprio
|
|
293
|
+
// onboarding set, que é o caminho para corrigir).
|
|
294
|
+
const preferencias = {};
|
|
295
|
+
for (const chave of ['language', 'depth', 'experience']) {
|
|
296
|
+
const valor = mapa(dados.owner)[chave];
|
|
297
|
+
if (valor === undefined)
|
|
298
|
+
continue;
|
|
299
|
+
try {
|
|
300
|
+
Object.assign(preferencias, (0, experiencia_1.validarPreferencias)({ [chave]: valor }));
|
|
301
|
+
}
|
|
302
|
+
catch (e) {
|
|
303
|
+
avisos.push(`${e.message}; vale o padrão até corrigir`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (fusoDoDono.origem === 'manifesto')
|
|
307
|
+
preferencias.timezone = fusoDoDono.fuso;
|
|
174
308
|
const board = mapa(dados.board);
|
|
175
309
|
const runtime = mapa(dados.runtime);
|
|
176
310
|
const conduction = mapa(dados.conduction);
|
|
@@ -299,6 +433,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
299
433
|
avisos.push('memory.mode: orkmind sem memory.database_url_env: o regime efetivo cai para files ' +
|
|
300
434
|
'(o `ork` nao adivinha DSN, e base alheia e pior do que base nenhuma)');
|
|
301
435
|
}
|
|
436
|
+
const embedding = lerEmbedding(memory.embedding, variavelDaDsn, erros);
|
|
302
437
|
const bytes = Buffer.byteLength(bruto, 'utf8');
|
|
303
438
|
if (bytes > exports.LIMITE_MANIFESTO_BYTES) {
|
|
304
439
|
erros.push(`manifesto com ${bytes} bytes, acima do limite duro de ${exports.LIMITE_MANIFESTO_BYTES} (prosa vai para memoria com tag, nao para o YAML)`);
|
|
@@ -313,7 +448,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
313
448
|
stage: texto(project.stage, 'nascente') ?? 'nascente',
|
|
314
449
|
repo_root: texto(project.repo_root, raiz),
|
|
315
450
|
},
|
|
316
|
-
...(
|
|
451
|
+
...(Object.keys(preferencias).length ? { owner: preferencias } : {}),
|
|
317
452
|
board: {
|
|
318
453
|
adapter: texto(board.adapter, 'hermes-kanban'),
|
|
319
454
|
default: texto(board.default, 'default'),
|
|
@@ -350,6 +485,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
350
485
|
required_for_ship: booleano(ci.required_for_ship, false),
|
|
351
486
|
context: texto(ci.context, 'ork-verify'),
|
|
352
487
|
command: ci.command === null || ci.command === undefined ? undefined : texto(ci.command, ''),
|
|
488
|
+
external_repositories: repositoriosExternos(ci.external_repositories, erros),
|
|
353
489
|
},
|
|
354
490
|
concurrency: {
|
|
355
491
|
max_parallel_threads: numero(concurrency.max_parallel_threads, 3),
|
|
@@ -382,6 +518,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
382
518
|
cli: texto(memory.cli, 'orkmind'),
|
|
383
519
|
tenant: texto(memory.tenant, '') || nomeProjeto,
|
|
384
520
|
timeout_ms: numero(memory.timeout_ms, 15000),
|
|
521
|
+
...(embedding ? { embedding } : {}),
|
|
385
522
|
},
|
|
386
523
|
// Bloco B5: governanca de custo dos auditores. Ausente no manifesto quer dizer
|
|
387
524
|
// "nao declarada", nao "sem limite": a rodada avisa que so `eco` e o escopo
|
|
@@ -402,7 +539,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
402
539
|
return { caminho: achado.caminho, raiz, legado: achado.legado, bytes, manifesto, erros, avisos };
|
|
403
540
|
}
|
|
404
541
|
/** Carrega o manifesto ou encerra com mensagem acionavel (uso nos subcomandos). */
|
|
405
|
-
function exigirManifesto(dirInicial =
|
|
542
|
+
function exigirManifesto(dirInicial = diretorioDoProjeto()) {
|
|
406
543
|
const carregado = carregarManifesto(dirInicial);
|
|
407
544
|
if (!carregado) {
|
|
408
545
|
throw new Error(`manifesto ${exports.NOME_MANIFESTO} nao encontrado a partir de ${dirInicial}. Rode: ork init`);
|