sijur-cli 2.0.0 → 2.1.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/README.md CHANGED
@@ -126,6 +126,32 @@ sijur tarefas_encerrar --tarefa_id 123456 --relatorio "Protocolado RExt" \
126
126
  --fechar_ns_entidade_pai --momento_processual 46 --dry_run
127
127
  ```
128
128
 
129
+ ## Para um agente
130
+
131
+ Agente nao abre navegador nem marca caixa de consentimento, entao ele nao usa `sijur login`.
132
+ Ele leva uma chave `sk_sijur_` do escritorio numa variavel:
133
+
134
+ ```bash
135
+ export SIJUR_API_KEY=sk_sijur_...
136
+ sijur tools
137
+ sijur prazos_listar --json
138
+ ```
139
+
140
+ A chave ja responde as duas perguntas que o consentimento responderia, e ambas se escolhem no
141
+ cadastro dela, dentro do SIJUR:
142
+
143
+ - **quais ferramentas o agente enxerga** — os escopos da chave; `sijur tools` lista exatamente isso;
144
+ - **em nome de quem ele escreve** — o titular da chave ("expedida em nome de"). Sem titular, o
145
+ trabalho sai no user-bot generico do escritorio.
146
+
147
+ Com a variavel definida ela **vence o cofre** e nada e gravado na maquina. E por isso que dois
148
+ agentes convivem no mesmo computador: cada um carrega a sua chave no proprio ambiente, sem disputar
149
+ o Keychain. `sijur login` recusa enquanto a variavel existir, para ninguem achar que trocou de
150
+ identidade sem ter trocado; `sijur whoami` mostra qual chave esta respondendo e o que ela alcanca.
151
+
152
+ O contexto que a CLI gasta no agente e o do catalogo sob demanda: `sijur tools` devolve uma linha
153
+ por ferramenta, e so a escolhida tem o schema lido, com `sijur tools <nome> --json`.
154
+
129
155
  ## Auditoria
130
156
 
131
157
  Toda chamada apenda uma linha em `~/.sijur-cli/audit.jsonl`: timestamp, ferramenta, argumentos e
@@ -136,3 +162,4 @@ se deu certo.
136
162
  | variavel | padrao |
137
163
  |---|---|
138
164
  | `SIJUR_MCP_URL` | `https://sijur.com.br/mcp` |
165
+ | `SIJUR_API_KEY` | (vazio) — definida, autentica com esta chave `sk_sijur_` e ignora o cofre |
package/bin/sijur.js CHANGED
@@ -23,6 +23,13 @@ GLOBAIS
23
23
  --json imprime numa linha so (o padrao e identado)
24
24
  -h, --help esta ajuda
25
25
 
26
+ AGENTE
27
+ Um agente nao passa por tela de consentimento: em vez de "sijur login", exporte
28
+ SIJUR_API_KEY com uma chave sk_sijur_ do escritorio. Ela ja carrega quais
29
+ ferramentas o agente ve e em nome de quem ele escreve, os dois escolhidos no
30
+ cadastro da chave. Definida a variavel, ela vence o cofre e nada e gravado na
31
+ maquina — entao dois agentes convivem no mesmo computador, cada um com a sua.
32
+
26
33
  CODIGOS DE SAIDA
27
34
  0 ok 1 erro de uso 2 autenticacao/escopo 4 erro da ferramenta ou da rede
28
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sijur-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "description": "Espelho de linha de comando das ferramentas MCP do SIJUR — um comando por ferramenta, sem regra propria",
6
6
  "bin": {
@@ -1,9 +1,9 @@
1
1
  import { fazerLogin, revogar } from '../auth.js';
2
2
  import * as cofre from '../keychain.js';
3
3
  import { SERVIDOR, escoposSuportados } from '../config.js';
4
- import { listarTools, fechar } from '../mcp.js';
4
+ import { listarTools, fechar, usandoChaveDoAmbiente, prefixoDaChave } from '../mcp.js';
5
5
 
6
- import { EXIT, erroAuth } from '../exit.js';
6
+ import { EXIT, erroAuth, erroUso } from '../exit.js';
7
7
 
8
8
 
9
9
  function emitir(obj, op) {
@@ -11,6 +11,12 @@ function emitir(obj, op) {
11
11
  }
12
12
 
13
13
  export async function cmdLogin(op) {
14
+ // WHY barrar em vez de avisar: com SIJUR_API_KEY definida a chave vence em toda chamada, entao o
15
+ // login gravaria no cofre e nao mudaria nada — e quem rodou ficaria achando que trocou de identidade.
16
+ if (usandoChaveDoAmbiente) {
17
+ throw erroUso('SIJUR_API_KEY esta definida: a CLI ja esta autenticando com essa chave, e o login ' +
18
+ 'nao mudaria nada. Tire a variavel do ambiente para autenticar como pessoa.');
19
+ }
14
20
  const r = await fazerLogin({ trocar: op.trocar });
15
21
  const blob = cofre.ler();
16
22
  const avisos = [];
@@ -36,11 +42,46 @@ export async function cmdLogout(op) {
36
42
  let revogado = { revogado: false, motivo: 'nao_pedido' };
37
43
  if (op.revogar) revogado = await revogar();
38
44
  cofre.apagar();
39
- emitir({ ok: true, credenciais_apagadas: true, revogado_no_servidor: revogado.revogado, detalhe: revogado }, op);
45
+ emitir({
46
+ ok: true,
47
+ credenciais_apagadas: true,
48
+ revogado_no_servidor: revogado.revogado,
49
+ detalhe: revogado,
50
+ avisos: usandoChaveDoAmbiente
51
+ ? ['SIJUR_API_KEY continua definida: apaguei o cofre, mas as proximas chamadas seguem ' +
52
+ 'autenticando com essa chave. Para parar de vez, tire a variavel do ambiente.']
53
+ : []
54
+ }, op);
40
55
  return EXIT.OK;
41
56
  }
42
57
 
43
58
  export async function cmdWhoami(op) {
59
+ // Com chave do ambiente nao ha blob local: escopo e titular vivem no cadastro da chave, no
60
+ // servidor. O que a CLI pode mostrar aqui e o efeito — as ferramentas que a chave enxerga.
61
+ if (usandoChaveDoAmbiente) {
62
+ let tools = null;
63
+ let vivo = false;
64
+ try {
65
+ tools = await listarTools();
66
+ vivo = true;
67
+ } catch (err) {
68
+ tools = `nao consegui listar: ${err.message}`;
69
+ } finally {
70
+ await fechar();
71
+ }
72
+ emitir({
73
+ ok: vivo,
74
+ servidor: SERVIDOR,
75
+ credencial: 'chave da variavel SIJUR_API_KEY',
76
+ prefixo: prefixoDaChave,
77
+ guardado_em: 'variavel de ambiente (nada gravado nesta maquina)',
78
+ sessao_viva: vivo,
79
+ total_ferramentas: Array.isArray(tools) ? tools.length : null,
80
+ tools_visiveis: tools
81
+ }, op);
82
+ return vivo ? EXIT.OK : EXIT.AUTH;
83
+ }
84
+
44
85
  const blob = cofre.ler();
45
86
  if (!blob?.tokens?.access_token) throw erroAuth('Sem credencial nesta maquina. Rode `sijur login`.');
46
87
 
package/src/mcp.js CHANGED
@@ -12,11 +12,21 @@ import { erroAuth, erroApi } from './exit.js';
12
12
  // servidor, entao o canal so tinha custo. Sem ele, some a classe inteira de timeout.
13
13
  // O SDK continua sendo a fonte do OAuth (`auth`), que e onde ele e solido.
14
14
 
15
+ // WHY chave no ambiente: agente nao passa por tela de consentimento. A chave `sk_sijur_` ja carrega
16
+ // o recorte de ferramentas (scopes) e o titular (mcp_api_keys.userid), ambos escolhidos no cadastro
17
+ // da chave — entao ELA e a credencial do agente, e o cofre fica para a pessoa no terminal. Efeito
18
+ // colateral desejado: sem cofre, dois agentes na mesma maquina nao disputam slot de keychain
19
+ // nenhum; cada um leva a sua chave no proprio ambiente.
20
+ const CHAVE_AMBIENTE = process.env.SIJUR_API_KEY || null;
21
+ export const usandoChaveDoAmbiente = !!CHAVE_AMBIENTE;
22
+ export const prefixoDaChave = CHAVE_AMBIENTE ? CHAVE_AMBIENTE.slice(0, 20) : null;
23
+
15
24
  const PROTOCOLO = '2025-06-18';
16
25
  let _sessao = null;
17
26
  let _proximoId = 1;
18
27
 
19
28
  function tokenGuardado() {
29
+ if (CHAVE_AMBIENTE) return CHAVE_AMBIENTE;
20
30
  return cofre.ler()?.tokens?.access_token || null;
21
31
  }
22
32
 
@@ -63,9 +73,18 @@ async function postar(corpo, { token, sessao }) {
63
73
  // Uma tentativa; devolve { resposta, token } ja com renovacao feita se tomou 401.
64
74
  async function postarComRenovacao(corpo, sessao) {
65
75
  let token = tokenGuardado();
66
- if (!token) throw erroAuth('Sem credencial nesta maquina. Rode `sijur login`.');
76
+ if (!token) {
77
+ throw erroAuth('Sem credencial nesta maquina. Rode `sijur login`, ou exporte ' +
78
+ 'SIJUR_API_KEY com uma chave sk_sijur_ se quem chama e um agente.');
79
+ }
67
80
  let r = await postar(corpo, { token, sessao });
68
81
  if (r.status === 401) {
82
+ // WHY nao renovar com chave do ambiente: `sk_sijur_` nao tem refresh. Cair no fluxo OAuth aqui
83
+ // trocaria "a chave foi recusada" por um erro sobre login, que nao e o que aconteceu.
84
+ if (CHAVE_AMBIENTE) {
85
+ throw erroAuth('A chave em SIJUR_API_KEY foi recusada (401). Confira se ela existe, ' +
86
+ 'nao expirou e nao foi revogada.');
87
+ }
69
88
  token = await renovarToken();
70
89
  r = await postar(corpo, { token, sessao });
71
90
  if (r.status === 401) throw erroAuth('Token recusado pelo servidor. Rode `sijur login`.');
@@ -104,7 +123,11 @@ const RE_SCOPE = /Scope insuficiente\.?\s*Requer:\s*([^\s"'}]+)/i;
104
123
 
105
124
  function checarEscopo(texto) {
106
125
  const m = RE_SCOPE.exec(texto || '');
107
- if (m) throw erroAuth(`Escopo faltando: ${m[1]}. Rode \`sijur login\` para reautorizar com esse escopo.`);
126
+ if (!m) return;
127
+ // Com chave do ambiente o escopo se conserta no cadastro da chave, nao com `sijur login`.
128
+ throw erroAuth(CHAVE_AMBIENTE
129
+ ? `Escopo faltando: ${m[1]}. A chave em SIJUR_API_KEY nao tem esse escopo — inclua-o no cadastro dela.`
130
+ : `Escopo faltando: ${m[1]}. Rode \`sijur login\` para reautorizar com esse escopo.`);
108
131
  }
109
132
 
110
133
  export const chamarBruto = (nome, argumentos) => _chamar(nome, argumentos, true);