@orkastery/cli 0.6.0 → 0.6.1
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/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/dist/adapters/claude-bg.js +6 -6
- package/dist/adapters/grok-events.js +227 -0
- package/dist/adapters/grok-mcp-servidor.js +612 -0
- package/dist/adapters/grok-mcp.js +178 -0
- package/dist/adapters/grok.js +686 -0
- package/dist/agendador-cpu.js +92 -0
- package/dist/branch-de-estado.js +7 -2
- package/dist/check-agil.js +48 -20
- package/dist/check-paralelo.js +73 -18
- package/dist/conducao.js +5 -0
- package/dist/docs.js +9 -16
- package/dist/doctor-forja.js +1 -3
- package/dist/doctor.js +1 -1
- package/dist/entrega-pr.js +10 -10
- package/dist/fabrica-estado.js +20 -11
- package/dist/horario.js +4 -1
- package/dist/index.js +20 -10
- package/dist/integracoes-locais.js +2 -0
- package/dist/locale-accounts.js +11 -3
- package/dist/locale-ajuda.js +10 -6
- package/dist/locale-forja.js +2 -4
- package/dist/locale-operacao-restante.js +2 -2
- package/dist/locale-retry.js +40 -2
- package/dist/locale-ship.js +10 -2
- package/dist/manifest.js +6 -1
- package/dist/mcp-install.js +22 -12
- package/dist/mcp-server.js +42 -16
- package/dist/merge-da-entrega.js +85 -0
- package/dist/network-roadmap.js +18 -14
- package/dist/noctum-aceite.js +154 -14
- package/dist/noctum-cli.js +69 -12
- package/dist/noctum-entrega.js +55 -7
- package/dist/noctum-envelope.js +1 -1
- package/dist/noctum-execucao.js +5 -1
- package/dist/noctum-fechamento.js +18 -7
- package/dist/noctum-fila.js +6 -1
- package/dist/noctum-guarda.js +81 -4
- package/dist/noctum-maquinas.js +5 -1
- package/dist/noctum-modo.js +35 -7
- package/dist/noctum-reconciliacao.js +4 -1
- package/dist/noctum-rede.js +102 -0
- package/dist/noctum-relatorio.js +44 -10
- package/dist/noctum-retomada-rede.js +71 -0
- package/dist/noctum-vigia.js +49 -9
- package/dist/parado-no-condutor.js +14 -15
- package/dist/phase.js +43 -15
- package/dist/postura-local.js +7 -3
- package/dist/preflight.js +2 -1
- package/dist/ratelimit.js +1 -0
- package/dist/retry.js +103 -10
- package/dist/runtime-context.js +165 -45
- package/dist/runtime-escada.js +7 -1
- package/dist/runtime-profiles.js +27 -17
- package/dist/runtimes.js +42 -5
- package/dist/security-scan/agil.js +18 -7
- package/dist/security-scan/executor.js +8 -0
- package/dist/session-watcher-grok.js +425 -0
- package/dist/session-watcher.js +19 -4
- package/dist/sessoes-contas.js +33 -5
- package/dist/setup.js +11 -11
- package/dist/varredura-acoes.js +8 -15
- package/dist/verify.js +26 -10
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "orkastery",
|
|
3
3
|
"name": "Orkastery",
|
|
4
4
|
"description": "Conducao de looping threads em 6 fases pelo nucleo `ork`, exposta ao OpenClaw como tools `ork_*`. Zero regra de negocio no host: cada tool e uma chamada de CLI. Toda tool de projeto aceita projeto (nome do projeto pedido); o cwd do gateway nunca escolhe o projeto. O status do roadmap vem de ork_network_roadmap; as maquinas da pessoa, de ork_network_status (sem projeto).",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.1",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"properties": {},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orkastery/openclaw-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Plugin Orkastery para OpenClaw: 32 tools ork_* que traduzem intencao em chamada de CLI do nucleo `ork`. Zero regra de negocio no host.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": true,
|
|
@@ -42,7 +42,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
42
42
|
};
|
|
43
43
|
})();
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.DAEMON_DO_CLAUDE = exports.PRAZO_DO_DAEMON_MS = exports.JANELA_CURTA_MAX_MS = exports.MOD_DA_ENTREGA = exports.MOD_DOS_TETOS = exports.MINIMO_DA_API_DE_MODS = exports.NOME_ADAPTER = void 0;
|
|
45
|
+
exports.DAEMON_DO_CLAUDE = exports.PRAZO_DO_DAEMON_MS = exports.JANELA_CURTA_MAX_MS = exports.MOD_DA_ENTREGA = exports.MOD_DOS_TETOS = exports.MINIMO_DA_API_DE_MODS = exports.MUTACOES_WORKTREE = exports.CONSULTAS_MCP = exports.NOME_ADAPTER = void 0;
|
|
46
46
|
exports.ambienteDoPerfil = ambienteDoPerfil;
|
|
47
47
|
exports.semContextoDeDespacho = semContextoDeDespacho;
|
|
48
48
|
exports.disponivel = disponivel;
|
|
@@ -103,8 +103,8 @@ function semContextoDeDespacho(base, contexto) {
|
|
|
103
103
|
delete env[chave];
|
|
104
104
|
return env;
|
|
105
105
|
}
|
|
106
|
-
/** Consultas do perfil interactive; gates e respostas humanas nunca recebem grant. */
|
|
107
|
-
|
|
106
|
+
/** Consultas do perfil interactive; gates e respostas humanas nunca recebem grant. RM-067 (F4b): o grok usa as mesmas. */
|
|
107
|
+
exports.CONSULTAS_MCP = [
|
|
108
108
|
'mcp__orkastery__ork_thread_status',
|
|
109
109
|
'mcp__orkastery__ork_phase_list',
|
|
110
110
|
'mcp__orkastery__ork_hitl_pending',
|
|
@@ -113,7 +113,7 @@ const CONSULTAS_MCP = [
|
|
|
113
113
|
'mcp__orkastery__ork_claims_list',
|
|
114
114
|
];
|
|
115
115
|
// RM-037 (defeito 1): a decisao autonoma pelo MCP tem o mesmo grant nos dois runtimes.
|
|
116
|
-
|
|
116
|
+
exports.MUTACOES_WORKTREE = [
|
|
117
117
|
'mcp__orkastery__ork_artifact_write', 'mcp__orkastery__ork_claim_add', 'mcp__orkastery__ork_decision_record',
|
|
118
118
|
'mcp__orkastery__ork_git_commit', 'mcp__orkastery__ork_verify', 'mcp__orkastery__ork_ship',
|
|
119
119
|
];
|
|
@@ -133,7 +133,7 @@ function permissoesDaWorktree(cwd, permiteEditarProduto, consultas) {
|
|
|
133
133
|
for (const prefixo of [`${absoluto}/${nome}`, `${absoluto}/**/${nome}`])
|
|
134
134
|
deny.push(`Edit(${prefixo})`, `Edit(${prefixo}/**)`);
|
|
135
135
|
}
|
|
136
|
-
return { allow: [...consultas, 'mcp__orkastery__ork_git_status', ...MUTACOES_WORKTREE, ...(permiteEditarProduto ? [`Edit(${absoluto}/**)`] : [])], deny };
|
|
136
|
+
return { allow: [...consultas, 'mcp__orkastery__ork_git_status', ...exports.MUTACOES_WORKTREE, ...(permiteEditarProduto ? [`Edit(${absoluto}/**)`] : [])], deny };
|
|
137
137
|
}
|
|
138
138
|
/** O runtime esta disponivel nesta maquina? */
|
|
139
139
|
function disponivel() {
|
|
@@ -246,7 +246,7 @@ function montarComando(pedido) {
|
|
|
246
246
|
args.push('--settings', JSON.stringify({ env: doDespacho, ...(teams ? { teammateMode: teams.teammateMode } : {}), ...(entrada ?? {}) }));
|
|
247
247
|
}
|
|
248
248
|
if (contexto) {
|
|
249
|
-
const consultas = [...CONSULTAS_MCP, ...(contexto.grafoMcp ? consultasDoGrafoMcp() : [])];
|
|
249
|
+
const consultas = [...exports.CONSULTAS_MCP, ...(contexto.grafoMcp ? consultasDoGrafoMcp() : [])];
|
|
250
250
|
// Somente o servidor gerado: nao mesclar MCP de outros escopos nesta sessao filha.
|
|
251
251
|
const permissoes = contexto.permissoesFilho === 'worktree'
|
|
252
252
|
? permissoesDaWorktree(pedido.cwd, contexto.permiteEditarProduto && !plano, consultas) : null;
|
|
@@ -0,0 +1,227 @@
|
|
|
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.ParserGrok = exports.LIMITE_ERRO_GROK = exports.LIMITE_LINHA_GROK = exports.STOP_CANCELADO = exports.STOP_FIM_NORMAL = void 0;
|
|
37
|
+
exports.redigirTextoGrok = redigirTextoGrok;
|
|
38
|
+
exports.redigirErroGrok = redigirErroGrok;
|
|
39
|
+
exports.tokensDoFim = tokensDoFim;
|
|
40
|
+
exports.falhaDeContaDoTextoGrok = falhaDeContaDoTextoGrok;
|
|
41
|
+
exports.falhaDoErroGrok = falhaDoErroGrok;
|
|
42
|
+
exports.lerStreamGrok = lerStreamGrok;
|
|
43
|
+
/**
|
|
44
|
+
* RM-067 (F2 da ork-grokbuild, D7): o stream `streaming-json` do Grok Build (`grok -p ... --output-format
|
|
45
|
+
* streaming-json`), lido por linha.
|
|
46
|
+
*
|
|
47
|
+
* Formato do guia `14-headless-mode` do repositório `xai-org/grok-build`, lido em 25/10/2026: NDJSON com `type`. O `end`
|
|
48
|
+
* é sempre o último evento e traz `stopReason` em snake_case (`end_turn`, `max_tokens`, `max_turn_requests`, `refusal`,
|
|
49
|
+
* `cancelled`), `sessionId`, `requestId`, `usage`, `num_turns`, `modelUsage` e, só quando o servidor informa o custo
|
|
50
|
+
* inteiro, `total_cost_usd`; o `error` traz `message`. A lista de tipos não é exaustiva (`max_turns_reached`,
|
|
51
|
+
* `auto_compact_*`): tipo desconhecido é ignorado. O texto do agente (`text`, `thought`) nunca classifica nada; só o
|
|
52
|
+
* `end` e o `error`, que o próprio runtime grava, entram no resultado.
|
|
53
|
+
*/
|
|
54
|
+
const fs = __importStar(require("node:fs"));
|
|
55
|
+
const hitl_1 = require("../hitl");
|
|
56
|
+
const claude_bg_1 = require("./claude-bg");
|
|
57
|
+
/** O `stopReason` do fim normal do turno. */
|
|
58
|
+
exports.STOP_FIM_NORMAL = 'end_turn';
|
|
59
|
+
/** O `stopReason` do turno cancelado: interrupção, como a saída 130 e a 143. */
|
|
60
|
+
exports.STOP_CANCELADO = 'cancelled';
|
|
61
|
+
/** Linha maior que isto não é evento: invalida o stream sem reter a memória. */
|
|
62
|
+
exports.LIMITE_LINHA_GROK = 1024 * 1024;
|
|
63
|
+
/** Teto da mensagem de erro do runtime que vai ao ledger. */
|
|
64
|
+
exports.LIMITE_ERRO_GROK = 300;
|
|
65
|
+
const STOP = /^[a-z][a-z0-9_]{0,39}$/;
|
|
66
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/;
|
|
67
|
+
/**
|
|
68
|
+
* D7: um texto do runtime pronto para o ledger, sem corte: os segredos que o núcleo já conhece, a chave da xAI
|
|
69
|
+
* (`xai-...`) e sem controle. GO-FIX 1 (A4): quem corta, corta depois de redigir, para nenhum segredo perder o prefixo.
|
|
70
|
+
*/
|
|
71
|
+
function redigirTextoGrok(texto) {
|
|
72
|
+
if (typeof texto !== 'string')
|
|
73
|
+
return '';
|
|
74
|
+
return (0, hitl_1.redigirSegredos)(texto)
|
|
75
|
+
.replace(/\bxai-[A-Za-z0-9_-]{16,}/g, '[redigido]')
|
|
76
|
+
.replace(/\p{Cc}/gu, ' ').replace(/\s+/g, ' ').trim();
|
|
77
|
+
}
|
|
78
|
+
/** D7: a mensagem de erro do runtime, redigida e até `LIMITE_ERRO_GROK` caracteres, sem partir par substituto. */
|
|
79
|
+
function redigirErroGrok(texto) {
|
|
80
|
+
return Array.from(redigirTextoGrok(texto)).slice(0, exports.LIMITE_ERRO_GROK).join('');
|
|
81
|
+
}
|
|
82
|
+
const tokensIndisponiveis = () => ({ disponivel: false, input: null, cachedInput: null, output: null,
|
|
83
|
+
total: null, unidade: 'tokens', escopo: 'sessao', fonte: 'unavailable' });
|
|
84
|
+
/** O `usage` do `end`, só com inteiros não negativos e total que cobre entrada e saída; fora disso, indisponível. */
|
|
85
|
+
function tokensDoFim(v) {
|
|
86
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
87
|
+
return tokensIndisponiveis();
|
|
88
|
+
const u = v;
|
|
89
|
+
const inteiro = (n) => Number.isSafeInteger(n) && n >= 0;
|
|
90
|
+
const input = u.input_tokens, output = u.output_tokens, cache = u.cache_read_input_tokens;
|
|
91
|
+
if (!inteiro(input) || !inteiro(output) || (cache !== undefined && !inteiro(cache)))
|
|
92
|
+
return tokensIndisponiveis();
|
|
93
|
+
const total = u.total_tokens === undefined ? input + output : u.total_tokens;
|
|
94
|
+
if (!inteiro(total) || total < input + output)
|
|
95
|
+
return tokensIndisponiveis();
|
|
96
|
+
return { disponivel: true, input, cachedInput: cache === undefined ? null : cache, output, total, unidade: 'tokens',
|
|
97
|
+
escopo: 'sessao', fonte: 'grok.end.usage' };
|
|
98
|
+
}
|
|
99
|
+
class ParserGrok {
|
|
100
|
+
pendente = Buffer.alloc(0);
|
|
101
|
+
descartando = false;
|
|
102
|
+
estado;
|
|
103
|
+
/** Eventos válidos lidos por esta instância: o progresso de uma leitura. */
|
|
104
|
+
eventosValidos = 0;
|
|
105
|
+
constructor(sessaoEsperada, estado) {
|
|
106
|
+
if (estado && estado.sessaoEsperada !== sessaoEsperada)
|
|
107
|
+
throw new Error('estado do parser do grok de outra sessão');
|
|
108
|
+
this.estado = estado ? structuredClone(estado)
|
|
109
|
+
: { sessaoEsperada, fim: null, erro: null, invalido: false, sessaoDivergente: false, eventos: 0 };
|
|
110
|
+
}
|
|
111
|
+
/** Lê as linhas completas do bloco; o resto fica pendente para o próximo. */
|
|
112
|
+
push(chunk) {
|
|
113
|
+
const dados = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
114
|
+
let inicio = 0;
|
|
115
|
+
for (let i = 0; i < dados.length; i++) {
|
|
116
|
+
if (dados[i] !== 10)
|
|
117
|
+
continue;
|
|
118
|
+
const linha = Buffer.concat([this.pendente, dados.subarray(inicio, i)]);
|
|
119
|
+
if (!this.descartando && linha.length <= exports.LIMITE_LINHA_GROK) {
|
|
120
|
+
if (linha.length) {
|
|
121
|
+
try {
|
|
122
|
+
this.evento(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(linha)));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
this.estado.invalido = true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
this.estado.invalido = true;
|
|
131
|
+
this.pendente = Buffer.alloc(0);
|
|
132
|
+
this.descartando = false;
|
|
133
|
+
inicio = i + 1;
|
|
134
|
+
}
|
|
135
|
+
const resto = dados.subarray(inicio);
|
|
136
|
+
if (this.pendente.length + resto.length > exports.LIMITE_LINHA_GROK) {
|
|
137
|
+
this.descartando = true;
|
|
138
|
+
this.estado.invalido = true;
|
|
139
|
+
this.pendente = Buffer.alloc(0);
|
|
140
|
+
}
|
|
141
|
+
else if (!this.descartando)
|
|
142
|
+
this.pendente = Buffer.concat([this.pendente, resto]);
|
|
143
|
+
}
|
|
144
|
+
evento(bruto) {
|
|
145
|
+
if (!bruto || typeof bruto !== 'object' || Array.isArray(bruto)) {
|
|
146
|
+
this.estado.invalido = true;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const e = bruto;
|
|
150
|
+
if (typeof e.type !== 'string' || e.type === '') {
|
|
151
|
+
this.estado.invalido = true;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
// O guia diz que o `end` é sempre o último: outro `end` ou um `error` depois dele é contrato quebrado.
|
|
155
|
+
if (this.estado.fim && (e.type === 'end' || e.type === 'error')) {
|
|
156
|
+
this.estado.invalido = true;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.eventosValidos++;
|
|
160
|
+
this.estado.eventos++;
|
|
161
|
+
if (e.type === 'error') {
|
|
162
|
+
this.estado.erro = { mensagem: redigirErroGrok(e.message) || 'erro do grok sem mensagem' };
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (e.type !== 'end')
|
|
166
|
+
return;
|
|
167
|
+
const sessionId = typeof e.sessionId === 'string' && ID.test(e.sessionId) ? e.sessionId : null;
|
|
168
|
+
if (sessionId !== this.estado.sessaoEsperada)
|
|
169
|
+
this.estado.sessaoDivergente = true;
|
|
170
|
+
this.estado.fim = {
|
|
171
|
+
stopReason: typeof e.stopReason === 'string' && STOP.test(e.stopReason) ? e.stopReason : 'desconhecido',
|
|
172
|
+
sessionId,
|
|
173
|
+
requestId: typeof e.requestId === 'string' && ID.test(e.requestId) ? e.requestId : null,
|
|
174
|
+
numTurnos: Number.isSafeInteger(e.num_turns) && e.num_turns >= 0 ? e.num_turns : null,
|
|
175
|
+
tokens: tokensDoFim(e.usage),
|
|
176
|
+
custoUsd: typeof e.total_cost_usd === 'number' && Number.isFinite(e.total_cost_usd) && e.total_cost_usd >= 0
|
|
177
|
+
? e.total_cost_usd : null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
exports.ParserGrok = ParserGrok;
|
|
182
|
+
/**
|
|
183
|
+
* D7 e RM-067 (F3): a falha da CONTA dita por um texto do `grok` (a mensagem do `error` ou o stderr), pelo critério único
|
|
184
|
+
* da D16 (`parseFalhaDeConta`), sem a cota. O formato do erro de cota esgotada do `grok` não está confirmado (E5 da página
|
|
185
|
+
* da RM-067): a fonte não diz o campo nem a mensagem. O sinal tipado de cota só pode vir de erro estruturado do runtime, e
|
|
186
|
+
* uma frase parecida marcaria o perfil e tiraria a conta do rodízio por suposição; até o ensaio real (F5) ler o formato,
|
|
187
|
+
* nenhum texto do `grok` vira `runtime.quota-exhausted`, e o `grok` fica fora da escada automática e do fallback por
|
|
188
|
+
* cota. O login e o modelo seguem pela frase, como na F2.
|
|
189
|
+
*/
|
|
190
|
+
function falhaDeContaDoTextoGrok(texto, agoraMs = Date.now()) {
|
|
191
|
+
const falha = (0, claude_bg_1.parseFalhaDeConta)(texto, agoraMs);
|
|
192
|
+
return falha?.motivo === 'runtime.quota-exhausted' ? null : falha;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* D7: a falha da CONTA dita pelo próprio runtime no objeto `error` do stream (login, modelo), sem a cota
|
|
196
|
+
* (`falhaDeContaDoTextoGrok`): o que a frase não tipa fica `null`, e a parada segue tipada como `runtime.unavailable`.
|
|
197
|
+
*/
|
|
198
|
+
function falhaDoErroGrok(erro, agoraMs = Date.now()) {
|
|
199
|
+
return erro ? falhaDeContaDoTextoGrok(erro.mensagem, agoraMs) : null;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* O stream inteiro de um despacho curto (a falha antes da sessão), lido sem seguir link e até `limiteBytes`; `null`
|
|
203
|
+
* quando o arquivo não existe, não é arquivo comum ou passa do limite.
|
|
204
|
+
*/
|
|
205
|
+
function lerStreamGrok(arquivo, sessaoEsperada, limiteBytes = 8 * 1024 * 1024) {
|
|
206
|
+
let fd;
|
|
207
|
+
try {
|
|
208
|
+
fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
const st = fs.fstatSync(fd);
|
|
215
|
+
if (!st.isFile() || st.size > limiteBytes)
|
|
216
|
+
return null;
|
|
217
|
+
const bytes = Buffer.alloc(st.size);
|
|
218
|
+
fs.readSync(fd, bytes, 0, st.size, 0);
|
|
219
|
+
const parser = new ParserGrok(sessaoEsperada);
|
|
220
|
+
// A última linha sem quebra também é evento: o processo terminou e não vai completá-la.
|
|
221
|
+
parser.push(bytes.length && bytes[bytes.length - 1] !== 10 ? Buffer.concat([bytes, Buffer.from('\n')]) : bytes);
|
|
222
|
+
return parser.estado;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
fs.closeSync(fd);
|
|
226
|
+
}
|
|
227
|
+
}
|