@genne/genne-cli 0.0.0-stage → 0.1.33
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/GR.md +83 -0
- package/README.md +27 -2
- package/package.json +29 -4
- package/skills/genne-cli/SKILL.md +152 -0
- package/src/api.js +1 -0
- package/src/cli.js +7 -0
- package/src/commands/access.js +1 -0
- package/src/commands/companies.js +1 -0
- package/src/commands/content.js +2 -0
- package/src/commands/crm.js +4 -0
- package/src/commands/desktop-integrations.js +1 -0
- package/src/commands/driver-routes.js +1 -0
- package/src/commands/driver.js +1 -0
- package/src/commands/feedback.js +3 -0
- package/src/commands/financial.js +1 -0
- package/src/commands/gr.js +1 -0
- package/src/commands/identity.js +1 -0
- package/src/commands/logistics.js +1 -0
- package/src/commands/notifications.js +2 -0
- package/src/commands/operational-input.js +1 -0
- package/src/commands/operations-config.js +1 -0
- package/src/commands/orders.js +1 -0
- package/src/commands/routes.js +1 -0
- package/src/commands/support.js +2 -0
- package/src/commands/tower.js +1 -0
- package/src/commands/users.js +1 -0
- package/src/core-fetch.js +1 -0
- package/src/device-login.js +1 -0
- package/src/global.js +5 -0
- package/src/help.js +1 -0
- package/src/local-file.js +1 -0
- package/src/mcp-administration.js +1 -0
- package/src/mcp-desktop-operations.js +1 -0
- package/src/mcp-logistics.js +1 -0
- package/src/mcp-operational.js +1 -0
- package/src/mcp-routes-tower.js +1 -0
- package/src/mcp-support-feedback.js +1 -0
- package/src/mcp.js +2 -0
- package/src/operations.js +1 -0
- package/src/private-file.js +1 -0
- package/src/remote-auth-store.js +31 -0
- package/src/remote-auth.js +1 -0
- package/src/remote-core.js +1 -0
- package/src/remote-http.js +1 -0
- package/src/remote-mcp.js +1 -0
- package/src/remote-server.js +2 -0
- package/src/request-scope.js +1 -0
- package/src/session.js +1 -0
package/GR.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# GR pelo terminal
|
|
2
|
+
|
|
3
|
+
A CLI usa os endpoints GR existentes do Core. Readiness, vínculo, mercado, configuração, estado, permissões e execução externa continuam sendo decisões do Core. `genne help admin gr`, `genne help carrier gr` e `genne help driver profile` mostram sintaxe, filtros, JSON e confirmação.
|
|
4
|
+
|
|
5
|
+
## Papéis e ACL
|
|
6
|
+
|
|
7
|
+
| Sessão ativa | Responsabilidade | Autorização no Core |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| Driver | Enviar o próprio cadastro/documentos, informar treinamento e consultar prontidão/pendências | Papel driver e identidade da sessão; sem gestão de providers, requests ou outros motoristas |
|
|
10
|
+
| Shipper | Não possui gestão de GR neste contrato | GR está fora do catálogo customer shipper; usar uma base shipper como operação não concede gestão |
|
|
11
|
+
| Carrier | Configurar providers/mappings e operar motoristas vinculados e requests próprias | Membership ativa, módulo GR ativo e permissões efetivas; conta derivada da sessão |
|
|
12
|
+
| Admin | Diretório global, gestão das carriers e definições globais | Super admin ou grants explícitos do staff; papel admin sozinho não concede acesso total |
|
|
13
|
+
|
|
14
|
+
Leituras Admin/carrier aceitam `gr.view` ou `gr.manage`. Mutações exigem `gr.manage`; declarações pessoais no Admin exigem também `privacy.manage`. Senhas continuam write-only, conforme o DTO do Core. Nenhuma chave de permissão ou entitlement foi acrescentada, portanto não há seed/backfill de ACL neste incremento.
|
|
15
|
+
|
|
16
|
+
O `role` de `/auth/me` é o papel ativo. Uma identidade driver + admin precisa de sessões separadas; a CLI não soma `roles` nem interpreta grants para autorizar chamadas. Um 403 do Core encerra a operação, sem trocar papel ou conta.
|
|
17
|
+
|
|
18
|
+
## Cobertura operacional
|
|
19
|
+
|
|
20
|
+
IDs nos comandos são UUIDs públicos. `admin gr` usa `/api/v1/admin/gr`; `carrier gr` usa `/api/v1/carrier/gr`.
|
|
21
|
+
|
|
22
|
+
| Trabalho | Comando relativo a `admin gr` ou `carrier gr` |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| Diretório global, incluindo perfis sem vínculo | Admin `drivers [carrier_account_id=UUID] [filtros]` → `GET /admin/gr/drivers` |
|
|
25
|
+
| Diretório da carrier | Carrier `drivers [filtros]`; Admin `carriers CARRIER_ID drivers [filtros]` |
|
|
26
|
+
| Detalhe global | Admin `driver-profiles PROFILE_ID show` |
|
|
27
|
+
| Dashboard e bloqueados | Carrier `dashboard`, `blacklist list`; Admin `carriers CARRIER_ID dashboard`, `… blacklist list` |
|
|
28
|
+
| Bloquear/desbloquear vínculo | Carrier `links LINK_ID blacklist\|unblock`; Admin `carriers CARRIER_ID links LINK_ID blacklist\|unblock` |
|
|
29
|
+
| Aprovar/revogar GR externo | Carrier `links LINK_ID manual-approval`; Admin `carriers CARRIER_ID links LINK_ID manual-approval`; JSON stdin `{"approved":true\|false}` |
|
|
30
|
+
| Exportar e reprocessar documento | Carrier `drivers PROFILE_ID export`, `… documents TYPE reprocess`; Admin sob `carriers CARRIER_ID drivers …` |
|
|
31
|
+
| Registrar treinamento | `driver-profiles PROFILE_ID training` |
|
|
32
|
+
| Definições globais | `provider-definitions list`; somente Admin `provider-definitions PROVIDER_ID update` |
|
|
33
|
+
| Contas de providers | `providers list\|create\|PROVIDER_ID update\|test\|operations` |
|
|
34
|
+
| Mapping da operação | `providers PROVIDER_ID mappings list\|upsert` |
|
|
35
|
+
| Fila e criação | `requests list\|create` |
|
|
36
|
+
| Estado e histórico | `requests REQUEST_ID show\|attempts\|evidence\|orchestration-events` |
|
|
37
|
+
| Transições | `requests REQUEST_ID retry\|verify\|cancel\|confirm-recovery\|operation\|personal-declarations` |
|
|
38
|
+
|
|
39
|
+
O driver usa `driver profile draft|complete` com JSON por stdin e documentos locais opcionais. `training_watched` aceita booleano. `driver profile show` retorna treinamento, `ready_for_gr`, blocos e status documental próprios. Isso não cria uma request nem declara aprovação pelo provider.
|
|
40
|
+
|
|
41
|
+
## Sequência operacional
|
|
42
|
+
|
|
43
|
+
1. Confira `genne env show` e `genne whoami`; selecione a sessão e a conta corretas.
|
|
44
|
+
2. Consulte diretório e detalhe/export. Corrija pendências pelo cadastro/documentos existentes; prontidão é calculada no Core.
|
|
45
|
+
3. Consulte definitions, providers, operations e mappings. Escolha provider e operação explicitamente; a CLI não infere operação por cidade, documento ou nome. O fallback legado permanece no Core.
|
|
46
|
+
4. Envie `driver_profile_id`, `provider_account_id` e `operation_key` por stdin a `gr requests create`. Confirme no terminal.
|
|
47
|
+
5. Consulte `requests REQUEST_ID show` e históricos. Use `allowed_actions` e requirements do Core; `202`/`queued` são aceitação, não conclusão.
|
|
48
|
+
6. `verify` reconcilia/verifica; `retry` solicita nova execução permitida. Para recovery, envie a revisão e operações exatas da prévia vigente. A confirmação mostra esses valores; o Core rejeita revisão obsoleta.
|
|
49
|
+
|
|
50
|
+
Use `meta.next_cursor` mantendo os demais filtros. `limit` é tamanho do lote, não página. Requests e attempts usam `pagination=cursor` automaticamente. Proteja o histórico do shell e as saídas operacionais que contêm dados pessoais.
|
|
51
|
+
|
|
52
|
+
A cobertura funcional corresponde às ações de gestão das telas GR: reprocessamento, transições, configuração e acompanhamento reutilizam o backend já existente. A release 0.1.2 distribui esta integração; a revalidação do backend é evidência adicional.
|
|
53
|
+
|
|
54
|
+
## Aprovação manual de GR externo
|
|
55
|
+
|
|
56
|
+
Use o UUID público do vínculo (`link_id`), não o perfil ou usuário do motorista.
|
|
57
|
+
Confira `genne env show` e `genne whoami` antes de operar. A sessão precisa ter
|
|
58
|
+
`gr.manage`; o Core valida o vínculo ativo, não bloqueado e da transportadora correta.
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
printf '%s' '{"approved":true}' | genne carrier gr links LINK_ID manual-approval
|
|
62
|
+
printf '%s' '{"approved":false}' | genne admin gr carriers CARRIER_ID links LINK_ID manual-approval
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Confirme o `LINK_ID` no terminal após revisar a ação e a transportadora. `true`
|
|
66
|
+
registra a aprovação externa; `false` remove somente essa aprovação manual e
|
|
67
|
+
volta a considerar o resultado do provider. Não altera treinamento, documentos,
|
|
68
|
+
evidências do provider ou aprovação em outra transportadora. A resposta síncrona
|
|
69
|
+
traz `link_id`, `driver_user_id` e `manual_approval` com `approved`, `updated_at`
|
|
70
|
+
e `updated_by_user_id`, definidos pelo Core. A CLI não repete escritas automaticamente.
|
|
71
|
+
|
|
72
|
+
Requer os endpoints de aprovação manual do [Core #132](https://github.com/genneco/genne-core/pull/132).
|
|
73
|
+
Testes locais com API simulada comprovam o contrato do cliente, não deploy nem
|
|
74
|
+
aprovação real em produção. Não há tool MCP de aprovação manual: o stdio segue
|
|
75
|
+
somente leitura e o MCP remoto exige uma intenção específica no Core.
|
|
76
|
+
|
|
77
|
+
## MCP e evidência
|
|
78
|
+
|
|
79
|
+
Leituras são compartilhadas pelo MCP local e remoto. `genne_admin_gr_driver_directory` expõe o diretório global; `genne_admin_gr_drivers` continua carrier-scoped. Treinamento tem escrita remota com intenção/aprovação existente; as demais mutações GR da matriz são operações CLI com confirmação interativa. Mutações não são expostas pelo MCP stdio.
|
|
80
|
+
|
|
81
|
+
Testes CLI exercitam endpoints, envelopes, cursor, papéis ativos, escopo forjado, propagação de 403 e ausência de escrita após confirmação recusada. Os testes HTTP usam servidor simulado: comprovam o cliente, não o enforcement de uma implantação real.
|
|
82
|
+
|
|
83
|
+
Neste incremento, ACL/ownership foram conferidos no catálogo, guards, controllers e testes existentes do monorepo `442cc64c54cd7b08d38ab2b1f9da36bcd6236ed3`. A execução do controller carrier pelo runner Dev falhou antes dos testes: source snapshot indisponível em origin e `tools/dev-cli` ausente no checkout do runner. Em 2026-10-01, smoke read-only Admin em Prod passou para diretório global (`limit=1`), definitions, provider accounts e requests (`limit=1`). Não havia sessão Dev salva; smokes positivos driver/carrier/shipper, negações reais de ACL e testes Core permanecem pendentes. Não houve escrita externa GR, deploy ou release.
|
package/README.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Genne CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Cliente Node.js 22+ para operações Genne autorizadas pelo Core. O papel ativo,
|
|
4
|
+
a conta selecionada e as permissões do usuário limitam cada operação.
|
|
5
|
+
|
|
6
|
+
O registry desta distribuição é informado em `package.json.publishConfig.registry`.
|
|
7
|
+
Para a distribuição privada, configure `@genneco:registry=https://npm.pkg.github.com`
|
|
8
|
+
e autentique o npm com acesso de leitura ao pacote, fora de arquivos compartilhados.
|
|
9
|
+
A distribuição no npm público usa `https://registry.npmjs.org` e não exige acesso
|
|
10
|
+
ao repositório privado. O updater gerenciado usa o mesmo registry da distribuição.
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install --global @genne/genne-cli@latest
|
|
14
|
+
genne login
|
|
15
|
+
genne whoami
|
|
16
|
+
genne help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use `genne help CAMINHO` para sintaxe, papéis, entrada e confirmação. O MCP
|
|
20
|
+
local usa `genne-mcp` e oferece somente leituras. O MCP remoto exige OAuth;
|
|
21
|
+
escritas remotas mantêm intenção e aprovação separada no Genne.
|
|
22
|
+
|
|
23
|
+
Este pacote contém módulos JavaScript minificados gerados pelo build, sem
|
|
24
|
+
source maps ou fontes originais. Os caminhos `src/` são preservados para
|
|
25
|
+
compatibilidade com consumidores existentes. Minificação não impede a
|
|
26
|
+
inspeção ou análise do código distribuído.
|
|
27
|
+
|
|
28
|
+
Suporte: https://genne.ai/en/support
|
package/package.json
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genne/genne-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.33",
|
|
4
|
+
"description": "CLI de produto Genne",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"genne": "./src/cli.js",
|
|
8
|
+
"genne-mcp": "./src/mcp.js",
|
|
9
|
+
"genne-mcp-remote": "./src/remote-server.js"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@modelcontextprotocol/server": "^2.3.1",
|
|
16
|
+
"zod": "^4.6.5"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/genneco/genne-cli.git"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"registry": "https://registry.npmjs.org",
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"src",
|
|
28
|
+
"skills",
|
|
29
|
+
"GR.md"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: genne-cli
|
|
3
|
+
description: Consultar e operar o Genne pelo MCP remoto com OAuth, preferindo a CLI opcional quando houver terminal, instalação e sessão válida, sempre com o papel e a conta ativos.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Genne
|
|
7
|
+
|
|
8
|
+
Escolha o transporte antes de cada operação pela capacidade real da sessão.
|
|
9
|
+
Estar em um PC não comprova acesso ao terminal ou à CLI.
|
|
10
|
+
|
|
11
|
+
- Com terminal acessível e CLI já instalada, confira `genne env show` e `genne whoami`. Prefira a CLI somente com sessão válida para a identidade, o papel e a conta pretendidos.
|
|
12
|
+
- Sem terminal, sem CLI ou sem sessão CLI válida, use somente o MCP remoto da integração Genne com OAuth. Isso inclui o celular. Não bloqueie o plugin pedindo instalação, `genne login` ou tokens. Use `genne_whoami`, `tools/list` e `genne_help` sem shell para confirmar a sessão e descobrir as tools disponíveis.
|
|
13
|
+
|
|
14
|
+
As sessões CLI e MCP são independentes. Confirme identidade, papel ativo e
|
|
15
|
+
conta no transporte escolhido. Se ambas estiverem conectadas e acessíveis,
|
|
16
|
+
compare esses dados. Havendo divergência, peça ao usuário para escolher a
|
|
17
|
+
sessão antes de operar. Não combine permissões, contas, tokens ou resultados
|
|
18
|
+
das duas sessões. Para conectar ou reconectar o MCP, siga o OAuth da integração;
|
|
19
|
+
uma sessão CLI não autentica o MCP.
|
|
20
|
+
|
|
21
|
+
Não troque de transporte, papel ou conta para contornar 403, ACL ou intenção
|
|
22
|
+
aguardando aprovação, rejeitada ou obsoleta. Toda escrita MCP remota exige
|
|
23
|
+
intenção e aprovação separada no Core antes da execução. O MCP local por
|
|
24
|
+
stdio oferece somente leituras. Se a operação faltar em `tools/list` e a CLI
|
|
25
|
+
não estiver disponível, informe a limitação. O catálogo CLI em `genne_help`
|
|
26
|
+
não comprova uma tool MCP equivalente. Não chame HTTP Core diretamente nem
|
|
27
|
+
prometa paridade entre os transportes.
|
|
28
|
+
|
|
29
|
+
CLI é uma preferência opcional, sem requisito de execução local para usar o
|
|
30
|
+
plugin. Conexão, deploy, aceite no celular e publicação no diretório oficial
|
|
31
|
+
são evidências distintas; não afirme disponibilidade sem confirmação.
|
|
32
|
+
|
|
33
|
+
Se o usuário optar por configurar a CLI local, instale `@genne/genne-cli` quando a distribuição pública estiver disponível, ou `@genneco/cli` no GitHub Packages privado para usuários autorizados, e use `genne global install`, `genne login` e `genne whoami`. A distribuição privada exige npm autenticado; a pública não exige acesso ao GitHub privado. Se o pacote não estiver disponível, informe a limitação e use o MCP remoto autorizado. O launcher gerenciado acompanha `latest` do mesmo pacote e registry da distribuição a cada hora; `genne global update` força a verificação e `genne global status` mostra a instalação efetiva. `genne-cli` é um alias de bin disponível quando o shell já usa o nome `genne`. Desenvolvimento ocorre no Git root próprio de `genne-cli`; a integração pode usar o submodule `tools/genne-cli`.
|
|
34
|
+
|
|
35
|
+
No uso da CLI, não peça um checkout do monorepo nem a URL da API ao usuário. A CLI usa Prod (`https://genne.ai`) por padrão e não oferece troca de ambiente. Seleções antigas em `environment.json` são ignoradas; `GENNE_API_URL` continua como override opcional para origens customizadas. A sessão Prod salva continua válida e credenciais antigas de Dev não são reutilizadas em Prod.
|
|
36
|
+
|
|
37
|
+
Na CLI, use `genne help` para descobrir todos os comandos e `genne help <caminho>` para obter sintaxe, papel elegível, entrada JSON e confirmação. `genne <caminho> --help` funciona sem sessão.
|
|
38
|
+
|
|
39
|
+
No MCP, descubra `tools/list` e use `genne_help` para consultar os comandos CLI disponíveis. Não dependa do README como catálogo e não chame endpoints Core diretamente quando faltar um comando.
|
|
40
|
+
|
|
41
|
+
Na CLI, use `genne env show` para conferir a origem efetiva e a disponibilidade da sessão antes de agir. `GENNE_API_URL` substitui explicitamente o destino padrão para origens customizadas. `genne login` abre autorização por código no navegador; a sessão CLI herda o **papel ativo e a conta selecionada** da sessão que aprova. Consulte `genne whoami` antes de agir.
|
|
42
|
+
|
|
43
|
+
Em ambos os transportes, os papéis são `driver`, `admin`, `carrier` e `shipper`; uma identidade `driver + admin` precisa de sessões distintas para cada papel. Papel elegível no help não concede acesso: o Core verifica grants, módulos, membership, bases e ACL a cada chamada. Em 403, reporte a recusa; não tente outro papel ou conta automaticamente.
|
|
44
|
+
|
|
45
|
+
Leituras estão disponíveis na CLI e, quando registradas, no MCP local e remoto. Na CLI, mutações usam objeto JSON por stdin e confirmação interativa quando indicada pelo help. Nunca passe senhas, códigos ou tokens em argumentos. `GENNE_ACCESS_TOKEN` é uma substituição explícita para automação; não o imprima. `logout` revoga a sessão salva.
|
|
46
|
+
|
|
47
|
+
Um owner carrier pode habilitar o próprio perfil de motorista com `genne user me driver-access enable`, enviando somente `current_password` por JSON stdin e confirmando seu UUID. Suporte interno usa `genne admin users USER_ID driver-access enable`, somente `reason` por stdin e confirmação do UUID alvo; o Core exige `users.internal_admin.manage` e `access_governance.manage`. A habilitação preserva identidade, empresas e memberships. Não aprova documentos/GR, não cria vínculo operacional e não renova benefícios. `changed=true` e `reauthentication_required=true` referem-se ao titular habilitado; a CLI remove somente sua credencial salva no caminho pessoal, preservando o login do executor de suporte. Faça `genne login` novamente para usar a superfície desejada. Já habilitado retorna `changed=false` sem revogar sessões. Senha incorreta retorna 422 `invalid_current_password` e preserva a sessão. Após timeout ou resposta perdida, faça novo login como titular e consulte `genne user me` antes de repetir; a CLI não repete a escrita automaticamente. Esses comandos não têm tools de mutação MCP.
|
|
48
|
+
|
|
49
|
+
Para completar uma integração Desktop, consulte `carrier desktop providers list`, ative o UUID escolhido com `providers PROVIDER_ID activate` e consulte `grants GRANT_ID flow-installations list`. A ativação retorna grant/conexão, sem habilitar automaticamente a entrada de pedidos. `flow-installations create`, `flow-installations INSTALLATION_ID update|run`, `order-import-batches create` e `extractions preview` têm sintaxe e JSON no help. As escritas confirmam UUIDs, sem imprimir registros ou snapshots. A execução manual mantém a chave no body; o lote de importação usa `Idempotency-Key`. Uma execução `queued`, mesmo com HTTP 201, exige acompanhamento por `carrier desktop executions EXECUTION_ID show`. O Core valida dispositivo, claim, grant, base, políticas e idempotência.
|
|
50
|
+
|
|
51
|
+
`carrier operations config show|update` opera a conta ativa; Admin usa `admin operations CARRIER_ID config show|update`. Envie somente os campos alterados. O Core deriva os modos e aplica a ACL; a CLI confirma `my-account` ou o UUID Admin. As leituras MCP correspondentes são `genne_carrier_desktop_providers`, `genne_carrier_desktop_flow_installations`, `genne_carrier_operations_config` e `genne_admin_operations_config`.
|
|
52
|
+
|
|
53
|
+
As escritas remotas desse grupo usam `genne_carrier_desktop_provider_activate`, `genne_carrier_desktop_flow_installation_create`, `genne_carrier_desktop_flow_installation_update`, `genne_carrier_desktop_flow_installation_run`, `genne_carrier_desktop_order_import_batch_create`, `genne_carrier_desktop_extraction_preview`, `genne_carrier_operations_config_update` e `genne_admin_operations_config_update`. Consulte o schema de cada tool em `tools/list`: criação recebe `installation`, atualização recebe `changes`, execução recebe `input`, importação recebe `batch` e extração recebe `extraction`. Admin informa `carrier_account_id` operacional; carrier usa a conta ativa.
|
|
54
|
+
|
|
55
|
+
Envie uma `idempotency_key` estável de 16 a 128 caracteres ASCII alfanuméricos ou `._:-`. A primeira chamada solicita a intenção; revise o efeito no link de aprovação em outra sessão de senha, com o mesmo papel e conta. Só depois repita os mesmos campos e chave com `intent_id`. O Core revalida ACL, snapshot e referências; `412` exige releitura e outra chave. Replay devolve o receipt sem repetir a mutação. Execução queued exige acompanhamento por `genne_carrier_desktop_execution`, e prévia de extração não cria pedidos. O stdio continua somente leitura. Código e testes dos três repositórios não comprovam deploy ou smoke autenticado.
|
|
56
|
+
|
|
57
|
+
Admin usa `admin financial payment-profiles create` com JSON por stdin.
|
|
58
|
+
`profile_mode` e `configuration` são obrigatórios; consulte os campos opcionais no help.
|
|
59
|
+
Para `shipper_owned`, `shipper_account_id` e `base_id` são operacionais, enquanto
|
|
60
|
+
`reference_carrier_id` é financeiro. O Core resolve payer e origem, publica a versão
|
|
61
|
+
e pode desativar o perfil ativo do mesmo contexto. A CLI confirma o UUID de base,
|
|
62
|
+
origem, owner ou client, nessa ordem, ou `payment-profiles` quando ausentes.
|
|
63
|
+
Use `data.origin.id` retornado em `admin financial carrier-margins ORIGIN_ID upsert`,
|
|
64
|
+
com `carrier_id` financeiro e `margin_percent` por stdin. Confirma `ORIGIN_ID` e
|
|
65
|
+
substitui a regra ativa do par carrier/origem, preservando histórico no Core.
|
|
66
|
+
`origin_id` opcional deve coincidir com o path; o body enviado usa o UUID do path.
|
|
67
|
+
Ambos retornam o envelope Core de forma síncrona e exigem `network_configuration.manage`.
|
|
68
|
+
A CLI preserva zero, null e strings decimais, sem calcular valores nem repetir escritas.
|
|
69
|
+
Configuração, faixa da margem e ACL pertencem ao Core. Esses dois comandos não têm
|
|
70
|
+
tools de mutação MCP. Contratos consultados em `genneco/genne-core@c045664b1a5d6e134cff861cb2ae0bd4b7cdc122`,
|
|
71
|
+
`contracts/financial/openapi.yaml` e controllers `admin/payment_profile_controller.ex`
|
|
72
|
+
e `financial/carrier_margin_controller.ex` sob `services/core/apps/genne_web/lib/genne_web_web/controllers/`.
|
|
73
|
+
|
|
74
|
+
O MCP também oferece as leituras Admin de suporte (`genne_admin_support_threads`, `genne_admin_support_thread_messages`), feedback (`genne_admin_feedbacks`, `genne_admin_feedback_show`, `genne_admin_feedback_comments`, `genne_admin_feedback_receipt`), CRM (`genne_admin_crm_assignees`, `genne_admin_crm_leads`, `genne_admin_crm_lead_show`), notificações (`genne_admin_notifications`, `genne_admin_notification_show`), conteúdo (`genne_admin_blog_posts`, `genne_admin_faqs`), privacidade (`genne_admin_privacy_requests`) e onboarding (`genne_admin_onboarding_users`). O driver consulta requisitos e contas de repasse mascaradas por `genne_driver_payout_requirements` e `genne_driver_payout_accounts`. Descubra filtros e UUIDs em `tools/list`; cada tool reutiliza o comando CLI e suas projeções. Esses GETs não autorizam mutações do mesmo domínio.
|
|
75
|
+
|
|
76
|
+
Trate `202` como trabalho aceito, não concluído. Quando a resposta trouxer receipt ou status pendente, consulte o comando de acompanhamento indicado no help antes de afirmar conclusão. Em retries, preserve a mesma chave de idempotência e o mesmo payload; não repita mutações sem verificar o resultado. Em `412 stale_version`, releia o objeto e solicite nova intenção com outra chave; a aprovação anterior não autoriza os dados alterados. O MCP remoto requer autorização OAuth e pode expor intenções de escrita com aprovação Core separada; consentimento OAuth não executa a escrita. Para `genne_user_me_update`, envie somente `changes` de nome, telefone, tipo de veículo ou preferência de navegação, mais chave estável. Para `genne_company_my_account_update`, envie somente `legal_name` ou `trading_name` da empresa da conta carrier/shipper ativa. Para `genne_gr_driver_training_update`, envie o ID público do perfil do motorista e `training_watched` booleano; admin e carrier precisam de `gr.manage`, e carrier só pode alterar um motorista com vínculo ativo. Para `genne_order_triage_address_update`, use IDs públicos do lote e pedido, endereço corrigido e coordenadas em par quando tiver confirmação manual; exige ACL Admin de triagem e invalida as rotas candidatas antigas. Para `genne_receiving_scan`, envie o barcode físico; admin informa também `carrier_account_id`, carrier usa a conta ativa. O Core valida alias, conta, base e ACL e o recibo de recebimento não cria rota. Para `genne_dispatch_route_create`, envie `driver_user_id`, barcodes recebidos e `route_name` opcional; Admin também informa `carrier_account_id`. Revise motorista e pedidos no link de aprovação antes de executar. O receipt contém o ID da rota; confira a cotação separadamente quando ela estiver habilitada. Para `genne_dispatch_route_add_orders`, envie `route_id`, a lista ordenada de `barcodes` recebidos e `reason` opcional; Admin também informa `carrier_account_id`, enquanto carrier usa a conta ativa. A operação só aceita rota manual de expedição ativa, inclusive se a execução já começou. Revise no link de aprovação a rota, o manifesto atual, os pedidos e o motivo antes de executar. O Core rejeita mudanças relevantes de rota, manifesto ou conteúdo dos pedidos desde a aprovação; em retries, preserve a mesma lista e chave. Depois repita o tool com `intent_id` e os mesmos campos. Para `genne_driver_route_start`, informe uma SavedRoute própria do motorista e revise seu estado antes de iniciar; o receipt registra o início e a cotação pode ficar indisponível. A pessoa revisa a intenção no link do Core com o mesmo papel e conta; depois repita o tool com `intent_id` e os mesmos dados.
|
|
77
|
+
|
|
78
|
+
Para `genne_receiving_rule_create`, envie `rule` completo com `code`, `name`, `postal_code_start` e `postal_code_end`; `shipper_id` e `base_id` nulos representam regra global, somente `shipper_id` representa regra do embarcador e `base_id` representa base canônica do embarcador. Admin também informa `carrier_account_id`; carrier usa a conta ativa. Para `genne_receiving_rule_update`, envie `rule_id` e `changes` parcial. Campos de escopo omitidos são preservados e `null` explícito é enviado ao Core para limpar o escopo. O Core exige `physical_receiving.manage`, valida a relação carrier/shipper/base e rejeita base owner carrier em regra por base. Revise before, change, after, CEP normalizado e escopo no link de aprovação; repita com `intent_id`, os mesmos dados e a mesma chave. Um `412 stale_version` exige nova leitura e nova intenção. O receipt devolve a regra pública e o replay não repete a escrita.
|
|
79
|
+
|
|
80
|
+
Para `admin support threads THREAD_ID close|reopen`, a CLI consulta mensagens como prévia, confirma o UUID e envia somente `status: closed|open` ao PATCH. O Core decide `messaging.manage`; cancelamento, UUID divergente, 403/404 e resposta com status inesperado não gravam. Para feedback, `admin feedbacks delete FEEDBACK_ID` faz hard-delete permanente, sem restore do feedback; `admin feedbacks comments FEEDBACK_ID COMMENT_ID delete|restore` oculta ou restaura somente o comentário. As três ações consultam detalhe como prévia, exigem confirmação e retornam 204.
|
|
81
|
+
|
|
82
|
+
O MCP local por stdio permanece read-only. No adaptador remoto, `genne_admin_support_thread_set_status` usa `admin.support_thread.set_status`, `genne_admin_feedback_delete` usa `admin.feedback.delete`, `genne_admin_feedback_comment_delete` usa `admin.feedback_comment.hide` e `genne_admin_feedback_comment_restore` usa `admin.feedback_comment.restore`. Cada schema exige IDs UUID e `idempotency_key` de 16 a 128 caracteres; `intent_id` é opcional no pedido e acompanha o replay com os mesmos dados e chave após aprovação. A aprovação usa senha em outra sessão Admin; um `412` exige releitura, nova intenção e nova chave. O código e os testes locais existem, mas handlers/contratos Core, runtime e smoke autenticado ainda precisam ser validados.
|
|
83
|
+
|
|
84
|
+
Para pedidos, descubra `orders` e `shippers` no help. O embarcador da transportadora usa vínculo gerido pelo carrier; o embarcador cliente usa a própria sessão. A importação cria um lote assíncrono: acompanhe `show` e `orders list`, corrija dados ou geocode pelos comandos do lote. `driver` não tem importação independente; seu fluxo de pedido é o da rota atribuída. Entrega e insucesso são registrados pela execução da rota no Core, sem comando de baixa direta de pedido.
|
|
85
|
+
|
|
86
|
+
Para importar paradas de rota avulsa, use `driver routes imports preview ARQUIVO` com o CSV, XLS, XLSX ou PDF original, inclusive sem cabeçalho. A prévia não cria rota. Em sucesso com `data.ai_inference.status: failed`, a prévia continua utilizável: revise as colunas e repita com `column_mapping=JSON` para o mapeamento manual. Não exija editar a planilha para compensar a inferência. Um 422 de processamento conhecido retorna `error.code`, orientação segura em `error.message` e causa/etapa em `error.data.reason`/`stage`; erros desconhecidos mantêm o fallback HTTP. A importação não é exposta como tool MCP de upload.
|
|
87
|
+
|
|
88
|
+
Para rota avulsa, o motorista cria `driver routes draft create` com origem pura e paradas. Resolva endereços por `driver places search` e `details`, salve os waypoints com coordenadas por `waypoints set` e rode `coordinates validate`. Antes de `optimize` ou `calculate`, guarde `updated_at` e IDs na ordem de `show`. Os comandos retornam IDs de jobs; consulte `driver optimization|calculation JOB_ID show`. Somente após `completed`, aplique a geometria com `optimize|calculate apply JOB_ID`, enviando `expected_updated_at` e `expected_waypoint_ids` do snapshot anterior. O Core compara o snapshot completo no momento da escrita; em 409, consulte a rota e recalcule. A otimização só reordena após esse comando explícito. Revise `show` e use `start` quando apropriado.
|
|
89
|
+
|
|
90
|
+
Para pedidos B2B, materialize a rota pelos comandos de dispatch/workbench da carrier ou shipper. O motorista dono consulta a SavedRoute e executa `failure-policy`, `waypoints WAYPOINT_ID status` ou `items ITEM_ID status` e `finish`; falha requer motivo/observação e pode exigir código de autorização. O MCP remoto expõe `genne_driver_waypoint_notes` com aprovação vinculada para substituir ou limpar a observação da parada própria. O tool remoto `genne_driver_waypoint_packages` altera volumes de uma parada própria sem manifesto importado, com aprovação e repetição segura; zero é válido. O tool remoto `genne_driver_waypoint_navigation_start` registra um evento aditivo aprovado; telemetria é opcional e não altera carga/status. Descubra no help `notes`, `packages`, `navigation start` e `items ITEM_ID enrich`. `navigation start` registra um evento; `packages` não substitui a quantidade controlada pelo manifesto importado. Não altere o manifesto canônico com `waypoints set`. O Core verifica posse, vínculo, estado, policy e ACL; 202 e job `processing` nunca significam rota otimizada ou entrega concluída.
|
|
91
|
+
|
|
92
|
+
Na torre, use `genne help <papel> tower` para descobrir cockpit, chat, consulta de documento e policy conforme o papel ativo. Uma validação de DANFE fornece `assist_token` de uso único para `driver tower assist`; `query_id` permite consultar o resultado. Chat e código de autorização são ações explícitas do operador; nunca envie mensagem nem gere código sem pedido do usuário.
|
|
93
|
+
|
|
94
|
+
Para `genne_tower_failure_policy_update`, envie `reason_codes` do catálogo da torre. Carrier e shipper usam a conta ativa; Admin informa `account_type` e `account_id`. Revise a política anterior e os códigos propostos no link antes de aprovar. A execução é recusada se a política mudar entre prévia e execução.
|
|
95
|
+
|
|
96
|
+
Para `genne_admin_internal_user_access_set`, envie o ID público do usuário interno Admin, `admin_access_level` (`staff` ou `super_admin`) e a lista completa `permission_keys`; para `super_admin`, use lista vazia. O Core exige papel ativo Admin e `access_governance.manage`; a transição de super_admin requer um ator super_admin. Revise usuário e ACL antes/depois no link, depois repita os mesmos campos e a chave com `intent_id`. Um 412 indica que o acesso mudou desde a prévia; consulte o estado atual e crie nova intenção. O receipt contém o nível e permissões efetivas após a escrita.
|
|
97
|
+
|
|
98
|
+
Para `genne_business_member_access_set`, envie `membership_id`, `membership_role` (`owner`, `operator`, `dispatcher` ou `finance`), a lista completa `permission_keys` e `idempotency_key`. Admin também informa `account_id` e precisa de `access_governance.manage`; carrier e shipper usam a conta ativa, omitem `account_id` e precisam de `workspace.permissions.manage`. A aprovação mostra empresa, membro e ACL antes/depois. Repita os mesmos campos e chave com `intent_id`. O Core rejeita permissões fora dos módulos ativos, alterações concorrentes com 412 e remoção do último owner com 409. O receipt mostra papel e permissões efetivas aplicadas.
|
|
99
|
+
|
|
100
|
+
Para `genne_business_member_status_set`, envie `membership_id`, `status` (`active` ou `inactive`) e `idempotency_key`; Admin também informa `account_id`, carrier e shipper usam a conta ativa. Exige `business_accounts_registry.manage` no Admin ou `workspace.members.manage` na conta. Revise empresa, membro e status anterior/proposto em outra sessão browser, depois repita os mesmos campos e chave com `intent_id`. Desativar revoga as sessões do vínculo; o Core rejeita estado concorrente com 412 e a desativação do último owner com 409.
|
|
101
|
+
|
|
102
|
+
Para `genne_business_member_invite`, envie `member` com `email`, `membership_role` e chave estável. Admin também informa `account_id` e `member.workspace_role` como `carrier` ou `shipper`; clientes omitem ambos e usam a conta e o papel ativos. Carrier pode convidar `operator`, `dispatcher` ou `finance`; shipper pode convidar `operator` ou `finance`; Admin também pode atribuir `owner`. Um vínculo existente conserva o acesso e não recebe outro envio. Para `genne_business_member_invitation_resend`, envie `membership_id`, a chave e, no Admin, `account_id`. Revise empresa, destinatário e papel na aprovação separada; depois repita os mesmos campos com `intent_id`. O receipt `delivery_status: pending` indica envio enfileirado. Acompanhe `invitation_delivery` em `genne_my_account_members` ou `genne_admin_account_members`; na CLI, use os comandos de membros do help. Cadastro já enviado não admite reenvio conforme a regra atual. Nunca afirme que o e-mail foi enviado somente pelo receipt MCP.
|
|
103
|
+
|
|
104
|
+
Para `genne_admin_carrier_base_create`, informe a transportadora e os campos obrigatórios da base (`code`, `name`, `origin_address`, `market_id`, `country_code`, `currency_code`, `timezone`). Exige sessão Admin com `network_configuration.manage`. Revise a conta e a base no link de aprovação e repita os mesmos dados e a chave de idempotência com `intent_id`; o receipt contém `base_id`.
|
|
105
|
+
|
|
106
|
+
Para `genne_admin_carrier_base_set_status`, informe `base_id`, `status` (`active` ou `inactive`) e `idempotency_key`. Exige Admin com `network_configuration.manage`; a aprovação mostra a base e a transição. Repita os mesmos dados com `intent_id`. O status altera a disponibilidade operacional da base, sem afirmar mudança da origem financeira. Em 412, consulte a base e crie nova intenção.
|
|
107
|
+
|
|
108
|
+
Para `genne_admin_shipper_base_update` e `genne_admin_carrier_base_update`, informe `account_id` operacional da conta proprietária, `base_id`, `changes` parcial e chave estável. Exigem Admin standalone e `network_configuration.manage`. Campos omitidos são preservados; `null`, mudança de mercado, `country_code`, moeda, ownership e status são recusados. Revise os valores atuais, a alteração e os valores resultantes no link de aprovação, depois repita com `intent_id` e os mesmos dados. A edição de base shipper sincroniza a origem financeira após o commit da alteração e do receipt. Para `genne_admin_shipper_base_set_status`, use os mesmos IDs com `status` (`active` ou `inactive`) em vez de `changes`. O Core revalida ownership, ACL e snapshot antes da escrita. Em 412, releia a base e solicite nova intenção com outra chave. Replay devolve o receipt original.
|
|
109
|
+
|
|
110
|
+
Para `genne_admin_company_update`, informe o `account_id` público da empresa B2B e apenas `legal_name` ou `trading_name` em `changes`, mais `idempotency_key`. Exige papel ativo Admin e `business_accounts_registry.manage`; carrier e shipper continuam usando `genne_company_my_account_update` para a própria conta. Revise UUID, nome e valores anteriores/propostos em outra sessão browser Admin. Repita os mesmos dados e a chave com `intent_id`; o Core recusa mudanças concorrentes e o replay devolve o mesmo receipt.
|
|
111
|
+
|
|
112
|
+
Para `admin companies onboarding-users list`, consulte as identidades ativas de todos os papéis canônicos com `search`, `limit` e `cursor` opcionais; o Core exige `business_accounts_registry.manage` e devolve somente `user_id`, nome, email e status. `account_type` não é filtro dessa coleção. Para `admin companies consult`, envie `cnpj` e `user_id` opcional (UUID) em JSON por stdin. O Core prioriza empresa persistida ou reaproveita a consulta Assertiva latest aprovada e confirmada para o mesmo CNPJ atual do alvo; Assertiva é somente fallback e uma nova consulta não é obrigatória. A resposta traz CNPJ, razão social, nome fantasia e endereço retornados pelo contrato. Para `admin companies onboard-existing-user`, envie `user_id`, `cnpj` e `send_email` opcional (padrão `false`) em JSON por stdin. A CLI confirma o usuário alvo, CNPJ e estado do e-mail antes do POST; o Core valida que a identidade ativa pode receber membership `owner` na conta carrier. O resultado inclui a empresa, o vínculo, os módulos ativos e `email_delivery.status` (`disabled`, `pending`, `sent` ou `failed`).
|
|
113
|
+
|
|
114
|
+
Para `genne_shipper_base_create`, envie `code` e `name` para a conta shipper ativa; endereço, coordenadas em par, timezone, regras de triagem e metadata são opcionais. Exige `route_operations.workbench`. Revise a base no link de aprovação e repita os mesmos dados e a chave de idempotência com `intent_id`. O receipt contém `base_id`; a sincronização financeira idempotente ocorre após o commit da base.
|
|
115
|
+
|
|
116
|
+
Para `genne_admin_shipper_base_create`, informe o embarcador e `code`, `name`, `origin_address`, `market_id`, `country_code`, `currency_code` e `timezone`. Exige sessão Admin autorizada pelo Core para criar bases de embarcadores. Revise a conta e a base em outra sessão e repita `account_id`, `base` e a chave com `intent_id`.
|
|
117
|
+
|
|
118
|
+
Para `genne_carrier_shipper_base_create`, informe `shipper_id` e uma base nova com `code` em maiúsculas e `name`; endereço, coordenadas em par, timezone, regras de triagem e metadata são opcionais. O Core exige a conta carrier ativa e sua ACL para o endpoint de bases do embarcador. A aprovação mostra ambas as contas e a base. Repita `shipper_id`, `base` e a mesma chave com `intent_id`; o receipt inclui base e vínculo carrier.
|
|
119
|
+
|
|
120
|
+
Para `genne_driver_route_finish`, informe a SavedRoute própria depois das baixas exigidas. Revise o estado da rota e a quantidade de paradas em outra sessão driver, depois repita `route_id` e a mesma chave com `intent_id`. O Core recusa versão obsoleta ou waypoint obrigatório pendente; o receipt registra a conclusão e a conciliação financeira existente ocorre após o commit.
|
|
121
|
+
|
|
122
|
+
Para `genne_driver_waypoint_deliver`, informe `route_id` e `waypoint_id` da parada pendente em rota própria. Revise rota, parada e estado no link de aprovação em outra sessão driver. Repita os mesmos IDs e `idempotency_key` com `intent_id`. O Core exige check-in quando aplicável, baixa somente os itens pendentes pelo serviço de execução e registra os eventos operacionais existentes; consulte a rota após o receipt para verificar o estado resultante.
|
|
123
|
+
|
|
124
|
+
Para `genne_driver_package_item_deliver`, informe `route_id`, `waypoint_id` e `item_id` de um item pendente do manifesto da rota própria. Revise o item específico no link de aprovação e repita os mesmos IDs, a chave e `intent_id`. O Core registra a baixa pelo fluxo canônico; o receipt mostra o item entregue e o status da parada, que pode continuar `pending` enquanto houver outros itens.
|
|
125
|
+
|
|
126
|
+
Para `genne_driver_waypoint_check_in`, informe `route_id`, `waypoint_id`, `lat`, `lng` e `accuracy_meters` opcional. O check-in é aditivo e pode ser registrado para qualquer estado da parada dentro da janela de edição; o Core calcula distância, limite e alerta. Revise as coordenadas no link de aprovação em outra sessão driver e repita os mesmos dados, a mesma chave e `intent_id`. O receipt retorna `distance_meters`, `within_threshold`, `alert` e `checked_in_at`; ele não baixa itens nem altera o status da parada.
|
|
127
|
+
|
|
128
|
+
Para `genne_driver_carrier_link`, informe apenas `driver_link_code` e uma `idempotency_key` estável. O Core resolve o código, mostra transportadora e vínculo anterior para aprovação em outra sessão browser do mesmo motorista e não grava o código na intenção ou no receipt. Depois da aprovação, repita o código e a chave com `intent_id`; o Core revalida o código e o estado do vínculo. Na CLI, `users driver carriers link` usa o endpoint direto existente e confirma a ação no terminal.
|
|
129
|
+
|
|
130
|
+
Para convidar um motorista registrado por CPF, use `users carrier drivers invitations create` com JSON por stdin e uma chave de idempotência estável. A CLI confirma sem mostrar o CPF; o Core decide ACL, limites e disponibilidade do destinatário. Consulte `users carrier drivers invitations list|show` e cancele com `invitations cancel`. O motorista consulta `users driver carriers invitations list|show` e responde com `invitations accept|decline`, após confirmação. Preserve a chave e os bytes do payload nos retries. Chaves ASCII sem perda usam header; os demais valores UTF-8 válidos usam JSON, sem trim ou normalização. O MCP expõe somente as leituras `genne_carrier_driver_invitations`, `genne_carrier_driver_invitation_show`, `genne_driver_carrier_invitations` e `genne_driver_carrier_invitation_show`. A disponibilidade depende do contrato Core implantado; convite pendente não confirma vínculo.
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
Para inspecionar atendimento e distribuição, descubra `genne_admin_shipper_base_zones`, `genne_admin_carrier_base_zones`, `genne_admin_shipper_base_carrier_shares` e `genne_carrier_base_reference_zones`. Na CLI, use `admin shippers|carriers OPERATIONAL_ACCOUNT_ID bases BASE_ID zone-overrides list`, `admin shippers OPERATIONAL_ACCOUNT_ID bases BASE_ID carrier-shares list` ou `carrier bases BASE_ID reference-zone-features list`. As leituras são síncronas, sem stdin ou confirmação, disponíveis por stdio e MCP remoto com escopo de leitura. Admin exige `network_configuration.view` ou `network_configuration.manage`; carrier usa a conta ativa e `route_operations.view`, `route_operations.workbench` ou `route_operations.publish`, revalidadas pelo Core. Driver e shipper não herdam acesso Admin/carrier; shipper conserva seus tools de shares e catálogo próprios.
|
|
134
|
+
|
|
135
|
+
Obtenha o ID operacional em `admin companies show BUSINESS_ACCOUNT_ID`, nos objetos `shipper_account.id` ou `carrier_account.id`, depois liste as bases dessa conta. Os tools Admin validam essa referência antes da consulta; isso não substitui a ACL Core. `zone-overrides` retorna zonas efetivas salvas, incluindo overrides, enquanto `reference-zone-features` é catálogo OSM para composição e não comprova cobertura operacional. Neste catálogo, `limit`, `cursor`, `search`, bbox (`min_lng`, `min_lat`, `max_lng`, `max_lat`) e `include_geometry` são opcionais; preserve `meta.next_cursor` para a próxima página. Shares configurados não comprovam alocação executada. Cutoff não tem GET dedicado. Escritas MCP de área, agrupamentos e shares aguardam handlers Core com snapshot de filhos/referências, locks compartilhados com writers HTTP e mutation/receipt atômicos; não execute o comando CLI mutável como substituto de aprovação MCP.
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
Para `genne_admin_shipper_base_cutoff_windows_set` e `genne_admin_carrier_base_cutoff_windows_set`, use `account_id` operacional proprietário, `base_id`, `windows` e chave estável de idempotência. Cada janela contém `weekday` 0..6 (domingo..sábado), `cutoff_time` HH:MM[:SS] no timezone da base e `auto_publish` booleano opcional (padrão true). A lista substitui todos os horários; `[]` remove todos. Exige Admin standalone com `network_configuration.manage`. Revise conta, base, timezone, filhos atuais e resultado proposto em outra sessão browser. Depois repita exatamente os dados e a chave com `intent_id`. O Core compara também os filhos e grava substituição e receipt na mesma transação, usando o lock dos writers HTTP. Em 412, releia e crie nova intenção com nova chave. Replay devolve o receipt original. As tools são remotas com escopo de escrita; stdio conserva somente leituras. Disponibilidade depende do deploy do handler Core correspondente, verificado separadamente.
|
|
139
|
+
|
|
140
|
+
Consultas adicionais são síncronas e não exigem confirmação: `genne_admin_gr_driver_profile` usa o perfil público e ACL GR Admin; `genne_import_source_preview` usa `role` admin/carrier/shipper e `batch_id` autorizado. `genne_shipper_route_candidates` e `genne_carrier_route_candidates` consultam candidatos de planejamento pelos comandos `shipper|carrier route-candidates list`, distintos das rotas operacionais e SavedRoutes. `genne_tower_failure_catalog` consulta o catálogo global pelos comandos `<papel> tower failure-reasons catalog-global`; não gera códigos nem consome autorização. Os filtros disponíveis estão no schema MCP e help CLI.
|
|
141
|
+
|
|
142
|
+
Para gerenciar GR pelo terminal, consulte `genne help admin gr` ou `genne help carrier gr`. `admin gr drivers` consulta o diretório global, incluindo motoristas sem vínculo, e aceita `carrier_account_id` opcional; o MCP usa `genne_admin_gr_driver_directory`, enquanto `genne_admin_gr_drivers` conserva o escopo carrier explícito. Driver e shipper não operam essas ferramentas. O motorista informa `training_watched` booleano em `driver profile draft|complete` e consulta o estado em `driver profile show`.
|
|
143
|
+
|
|
144
|
+
Para GR aprovado fora do sistema, use `carrier gr links LINK_ID manual-approval` ou `admin gr carriers CARRIER_ID links LINK_ID manual-approval`. Envie somente `{"approved":true}` ou `{"approved":false}` por JSON stdin e confirme o UUID público do vínculo após revisar a ação e a transportadora. Não use o ID do usuário/perfil. Carrier usa a conta da sessão; Admin informa a transportadora explicitamente. O Core exige `gr.manage` e vínculo ativo não bloqueado, registra autor/data e limita a decisão àquela transportadora. Revogar remove a aprovação manual, sem alterar evidências/status do provider nem treinamento. A resposta síncrona inclui `manual_approval`; não repita automaticamente após erro. Requer o endpoint Core de aprovação manual disponível no ambiente. Essa mutação é exclusiva da CLI, sem tool MCP equivalente ou chamada HTTP direta como fallback.
|
|
145
|
+
|
|
146
|
+
Reprocessamento documental usa `carrier gr drivers PROFILE_ID documents TYPE reprocess` ou `admin gr carriers CARRIER_ID drivers PROFILE_ID documents TYPE reprocess`, com JSON por stdin contendo `document_id`, `registration_request_id` e `requirements_revision` quando presentes na prévia. `requests REQUEST_ID retry` solicita nova execução; `verify` reconcilia; `confirm-recovery` recebe a revisão e operações exatas mostradas na prévia. A confirmação terminal mostra esses valores e o Core rejeita revisão obsoleta. `show`, `attempts`, `evidence` e `orchestration-events` acompanham o resultado; 202 não comprova conclusão. A matriz completa está em `GR.md`, distribuído no pacote.
|
|
147
|
+
|
|
148
|
+
GR: o Core resolve mapeamento ativo solicitado, primeiro mapeamento ativo ou fallback legado existente. `operation` altera apenas solicitações legadas antes da primeira escrita externa; novas solicitações usam o workflow atual. Não trate o fallback como uma operação ativa escolhida pelo cliente. Pedidos: upsert carrier/Admin mantém a semântica existente; não equivale a patch e não há upsert shipper. Prévia de origem não cria lote nem pedido.
|
|
149
|
+
|
|
150
|
+
O handler atual de candidatos shipper ignora `base_id`/`status` anunciados no contrato e retorna somente `data`; carrier também não retorna cursor. Não afirme que os candidatos foram filtrados nem invente paginação no cliente. TD-08 acompanha o alinhamento Core.
|
|
151
|
+
|
|
152
|
+
Cutoff shipper com dispatches vinculados (inclusive histórico) retorna `409 cutoff_dispatches_exist` e preserva horários/execuções. Se a referência surgir após aprovação, o snapshot fica obsoleto e exige nova intenção. Não há cancelamento ou reagendamento implícito.
|
package/src/api.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var A=Object.defineProperty;var d=(e,o)=>A(e,"name",{value:o,configurable:!0});import{accessToken as b,clearSession as P,selectedOrigin as O}from"./session.js";import{currentRequestSignal as j}from"./request-scope.js";const w={401:"Sess\xE3o inv\xE1lida ou expirada",403:"Acesso negado",404:"Recurso n\xE3o encontrado",409:"Conflito no contexto da conta",422:"Dados inv\xE1lidos"},E={invalid_cpf:"CPF inv\xE1lido",invalid_input:"Dados do convite inv\xE1lidos",rate_limited:"Limite de convites atingido; tente novamente mais tarde",idempotency_conflict:"A chave de idempot\xEAncia j\xE1 foi usada com outra entrada",invitation_conflict:"O convite j\xE1 foi encerrado ou recebeu outra resposta",driver_already_linked:"Motorista j\xE1 vinculado \xE0 transportadora",invitation_recipient_unavailable:"Destinat\xE1rio indispon\xEDvel para convite",driver_carrier_link_blocked:"V\xEDnculo bloqueado pela transportadora",invitation_already_pending:"J\xE1 existe convite pendente",invitation_cooldown:"Aguarde o intervalo para enviar novo convite",carrier_unavailable:"Transportadora indispon\xEDvel"},F=new Set(["unsupported_file_type","invalid_file","empty_file","invalid_headers","invalid_pdf","pdf_encrypted","pdf_page_limit_exceeded","file_too_large","pdf_text_extraction_unavailable","pdf_text_extraction_failed","pdf_no_text","pdf_table_not_found","pdf_table_invalid","pdf_layout_invalid","pdf_layout_inference_unavailable","pdf_layout_inference_failed","pdf_visual_extraction_unavailable","pdf_visual_extraction_failed"]),I={unsupported_file_type:{stage:"reading",message:"N\xE3o conseguimos processar esse formato de arquivo. Envie uma planilha XLSX, XLS ou CSV, ou um PDF com as entregas."},empty_file:{stage:"reading",message:"N\xE3o conseguimos processar o arquivo porque a planilha est\xE1 vazia. Confira se selecionou o arquivo de entregas correto."},invalid_headers:{stage:"interpretation",message:"N\xE3o conseguimos processar a planilha porque n\xE3o identificamos quais colunas cont\xEAm os pedidos, endere\xE7os e volumes. Fale com o suporte para ajudar na importa\xE7\xE3o."},invalid_pdf:{stage:"reading",message:"N\xE3o conseguimos abrir o PDF recebido. Tente enviar o arquivo novamente. Se continuar, fale com o suporte."},pdf_encrypted:{stage:"reading",message:"N\xE3o conseguimos ler o PDF porque ele est\xE1 protegido por senha. Pe\xE7a a quem enviou o arquivo uma c\xF3pia sem senha."},pdf_page_limit_exceeded:{stage:"reading",message:"N\xE3o conseguimos processar o PDF porque ele excede o limite de p\xE1ginas por importa\xE7\xE3o. Pe\xE7a ajuda ao suporte para importar esse arquivo."},file_too_large:{stage:"reading",message:"N\xE3o conseguimos processar o arquivo porque ele excede o limite por importa\xE7\xE3o. Pe\xE7a ajuda ao suporte para importar esse arquivo."},pdf_text_extraction_unavailable:{stage:"reading",message:"N\xE3o conseguimos ler o conte\xFAdo do PDF neste momento. Tente novamente em alguns minutos."},pdf_text_extraction_failed:{stage:"reading",message:"N\xE3o conseguimos processar o PDF porque n\xE3o foi poss\xEDvel ler os dados das entregas. Fale com o suporte para ajudar na importa\xE7\xE3o."},invalid_csv:{stage:"reading",message:"N\xE3o conseguimos ler o arquivo CSV porque alguns campos de texto n\xE3o terminam como esperado. Tente enviar o arquivo novamente. Se continuar, fale com o suporte."},invalid_encoding:{stage:"reading",message:"N\xE3o conseguimos processar o arquivo porque n\xE3o reconhecemos alguns caracteres do texto. Fale com o suporte para ajudar na importa\xE7\xE3o."},unreadable_file:{stage:"reading",message:"N\xE3o conseguimos abrir o arquivo recebido. Tente enviar o arquivo novamente. Se continuar, fale com o suporte."},unreadable_content:{stage:"interpretation",message:"N\xE3o conseguimos processar o arquivo porque o conte\xFAdo n\xE3o ficou leg\xEDvel na leitura. Tente enviar o arquivo novamente. Se continuar, fale com o suporte."},no_delivery_table:{stage:"interpretation",message:"N\xE3o conseguimos processar o arquivo porque n\xE3o encontramos uma lista de entregas no conte\xFAdo lido. Confira se selecionou o arquivo certo. Se estiver correto, fale com o suporte."},missing_addresses:{stage:"interpretation",message:"N\xE3o conseguimos processar o arquivo porque n\xE3o identificamos os endere\xE7os das entregas. Fale com o suporte para ajudar na importa\xE7\xE3o."},missing_order_identifiers:{stage:"interpretation",message:"N\xE3o conseguimos processar o arquivo porque n\xE3o identificamos os c\xF3digos dos pedidos ou de rastreio. Fale com o suporte para ajudar na importa\xE7\xE3o."},ambiguous_columns:{stage:"interpretation",message:"N\xE3o conseguimos processar o arquivo porque mais de uma coluna pode conter os mesmos dados de entrega e n\xE3o foi poss\xEDvel distinguir qual usar. Fale com o suporte para ajudar na importa\xE7\xE3o."},analysis_timeout:{stage:"interpretation",message:"N\xE3o conseguimos concluir a leitura do arquivo porque a an\xE1lise demorou mais que o esperado. Tente novamente em instantes."},analysis_rate_limited:{stage:"interpretation",message:"N\xE3o conseguimos analisar o arquivo agora porque o servi\xE7o de leitura est\xE1 ocupado. Tente novamente em instantes."},analysis_unavailable:{stage:"interpretation",message:"N\xE3o conseguimos analisar o arquivo agora porque o servi\xE7o de leitura est\xE1 indispon\xEDvel. Tente novamente em instantes."},invalid_ai_response:{stage:"interpretation",message:"Tentamos analisar o arquivo, mas n\xE3o conseguimos interpretar as entregas com seguran\xE7a. Tente novamente. Se continuar, fale com o suporte."}};function C(e,o){if(!e||typeof e!="object"||Array.isArray(e))return null;const{error:i,reason:s,stage:n}=e;if(typeof i!="string"||typeof s!="string"||typeof n!="string"||typeof e.message!="string"||!F.has(i))return null;const u=I[s];if(!u||u.stage!==n)return null;const p=new q(i,u.message,o);return p.data={reason:s,stage:n},p}d(C,"routeImportError");class q extends Error{static{d(this,"CliError")}constructor(o,i,s){super(i),this.code=o,this.status=s}}function r(e,o,i){throw new q(e,o,i)}d(r,"fail");function L(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e??"")||r("USAGE","ID deve ser um UUID"),e}d(L,"uuid");function R(e){e||r("CONFIG","Defina GENNE_API_URL");let o;try{o=new URL(e)}catch{r("CONFIG","GENNE_API_URL deve ser uma origem HTTP ou HTTPS")}return(!["http:","https:"].includes(o.protocol)||o.username||o.password||o.pathname!=="/"||o.search||o.hash)&&r("CONFIG","GENNE_API_URL deve ser uma origem HTTP ou HTTPS"),o.protocol!=="https:"&&!["localhost","127.0.0.1","[::1]"].includes(o.hostname)&&r("CONFIG","A API exige HTTPS fora da m\xE1quina local"),o.origin}d(R,"apiOrigin");function k(e,{allowAnonymous:o=!1}={}){const i=R(O(e));return async(s,n,u,{idempotencyKey:p,deletionIntent:N,clientPlatform:y,ifMatch:S,withStatus:h,invitationErrorProjection:T}={})=>{(!n.startsWith("/")||n.startsWith("//"))&&r("USAGE","Caminho de API inv\xE1lido");const g=s==="PUT"&&n==="/auth/me/driver-access",f=g||s==="PUT"&&/^\/admin\/users\/[0-9a-f-]+\/driver-access$/i.test(n),x=g?"N\xE3o foi poss\xEDvel confirmar a habilita\xE7\xE3o; fa\xE7a genne login e consulte genne user me antes de repetir.":"N\xE3o foi poss\xEDvel confirmar a habilita\xE7\xE3o; confira os pap\xE9is do usu\xE1rio alvo antes de repetir. O titular pode precisar de novo login.",v=o?null:await b(e,i),_=u instanceof FormData;let a;try{a=await fetch(`${i}/api/v1${n}`,{method:s,redirect:"error",headers:{accept:"application/json",...v?{authorization:`Bearer ${v}`}:{},...p?{"idempotency-key":p}:{},...S?{"if-match":S}:{},...N?{"x-deletion-intent":N}:{},...y?{"x-client-platform":y}:{},...u===void 0||_?{}:{"content-type":"application/json"}},...u===void 0?{}:{body:_?u:JSON.stringify(u)},signal:AbortSignal.any([AbortSignal.timeout(_?6e4:15e3),j()].filter(Boolean))})}catch{f&&r("NETWORK",x),r("NETWORK","N\xE3o foi poss\xEDvel conectar \xE0 API")}if(!a.ok){if(f&&[409,422].includes(a.status)){let t;try{t=await a.json()}catch{}g&&a.status===422&&t?.error==="invalid_current_password"&&r("invalid_current_password","Senha atual incorreta",422),a.status===409&&t?.error==="unsupported_role_transition"&&r("unsupported_role_transition","Este usu\xE1rio n\xE3o pode habilitar motorista por esta opera\xE7\xE3o",409)}if(a.status===422&&s==="POST"&&n==="/routes/imports/preview"){let t;try{t=await a.json()}catch{}const m=C(t,a.status);if(m)throw m}const l=a.status===429&&s==="POST"&&n==="/carrier/driver-invitations";if(T&&([409,422].includes(a.status)||l)){let t;try{t=await a.json()}catch{}if(t&&typeof t.error=="string"&&Object.hasOwn(E,t.error)&&(t.error==="rate_limited"?l:!l)){const m=new q(t.error,E[t.error],a.status);throw a.status===409&&t.error==="invitation_conflict"&&t.data&&typeof t.data=="object"&&!Array.isArray(t.data)&&(m.data=T(t.data)),m}}r(`HTTP_${a.status}`,w[a.status]??"A API recusou a requisi\xE7\xE3o",a.status)}if(a.status===204)return!o&&!e.GENNE_ACCESS_TOKEN&&s==="POST"&&["/auth/logout","/auth/logout-all"].includes(n)&&await P(e,i),h?{http_status:a.status}:{};let c;try{const l=await a.text();if(!l)return{};c=JSON.parse(l)}catch{f&&r("RESPONSE",x),r("RESPONSE","Resposta inv\xE1lida da API")}return(!c||typeof c!="object"||Array.isArray(c))&&r("RESPONSE","Resposta inv\xE1lida da API"),g&&c.data?.status==="enabled"&&c.data.changed===!0&&c.data.reauthentication_required===!0&&!e.GENNE_ACCESS_TOKEN?.trim()&&await P(e,i,v),h?{...c,http_status:a.status}:c}}d(k,"createApi");export{q as CliError,R as apiOrigin,k as createApi,r as fail,L as uuid};
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var p=Object.defineProperty;var l=(e,t)=>p(e,"name",{value:t,configurable:!0});import{closeSync as g,openSync as h,readSync as y,writeSync as v}from"node:fs";import{CliError as N,fail as n}from"./api.js";import{deviceLogin as O,deviceLogout as b}from"./device-login.js";import{runOperation as w}from"./operations.js";import{environmentCommand as S}from"./session.js";import{globalCommand as C}from"./global.js";let a;async function I(){if(a!==void 0)return a;process.stdin.isTTY&&n("INPUT","Forne\xE7a JSON em stdin");let e="";for await(const t of process.stdin)e+=t,e.length>1e6&&n("INPUT","Entrada muito grande");try{a=JSON.parse(e)}catch{n("INPUT","JSON inv\xE1lido em stdin")}return(!a||typeof a!="object"||Array.isArray(a))&&n("INPUT","JSON em stdin deve ser um objeto"),a}l(I,"input");async function k(e,t){let i;try{i=h("/dev/tty","r")}catch{n("CONFIRM","Confirma\xE7\xE3o interativa necess\xE1ria")}try{v(2,`${e}
|
|
3
|
+
Digite ${t} para confirmar: `);const r=[],o=Buffer.alloc(1024);let f=0,d="";for(;;){const u=y(i,o,0,o.length,null);u===0&&n("CONFIRM","Opera\xE7\xE3o cancelada");const s=o.subarray(0,u),c=s.indexOf(10),m=c===-1?s:s.subarray(0,c);if(f+=m.length,f>1e6&&n("CONFIRM","Confirma\xE7\xE3o muito longa"),r.push(Buffer.from(m)),c!==-1)break}return d=Buffer.concat(r).toString("utf8"),d.trim()!==t&&n("CONFIRM","Opera\xE7\xE3o cancelada"),!0}finally{g(i)}}l(k,"confirm");try{let e=process.argv.slice(2);const t=e[0]==="--non-interactive";if(t){e=e.slice(1);const r=e.length===5&&e[0]==="admin"&&e[1]==="blog"&&e[2]==="posts"&&e[3]==="create"&&e[4].startsWith("key="),o=e.length===5&&e[0]==="admin"&&e[1]==="feedbacks"&&["comment","status"].includes(e[2])&&e[4].startsWith("key=");!r&&!o&&n("USAGE","--non-interactive \xE9 aceito somente para Growth e Donna")}const i=e[0]==="global"&&e.at(-1)!=="--help"?(()=>{try{return C(e.slice(1))}catch(r){n("GLOBAL",r.message)}})():e[0]==="env"&&e.at(-1)!=="--help"?await S(process.env,e.slice(1)):e.length===1&&e[0]==="login"?await O(process.env,({verification_uri:r,user_code:o})=>{process.stderr.write(`Abra ${r}
|
|
4
|
+
Digite o c\xF3digo ${o} e aprove o acesso.
|
|
5
|
+
`)}):e.length===1&&e[0]==="logout"?await b(process.env):await w(e,{input:I,confirm:t?async()=>!0:k});process.stdout.write(`${JSON.stringify(i)}
|
|
6
|
+
`)}catch(e){const t=e instanceof N;process.stderr.write(`${JSON.stringify({error:{code:t?e.code:"UNKNOWN",message:t?e.message:"Falha interna do CLI",...t&&e.status?{status:e.status}:{},...t&&e.data?{data:e.data}:{}}})}
|
|
7
|
+
`),process.exitCode=1}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var M=Object.defineProperty;var b=(e,t)=>M(e,"name",{value:t,configurable:!0});import{CliError as I}from"../api.js";const G=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function A(e,t="O corpo deve ser um objeto JSON"){if(!e||typeof e!="object"||Array.isArray(e))throw new I("USAGE",t);return e}b(A,"assertObject");function O(e,t){const m=A(e),u=Object.keys(m).filter(E=>!t.includes(E));if(u.length)throw new I("USAGE",`Campo n\xE3o suportado: ${u[0]}`);return m}b(O,"only");async function h(e,t,m){const u=A(await e()),E=Object.prototype.hasOwnProperty.call(u,t);if(E&&Object.keys(u).length!==1)throw new I("USAGE",`O corpo deve conter apenas ${t}`);const f=E?u[t]:u,d=O(f,m);if(Object.keys(d).length===0)throw new I("USAGE","O corpo n\xE3o pode ser vazio");return{[t]:d}}b(h,"bodyFor");async function l(e,t,m){if(typeof e.confirm!="function")throw new I("CONFIRM","Confirma\xE7\xE3o interativa necess\xE1ria");await e.confirm(t,m)}b(l,"confirmMutation");function w(e,t,m){try{return m(e)}catch{throw new I("USAGE",`${t} deve ser um UUID`)}}b(w,"requireUuid");function $(e,t){return!e||typeof e!="object"||Array.isArray(e)?e:Object.fromEntries(t.filter(m=>m in e).map(m=>[m,e[m]]))}b($,"memberFields");function T(e){const t=$(e,["id","membership_id","business_account_id","workspace_role","membership_role","status"]);return e?.user&&(t.user=$(e.user,["id","name","email","role","status"])),e?.business_account&&(t.business_account=$(e.business_account,["id","legal_name","trading_name","status"])),e?.invitation_delivery&&(t.invitation_delivery=$(e.invitation_delivery,["status","attempt_count","sent_at","error_code"])),t}b(T,"projectMember");function _(e){return Array.isArray(e?.data)?{...e,data:e.data.map(T)}:e?.data&&typeof e.data=="object"?{...e,data:T(e.data)}:e}b(_,"projectMembers");async function j(e,t){if(e[0]!=="access")return null;const[,m,u,E]=e,f=t.uuid??(n=>{if(!G.test(n))throw new Error("invalid uuid");return n}),d=t.input??(async()=>({})),o=t.api,y=t.fail??((n,r)=>{throw Object.assign(new Error(r),{code:n})}),U=b(()=>t.requireRole("admin"),"admin"),P=b(()=>{t.user?.role!=="shipper"&&t.user?.role!=="carrier"&&y("HTTP_403","Acesso negado",403)},"customer");if(m==="catalog"&&e.length===2)return U(),o("GET","/admin/permissions/catalog");if(m==="accounts"&&u==="list"){U();const n=new URLSearchParams;for(const s of e.slice(3)){const c=s.indexOf("="),a=s.slice(0,c);if(c<1||!["account_type","search","limit","cursor"].includes(a))return y("USAGE",`Filtro n\xE3o suportado: ${s}`,422);n.set(a,s.slice(c+1))}const r=await o("GET",`/admin/permissions/business-accounts${n.size?`?${n}`:""}`);return Array.isArray(r?.data)?{...r,data:r.data.map(s=>$(s,["id","business_account_id","account_type","name","status"]))}:r}if(m==="members"&&u==="list"){U();const n=new URLSearchParams;for(const r of e.slice(3)){const s=r.indexOf("="),c=r.slice(0,s);if(s<1||!["limit","cursor","search","status","membership_role","carrier_account_id","shipper_account_id"].includes(c))return y("USAGE",`Filtro n\xE3o suportado: ${r}`,422);n.set(c,r.slice(s+1))}return _(await o("GET",`/admin/business-account-members${n.size?`?${n}`:""}`))}if(m==="internal"){if(U(),u==="list"&&e.length===3){const n=await o("GET","/admin/permissions/internal-users");return Array.isArray(n?.data)?{...n,data:n.data.map(r=>$(r,["id","name","email","role","roles","admin_access_level","is_internal","status"]))}:n}if((u==="show"||u==="set")&&E){const n=w(E,"USER_ID",f),r=`/admin/permissions/internal-users/${n}/access`;if(u==="show"&&e.length===4)return o("GET",r);if(u==="set"&&e.length===4){const s=await h(d,"access",["admin_access_level","permission_keys"]);return await l(t,`Alterar ACL do usu\xE1rio ${n}?`,n),o("PUT",r,s)}}}if(m==="my-account"){P();const n=u,r=e[3],s=e[4],c=`/${t.user.role}/business-account`;if(n==="catalog"&&e.length===3)return o("GET",`${c}/permissions/catalog`);if(n==="members"&&e.length===3)return _(await o("GET",`${c}/members`));if(n==="members"&&r==="create"&&e.length===4){const a=await h(d,"member",["email","membership_role"]);return(typeof a.member.email!="string"||!a.member.email.trim())&&y("INPUT","Informe o email do membro",422),await l(t,`Convidar ${a.member.email} para a conta ativa?`,"my-account"),_(await o("POST",`${c}/members`,a))}if(n==="members"&&r&&e.length===5&&s==="update"){const a=w(r,"MEMBERSHIP_ID",f),i=await h(d,"member",["status","membership_role","metadata"]);return await l(t,`Alterar membro ${a}?`,a),_(await o("PUT",`${c}/members/${a}`,i))}if(n==="members"&&r&&e.length===5&&s==="resend-invitation"){const a=w(r,"MEMBERSHIP_ID",f);return await l(t,`Reenviar convite do membro ${a}?`,a),_(await o("POST",`${c}/members/${a}/invitation/resend`))}if(n==="entitlements"){const a=`${c}/module-entitlements`;if(e.length===3)return o("GET",a);if(e.length===4&&r==="set"){const i=await h(d,"access",["module_keys"]);return await l(t,"Alterar entitlements da conta ativa?","my-account"),o("PUT",a,i)}}if(n==="member-access"&&r){const a=w(r,"MEMBERSHIP_ID",f),i=`${c}/member-access/${a}`;if(e.length===4)return o("GET",i);if(e.length===5&&s==="set"){const p=await h(d,"access",["permission_keys"]);return await l(t,`Alterar ACL do membro ${a}?`,a),o("PUT",i,p)}}return y("USAGE","Use: genne access my-account members [create|MEMBERSHIP_ID update|MEMBERSHIP_ID resend-invitation]",2)}if(m==="account"){if(t.user?.role!=="admin")return y("USAGE","Use `access my-account` for the active shipper or carrier account",2);const n=w(u,"BUSINESS_ACCOUNT_ID",f),r=e[3],s=e[4],c=e[5];U();const a=`/admin/permissions/business-accounts/${n}`;if(r==="members"&&e.length===4)return _(await o("GET",`${a}/members`));if(r==="members"&&s==="create"&&e.length===5){const i=await h(d,"member",["email","membership_role","workspace_role"]);return(typeof i.member.email!="string"||!i.member.email.trim())&&y("INPUT","Informe o email do membro",422),await l(t,`Criar membro na conta ${n}?`,n),_(await o("POST",`/admin/business-accounts/${n}/members`,i))}if(r==="members"&&s&&e.length===6&&c==="update"){const i=w(s,"MEMBERSHIP_ID",f),p=await h(d,"member",["status","membership_role","workspace_role","metadata"]);return await l(t,`Alterar membro ${i}?`,i),_(await o("PUT",`/admin/business-accounts/${n}/members/${i}`,p))}if(r==="members"&&s&&e.length===6&&c==="resend-invitation"){const i=w(s,"MEMBERSHIP_ID",f);return await l(t,`Reenviar convite do membro ${i}?`,i),_(await o("POST",`/admin/business-accounts/${n}/members/${i}/invitation/resend`))}if(r==="entitlements"){const i=`${a}/module-entitlements`;if(e.length===4)return o("GET",i);if(e.length===5&&s==="set"){const p=await h(d,"access",["module_keys"]);return await l(t,`Alterar entitlements da conta ${n}?`,n),o("PUT",i,p)}}if(r==="member-access"&&s){const i=w(s,"MEMBERSHIP_ID",f),p=`${a}/member-access/${i}`;if(e.length===5)return o("GET",p);if(e.length===6&&c==="set"){const S=await h(d,"access",["permission_keys"]);return await l(t,`Alterar ACL do membro ${i}?`,i),o("PUT",p,S)}}if(r==="member-bases"&&s){const i=w(s,"MEMBERSHIP_ID",f),p=`${a}/member-access/${i}/bases`;if(e.length===5)return o("GET",p);if(e.length===6&&c==="set"){const S=await h(d,"access",["base_ids"]);return await l(t,`Alterar bases do membro ${i}?`,i),o("PUT",p,S)}}}return y("USAGE","Use: genne access catalog | internal list|show USER_ID|set USER_ID | account BUSINESS_ACCOUNT_ID members|entitlements|member-access MEMBERSHIP_ID",2)}b(j,"accessCommand");export{j as accessCommand};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var P=Object.defineProperty;var c=(i,e)=>P(i,"name",{value:e,configurable:!0});const b=["legal_name","trading_name","document","issuing_country_code","legal_identifier","status","account_type","account_types","contact_points","addresses"],S=b.filter(i=>!["status","account_type","account_types"].includes(i)),y=["code","name","origin_address","origin_lat","origin_lng","postal_code","place_id","street","street_number","neighborhood","city","state","state_code","country","geocode_provider","timezone","triage_rules"],w=[...y,"market_id","country_code","currency_code"],N=[...y,"balancing_policy","metadata"],A=["code","name","origin_address","origin_lat","origin_lng","timezone","triage_rules","metadata"],$=["carrier_account_id","target_share_percent","quality_weight","priority_order","allocation_enabled"],v=["limit","cursor","search","min_lng","min_lat","max_lng","max_lat","include_geometry"];function d(i,e,n){return(!i||typeof i!="object"||Array.isArray(i)||!Object.keys(i).length||Object.keys(i).some(a=>!e.includes(a)))&&n.fail("INPUT","Objeto JSON vazio ou com campo n\xE3o suportado",422),i}c(d,"object");function j(i,e){const n=d(i,["cnpj","user_id"],e);return(typeof n.cnpj!="string"||!n.cnpj.trim())&&e.fail("INPUT","Informe cnpj em stdin",422),n.user_id===void 0?{cnpj:n.cnpj}:((typeof n.user_id!="string"||!n.user_id.trim())&&e.fail("INPUT","user_id deve ser UUID em stdin",422),{cnpj:n.cnpj,user_id:e.uuid(n.user_id.trim())})}c(j,"companyConsultBody");function z(i,e){const n=d(i,["user_id","cnpj","send_email"],e);(typeof n.user_id!="string"||!n.user_id.trim())&&e.fail("INPUT","Informe user_id como UUID em stdin",422);const a=e.uuid(n.user_id.trim());return(typeof n.cnpj!="string"||!n.cnpj.trim())&&e.fail("INPUT","Informe cnpj em stdin",422),n.send_email!==void 0&&typeof n.send_email!="boolean"&&e.fail("INPUT","send_email deve ser booleano",422),{user_id:a,cnpj:n.cnpj,send_email:n.send_email??!1}}c(z,"existingUserOnboarding");function g(i,e,n,a){return Array.isArray(i)||n.fail("INPUT",`${a} deve ser uma lista`,422),i.map(r=>d(r,e,n))}c(g,"arrayObject");function h(i,e,n){return(!Array.isArray(i)||i.some(a=>{try{return e.uuid(a),!1}catch{return!0}}))&&e.fail("INPUT",`${n} deve conter somente UUIDs`,422),i}c(h,"uuidList");function O(i,e){const n=d(i,["windows"],e),a=g(n.windows,["weekday","cutoff_time","auto_publish"],e,"windows");for(const r of a)(!Number.isInteger(r.weekday)||r.weekday<0||r.weekday>6||typeof r.cutoff_time!="string"||!/^\d{2}:\d{2}(:\d{2})?$/.test(r.cutoff_time)||r.auto_publish!==void 0&&typeof r.auto_publish!="boolean")&&e.fail("INPUT","Cada janela exige weekday 0..6, cutoff_time HH:MM[:SS] e auto_publish booleano opcional",422);return{windows:a}}c(O,"cutoffBody");function C(i,e){const n=d(i,["reference_zone_public_ids"],e);return h(n.reference_zone_public_ids,e,"reference_zone_public_ids"),n}c(C,"serviceAreaBody");function D(i,e){const n=d(i,["reference_zone_public_ids","name"],e);return h(n.reference_zone_public_ids,e,"reference_zone_public_ids"),(typeof n.name!="string"||!n.name.trim())&&e.fail("INPUT","Informe o nome do agrupamento",422),n}c(D,"regionGroupBody");function I(i,e){const n=d(i,$,e);return typeof n.carrier_account_id!="string"&&e.fail("INPUT","Informe carrier_account_id",422),e.uuid(n.carrier_account_id),(typeof n.target_share_percent!="number"||!Number.isFinite(n.target_share_percent)||n.target_share_percent<=0||n.target_share_percent>100)&&e.fail("INPUT","target_share_percent deve estar entre 0 e 100",422),n.quality_weight!==void 0&&(typeof n.quality_weight!="number"||!Number.isFinite(n.quality_weight))&&e.fail("INPUT","quality_weight inv\xE1lido",422),n.priority_order!==void 0&&(!Number.isInteger(n.priority_order)||n.priority_order<0)&&e.fail("INPUT","priority_order inv\xE1lido",422),n.allocation_enabled!==void 0&&typeof n.allocation_enabled!="boolean"&&e.fail("INPUT","allocation_enabled deve ser booleano",422),n}c(I,"carrierShare");function T(i,e){const n=new URLSearchParams;for(const a of i){const r=a.indexOf("="),t=a.slice(0,r),s=a.slice(r+1);(r<1||!v.includes(t)||!s||n.has(t))&&e.fail("USAGE",`Filtro n\xE3o suportado: ${a}`,422),t==="limit"&&(!/^\d+$/.test(s)||Number(s)<1||Number(s)>100)&&e.fail("INPUT","limit inv\xE1lido",422),t==="include_geometry"&&!["true","false"].includes(s)&&e.fail("INPUT","include_geometry inv\xE1lido",422),n.set(t,s)}return n.size?`?${n}`:""}c(T,"referenceZoneQuery");function U(i,e,n=["account_type","search","limit","cursor"]){const a=new URLSearchParams;for(const r of i){const t=r.indexOf("="),s=r.slice(0,t),u=r.slice(t+1);(t<1||!n.includes(s)||!u||a.has(s))&&e.fail("USAGE",`Filtro n\xE3o suportado: ${r}`,422),s==="account_type"&&!["shipper","carrier"].includes(u)&&e.fail("INPUT","account_type inv\xE1lido",422),s==="limit"&&(!/^\d+$/.test(u)||Number(u)<1||Number(u)>100)&&e.fail("INPUT","limit inv\xE1lido",422),a.set(s,u)}return a.size?`?${a}`:""}c(U,"filters");async function G(i,e){const[,,n,a]=i;if(n==="onboarding-users"&&a==="list"&&i.length>=4)return e.api("GET",`/admin/business-accounts/onboarding-users${U(i.slice(4),e,["search","limit","cursor"])}`);if(n==="list")return e.api("GET",`/admin/business-accounts${U(i.slice(3),e)}`);if(n==="show"&&i.length===4)return e.api("GET",`/admin/business-accounts/${e.uuid(a)}`);if(n==="consult"&&i.length===3)return e.api("POST","/admin/business-accounts/company/consult",j(await e.input(),e));if(n==="onboard-existing-user"&&i.length===3){const r=z(await e.input(),e);return await e.confirm(`Cadastrar usu\xE1rio existente ${r.user_id} na empresa CNPJ ${r.cnpj}? E-mail opcional: ${r.send_email?"ativado (send_email=true)":"desativado (send_email=false)"}.`,r.user_id),e.api("POST","/admin/business-accounts/onboard-existing-user",r)}if(n==="create"&&i.length===3){const r=d(await e.input(),b,e);return(typeof r.legal_name!="string"||!r.legal_name.trim()||!(r.document||r.issuing_country_code&&r.legal_identifier))&&e.fail("INPUT","Informe legal_name e identifica\xE7\xE3o legal",422),await e.confirm(`Criar empresa ${r.legal_name}?`,r.legal_name),e.api("POST","/admin/business-accounts",{business_account:r})}if(a==="update"&&i.length===4){const r=e.uuid(n),t=d(await e.input(),b,e);return await e.confirm(`Alterar empresa ${r}? Campos: ${Object.keys(t).join(", ")}`,r),e.api("PUT",`/admin/business-accounts/${r}`,{business_account:t})}e.fail("USAGE","Use: genne admin companies list|show ID|consult|onboarding-users list|onboard-existing-user|create|ID update",2)}c(G,"company");async function k(i,e){const[,n,a,,r,t,s]=i,u=e.uuid(a),f=n==="carriers",m=`/admin/${n}/${u}/bases`,p=`/admin/${f?"carrier-bases":"bases"}`;if(r==="list"&&i.length===5)return e.api("GET",m);if(r==="create"&&i.length===5){const o=d(await e.input(),f?w:[...w,"balancing_policy","metadata"],e);return(!o.market_id||!/^[A-Z]{2}$/.test(o.country_code??"")||!/^[A-Z]{3}$/.test(o.currency_code??"")||[o.timezone,o.code,o.name].some(_=>typeof _!="string"||!_.trim()))&&e.fail("INPUT","Informe market_id, country_code, currency_code, timezone, code e name",422),e.uuid(o.market_id),await e.confirm(`Criar base ${o.name} em ${u}?`,u),e.api("POST",m,o)}if(r&&t&&i.length>=6){const o=e.uuid(r),_=s==="list"&&i.length===7&&(t==="zone-overrides"||!f&&t==="carrier-shares");if(_||["update","deactivate","reactivate","cutoff-windows","service-area","region-groups"].includes(t)){const l=await e.api("GET",m);(!Array.isArray(l?.data)||!l.data.some(E=>E.id===o))&&e.fail("INPUT","Base n\xE3o pertence \xE0 empresa informada",422)}if(_)return e.api("GET",`${p}/${o}/${t}`);if(t==="update"&&i.length===6){const l=d(await e.input(),f?y:N,e);return await e.confirm(`Alterar base ${o}? Campos: ${Object.keys(l).join(", ")}`,o),e.api("PUT",`${p}/${o}`,l)}if(["deactivate","reactivate"].includes(t)&&i.length===6)return await e.confirm(`${t==="deactivate"?"Desativar":"Reativar"} base ${o}?`,o),e.api("POST",`${p}/${o}/${t}`);if(t==="cutoff-windows"&&["set","update"].includes(s)&&i.length===7){const l=O(await e.input(),e);return await e.confirm(`Substituir hor\xE1rios de corte da base ${o}?`,o),e.api("PUT",`${p}/${o}/cutoff-windows`,l)}if(t==="service-area"&&s==="set"&&i.length===7){const l=C(await e.input(),e);return await e.confirm(`Substituir a cobertura da base ${o}?`,o),e.api("POST",`${p}/${o}/service-area`,l)}if(t==="service-area"&&s==="clear"&&i.length===7)return await e.confirm(`Remover a cobertura da base ${o}?`,o),e.api("DELETE",`${p}/${o}/service-area`);if(t==="region-groups"&&s==="create"&&i.length===7){const l=D(await e.input(),e);return await e.confirm(`Criar agrupamento de regi\xF5es na base ${o}?`,o),e.api("POST",`${p}/${o}/region-groups`,l)}}e.fail("USAGE","Use: genne admin shippers|carriers ACCOUNT_ID bases list|create|BASE_ID update|deactivate|reactivate|cutoff-windows set|service-area set|clear|region-groups create|zone-overrides list (ambos)|carrier-shares list (shipper)",2)}c(k,"bases");async function R(i,e){if(i[0]!=="shipper"||i[1]!=="bases")return null;e.requireRole("shipper");const n=i[2],a="/shipper/bases";if(n==="list"&&i.length===3)return e.api("GET",a);if(n==="create"&&i.length===3){const u=d(await e.input(),A,e);return[u.code,u.name].some(f=>typeof f!="string"||!f.trim())&&e.fail("INPUT","Informe code e name",422),await e.confirm(`Criar base ${u.name} na conta ativa?`,"shipper-bases"),e.api("POST",a,u)}const r=n?e.uuid(n):null,t=i[3];(!r||i.length<5)&&e.fail("USAGE","Use: genne shipper bases list|create|BASE_ID carrier-shares list|create|replace|reference-zone-features list|merge",2);const s=`${a}/${r}`;if(t==="carrier-shares"&&i[4]==="list"&&i.length===5)return e.api("GET",`${s}/carrier-shares`);if(t==="carrier-shares"&&i[4]==="create"&&i.length===5){const u=I(await e.input(),e);return await e.confirm(`Adicionar share de carrier \xE0 base ${r}?`,r),e.api("POST",`${s}/carrier-shares`,u)}if(t==="carrier-shares"&&i[4]==="replace"&&i.length===5){const u=d(await e.input(),["carriers"],e),f=g(u.carriers,$,e,"carriers").map(m=>I(m,e));return await e.confirm(`Substituir os shares de carriers da base ${r}?`,r),e.api("PUT",`${s}/carrier-shares`,{carriers:f})}if(t==="reference-zone-features"&&i[4]==="list"&&i.length>=5)return e.api("GET",`${s}/reference-zone-features${T(i.slice(5),e)}`);if(t==="reference-zone-features"&&i[4]==="merge"&&i.length===5){const u=d(await e.input(),["reference_zone_public_ids","code","name"],e);return h(u.reference_zone_public_ids,e,"reference_zone_public_ids"),await e.confirm(`Mesclar zonas de refer\xEAncia na base ${r}?`,r),e.api("POST",`${s}/reference-zone-features/merge`,u)}e.fail("USAGE","Use: genne shipper bases list|create|BASE_ID carrier-shares list|create|replace|reference-zone-features list|merge",2)}c(R,"selfBases");async function L(i,e){if(i[0]==="carrier"&&i[1]==="bases"&&i[3]==="reference-zone-features"&&i[4]==="list"&&i.length>=5){e.requireRole("carrier");const a=e.uuid(i[2]);return e.api("GET",`/carrier/carrier-bases/${a}/reference-zone-features${T(i.slice(5),e)}`)}const n=await R(i,e);if(n!==null)return n;if(i[0]==="company"&&i[1]==="my-account"){["shipper","carrier"].includes(e.user?.role)||e.fail("HTTP_403","Acesso negado",403);const a=`/${e.user.role}/business-account`;if(i[2]==="show"&&i.length===3)return e.api("GET",a);if(i[2]==="update"&&i.length===3){const r=d(await e.input(),S,e);return await e.confirm(`Alterar empresa ativa? Campos: ${Object.keys(r).join(", ")}`,"my-account"),e.api("PUT",a,{business_account:r})}e.fail("USAGE","Use: genne company my-account show|update",2)}return i[0]!=="admin"||!["companies","shippers","carriers"].includes(i[1])?null:(e.requireRole("admin"),i[1]==="companies"?G(i,e):i[3]==="bases"&&i.length>=5?k(i,e):null)}c(L,"companiesCommand");export{L as companiesCommand};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var E=Object.defineProperty;var l=(i,e)=>E(i,"name",{value:e,configurable:!0});import{readFile as k,stat as $}from"node:fs/promises";import{basename as _,extname as S}from"node:path";const I=new Set(["pt-BR","en","es"]),g=/^[a-z0-9]+(?:-[a-z0-9]+)*$/,C=new Set(["id","public_id","translation_group_id","published_at","inserted_at","updated_at","available_locales"]),w=new Set(["locale","slug","title","description","author","category","tags","cover_image_url","content_markdown","content_html","published"]),N=new Set(["locale","question","answer","category","tags","paths","route_keys","featured_on_home","position","published"]);function m(i,e){return i===void 0?!1:(typeof i!="boolean"&&e.fail("INPUT","published deve ser booleano",422),i)}l(m,"published");function T(i,e){return(i.length!==1||!i[0].startsWith("key=")||i[0].length<20||i[0].length>259)&&e.fail("USAGE","Informe key=CHAVE est\xE1vel de 16 a 255 caracteres",2),i[0].slice(4)}l(T,"createKey");function P(i,e){const o=i?.startsWith("version=")?i.slice(8):"";return(!/^[1-9][0-9]*$/.test(o)||!Number.isSafeInteger(Number(o)))&&e.fail("USAGE","Informe version=N retornada pelo Admin",2),Number(o)}l(P,"expectedVersion");function U(i,e,o){const n=p(i,e);h(n,e),c(n,o,e),Object.keys(n).length===0&&e.fail("INPUT","Informe ao menos um campo para alterar",422);const t={};for(const[a,s]of Object.entries(n))a==="published"?t[a]=m(s,e):a==="locale"?t[a]=b(s,e):["tags","paths","route_keys"].includes(a)?t[a]=d(s,a,e):a==="featured_on_home"?(typeof s!="boolean"&&e.fail("INPUT","featured_on_home deve ser booleano",422),t[a]=s):a==="position"?((!Number.isInteger(s)||s<0)&&e.fail("INPUT","position deve ser inteiro n\xE3o negativo",422),t[a]=s):(t[a]=u(s,a,e),a==="slug"&&!g.test(t[a])&&e.fail("INPUT","slug inv\xE1lido",422),a==="question"&&t[a].length<5&&e.fail("INPUT","question deve ter ao menos 5 caracteres",422),a==="answer"&&t[a].length<5&&e.fail("INPUT","answer deve ter ao menos 5 caracteres",422));return t}l(U,"partialInput");function v(i,e,o){const n=p(i,e);(!Number.isSafeInteger(n.version)||n.version<1)&&e.fail("INPUT","version deve ser inteiro positivo retornado pelo Admin",422);const{version:t,...a}=n;return{version:t,payload:o(a,e)}}l(v,"versionedInput");function A(i,e,o){const n=new URLSearchParams;for(const t of i){const a=t.indexOf("="),s=t.slice(0,a),r=t.slice(a+1);(a<1||!e.includes(s)||!r||n.has(s))&&o.fail("USAGE",`Filtro n\xE3o suportado: ${t}`,422),s==="locale"&&!I.has(r)&&o.fail("INPUT","Locale inv\xE1lido",422),s==="translation_group_id"&&o.uuid(r),s==="featured"&&!["true","false"].includes(r)&&o.fail("INPUT","Featured deve ser true ou false",422),n.set(s,r)}return n.size?`?${n}`:""}l(A,"query");function p(i,e){return(!i||typeof i!="object"||Array.isArray(i))&&e.fail("INPUT","A entrada deve ser um objeto JSON",422),i}l(p,"parseObject");function h(i,e){const o=Object.keys(i).find(n=>C.has(n));o&&e.fail("INPUT",`Campo n\xE3o permitido: ${o}`,422)}l(h,"rejectForbidden");function c(i,e,o){const n=Object.keys(i).find(t=>!e.has(t));n&&o.fail("INPUT",`Campo n\xE3o permitido: ${n}`,422)}l(c,"rejectUnknown");function u(i,e,o){return(typeof i!="string"||!i.trim())&&o.fail("INPUT",`${e} \xE9 obrigat\xF3rio`,422),i.trim()}l(u,"requireText");function d(i,e,o){return(!Array.isArray(i)||i.some(n=>typeof n!="string"||!n.trim()))&&o.fail("INPUT",`${e} deve ser uma lista de textos`,422),i.map(n=>n.trim())}l(d,"requireStringList");function b(i,e){if(i!==void 0)return(typeof i!="string"||!I.has(i))&&e.fail("INPUT","Locale inv\xE1lido",422),i}l(b,"optionalLocale");function O(i,e){const o=p(i,e);h(o,e),c(o,w,e);const n={slug:u(o.slug,"slug",e),title:u(o.title,"title",e),description:u(o.description,"description",e),author:u(o.author,"author",e),category:u(o.category,"category",e),tags:o.tags===void 0?[]:d(o.tags,"tags",e),content_markdown:u(o.content_markdown,"content_markdown",e),content_html:u(o.content_html,"content_html",e),published:m(o.published,e)},t=b(o.locale,e);return t&&(n.locale=t),g.test(n.slug)||e.fail("INPUT","slug inv\xE1lido",422),o.cover_image_url!==void 0&&((typeof o.cover_image_url!="string"||!o.cover_image_url.trim())&&e.fail("INPUT","cover_image_url deve ser texto n\xE3o vazio",422),n.cover_image_url=o.cover_image_url.trim()),n}l(O,"blogCreateInput");function F(i,e){const o=p(i,e);h(o,e),c(o,N,e);const n=u(o.question,"question",e),t=u(o.answer,"answer",e);n.length<5&&e.fail("INPUT","question deve ter ao menos 5 caracteres",422),t.length<5&&e.fail("INPUT","answer deve ter ao menos 5 caracteres",422),o.featured_on_home!==void 0&&typeof o.featured_on_home!="boolean"&&e.fail("INPUT","featured_on_home deve ser booleano",422),o.position!==void 0&&(!Number.isInteger(o.position)||o.position<0)&&e.fail("INPUT","position deve ser inteiro maior ou igual a zero",422);const a=o.paths===void 0?[]:d(o.paths,"paths",e),s=o.route_keys===void 0?[]:d(o.route_keys,"route_keys",e);a.length===0&&s.length===0&&e.fail("INPUT","Informe ao menos um path ou route_key",422);const r={question:n,answer:t,category:u(o.category,"category",e),tags:o.tags===void 0?[]:d(o.tags,"tags",e),paths:a,published:m(o.published,e)},y=b(o.locale,e);return y&&(r.locale=y),s.length&&(r.route_keys=s),o.featured_on_home!==void 0&&(r.featured_on_home=o.featured_on_home),o.position!==void 0&&(r.position=o.position),r}l(F,"faqCreateInput");async function f(i,e,o,n){await i.confirm(`${e}
|
|
2
|
+
${JSON.stringify(o,null,2)}`,n)}l(f,"confirmCreate");function q(i,e){return(!i?.data?.id||typeof i.data.published!="boolean")&&e.fail("RESPONSE","Resposta inv\xE1lida da API"),i}l(q,"assertCreated");async function G(i,e){if(i[0]!=="admin"||!["blog","faqs"].includes(i[1]))return null;if(e.requireRole("admin"),i[1]==="blog"&&i[2]==="posts"&&i[3]==="list")return e.api("GET",`/admin/blog/posts${A(i.slice(4),["locale","translation_group_id"],e)}`);if(i[1]==="blog"&&i[2]==="posts"&&i[3]==="create"){const o=O(await e.input(),e),n=T(i.slice(4),e);return await f(e,"Criar post?",o,o.slug),q(await e.api("POST","/admin/blog/posts",o,{idempotencyKey:n}))}if(i[1]==="blog"&&i[2]==="posts"&&i[3]&&i[4]==="update"&&i.length===5){const o=e.uuid(i[3]),{version:n,payload:t}=v(await e.input(),e,(a,s)=>U(a,s,w));return await f(e,"Atualizar post?",t,o),e.api("PUT",`/admin/blog/posts/${o}`,t,{ifMatch:`"${n}"`})}if(i[1]==="blog"&&i[2]==="posts"&&i[3]&&["publish","unpublish"].includes(i[4])&&i.length===6){const o=e.uuid(i[3]),n=P(i[5],e),t={published:i[4]==="publish"};return await f(e,"Alterar publica\xE7\xE3o do post?",t,o),e.api("PUT",`/admin/blog/posts/${o}`,t,{ifMatch:`"${n}"`})}if(i[1]==="blog"&&i[2]==="posts"&&i[3]&&i[4]==="delete"&&i.length===5){const o=e.uuid(i[3]);return await e.confirm("Excluir post?",o),e.api("DELETE",`/admin/blog/posts/${o}`)}if(i[1]==="blog"&&i[2]==="images"&&i[3]==="upload"&&i.length===5){const o=i[4],n={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".gif":"image/gif"}[S(o).toLowerCase()];n||e.fail("INPUT","Imagem deve ser PNG, JPEG, WebP ou GIF",422);let t,a;try{t=await $(o),t.isFile()&&t.size<=8*1024*1024&&(a=await k(o))}catch{e.fail("INPUT","N\xE3o foi poss\xEDvel ler a imagem",422)}(!t.isFile()||t.size>8*1024*1024)&&e.fail("INPUT","Imagem inv\xE1lida ou maior que 8 MiB",422);const s=new FormData;return s.append("image",new Blob([a],{type:n}),_(o)),await e.confirm("Enviar imagem para o blog?",_(o)),e.api("POST","/admin/blog/uploads/images",s)}if(i[1]==="faqs"&&i[2]==="list")return e.api("GET",`/admin/faqs${A(i.slice(3),["path","route_key","locale","category","featured","q"],e)}`);if(i[1]==="faqs"&&i[2]==="create"){const o=F(await e.input(),e),n=T(i.slice(3),e);return await f(e,"Criar FAQ?",o,o.question),q(await e.api("POST","/admin/faqs",o,{idempotencyKey:n}))}if(i[1]==="faqs"&&i[2]&&i[3]==="update"&&i.length===4){const o=e.uuid(i[2]),{version:n,payload:t}=v(await e.input(),e,(a,s)=>U(a,s,N));return await f(e,"Atualizar FAQ?",t,o),e.api("PUT",`/admin/faqs/${o}`,t,{ifMatch:`"${n}"`})}if(i[1]==="faqs"&&i[2]&&["publish","unpublish"].includes(i[3])&&i.length===5){const o=e.uuid(i[2]),n=P(i[4],e),t={published:i[3]==="publish"};return await f(e,"Alterar publica\xE7\xE3o da FAQ?",t,o),e.api("PUT",`/admin/faqs/${o}`,t,{ifMatch:`"${n}"`})}if(i[1]==="faqs"&&i[2]&&i[3]==="delete"&&i.length===4){const o=e.uuid(i[2]);return await e.confirm("Excluir FAQ?",o),e.api("DELETE",`/admin/faqs/${o}`)}e.fail("USAGE","Use: genne admin blog posts list|create key=CHAVE|ID update|ID publish|unpublish version=N|ID delete | blog images upload PATH | faqs list|create key=CHAVE|ID update|ID publish|unpublish version=N|ID delete",2)}l(G,"contentCommand");export{G as contentCommand};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var A=Object.defineProperty;var d=(a,i)=>A(a,"name",{value:i,configurable:!0});const c=new Set(["novo","em_contato","qualificado","perdido","convertido"]);function w(a,i,e){const t=new URLSearchParams;for(const n of a){const l=n.indexOf("="),r=n.slice(0,l),s=n.slice(l+1);(l<1||!i.includes(r)||!s||t.has(r))&&e.fail("USAGE",`Filtro n\xE3o suportado: ${n}`,422),r==="status"&&!c.has(s)&&e.fail("INPUT","Status inv\xE1lido",422),r==="limit"&&(!/^\d+$/.test(s)||Number(s)<1||Number(s)>100)&&e.fail("INPUT","Limit deve estar entre 1 e 100",422),t.set(r,s)}return t.size?`?${t}`:""}d(w,"query");function f(a){if(!a||typeof a!="object")return a;const{assigned_admin_id:i,...e}=a;return e}d(f,"projectLead");function _(a){if(!a||typeof a!="object")return a;const{admin_id:i,metadata:e,...t}=a;return t}d(_,"projectEvent");function o(a){if(!a?.data||typeof a.data!="object")return a;const i=a.data;return i.lead?{...a,data:{lead:f(i.lead),events:Array.isArray(i.events)?i.events.map(_):i.events}}:i.id?{...a,data:f(i)}:a}d(o,"projectLeadDetail");function S(a){return a?.data?Array.isArray(a.data)?{...a,data:a.data.map(f)}:o(a):a}d(S,"projectLeadList");async function m(a,i){const e=await i.api("GET",`/admin/crm/leads/${a}`);return(e?.data?.lead?.id!==a||!Array.isArray(e?.data?.events))&&i.fail("RESPONSE","Resposta inv\xE1lida da API"),e}d(m,"loadLead");async function y(a){const i=await a.api("GET","/admin/crm/assignees");return(!Array.isArray(i?.data)||i.data.some(e=>!e||typeof e.id!="string"||typeof e.name!="string"||typeof e.email!="string"))&&a.fail("RESPONSE","Resposta inv\xE1lida da API"),i.data.map(({id:e,name:t,email:n})=>({id:e,name:t,email:n}))}d(y,"loadAssignees");async function g(a,i){if(a[0]!=="admin"||a[1]!=="crm")return null;if(i.requireRole("admin"),a[2]==="assignees"&&a.length===3)return{data:await y(i)};if(a[2]!=="leads")return null;if(a[3]==="list"){const t=await i.api("GET",`/admin/crm/leads${w(a.slice(4),["status","search","limit","cursor"],i)}`);return S(t)}if(a[3]==="show"&&a.length===5){const t=i.uuid(a[4]);return o(await i.api("GET",`/admin/crm/leads/${t}`))}const e=i.uuid(a[3]);if(a[4]==="status"&&a.length===5){const t=await i.input();(!t||typeof t!="object"||Array.isArray(t)||Object.keys(t).length!==1||typeof t.new_status!="string"||!c.has(t.new_status))&&i.fail("INPUT","Informe somente new_status com um status v\xE1lido em JSON pelo stdin",422);const n=await m(e,i);return await i.confirm(`Alterar status do lead ${e}?
|
|
2
|
+
${JSON.stringify({lead:o(n).data,new_status:t.new_status},null,2)}`,e),await i.api("PATCH",`/admin/crm/leads/${e}/status`,t),o(await i.api("GET",`/admin/crm/leads/${e}`))}if(a[4]==="note"&&a.length===5){const t=await i.input();(!t||typeof t!="object"||Array.isArray(t)||Object.keys(t).length!==1||typeof t.note!="string"||!t.note.trim()||t.note.length>2e3)&&i.fail("INPUT","Informe somente note com 1 a 2000 caracteres em JSON pelo stdin",422);const n=await m(e,i);return await i.confirm(`Adicionar nota ao lead ${e}?
|
|
3
|
+
${JSON.stringify({lead:o(n).data,note:t.note},null,2)}`,e),await i.api("POST",`/admin/crm/leads/${e}/notes`,t),o(await i.api("GET",`/admin/crm/leads/${e}`))}if(a[4]==="assign"&&a.length===5){const t=await i.input();(!t||typeof t!="object"||Array.isArray(t)||Object.keys(t).length!==1||typeof t.admin_id!="string")&&i.fail("INPUT","Informe somente admin_id como UUID em JSON pelo stdin",422);const n=i.uuid(t.admin_id),[l,r]=await Promise.all([m(e,i),y(i)]),s=r.find(p=>p?.id===n);s||i.fail("INPUT","admin_id n\xE3o est\xE1 entre os respons\xE1veis eleg\xEDveis",422),await i.confirm(`Atribuir lead ${e}?
|
|
4
|
+
${JSON.stringify({lead:o(l).data,assignee:s},null,2)}`,e);const u=await i.api("PATCH",`/admin/crm/leads/${e}/assign`,{admin_id:n});return(u?.data?.id!==e||u?.data?.assigned_admin_id!==n)&&i.fail("RESPONSE","Resposta inv\xE1lida da API"),u}i.fail("USAGE","Use: genne admin crm assignees | leads list|show LEAD_ID|LEAD_ID status|note|assign",2)}d(g,"crmCommand");export{g as crmCommand};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var v=Object.defineProperty;var o=(e,i)=>v(e,"name",{value:i,configurable:!0});import{jsonBody as l,requireRole as w}from"./operational-input.js";const u=/^[A-Za-z0-9._:-]+$/,I=["flow_id","connection_id","base_id","trigger","trigger_key","schedule","timezone","enabled","policies"],A=["enabled","schedule","timezone","policies","flow_version_id","expected_current_version_id"],T=["execution_id","device_id","records","next_cursor","checkpoint","idempotency_key"],y=["external_id","external_base_code","order_id","external_shipper_label","provider_status","provider_status_label","provider_failure_reason","provider_failure_reason_label","order_requested_at","destination_address","postal_code","destination_complement","packages_count","nfe_key","recipient_doc","recipient_name","recipient_phone","recipient_email","metadata"],c=["run_id","base_id","flow_version","page_url","instruction","fields","snapshot"];function a(e,i){e.fail("INPUT",i,422)}o(a,"fail");function d(e,i,n){if(e!==void 0)return e===null&&a(n,`${i} n\xE3o pode ser nulo`),n.uuid(e),e}o(d,"uuid");function t(e,i,n,{nullable:r=!1,max:s}={}){return e===void 0||r&&e===null||((typeof e!="string"||!r&&!e.trim())&&a(n,`${i} inv\xE1lido`),s!==void 0&&e.length>s&&a(n,`${i} excede o limite do contrato`)),e}o(t,"optionalString");function _(e,i,n,{nullable:r=!1}={}){return e===void 0||r&&e===null||(!e||typeof e!="object"||Array.isArray(e))&&a(n,`${i} deve ser um objeto`),e}o(_,"optionalObject");function m(e,i,n){return e!==void 0&&typeof e!="boolean"&&a(n,`${i} deve ser booleano`),e}o(m,"optionalBoolean");function f(e,i,n,{nullable:r=!1}={}){return e===void 0||r&&e===null?e:d(e,i,n)}o(f,"optionalUuid");function g(e,i){return(typeof e!="string"||e.length<16||e.length>128||!u.test(e))&&a(i,"idempotency_key deve ter de 16 a 128 caracteres"),e}o(g,"idempotencyKey");function E(e,i){return(typeof e!="string"||e.length<8||e.length>128||!u.test(e))&&a(i,"device_id deve ter de 8 a 128 caracteres alfanum\xE9ricos"),e}o(E,"deviceId");function h(e,i){return e===void 0||(!Array.isArray(e)||e.length<1||e.length>5||e.some(n=>!n||typeof n!="object"||Array.isArray(n)))&&a(i,"policies deve conter de 1 a 5 objetos"),e}o(h,"policies");function S(e,i){d(e.flow_id,"flow_id",i),f(e.connection_id,"connection_id",i),f(e.base_id,"base_id",i,{nullable:!0}),t(e.trigger,"trigger",i),t(e.trigger_key,"trigger_key",i,{nullable:!0,max:100}),_(e.schedule,"schedule",i),t(e.timezone,"timezone",i),m(e.enabled,"enabled",i),h(e.policies,i)}o(S,"validateFlowInstallationCreate");function D(e,i){Object.keys(e).length===0&&a(i,"Informe ao menos um campo para atualizar"),m(e.enabled,"enabled",i),_(e.schedule,"schedule",i),t(e.timezone,"timezone",i),h(e.policies,i),f(e.flow_version_id,"flow_version_id",i),f(e.expected_current_version_id,"expected_current_version_id",i)}o(D,"validateFlowInstallationUpdate");function $(e,i){_(e,"input",i);for(const[n,r]of Object.entries(e))(!/^[^\n\r]{1,255}$/.test(n)||typeof r!="string"||r.length>500)&&a(i,"input deve conter apenas valores string de at\xE9 500 caracteres");return e}o($,"validateRunInput");function O(e,i){(!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).some(n=>!y.includes(n)))&&a(i,"records cont\xE9m um item inv\xE1lido"),(e.external_id===void 0||e.order_requested_at===void 0||e.destination_address===void 0||e.packages_count===void 0)&&a(i,"cada record deve conter external_id, order_requested_at, destination_address e packages_count"),t(e.external_id,"external_id",i,{max:255}),t(e.external_base_code,"external_base_code",i,{max:255}),t(e.order_id,"order_id",i,{max:255}),t(e.external_shipper_label,"external_shipper_label",i,{max:255}),e.provider_status!==void 0&&!(typeof e.provider_status=="string"&&e.provider_status.length>0&&e.provider_status.length<=255||Number.isInteger(e.provider_status))&&a(i,"provider_status inv\xE1lido"),t(e.provider_status_label,"provider_status_label",i,{max:255}),e.provider_failure_reason!==void 0&&!(typeof e.provider_failure_reason=="string"&&e.provider_failure_reason.length>0&&e.provider_failure_reason.length<=255||Number.isInteger(e.provider_failure_reason))&&a(i,"provider_failure_reason inv\xE1lido"),t(e.provider_failure_reason_label,"provider_failure_reason_label",i,{max:255}),t(e.order_requested_at,"order_requested_at",i,{max:100}),t(e.destination_address,"destination_address",i,{max:2e3}),t(e.postal_code,"postal_code",i,{max:30}),t(e.destination_complement,"destination_complement",i,{max:255}),(!Number.isInteger(e.packages_count)||e.packages_count<1||e.packages_count>1e5)&&a(i,"packages_count deve ser um inteiro positivo"),t(e.nfe_key,"nfe_key",i,{max:255}),t(e.recipient_doc,"recipient_doc",i,{max:255}),t(e.recipient_name,"recipient_name",i,{max:255}),t(e.recipient_phone,"recipient_phone",i,{max:255}),t(e.recipient_email,"recipient_email",i,{max:255}),_(e.metadata,"metadata",i)}o(O,"validateImportRecord");function R(e,i){d(e.execution_id,"execution_id",i),E(e.device_id,i),(!Array.isArray(e.records)||e.records.length>100)&&a(i,"records deve conter de 0 a 100 itens");for(const n of e.records)O(n,i);return t(e.next_cursor,"next_cursor",i),_(e.checkpoint,"checkpoint",i),e}o(R,"validateOrderImport");function U(e,i){d(e.run_id,"run_id",i),d(e.base_id,"base_id",i),e.flow_version!==1&&a(i,"flow_version deve ser 1"),t(e.page_url,"page_url",i,{max:2e3});try{const n=new URL(e.page_url);["http:","https:"].includes(n.protocol)||a(i,"page_url deve usar HTTP ou HTTPS")}catch{a(i,"page_url inv\xE1lida")}t(e.instruction,"instruction",i,{max:500}),(!Array.isArray(e.fields)||e.fields.length<1||e.fields.length>20||new Set(e.fields).size!==e.fields.length||e.fields.some(n=>typeof n!="string"||!/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(n)))&&a(i,"fields deve conter de 1 a 20 nomes \xFAnicos v\xE1lidos"),t(e.snapshot,"snapshot",i,{max:8e3})}o(U,"validateExtraction");function N(e){return e[0]==="providers"?e[1]==="list"||e[2]==="activate":e[0]==="grants"&&e[2]==="flow-installations"?["list","create"].includes(e[3]):e[0]==="flow-installations"?["update","run"].includes(e[2]):e[0]==="grants"&&e[2]==="order-import-batches"?e[3]==="create":e[0]==="grants"&&e[2]==="extractions"?e[3]==="preview":!1}o(N,"isNewDesktopCommand");async function z(e,i){if(e[0]!=="carrier"||e[1]!=="desktop")return null;const n=e.slice(2);if(!N(n))return null;if(w(i,["carrier"]),n[0]==="providers"&&n[1]==="list")return n.length!==2&&i.fail("USAGE","Use: carrier desktop providers list",2),i.api("GET","/carrier/desktop/providers");if(n[0]==="providers"&&n[2]==="activate"){n.length!==3&&i.fail("USAGE","Use: carrier desktop providers PROVIDER_ID activate",2);const r=d(n[1],"provider_id",i);return await i.confirm(`Ativar o provider Desktop ${r}?`,r),i.api("PUT",`/carrier/desktop/providers/${r}/activation`,{})}if(n[0]==="grants"&&n[2]==="flow-installations"){(!["list","create"].includes(n[3])||n.length!==4)&&i.fail("USAGE","Use: carrier desktop grants GRANT_ID flow-installations list|create",2);const r=d(n[1],"grant_id",i);if(n[3]==="list")return i.api("GET",`/carrier/desktop/provider-grants/${r}/flow-installations`);const s=l(await i.input(),I,["flow_id"],i);return S(s,i),await i.confirm(`Criar instala\xE7\xE3o Desktop no grant ${r}?`,r),i.api("POST",`/carrier/desktop/provider-grants/${r}/flow-installations`,s,{withStatus:!0})}if(n[0]==="flow-installations"){n.length!==3&&i.fail("USAGE","Use: carrier desktop flow-installations INSTALLATION_ID update|run",2);const r=d(n[1],"installation_id",i);if(n[2]==="update"){const p=l(await i.input(),A,[],i);return D(p,i),await i.confirm(`Atualizar a instala\xE7\xE3o Desktop ${r}?`,r),i.api("PATCH",`/carrier/desktop/flow-installations/${r}`,p)}const s=l(await i.input(),["idempotency_key","input"],["idempotency_key","input"],i);return g(s.idempotency_key,i),$(s.input,i),await i.confirm(`Executar a instala\xE7\xE3o Desktop ${r}?`,r),i.api("POST",`/carrier/desktop/flow-installations/${r}/runs`,s,{withStatus:!0})}if(n[0]==="grants"&&n[2]==="order-import-batches"){(n.length!==4||n[3]!=="create")&&i.fail("USAGE","Use: carrier desktop grants GRANT_ID order-import-batches create",2);const r=d(n[1],"grant_id",i),s=l(await i.input(),T,["execution_id","device_id","records","idempotency_key"],i);R(s,i);const p=g(s.idempotency_key,i),{idempotency_key:b,...k}=s;return await i.confirm(`Importar ${s.records.length} pedido(s) no grant Desktop ${r}?`,r),i.api("POST",`/carrier/desktop/provider-grants/${r}/order-import-batches`,k,{idempotencyKey:p,withStatus:!0})}if(n[0]==="grants"&&n[2]==="extractions"){(n.length!==4||n[3]!=="preview")&&i.fail("USAGE","Use: carrier desktop grants GRANT_ID extractions preview",2);const r=d(n[1],"grant_id",i),s=l(await i.input(),c,c,i);return U(s,i),await i.confirm(`Executar pr\xE9via de extra\xE7\xE3o Desktop no grant ${r}?`,r),i.api("POST",`/carrier/desktop/provider-grants/${r}/extractions`,s)}}o(z,"desktopIntegrationsCommand");export{z as desktopIntegrationsCommand};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var U=Object.defineProperty;var p=(n,e)=>U(n,"name",{value:e,configurable:!0});import{appendLocalFile as S}from"../local-file.js";import{jsonBody as c,query as O,requireRole as k}from"./operational-input.js";const I=["id","sequence","type","name","address","lat","lng","is_fixed","stop_kind","reference_kind","reference_base_id","order_constraint","service_duration_seconds","packages_count","notes"],A=["name","return_to_start","driver_payment_profile_public_id","waypoints"],E=["locations","waypoints","optimize","optimize_start","optimize_end","return_to_start","current_location","vehicle_type"],z=["waypoint_id","lat","lng","address","type","is_fixed","stop_kind","reference_kind","reference_base_id","order_constraint"],F={".csv":"text/csv",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".pdf":"application/pdf"},R=50*1024*1024;function T(n,e){(!Array.isArray(n)||n.length<2||n.length>500)&&e.fail("INPUT","A rota exige de 2 a 500 waypoints",422);const i=n.filter(o=>!Number.isFinite(o.lat)||!Number.isFinite(o.lng)||Math.abs(o.lat)>90||Math.abs(o.lng)>180);return{valid:i.length===0,missing:i.map(o=>({id:o.id??null,address:o.address??null}))}}p(T,"coordinates");function v(n,e){const i=c(n,I,["type"],e);return i.id&&e.uuid(i.id),i}p(v,"waypointInput");function M(n,e){if(typeof n=="string")return n.trim()||e.fail("INPUT","Cada localiza\xE7\xE3o deve ter um endere\xE7o",422),n;const i=c(n,z,[],e),o=typeof i.address=="string"&&i.address.trim().length>0,t=Number.isFinite(i.lat)&&Number.isFinite(i.lng)&&Math.abs(i.lat)<=90&&Math.abs(i.lng)<=180;return!o&&!t&&e.fail("INPUT","Cada localiza\xE7\xE3o exige address ou lat/lng v\xE1lidos",422),i.lat!==void 0&&(!Number.isFinite(i.lat)||Math.abs(i.lat)>90)&&e.fail("INPUT","lat inv\xE1lido",422),i.lng!==void 0&&(!Number.isFinite(i.lng)||Math.abs(i.lng)>180)&&e.fail("INPUT","lng inv\xE1lido",422),i.type!==void 0&&!["start","end","stop"].includes(i.type)&&e.fail("INPUT","type inv\xE1lido",422),i.is_fixed!==void 0&&typeof i.is_fixed!="boolean"&&e.fail("INPUT","is_fixed deve ser booleano",422),i.stop_kind!==void 0&&i.stop_kind!==null&&!["delivery","pickup","pause"].includes(i.stop_kind)&&e.fail("INPUT","stop_kind inv\xE1lido",422),i.reference_kind!==void 0&&i.reference_kind!==null&&!["base","current_location","custom"].includes(i.reference_kind)&&e.fail("INPUT","reference_kind inv\xE1lido",422),i.reference_base_id!==void 0&&i.reference_base_id!==null&&e.uuid(i.reference_base_id),i.order_constraint!==void 0&&i.order_constraint!==null&&!["auto","first","last"].includes(i.order_constraint)&&e.fail("INPUT","order_constraint inv\xE1lido",422),i}p(M,"planningLocationInput");function j(n,e){const i=c(n,E,[],e),o=["locations","waypoints"].filter(l=>i[l]!==void 0);o.length!==1&&e.fail("INPUT","Informe locations ou waypoints, mas n\xE3o os dois",422);const t=i[o[0]];(!Array.isArray(t)||t.length<2||t.length>500)&&e.fail("INPUT","O planejamento exige de 2 a 500 localiza\xE7\xF5es",422),i[o[0]]=t.map(l=>M(l,e));for(const l of["optimize","optimize_start","optimize_end","return_to_start"])i[l]!==void 0&&typeof i[l]!="boolean"&&e.fail("INPUT",`${l} deve ser booleano`,422);if(i.optimize===void 0&&(i.optimize=!1),i.current_location!==void 0){const l=c(i.current_location,["lat","lng"],["lat","lng"],e);(!Number.isFinite(l.lat)||Math.abs(l.lat)>90||!Number.isFinite(l.lng)||Math.abs(l.lng)>180)&&e.fail("INPUT","current_location inv\xE1lida",422)}return i.vehicle_type!==void 0&&!["car","motorcycle","bicycle"].includes(i.vehicle_type)&&e.fail("INPUT","vehicle_type inv\xE1lido",422),i}p(j,"planInput");function C(n,e){const i={};for(const o of n){const t=o.indexOf("="),l=t>0?o.slice(0,t):"",f=t>0?o.slice(t+1):"";if((!f||!["column_mapping","persist_order_id_choice"].includes(l)||i[l]!==void 0)&&e.fail("USAGE",`Op\xE7\xE3o de importa\xE7\xE3o n\xE3o suportada: ${o}`,2),l==="persist_order_id_choice"){["true","false"].includes(f)||e.fail("INPUT","persist_order_id_choice deve ser true ou false",422),i[l]=f==="true";continue}let r;try{r=JSON.parse(f)}catch{e.fail("INPUT","column_mapping deve ser JSON",422)}(!r||typeof r!="object"||Array.isArray(r))&&e.fail("INPUT","column_mapping deve ser um objeto",422),i[l]=r}return i}p(C,"importOptions");async function G(n,e,i){const o=new FormData;await S(o,"file",n,F,R,i);for(const[t,l]of Object.entries(C(e,i)))if(t==="column_mapping")for(const[f,r]of Object.entries(l))(!f||typeof r!="string"||!r.trim())&&i.fail("INPUT","column_mapping deve mapear nomes para colunas n\xE3o vazias",422),o.append(`column_mapping[${f}]`,r);else o.append(t,String(l));return i.api("POST","/routes/imports/preview",o)}p(G,"importPreview");async function y(n,e){const i=await e.api("GET",`/routes/${n}`);return(!i.data||!Array.isArray(i.data.waypoints))&&e.fail("RESPONSE","Rota sem waypoints v\xE1lidos"),i.data}p(y,"ownedRoute");function L(n){return n.waypoints.map(e=>({waypoint_id:e.id,lat:e.lat,lng:e.lng,address:e.address,type:e.type,stop_kind:e.stop_kind,reference_kind:e.reference_kind,reference_base_id:e.reference_base_id,order_constraint:e.order_constraint,service_duration_seconds:e.service_duration_seconds}))}p(L,"locations");function N(n,e){const i=c(n,["status","failure_reason_code","failure_observation","failure_authorization_code"],["status"],e);return["pending","delivered","failed"].includes(i.status)||e.fail("INPUT","status inv\xE1lido",422),i.status==="failed"&&(!i.failure_reason_code||!i.failure_observation)&&e.fail("INPUT","Falha exige failure_reason_code e failure_observation",422),i.status!=="failed"&&Object.keys(i).some(o=>o.startsWith("failure_"))&&e.fail("INPUT","Dados de falha s\xF3 s\xE3o aceitos para failed",422),i}p(N,"statusInput");async function W(n,e){if(n[0]!=="driver"||!["routes","places","optimization","calculation"].includes(n[1]))return null;k(e,["driver"]);const[,i,o,t,l,f]=n;if(i==="places"){if(o==="search"&&n.length===4&&t)return e.api("GET",`/places/search?q=${encodeURIComponent(t)}`);if(o==="details"&&n.length===4&&t)return e.api("GET",`/places/details/${encodeURIComponent(t)}`)}if(["optimization","calculation"].includes(i)&&t==="show"&&n.length===4)return e.api("GET",`/${i==="optimization"?"optimize":"calculate"}/${e.uuid(o)}`);if(i!=="routes"&&e.fail("USAGE","Use genne help driver",2),o==="plan"&&t==="create"&&n.length===4){const s=j(await e.input(),e);return await e.confirm("Planejar rota?","plan"),e.api("POST","/plan",s,{withStatus:!0})}if(o==="plan"&&l==="show"&&n.length===5)return e.api("GET",`/plan/${e.uuid(t)}`);if(o==="imports"&&t==="preview"&&n.length>=5){const[s,...u]=n.slice(4);return G(s,u,e)}if(o==="list")return e.api("GET",`/routes${O(n.slice(3),["limit","cursor","status","is_favorite","date_from","date_to","search"],e)}`);if(o==="draft"&&t==="create"&&n.length===4){const s=c(await e.input(),A,["name","waypoints"],e);return(!Array.isArray(s.waypoints)||s.waypoints.length<1)&&e.fail("INPUT","Informe waypoints",422),s.waypoints=s.waypoints.map(u=>v(u,e)),await e.confirm(`Criar draft ${s.name}?`,s.name),e.api("POST","/routes",{route:s},{withStatus:!0})}const r=e.uuid(o),_=`/routes/${r}`;if(t==="show"&&n.length===4)return e.api("GET",_);if(t==="coordinates"&&l==="validate"&&n.length===5){const s=await y(r,e);return{data:T(s.waypoints,e)}}if(t==="waypoints"&&l==="set"&&n.length===5){const s=c(await e.input(),["waypoints"],["waypoints"],e);(!Array.isArray(s.waypoints)||s.waypoints.length<1)&&e.fail("INPUT","Informe waypoints",422);const u=s.waypoints.map(a=>v(a,e));return await y(r,e),await e.confirm(`Salvar waypoints da rota ${r}?`,r),e.api("PUT",_,{route:{waypoints:u}})}if(["optimize","calculate"].includes(t)&&n.length===4){const s=await y(r,e),u=T(s.waypoints,e);return u.valid||e.fail("INPUT",`Coordenadas pendentes: ${JSON.stringify(u.missing)}`,422),await e.confirm(`${t} rota ${r}?`,r),e.api("POST",`/${t}`,{locations:L(s),route_id:r,return_to_start:s.return_to_start===!0},{withStatus:!0})}if(["optimize","calculate"].includes(t)&&l==="apply"&&n.length===6){const s=e.uuid(f),u=c(await e.input(),["expected_waypoint_ids","expected_updated_at"],["expected_waypoint_ids","expected_updated_at"],e),a=await y(r,e);a.is_operational&&e.fail("INPUT","A aplica\xE7\xE3o deste c\xE1lculo exige uma rota manual",422);const d=a.waypoints.map(m=>m.id);(u.expected_updated_at!==a.updated_at||!Array.isArray(u.expected_waypoint_ids)||JSON.stringify(u.expected_waypoint_ids)!==JSON.stringify(d))&&e.fail("INPUT","Rota mudou; confira os waypoints e solicite novo c\xE1lculo",409);const b=await e.api("GET",`/${t}/${s}`),h=b.status==="completed"?b.data:null,w=t==="optimize"?h?.route?.features?.[0]?.properties?.waypoint_order:d.map((m,g)=>g);h?.route||e.fail("INPUT","O job ainda n\xE3o concluiu com geometria aplic\xE1vel",409),(!Array.isArray(w)||w.length!==d.length||new Set(w).size!==w.length||w.some(m=>!Number.isInteger(m)||m<0||m>=d.length))&&e.fail("INPUT","O job ainda n\xE3o concluiu ou n\xE3o retornou ordem aplic\xE1vel",409);const P=w.map((m,g)=>({...v(Object.fromEntries(Object.entries(a.waypoints[m]).filter(([$])=>I.includes($))),e),sequence:g}));return await e.confirm(`Aplicar otimiza\xE7\xE3o ${s} \xE0 rota ${r}?`,r),e.api("PUT",_,{expected_route_snapshot:a,route:{waypoints:P,route:h.route,stats:{...a.stats,total_distance:h.distance,total_time:h.duration,route_signature:h.route_signature,waypoint_etas:h.waypoint_etas}}})}if(t==="start"&&n.length===4)return await y(r,e),await e.confirm(`Iniciar rota ${r}?`,r),e.api("POST",`${_}/start`);if(t==="finish"&&n.length===4)return await y(r,e),await e.confirm(`Concluir rota ${r}?`,r),e.api("POST",`${_}/execution/finish`);if(t==="reactivate"&&n.length===4)return await y(r,e),await e.confirm(`Reativar rota ${r} para hoje?`,r),e.api("POST",`${_}/reactivate`);if(t==="clone"&&n.length===4)return await y(r,e),await e.confirm(`Clonar rota ${r} em um novo draft?`,r),e.api("POST",`${_}/clone`);if(t==="waypoints"&&n.length>=6){const s=e.uuid(l),u=`${_}/execution/waypoints/${s}`;if(f==="failure-policy"&&n.length===6)return e.api("GET",`${u}/failure-policy`);if(["notes","packages"].includes(f)&&n.length===6){const a=f==="notes"?"notes":"packages_count",d=c(await e.input(),[a],[],e);return(a==="notes"?typeof d.notes!="string":!Number.isSafeInteger(d.packages_count)||d.packages_count<0)&&e.fail("INPUT",`${a} inv\xE1lido`,422),await e.confirm(`Atualizar ${f==="notes"?"observa\xE7\xE3o":"quantidade de pacotes"} da parada ${s}?`,s),e.api("POST",`${u}/${f}`,d)}if(f==="navigation"&&n[6]==="start"&&n.length===7){const a=c(await e.input(),["navigation_provider","nav_sdk_version","boot_duration_ms","waypoint_index","packages_count"],[],e);for(const d of["navigation_provider","nav_sdk_version"])a[d]!==void 0&&(typeof a[d]!="string"||!a[d].trim())&&e.fail("INPUT",`${d} inv\xE1lido`,422);for(const d of["boot_duration_ms","waypoint_index","packages_count"])a[d]!==void 0&&(!Number.isSafeInteger(a[d])||a[d]<0)&&e.fail("INPUT",`${d} inv\xE1lido`,422);return await e.confirm(`Registrar in\xEDcio de navega\xE7\xE3o no waypoint ${s}?`,s),e.api("POST",`${u}/navigation/start`,a,{withStatus:!0})}if(f==="check-in"&&n.length===6){const a=c(await e.input(),["lat","lng","accuracy_meters"],["lat","lng"],e);return(!Number.isFinite(a.lat)||Math.abs(a.lat)>90||!Number.isFinite(a.lng)||Math.abs(a.lng)>180||a.accuracy_meters!==void 0&&(!Number.isFinite(a.accuracy_meters)||a.accuracy_meters<0))&&e.fail("INPUT","Informe coordenadas e precis\xE3o num\xE9ricas v\xE1lidas",422),await e.confirm(`Registrar check-in no waypoint ${s}?`,s),e.api("POST",`${u}/check-in`,a)}if(f==="status"&&n.length===6){const a=N(await e.input(),e);return await e.confirm(`Registrar ${a.status} no waypoint ${s}?`,s),e.api("POST",`${u}/status`,a)}if(f==="items"&&["status","enrich"].includes(n[7])&&n.length===8){const a=n[6];if((!a||a==="."||a===".."||a.includes("/"))&&e.fail("USAGE","ITEM_ID inv\xE1lido",2),n[7]==="enrich")return await e.confirm(`Consultar dados do item ${a}?`,a),e.api("POST",`${u}/items/${encodeURIComponent(a)}/enrich`);const d=N(await e.input(),e);return await e.confirm(`Registrar ${d.status} no item ${a}?`,a),e.api("POST",`${u}/items/${encodeURIComponent(a)}/status`,d)}}e.fail("USAGE","Use genne help driver routes",2)}p(W,"driverRoutesCommand");export{W as driverRoutesCommand};
|