@orkastery/cli 0.5.3 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/README.md +158 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +1 -1
- package/adapters/openclaw/README.md +2 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/dist/adapters/claude-bg.js +77 -13
- package/dist/adapters/codex.js +10 -1
- package/dist/agents-md.js +24 -15
- package/dist/auditrun.js +17 -2
- package/dist/board.js +4 -3
- package/dist/branch-de-estado.js +23 -1
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +13 -7
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +3 -1
- package/dist/company-brain-client.js +36 -1
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +36 -1
- package/dist/conducao-sentinela.js +388 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +122 -30
- package/dist/conhecimento.js +490 -0
- package/dist/docs.js +22 -8
- package/dist/doctor-pulse-cron.js +182 -27
- package/dist/doctor-rede.js +18 -17
- package/dist/doctor.js +115 -111
- package/dist/entrega-pr.js +22 -21
- package/dist/escopo-escrita.js +10 -1
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +38 -8
- package/dist/fabrica-publicar.js +3 -1
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +1 -1
- package/dist/gates.js +3 -28
- package/dist/handoff.js +10 -6
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-resumo.js +29 -3
- package/dist/hitl-tempo-parado.js +4 -4
- package/dist/hitl.js +29 -33
- package/dist/horario.js +14 -7
- package/dist/hosts.js +52 -1
- package/dist/impedimento.js +108 -14
- package/dist/index.js +623 -521
- package/dist/indice.js +7 -1
- package/dist/init.js +43 -47
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +92 -21
- package/dist/intelligence-graph-contexto.js +56 -17
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-query.js +66 -19
- package/dist/ledger.js +15 -1
- package/dist/liveness.js +6 -3
- package/dist/locale-ajuda.js +761 -0
- package/dist/locale-gerados.js +160 -0
- package/dist/locale-master.js +99 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-pulse.js +211 -0
- package/dist/locale-retry.js +385 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-ship.js +494 -0
- package/dist/locale-thread.js +277 -0
- package/dist/locale-verify.js +125 -0
- package/dist/locale.js +714 -0
- package/dist/manifest.js +129 -3
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +4 -6
- package/dist/master.js +21 -24
- package/dist/mcp-artifacts.js +11 -2
- package/dist/mcp-git.js +23 -6
- package/dist/mcp-grafo.js +7 -4
- package/dist/mcp-server.js +8 -2
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +5 -2
- package/dist/noctum-arco.js +911 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-envelope.js +810 -0
- package/dist/noctum-execucao.js +304 -0
- package/dist/noctum-fila.js +280 -0
- package/dist/ocupacao.js +29 -2
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +9 -2
- package/dist/orquestracao.js +13 -12
- package/dist/parado-no-condutor.js +34 -9
- package/dist/phase.js +94 -39
- package/dist/policies.js +4 -4
- package/dist/portfolio.js +4 -2
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +127 -4
- package/dist/prompts.js +6 -2
- package/dist/pulse-delivery.js +13 -2
- package/dist/pulse.js +52 -19
- package/dist/ratelimit.js +4 -2
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-status.js +3 -2
- package/dist/retry.js +232 -148
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +129 -33
- package/dist/runtime-context.js +181 -3
- package/dist/runtimes.js +2 -1
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +7 -1
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +1 -1
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +84 -81
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +153 -82
- package/dist/util.js +8 -1
- package/dist/verify.js +141 -52
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +165 -0
- package/dist/worktree.js +20 -0
- package/dist/yaml.js +27 -8
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
package/dist/policies.js
CHANGED
|
@@ -21,6 +21,7 @@ exports.bloqueantes = bloqueantes;
|
|
|
21
21
|
exports.motivoDominante = motivoDominante;
|
|
22
22
|
exports.linhasDeAviso = linhasDeAviso;
|
|
23
23
|
exports.textoDeViolacoes = textoDeViolacoes;
|
|
24
|
+
const locale_1 = require("./locale");
|
|
24
25
|
/**
|
|
25
26
|
* Variaveis que REDIRECIONAM o despacho do `claude` para um provider pago.
|
|
26
27
|
* Mesma lista do `ork doctor`: presenca delas com `subscription-only` e bloqueante.
|
|
@@ -313,10 +314,9 @@ function motivoDominante(violacoes) {
|
|
|
313
314
|
}
|
|
314
315
|
/** RM-008 (fatia 3): as linhas de aviso (policies em warn) para a saida do CLI. Nada quando nao ha aviso. */
|
|
315
316
|
function linhasDeAviso(violacoes, recuo = ' ') {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
]);
|
|
317
|
+
// RM-061 (F2a): a moldura segue o locale do comando; detalhe e correcao sao dado da policy avaliada.
|
|
318
|
+
const m = (0, locale_1.msg)().phase;
|
|
319
|
+
return avisos(violacoes).flatMap((v) => [m.avisoDaPolicy(recuo, v.policy, v.detalhe), m.avisoCorrecao(recuo, v.correcao)]);
|
|
320
320
|
}
|
|
321
321
|
/** Texto das violacoes para a saida do CLI. */
|
|
322
322
|
function textoDeViolacoes(violacoes) {
|
package/dist/portfolio.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ID_DE_ENTIDADE = void 0;
|
|
36
37
|
exports.readPortfolio = readPortfolio;
|
|
37
38
|
exports.findEntity = findEntity;
|
|
38
39
|
exports.createProduct = createProduct;
|
|
@@ -48,7 +49,8 @@ const estado_thread_1 = require("./estado-thread");
|
|
|
48
49
|
const company_brain_journal_1 = require("./company-brain-journal");
|
|
49
50
|
const util_1 = require("./util");
|
|
50
51
|
const creation_operation_store_1 = require("./creation-operation-store");
|
|
51
|
-
|
|
52
|
+
/** Id de produto, projeto ou iniciativa: o mesmo que o Brain recebe e o conhecimento cita (RM-026 K7, D3). */
|
|
53
|
+
exports.ID_DE_ENTIDADE = /^(prod|proj|init)-[a-z0-9][a-z0-9-]{2,47}$/;
|
|
52
54
|
const PREFIX = { product: 'prod-', project: 'proj-', initiative: 'init-' };
|
|
53
55
|
const statuses = ['idea', 'planned', 'ready', 'in_progress', 'validating', 'delivered', 'blocked', 'cancelled'];
|
|
54
56
|
const unique = (values = []) => [...new Set(values.map((v) => v.trim()).filter(Boolean))];
|
|
@@ -71,7 +73,7 @@ function mutatePortfolio(root, run) {
|
|
|
71
73
|
return (0, creation_operation_store_1.withCreationLock)((0, estado_thread_1.raizDoEstado)(root), 'portfolio', () => (0, company_brain_journal_1.portfolioMutation)(file(root), run));
|
|
72
74
|
}
|
|
73
75
|
function validateBase(kind, input) {
|
|
74
|
-
if (!
|
|
76
|
+
if (!exports.ID_DE_ENTIDADE.test(input.id) || !input.id.startsWith(PREFIX[kind]))
|
|
75
77
|
throw new Error(`${kind} exige id ${PREFIX[kind]}...`);
|
|
76
78
|
if (!input.title.trim() || input.title.length > 160)
|
|
77
79
|
throw new Error('título deve ter entre 1 e 160 caracteres');
|
|
@@ -0,0 +1,234 @@
|
|
|
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.POSTURAS_QUE_AFROUXAM = exports.ARQUIVO_POSTURA_LOCAL = exports.CONTRATO_POSTURA_LOCAL = void 0;
|
|
37
|
+
exports.pastaPrivadaDaPostura = pastaPrivadaDaPostura;
|
|
38
|
+
exports.caminhoDaPosturaLocal = caminhoDaPosturaLocal;
|
|
39
|
+
exports.posturaAfrouxada = posturaAfrouxada;
|
|
40
|
+
exports.comandoDeConfirmacao = comandoDeConfirmacao;
|
|
41
|
+
exports.lerArquivoPrivado = lerArquivoPrivado;
|
|
42
|
+
exports.raizRealDoEstado = raizRealDoEstado;
|
|
43
|
+
exports.lerPosturaLocal = lerPosturaLocal;
|
|
44
|
+
exports.recusaDePostura = recusaDePostura;
|
|
45
|
+
exports.gravarArquivoPrivado = gravarArquivoPrivado;
|
|
46
|
+
exports.revogarArquivoPrivado = revogarArquivoPrivado;
|
|
47
|
+
exports.confirmarPosturaLocal = confirmarPosturaLocal;
|
|
48
|
+
exports.revogarPosturaLocal = revogarPosturaLocal;
|
|
49
|
+
/**
|
|
50
|
+
* RM-047 (fronteira de confiança, P1): a postura de sandbox que afrouxa vale só com a confirmação
|
|
51
|
+
* desta máquina.
|
|
52
|
+
*
|
|
53
|
+
* O `runtime.sandbox` mora no `orkastery.yaml`, que vem com o repositório. Um valor que afrouxa o
|
|
54
|
+
* sandbox do agente (hoje, `danger-full-access`, que no Codex vai junto com aprovação `never`) não
|
|
55
|
+
* pode valer só porque o manifesto clonado o declara: o despacho recusa com
|
|
56
|
+
* `runtime.sandbox-nao-confirmado` até a máquina confirmar a mesma postura no setup local, com
|
|
57
|
+
* `ork setup sandbox confirmar <postura>`.
|
|
58
|
+
*
|
|
59
|
+
* A confirmação fica em `.orkastery/private/postura-local.json`, na raiz de estado: pasta 0700 e
|
|
60
|
+
* arquivo 0600 do próprio usuário, nunca link e nunca rastreado pelo git. Ela guarda a postura e o
|
|
61
|
+
* caminho real da raiz, então não vale para outra postura nem para outro checkout.
|
|
62
|
+
*/
|
|
63
|
+
const fs = __importStar(require("node:fs"));
|
|
64
|
+
const path = __importStar(require("node:path"));
|
|
65
|
+
const node_crypto_1 = require("node:crypto");
|
|
66
|
+
const codex_1 = require("./adapters/codex");
|
|
67
|
+
const estado_thread_1 = require("./estado-thread");
|
|
68
|
+
const procedencia_1 = require("./procedencia");
|
|
69
|
+
const util_1 = require("./util");
|
|
70
|
+
exports.CONTRATO_POSTURA_LOCAL = 'ork.postura-local/v1';
|
|
71
|
+
exports.ARQUIVO_POSTURA_LOCAL = 'postura-local.json';
|
|
72
|
+
const LIMITE_BYTES = 4096;
|
|
73
|
+
/**
|
|
74
|
+
* As posturas de sandbox que afrouxam o padrão (`workspace-write`). `read-only` aperta e vale sem
|
|
75
|
+
* confirmação. Uma postura nova do runtime entra aqui antes de poder ser declarada.
|
|
76
|
+
*/
|
|
77
|
+
exports.POSTURAS_QUE_AFROUXAM = ['danger-full-access'];
|
|
78
|
+
function uid() {
|
|
79
|
+
return typeof process.getuid === 'function' ? process.getuid() : null;
|
|
80
|
+
}
|
|
81
|
+
function pastaPrivadaDaPostura(raiz) {
|
|
82
|
+
return path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'private');
|
|
83
|
+
}
|
|
84
|
+
function caminhoDaPosturaLocal(raiz) {
|
|
85
|
+
return path.join(pastaPrivadaDaPostura(raiz), exports.ARQUIVO_POSTURA_LOCAL);
|
|
86
|
+
}
|
|
87
|
+
/** O manifesto pede uma postura que afrouxa o sandbox? */
|
|
88
|
+
function posturaAfrouxada(manifesto) {
|
|
89
|
+
const sandbox = manifesto.runtime.sandbox ?? codex_1.SANDBOX_PADRAO;
|
|
90
|
+
return exports.POSTURAS_QUE_AFROUXAM.includes(sandbox) ? sandbox : null;
|
|
91
|
+
}
|
|
92
|
+
/** Comando que confirma a postura nesta máquina; vai em toda recusa. */
|
|
93
|
+
function comandoDeConfirmacao(postura) {
|
|
94
|
+
return `ork setup sandbox confirmar ${postura}`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* RM-047 (P3): lê um arquivo de `.orkastery/private/` com as regras da confirmação local: pasta 0700 e
|
|
98
|
+
* arquivo 0600 do próprio usuário, nunca link, nunca rastreado pelo git, até 4 KiB e JSON válido.
|
|
99
|
+
* Devolve o objeto cru, ou `null` com o motivo.
|
|
100
|
+
*/
|
|
101
|
+
function lerArquivoPrivado(raiz, nome) {
|
|
102
|
+
const pasta = pastaPrivadaDaPostura(raiz), arquivo = path.join(pasta, nome);
|
|
103
|
+
const stPasta = fs.lstatSync(pasta, { throwIfNoEntry: false });
|
|
104
|
+
if (!stPasta)
|
|
105
|
+
return { bruto: null, motivo: 'sem confirmação local' };
|
|
106
|
+
const dono = uid();
|
|
107
|
+
if (!stPasta.isDirectory() || stPasta.isSymbolicLink() || (dono !== null && stPasta.uid !== dono) ||
|
|
108
|
+
(stPasta.mode & 0o077) !== 0)
|
|
109
|
+
return { bruto: null, motivo: '.orkastery/private precisa ser pasta 0700 do próprio usuário' };
|
|
110
|
+
const st = fs.lstatSync(arquivo, { throwIfNoEntry: false });
|
|
111
|
+
if (!st)
|
|
112
|
+
return { bruto: null, motivo: 'sem confirmação local' };
|
|
113
|
+
if (!st.isFile() || st.isSymbolicLink() || st.nlink !== 1 || (dono !== null && st.uid !== dono) ||
|
|
114
|
+
(st.mode & 0o077) !== 0 || st.size > LIMITE_BYTES) {
|
|
115
|
+
return { bruto: null, motivo: `${nome} precisa ser arquivo 0600 do próprio usuário` };
|
|
116
|
+
}
|
|
117
|
+
if ((0, procedencia_1.rastreadoPeloGit)(raiz, arquivo)) {
|
|
118
|
+
return { bruto: null, motivo: `${nome} está versionado no git e não vale como confirmação local` };
|
|
119
|
+
}
|
|
120
|
+
let bruto;
|
|
121
|
+
try {
|
|
122
|
+
bruto = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return { bruto: null, motivo: `${nome} com JSON inválido` };
|
|
126
|
+
}
|
|
127
|
+
if (!bruto || typeof bruto !== 'object' || Array.isArray(bruto))
|
|
128
|
+
return { bruto: null, motivo: `${nome} com JSON inválido` };
|
|
129
|
+
return { bruto: bruto, motivo: null };
|
|
130
|
+
}
|
|
131
|
+
/** Caminho real da raiz de estado: a confirmação local vale só para o checkout que a gravou. */
|
|
132
|
+
function raizRealDoEstado(raiz) {
|
|
133
|
+
try {
|
|
134
|
+
return fs.realpathSync((0, estado_thread_1.raizDoEstado)(raiz));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return path.resolve(raiz);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Lê a confirmação local. `null` quando ela não existe ou não vale (pasta ou arquivo com dono ou modo
|
|
142
|
+
* errado, link, rastreado pelo git, JSON inválido, outra raiz); o motivo vai em `motivo`.
|
|
143
|
+
*/
|
|
144
|
+
function lerPosturaLocal(raiz) {
|
|
145
|
+
const { bruto: o, motivo } = lerArquivoPrivado(raiz, exports.ARQUIVO_POSTURA_LOCAL);
|
|
146
|
+
if (!o)
|
|
147
|
+
return { postura: null, motivo };
|
|
148
|
+
if (o.contrato !== exports.CONTRATO_POSTURA_LOCAL || typeof o.sandbox !== 'string' ||
|
|
149
|
+
typeof o.raiz !== 'string' || typeof o.confirmadoEm !== 'string' || typeof o.por !== 'string') {
|
|
150
|
+
return { postura: null, motivo: `${exports.ARQUIVO_POSTURA_LOCAL} fora do contrato ${exports.CONTRATO_POSTURA_LOCAL}` };
|
|
151
|
+
}
|
|
152
|
+
if (o.raiz !== raizRealDoEstado(raiz))
|
|
153
|
+
return { postura: null, motivo: 'a confirmação local é de outro checkout' };
|
|
154
|
+
return { postura: o, motivo: null };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A recusa do despacho: `null` quando a postura do manifesto vale nesta máquina (não afrouxa, ou
|
|
158
|
+
* foi confirmada localmente com o mesmo valor); senão o erro tipado com o comando que confirma.
|
|
159
|
+
* Só o Codex recebe a postura de sandbox do manifesto.
|
|
160
|
+
*/
|
|
161
|
+
function recusaDePostura(raiz, manifesto, runtime) {
|
|
162
|
+
if (runtime !== 'codex')
|
|
163
|
+
return null;
|
|
164
|
+
const postura = posturaAfrouxada(manifesto);
|
|
165
|
+
if (!postura)
|
|
166
|
+
return null;
|
|
167
|
+
const { postura: local, motivo } = lerPosturaLocal(raiz);
|
|
168
|
+
if (local && local.sandbox === postura)
|
|
169
|
+
return null;
|
|
170
|
+
const porque = local ? `a máquina confirmou ${local.sandbox}, não ${postura}` : motivo ?? 'sem confirmação local';
|
|
171
|
+
return `runtime.sandbox-nao-confirmado: o orkastery.yaml pede runtime.sandbox: ${postura}, que desliga o sandbox ` +
|
|
172
|
+
`do agente, e esta máquina não confirmou essa postura (${porque}). Nada foi despachado. O manifesto vem com o ` +
|
|
173
|
+
`repositório; quem decide afrouxar o sandbox é a máquina. Para aceitar nesta máquina: ${comandoDeConfirmacao(postura)}` +
|
|
174
|
+
` (ou volte o manifesto para ${codex_1.SANDBOX_PADRAO}).`;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* RM-047 (P3): grava um objeto em `.orkastery/private/<nome>` com as regras da confirmação local
|
|
178
|
+
* (pasta 0700, arquivo 0600 por temporário exclusivo e `rename`, nunca seguindo link).
|
|
179
|
+
*/
|
|
180
|
+
function gravarArquivoPrivado(raiz, nome, objeto) {
|
|
181
|
+
const pasta = pastaPrivadaDaPostura(raiz), arquivo = path.join(pasta, nome);
|
|
182
|
+
const estado = path.dirname(pasta);
|
|
183
|
+
const stEstado = fs.lstatSync(estado, { throwIfNoEntry: false });
|
|
184
|
+
if (stEstado && (!stEstado.isDirectory() || stEstado.isSymbolicLink()))
|
|
185
|
+
throw new Error('estado.link: .orkastery precisa ser pasta de verdade');
|
|
186
|
+
fs.mkdirSync(estado, { recursive: true });
|
|
187
|
+
try {
|
|
188
|
+
fs.mkdirSync(pasta, { mode: 0o700 });
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
if (e.code !== 'EEXIST')
|
|
192
|
+
throw e;
|
|
193
|
+
}
|
|
194
|
+
const stPasta = fs.lstatSync(pasta);
|
|
195
|
+
if (!stPasta.isDirectory() || stPasta.isSymbolicLink())
|
|
196
|
+
throw new Error('estado.link: .orkastery/private precisa ser pasta de verdade');
|
|
197
|
+
fs.chmodSync(pasta, 0o700);
|
|
198
|
+
if ((0, procedencia_1.rastreadoPeloGit)(raiz, arquivo)) {
|
|
199
|
+
throw new Error(`estado.rastreado: ${nome} está versionado no git; tire-o do índice (git rm --cached) antes de confirmar`);
|
|
200
|
+
}
|
|
201
|
+
const temp = path.join(pasta, `.${nome}.${(0, node_crypto_1.randomUUID)()}.tmp`);
|
|
202
|
+
const fd = fs.openSync(temp, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_NOFOLLOW, 0o600);
|
|
203
|
+
try {
|
|
204
|
+
fs.fchmodSync(fd, 0o600);
|
|
205
|
+
fs.writeFileSync(fd, JSON.stringify(objeto, null, 2) + '\n');
|
|
206
|
+
fs.fsyncSync(fd);
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
fs.closeSync(fd);
|
|
210
|
+
}
|
|
211
|
+
fs.renameSync(temp, arquivo);
|
|
212
|
+
}
|
|
213
|
+
/** Apaga um arquivo de `.orkastery/private/`; devolve se havia um. */
|
|
214
|
+
function revogarArquivoPrivado(raiz, nome) {
|
|
215
|
+
const arquivo = path.join(pastaPrivadaDaPostura(raiz), nome);
|
|
216
|
+
const st = fs.lstatSync(arquivo, { throwIfNoEntry: false });
|
|
217
|
+
if (!st)
|
|
218
|
+
return false;
|
|
219
|
+
fs.rmSync(arquivo, { force: true });
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
/** Grava a confirmação local de uma postura de sandbox para este checkout. */
|
|
223
|
+
function confirmarPosturaLocal(raiz, sandbox, por) {
|
|
224
|
+
if (!codex_1.SANDBOXES_DO_CODEX.includes(sandbox)) {
|
|
225
|
+
throw new Error(`runtime.sandbox-invalido: "${sandbox}" (aceitos: ${codex_1.SANDBOXES_DO_CODEX.join(', ')})`);
|
|
226
|
+
}
|
|
227
|
+
const postura = { contrato: exports.CONTRATO_POSTURA_LOCAL, sandbox, raiz: raizRealDoEstado(raiz), confirmadoEm: (0, util_1.agora)(), por };
|
|
228
|
+
gravarArquivoPrivado(raiz, exports.ARQUIVO_POSTURA_LOCAL, postura);
|
|
229
|
+
return postura;
|
|
230
|
+
}
|
|
231
|
+
/** Apaga a confirmação local; devolve se havia uma. */
|
|
232
|
+
function revogarPosturaLocal(raiz) {
|
|
233
|
+
return revogarArquivoPrivado(raiz, exports.ARQUIVO_POSTURA_LOCAL);
|
|
234
|
+
}
|
package/dist/preflight.js
CHANGED
|
@@ -51,6 +51,7 @@ const modos_1 = require("./modos");
|
|
|
51
51
|
const setup_1 = require("./setup");
|
|
52
52
|
const phase_1 = require("./phase");
|
|
53
53
|
const runtimes_1 = require("./runtimes");
|
|
54
|
+
const locale_1 = require("./locale");
|
|
54
55
|
const codex_2 = require("./adapters/codex");
|
|
55
56
|
const runtime_ambiente_1 = require("./runtime-ambiente");
|
|
56
57
|
const slug_1 = require("./slug");
|
|
@@ -92,10 +93,10 @@ function umaskDoProcesso() {
|
|
|
92
93
|
function sondasDeAmbiente(raiz, umask = umaskDoProcesso()) {
|
|
93
94
|
const checks = [];
|
|
94
95
|
const octal = (v) => '0' + v.toString(8).padStart(3, '0');
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
const m = (0, locale_1.msg)().doctor;
|
|
97
|
+
checks.push(umask === null ? { nome: 'umask', nivel: 'warn', detalhe: m.umaskNaoMedido }
|
|
98
|
+
: (umask & 0o022) === 0o022 ? { nome: 'umask', nivel: 'ok', detalhe: m.umaskOk(octal(umask)) }
|
|
99
|
+
: { nome: 'umask', nivel: 'warn', detalhe: m.umaskAberto(octal(umask)), correcao: m.umaskCorrecao });
|
|
99
100
|
const estado = path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery');
|
|
100
101
|
let st = null;
|
|
101
102
|
try {
|
|
@@ -103,19 +104,19 @@ function sondasDeAmbiente(raiz, umask = umaskDoProcesso()) {
|
|
|
103
104
|
}
|
|
104
105
|
catch { /* ausencia e do check de estado do projeto */ }
|
|
105
106
|
if (st)
|
|
106
|
-
checks.push(!st.isDirectory() || st.isSymbolicLink() ? { nome: 'permissoes do estado', nivel: 'fail', detalhe:
|
|
107
|
-
: st.mode & 0o022 ? { nome: 'permissoes do estado', nivel: 'warn', detalhe:
|
|
108
|
-
: { nome: 'permissoes do estado', nivel: 'ok', detalhe:
|
|
107
|
+
checks.push(!st.isDirectory() || st.isSymbolicLink() ? { nome: 'permissoes do estado', nivel: 'fail', detalhe: m.estadoNaoDiretorio(estado) }
|
|
108
|
+
: st.mode & 0o022 ? { nome: 'permissoes do estado', nivel: 'warn', detalhe: m.estadoAberto(estado, octal(st.mode & 0o777)), correcao: `chmod go-w ${estado}` }
|
|
109
|
+
: { nome: 'permissoes do estado', nivel: 'ok', detalhe: m.estadoComModo(estado, octal(st.mode & 0o777)) });
|
|
109
110
|
const privada = path.join(estado, 'private');
|
|
110
111
|
let sp = null;
|
|
111
112
|
try {
|
|
112
113
|
sp = fs.lstatSync(privada);
|
|
113
114
|
}
|
|
114
115
|
catch { /* criada sob demanda */ }
|
|
115
|
-
checks.push(!sp ? { nome: 'pasta privada', nivel: 'ok', detalhe:
|
|
116
|
+
checks.push(!sp ? { nome: 'pasta privada', nivel: 'ok', detalhe: m.privadaAusente(privada) }
|
|
116
117
|
: !sp.isDirectory() || sp.isSymbolicLink() || sp.uid !== process.getuid?.() || (sp.mode & 0o777) !== 0o700
|
|
117
|
-
? { nome: 'pasta privada', nivel: 'fail', detalhe:
|
|
118
|
-
: { nome: 'pasta privada', nivel: 'ok', detalhe:
|
|
118
|
+
? { nome: 'pasta privada', nivel: 'fail', detalhe: m.privadaAberta(privada, octal(sp.mode & 0o777)), correcao: `chmod 700 ${privada}` }
|
|
119
|
+
: { nome: 'pasta privada', nivel: 'ok', detalhe: m.privadaOk(privada) });
|
|
119
120
|
return checks;
|
|
120
121
|
}
|
|
121
122
|
/**
|
|
@@ -0,0 +1,225 @@
|
|
|
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.rastreadoPeloGit = rastreadoPeloGit;
|
|
37
|
+
exports.exigirCwdLocal = exigirCwdLocal;
|
|
38
|
+
exports.cwdLocalOuNulo = cwdLocalOuNulo;
|
|
39
|
+
exports.diretorioDaThread = diretorioDaThread;
|
|
40
|
+
exports.exigirHostLocal = exigirHostLocal;
|
|
41
|
+
exports.caminhoContidoOuNulo = caminhoContidoOuNulo;
|
|
42
|
+
exports.exigirCaminhoContido = exigirCaminhoContido;
|
|
43
|
+
/**
|
|
44
|
+
* RM-047 (fronteira de confiança, P2): procedência do estado que escolhe onde o `ork` roda.
|
|
45
|
+
*
|
|
46
|
+
* O `.orkastery/` é estado desta máquina, mas nada impede um repositório de versioná-lo, e o clone
|
|
47
|
+
* traz então `thread.json`, ledger, fila e arquivos de host escritos em outro lugar. Duas regras
|
|
48
|
+
* separam o estado local do que veio no clone:
|
|
49
|
+
*
|
|
50
|
+
* 1. Estado rastreado pelo git não vale como estado local para escolher diretório ou executável.
|
|
51
|
+
* `thread.json`, ledger e fila rastreados não escolhem o cwd; `pulse-host.json` e
|
|
52
|
+
* `master-host.json` rastreados não escolhem o transporte.
|
|
53
|
+
* 2. O cwd do agente e do git, quando sai do estado (worktree da thread, cwd do despacho no ledger,
|
|
54
|
+
* cwd de um pedido da fila), só vale se for uma worktree que o `git worktree list` do próprio
|
|
55
|
+
* repositório registra. A raiz do projeto, escolhida por quem roda o comando, segue valendo.
|
|
56
|
+
*
|
|
57
|
+
* As recusas são tipadas: `estado.rastreado`, `estado.worktree-nao-registrada` e
|
|
58
|
+
* `transporte.rastreado`. Cada mensagem diz o que fazer.
|
|
59
|
+
*/
|
|
60
|
+
const fs = __importStar(require("node:fs"));
|
|
61
|
+
const path = __importStar(require("node:path"));
|
|
62
|
+
const estado_thread_1 = require("./estado-thread");
|
|
63
|
+
const util_1 = require("./util");
|
|
64
|
+
/** Caminho relativo à árvore principal, com `/` (o formato do `git ls-files`). */
|
|
65
|
+
function relativoAoRepositorio(principal, arquivo) {
|
|
66
|
+
const rel = path.relative(principal, path.resolve(arquivo));
|
|
67
|
+
if (!rel || rel.startsWith('..') || path.isAbsolute(rel))
|
|
68
|
+
return null;
|
|
69
|
+
return rel.split(path.sep).join('/');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* O arquivo está no índice do git da árvore principal? Sem git (projeto fora de repositório, git
|
|
73
|
+
* ausente), nada está rastreado. A consulta roda na raiz do projeto, como todo git do `ork`.
|
|
74
|
+
*/
|
|
75
|
+
function rastreadoPeloGit(raiz, arquivo) {
|
|
76
|
+
let principal;
|
|
77
|
+
try {
|
|
78
|
+
principal = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
principal = path.resolve(raiz);
|
|
82
|
+
}
|
|
83
|
+
const rel = relativoAoRepositorio(principal, arquivo);
|
|
84
|
+
if (rel === null)
|
|
85
|
+
return false;
|
|
86
|
+
const r = (0, util_1.exec)('git', ['ls-files', '-z', '--', rel], principal, 30000);
|
|
87
|
+
return r.ok && r.stdout.split('\0').some(Boolean);
|
|
88
|
+
}
|
|
89
|
+
/** As worktrees que o git do repositório registra, pelo caminho real. */
|
|
90
|
+
function worktreesRegistradas(principal) {
|
|
91
|
+
const r = (0, util_1.exec)('git', ['worktree', 'list', '--porcelain'], principal, 30000);
|
|
92
|
+
if (!r.ok)
|
|
93
|
+
return [];
|
|
94
|
+
return r.stdout.split('\n').filter(l => l.startsWith('worktree ')).map(l => {
|
|
95
|
+
const dir = l.slice('worktree '.length).trim();
|
|
96
|
+
try {
|
|
97
|
+
return fs.realpathSync(dir);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return path.resolve(dir);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function real(p) {
|
|
105
|
+
try {
|
|
106
|
+
return fs.realpathSync(p);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return path.resolve(p);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const ARQUIVO_DA_ORIGEM = {
|
|
113
|
+
thread: id => `.orkastery/threads/${id}/thread.json`,
|
|
114
|
+
ledger: id => `.orkastery/threads/${id}/ledger.jsonl`,
|
|
115
|
+
fila: () => '.orkastery/retry/fila.jsonl',
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Confere o diretório que o estado escolheu para rodar git ou agente. Devolve o próprio diretório
|
|
119
|
+
* quando ele vale; senão lança `estado.rastreado` ou `estado.worktree-nao-registrada`.
|
|
120
|
+
*
|
|
121
|
+
* A raiz do projeto (o checkout de quem roda o comando) e a árvore principal valem sem consulta: não
|
|
122
|
+
* saem do estado.
|
|
123
|
+
*/
|
|
124
|
+
function exigirCwdLocal(raiz, threadId, dir, origem) {
|
|
125
|
+
let principal;
|
|
126
|
+
try {
|
|
127
|
+
principal = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
principal = path.resolve(raiz);
|
|
131
|
+
}
|
|
132
|
+
const alvo = real(dir);
|
|
133
|
+
if (alvo === real(raiz) || alvo === real(principal))
|
|
134
|
+
return dir;
|
|
135
|
+
const arquivo = path.join(principal, ARQUIVO_DA_ORIGEM[origem](threadId));
|
|
136
|
+
if (rastreadoPeloGit(principal, arquivo)) {
|
|
137
|
+
throw new Error(`estado.rastreado: ${path.relative(principal, arquivo)} está versionado no git, e estado versionado ` +
|
|
138
|
+
'não escolhe onde o ork roda (ele pode ter vindo de outro repositório). Tire o estado do índice ' +
|
|
139
|
+
'(git rm -r --cached .orkastery e .orkastery/ no .gitignore) e rode o comando de novo.');
|
|
140
|
+
}
|
|
141
|
+
if (!path.isAbsolute(dir) || !fs.existsSync(dir) || !worktreesRegistradas(principal).includes(alvo)) {
|
|
142
|
+
throw new Error(`estado.worktree-nao-registrada: ${JSON.stringify(String(dir)).slice(0, 200)} não aparece em ` +
|
|
143
|
+
`git worktree list deste repositório, e o ork só roda git e agente na raiz do projeto ou numa worktree ` +
|
|
144
|
+
`registrada. Recrie a worktree da thread com ork worktree ensure ${threadId}.`);
|
|
145
|
+
}
|
|
146
|
+
return dir;
|
|
147
|
+
}
|
|
148
|
+
/** Como `exigirCwdLocal`, sem lançar: `null` quando o diretório não vale. */
|
|
149
|
+
function cwdLocalOuNulo(raiz, threadId, dir, origem) {
|
|
150
|
+
try {
|
|
151
|
+
return exigirCwdLocal(raiz, threadId, dir, origem);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* O diretório de trabalho de uma thread: a worktree registrada dela, ou a raiz do projeto quando a
|
|
159
|
+
* thread não tem worktree.
|
|
160
|
+
*/
|
|
161
|
+
function diretorioDaThread(raiz, thread) {
|
|
162
|
+
return thread.worktree ? exigirCwdLocal(raiz, thread.id, thread.worktree, 'thread') : raiz;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* O arquivo de host (`pulse-host.json`, `master-host.json`) escolhe o executável que recebe a
|
|
166
|
+
* mensagem. Só vale o arquivo local: versionado no git, ele veio com o repositório.
|
|
167
|
+
*/
|
|
168
|
+
function exigirHostLocal(raiz, arquivo) {
|
|
169
|
+
if (rastreadoPeloGit(raiz, arquivo)) {
|
|
170
|
+
throw new Error(`transporte.rastreado: ${path.basename(arquivo)} está versionado no git, e o transporte do ` +
|
|
171
|
+
'pulse e do digest só vale do arquivo local desta máquina. Tire-o do índice (git rm --cached) e grave-o ' +
|
|
172
|
+
'de novo nesta máquina.');
|
|
173
|
+
}
|
|
174
|
+
const st = fs.lstatSync(arquivo, { throwIfNoEntry: false });
|
|
175
|
+
if (st && !st.isFile()) {
|
|
176
|
+
throw new Error(`transporte.rastreado: ${path.basename(arquivo)} precisa ser um arquivo comum desta máquina, ` +
|
|
177
|
+
'não link nem diretório.');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** `filho` está dentro de `pai` (ou é o próprio `pai`), pelos caminhos reais? */
|
|
181
|
+
function dentroDe(pai, filho) {
|
|
182
|
+
const rel = path.relative(pai, filho);
|
|
183
|
+
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Um ponteiro que o estado guarda (`location` do `handoff.json`, `arquivo` de uma claim, `promptPath`
|
|
187
|
+
* de uma sessão, arquivo de handoff ou de lição da memória) só é lido se, pelo caminho real (links
|
|
188
|
+
* resolvidos), cair na raiz do projeto, na árvore principal ou na worktree registrada da thread.
|
|
189
|
+
* Devolve o caminho real, ou `null` quando ele sai dessas raízes ou não existe.
|
|
190
|
+
*/
|
|
191
|
+
function caminhoContidoOuNulo(raiz, thread, caminho) {
|
|
192
|
+
let alvo;
|
|
193
|
+
try {
|
|
194
|
+
alvo = fs.realpathSync(caminho);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
let principal;
|
|
200
|
+
try {
|
|
201
|
+
principal = (0, estado_thread_1.raizDoEstado)(raiz);
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
principal = path.resolve(raiz);
|
|
205
|
+
}
|
|
206
|
+
const raizes = [real(raiz), real(principal)];
|
|
207
|
+
if (thread?.worktree) {
|
|
208
|
+
const w = cwdLocalOuNulo(raiz, thread.id, thread.worktree, 'thread');
|
|
209
|
+
if (w)
|
|
210
|
+
raizes.push(real(w));
|
|
211
|
+
}
|
|
212
|
+
return raizes.some(r => dentroDe(r, alvo)) ? alvo : null;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Como `caminhoContidoOuNulo`, com a recusa tipada `ponteiro.fora-da-raiz` quando o arquivo existe
|
|
216
|
+
* mas sai das raízes. Arquivo ausente segue para quem chamou dizer que o ponteiro não resolve.
|
|
217
|
+
*/
|
|
218
|
+
function exigirCaminhoContido(raiz, thread, caminho, origem) {
|
|
219
|
+
const contido = caminhoContidoOuNulo(raiz, thread, caminho);
|
|
220
|
+
if (contido)
|
|
221
|
+
return contido;
|
|
222
|
+
throw new Error(`ponteiro.fora-da-raiz: ${JSON.stringify(String(origem)).slice(0, 200)} aponta para fora da raiz do ` +
|
|
223
|
+
'projeto e da worktree registrada da thread (também por link simbólico). Um ponteiro guardado no estado só lê ' +
|
|
224
|
+
'arquivo de dentro do projeto. Exporte o handoff de novo (ork handoff export) ou copie o arquivo para dentro do projeto.');
|
|
225
|
+
}
|