@badgie/crm-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +91 -0
- package/README.md +136 -0
- package/dist/bin.js +149 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/admin.js +90 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/clients.js +135 -0
- package/dist/commands/clients.js.map +1 -0
- package/dist/commands/directory.js +175 -0
- package/dist/commands/directory.js.map +1 -0
- package/dist/commands/docs.js +98 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/finance.js +311 -0
- package/dist/commands/finance.js.map +1 -0
- package/dist/commands/intelligence.js +147 -0
- package/dist/commands/intelligence.js.map +1 -0
- package/dist/commands/leads-list.js +25 -0
- package/dist/commands/leads-list.js.map +1 -0
- package/dist/commands/leads.js +224 -0
- package/dist/commands/leads.js.map +1 -0
- package/dist/commands/login.js +11 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.js +6 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/marketing.js +135 -0
- package/dist/commands/marketing.js.map +1 -0
- package/dist/commands/operations.js +232 -0
- package/dist/commands/operations.js.map +1 -0
- package/dist/commands/outbound.js +172 -0
- package/dist/commands/outbound.js.map +1 -0
- package/dist/commands/query.js +189 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/search.js +71 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/tasks.js +239 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/top-clients.js +82 -0
- package/dist/commands/top-clients.js.map +1 -0
- package/dist/commands/webhooks.js +82 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/commands/welcome.js +81 -0
- package/dist/commands/welcome.js.map +1 -0
- package/dist/commands/whoami.js +16 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/core/auth.js +107 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/config.js +53 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/env.js +46 -0
- package/dist/core/env.js.map +1 -0
- package/dist/core/format.js +88 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/http.js +42 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/list-query.js +35 -0
- package/dist/core/list-query.js.map +1 -0
- package/dist/core/services/leads.js +41 -0
- package/dist/core/services/leads.js.map +1 -0
- package/dist/core/supabase.js +53 -0
- package/dist/core/supabase.js.map +1 -0
- package/dist/registry.js +2 -0
- package/dist/registry.js.map +1 -0
- package/package.json +57 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# AGENTS.md — `badgie-crm` para agentes IA
|
|
2
|
+
|
|
3
|
+
Este archivo es la guía canónica para que un agente (openclaw, Claude, Cursor, custom GPT…) use el CLI `badgie-crm` de forma segura y efectiva.
|
|
4
|
+
|
|
5
|
+
## Qué es
|
|
6
|
+
|
|
7
|
+
CLI oficial del Badgie CRM. 70 comandos sobre Supabase. Instalación: ver `cli/README.md`.
|
|
8
|
+
|
|
9
|
+
## Fuente de verdad (machine-readable)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
badgie-crm docs --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Devuelve el manifest completo: `{ version, commands: [{ command, path, summary, description, args, options, tags, examples }] }`. Cárgalo al inicio de cada sesión como tu catálogo de tools.
|
|
16
|
+
|
|
17
|
+
Para filtrar por módulo:
|
|
18
|
+
```bash
|
|
19
|
+
badgie-crm docs finance --json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Convenciones críticas
|
|
23
|
+
|
|
24
|
+
1. **Salida**: JSON por defecto, parseable. Añade `--pretty` solo cuando quieras texto para un humano.
|
|
25
|
+
2. **Tags por comando** (los lees en el manifest):
|
|
26
|
+
- `[read]` — consulta, seguro, idempotente.
|
|
27
|
+
- `[write]` — muta la DB. Confirmable pero no destructivo.
|
|
28
|
+
- `[destructive]` — irreversible (delete, wipes). **Pide confirmación antes**.
|
|
29
|
+
- `[http]` — requiere que la app Next esté arriba Y que el endpoint acepte Bearer token. Si falla, avisa al usuario antes de reintentar.
|
|
30
|
+
3. **Exit code**: `0` = ok, `1` = error (mensaje en stderr).
|
|
31
|
+
4. **Filtros ad-hoc**: si no hay flag ergonómico, usa `badgie-crm query <table> --where col:op:val` (ops: eq, neq, gt, gte, lt, lte, like, ilike, in, is, not). `--where` es repetible.
|
|
32
|
+
|
|
33
|
+
## Patrón recomendado para agentes
|
|
34
|
+
|
|
35
|
+
1. `badgie-crm whoami` → verifica auth.
|
|
36
|
+
2. `badgie-crm docs --json` → cárgate el catálogo.
|
|
37
|
+
3. Ante una petición del humano:
|
|
38
|
+
- Traduce a un comando `[read]` primero para verificar estado.
|
|
39
|
+
- Si vas a mutar, muestra el comando propuesto al humano y espera confirmación (especialmente si lleva `[destructive]`).
|
|
40
|
+
4. Siempre incluye `--limit` razonable (default 50) para no agotar contexto.
|
|
41
|
+
5. Prefiere `query count <table> --where ...` antes de listar cuando solo necesites saber cuántos hay.
|
|
42
|
+
|
|
43
|
+
## Ejemplos orientativos
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# "¿Cuántas facturas de salarios hay en marzo?"
|
|
47
|
+
badgie-crm query count invoices \
|
|
48
|
+
--where "category:ilike:salar" \
|
|
49
|
+
--where "invoice_date:gte:2026-03-01" \
|
|
50
|
+
--where "invoice_date:lte:2026-03-31"
|
|
51
|
+
|
|
52
|
+
# "Lista los leads nuevos asignados a greg"
|
|
53
|
+
badgie-crm leads list --status nuevo --assigned-to <uuid-greg> --pretty
|
|
54
|
+
|
|
55
|
+
# "Busca cualquier cosa que mencione 'valencia'"
|
|
56
|
+
badgie-crm search valencia
|
|
57
|
+
|
|
58
|
+
# "Qué columnas tiene la tabla X"
|
|
59
|
+
badgie-crm query describe <table> --pretty
|
|
60
|
+
|
|
61
|
+
# "Registra un intento de contacto fallido sobre un lead"
|
|
62
|
+
badgie-crm leads log-attempt <lead-uuid> --type call --result no_answer --notes "buzón"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Setup rápido como skill de openclaw
|
|
66
|
+
|
|
67
|
+
1. Instala el CLI en la máquina donde corra el agente:
|
|
68
|
+
```bash
|
|
69
|
+
git clone <repo> && cd <repo>/cli && npm install && npm run build && npm link
|
|
70
|
+
```
|
|
71
|
+
2. Autentica una vez: `badgie-crm login` (o exporta `BADGIE_CRM_SERVICE_KEY` para modo no interactivo en CI/servidor).
|
|
72
|
+
3. Crea el skill en el agente apuntando a este `AGENTS.md` + referenciando el manifest:
|
|
73
|
+
```
|
|
74
|
+
Tool source: bash `badgie-crm docs --json`
|
|
75
|
+
Auth check: bash `badgie-crm whoami`
|
|
76
|
+
Entry docs: <ruta local del repo>/cli/AGENTS.md
|
|
77
|
+
```
|
|
78
|
+
4. Verifica: `badgie-crm docs --agent-hints` imprime guidance adicional.
|
|
79
|
+
|
|
80
|
+
## Qué NO hacer
|
|
81
|
+
|
|
82
|
+
- **No** construyas queries SQL por tu cuenta — usa los comandos tipados.
|
|
83
|
+
- **No** uses `[destructive]` sin confirmación explícita del humano.
|
|
84
|
+
- **No** asumas nombres de columnas — usa `query describe <table>` para descubrirlas.
|
|
85
|
+
- **No** mezcles `--pretty` con parseo JSON (rompe el output).
|
|
86
|
+
|
|
87
|
+
## Límites conocidos
|
|
88
|
+
|
|
89
|
+
- Comandos con tag `[http]` requieren que la app Next esté viva Y aceptando Bearer tokens. Hoy los endpoints siguen leyendo cookies — algunos fallarán hasta que se extiendan. Ver `tasks/badgie-crm-cli-mcp.md`.
|
|
90
|
+
- El CLI en modo `service` (env `BADGIE_CRM_SERVICE_KEY`) bypasa RLS: **solo en entornos confiables**.
|
|
91
|
+
- Mutaciones masivas (bulk update) aún no tienen comandos dedicados — usa `query` en modo lectura para verificar y pide al humano si necesitas un script ad-hoc.
|
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# @badgie/crm-cli (`badgie-crm`)
|
|
2
|
+
|
|
3
|
+
CLI para operar el Badgie CRM desde la terminal. Reemplaza los scripts sueltos de `/scripts/` y prepara el terreno para exponer el CRM vía MCP a agentes IA (openclaw, Claude Desktop, Cursor...).
|
|
4
|
+
|
|
5
|
+
**Nombre del binario: `badgie-crm`** — dejamos `badgie` reservado para el CLI principal de Badgie (próximo).
|
|
6
|
+
|
|
7
|
+
## Instalación (desarrollo)
|
|
8
|
+
|
|
9
|
+
Desde la raíz del repo:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd cli
|
|
13
|
+
npm install
|
|
14
|
+
npm run build
|
|
15
|
+
npm link # expone `badgie-crm` como binario global
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Después, desde cualquier sitio:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
badgie-crm # banner + estado + atajos
|
|
22
|
+
badgie-crm --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Para desarrollo rápido sin `build` continuo:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd cli && npm run dev -- leads list --pretty
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuración
|
|
32
|
+
|
|
33
|
+
El CLI lee credenciales de Supabase en este orden:
|
|
34
|
+
|
|
35
|
+
1. `BADGIE_CRM_SUPABASE_URL` / `BADGIE_CRM_SUPABASE_ANON_KEY` (env vars explícitas).
|
|
36
|
+
2. `BADGIE_SUPABASE_URL` / `BADGIE_SUPABASE_ANON_KEY` (retrocompat).
|
|
37
|
+
3. `NEXT_PUBLIC_SUPABASE_URL` / `NEXT_PUBLIC_SUPABASE_ANON_KEY` del `.env.local` del repo.
|
|
38
|
+
4. Valores guardados en `~/.badgie-crm/config.json` tras el primer `badgie-crm login`.
|
|
39
|
+
|
|
40
|
+
Si tenías un `~/.badgie/config.json` de la versión anterior, se migra automáticamente la primera vez que se arranca el CLI.
|
|
41
|
+
|
|
42
|
+
## Autenticación
|
|
43
|
+
|
|
44
|
+
Dos modos:
|
|
45
|
+
|
|
46
|
+
### Modo usuario (interactivo, respeta RLS)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
badgie-crm login
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Te pide email + contraseña de tu cuenta Badgie (el mismo login que la app web). Aplica las mismas reglas: cuentas `@badgie.com` entran directas, otras deben tener fila en `team_members` activa. La sesión se guarda en `~/.badgie-crm/config.json` (permisos 0600) y los tokens se refrescan automáticamente.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
badgie-crm whoami # JSON
|
|
56
|
+
badgie-crm whoami --pretty # texto
|
|
57
|
+
badgie-crm logout # limpia la sesión
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Modo service (CI / automatización, bypassa RLS)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export BADGIE_CRM_SERVICE_KEY=eyJhbGc...
|
|
64
|
+
badgie-crm leads list
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`BADGIE_CRM_SERVICE_KEY` (o `BADGIE_SERVICE_KEY` / `SUPABASE_SERVICE_ROLE_KEY`) tiene precedencia sobre la sesión interactiva. Úsalo solo en entornos confiables.
|
|
68
|
+
|
|
69
|
+
## Módulos (70 comandos)
|
|
70
|
+
|
|
71
|
+
`leads`, `clients`, `contacts`, `tasks`, `development`, `finance`, `outbound`, `directory`, `marketing`, `intelligence`, `mission-control`, `top-clients`, `web-design`, `team`, `sports`, `activities`, `demos`, `onboarding`, `webhooks`, `admin`, `search`, `query`, `docs`.
|
|
72
|
+
|
|
73
|
+
Explora todo con:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
badgie-crm docs # tree completo
|
|
77
|
+
badgie-crm docs finance # filtrado por módulo
|
|
78
|
+
badgie-crm docs --json # manifest JSON para agentes IA
|
|
79
|
+
badgie-crm docs --agent-hints # guidance extra para LLMs
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Ejemplos frecuentes
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Facturas de marzo de la categoría salarios
|
|
86
|
+
badgie-crm finance invoices list --category salarios --since 2026-03-01 --until 2026-03-31 --pretty
|
|
87
|
+
|
|
88
|
+
# Query genérico — cualquier columna de cualquier tabla
|
|
89
|
+
badgie-crm query invoices --where "category=salarios" --where "invoice_date:gte:2026-03-01" --pretty
|
|
90
|
+
badgie-crm query count bank_movements --where "amount:gte:1000" --where "movement_date:gte:2026-03-01"
|
|
91
|
+
badgie-crm query describe invoices --pretty
|
|
92
|
+
|
|
93
|
+
# Directory
|
|
94
|
+
badgie-crm directory schools list --sport golf --province Madrid --min-rating 4.5 --pretty
|
|
95
|
+
|
|
96
|
+
# Búsqueda global
|
|
97
|
+
badgie-crm search "valencia" --pretty
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Distribución al equipo
|
|
101
|
+
|
|
102
|
+
**Opción A — clone + link (hoy, más simple)**. Cada persona del equipo:
|
|
103
|
+
```bash
|
|
104
|
+
git clone <repo-url> badgie-crm && cd badgie-crm/cli
|
|
105
|
+
npm install && npm run build && npm link
|
|
106
|
+
badgie-crm login
|
|
107
|
+
```
|
|
108
|
+
Para actualizar: `git pull && cd cli && npm run build`.
|
|
109
|
+
|
|
110
|
+
**Opción B — publicar a npm (cuando queramos)**. Hoy `@badgie/crm-cli` está marcado `private`. Pasos cuando queramos publicar:
|
|
111
|
+
1. Crear una org `@badgie` en npm (o usar GitHub Packages con scope `@badgie`).
|
|
112
|
+
2. `npm login --scope=@badgie`.
|
|
113
|
+
3. En `cli/package.json` quitar `"private": true` si lo añadimos, o publicar a GitHub Packages con `publishConfig`.
|
|
114
|
+
4. `cd cli && npm publish --access restricted`.
|
|
115
|
+
5. Equipo instala: `npm install -g @badgie/crm-cli`.
|
|
116
|
+
|
|
117
|
+
**Vercel no interviene.** Vercel despliega la app Next (`/app`). El CLI es un paquete npm independiente que el equipo instala en sus máquinas.
|
|
118
|
+
|
|
119
|
+
**Git**: sí, empuja el commit con `cli/` a GitHub para que el equipo pueda clonar. El CLI está excluido de la build de Next, así que el deploy de la app no se ve afectado.
|
|
120
|
+
|
|
121
|
+
## Agentes IA (openclaw, Claude, Cursor…)
|
|
122
|
+
|
|
123
|
+
Ver `cli/AGENTS.md`. Dale eso al agente + el manifest JSON:
|
|
124
|
+
```bash
|
|
125
|
+
badgie-crm docs --json > ~/.badgie-crm/manifest.json
|
|
126
|
+
```
|
|
127
|
+
El agente carga ese JSON como catálogo de tools y respeta los tags `[read|write|destructive|http]` para saber qué es seguro.
|
|
128
|
+
|
|
129
|
+
## Roadmap
|
|
130
|
+
|
|
131
|
+
Ver `tasks/badgie-crm-cli-mcp.md`. Próximo: **`badgie-crm mcp serve`** para que openclaw y otros agentes consuman el CRM como tools MCP tipadas (reutilizando el mismo registry).
|
|
132
|
+
|
|
133
|
+
## Notas
|
|
134
|
+
|
|
135
|
+
- La carpeta `cli/` está excluida de la build de Next (`outputFileTracingExcludes` en `next.config.ts` + `exclude` en `tsconfig.json` raíz).
|
|
136
|
+
- `~/.badgie-crm/config.json` no debe commitearse — es local del usuario.
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { loginCommand } from './commands/login.js';
|
|
4
|
+
import { logoutCommand } from './commands/logout.js';
|
|
5
|
+
import { whoamiCommand } from './commands/whoami.js';
|
|
6
|
+
import { welcomeCommand } from './commands/welcome.js';
|
|
7
|
+
import { leadsModule } from './commands/leads.js';
|
|
8
|
+
import { clientsModule } from './commands/clients.js';
|
|
9
|
+
import { tasksModule } from './commands/tasks.js';
|
|
10
|
+
import { financeModule } from './commands/finance.js';
|
|
11
|
+
import { outboundModule } from './commands/outbound.js';
|
|
12
|
+
import { marketingModule } from './commands/marketing.js';
|
|
13
|
+
import { intelligenceModule } from './commands/intelligence.js';
|
|
14
|
+
import { operationsModule } from './commands/operations.js';
|
|
15
|
+
import { webhooksModule } from './commands/webhooks.js';
|
|
16
|
+
import { adminModule } from './commands/admin.js';
|
|
17
|
+
import { searchModule } from './commands/search.js';
|
|
18
|
+
import { queryModule } from './commands/query.js';
|
|
19
|
+
import { directoryModule } from './commands/directory.js';
|
|
20
|
+
import { topClientsModule } from './commands/top-clients.js';
|
|
21
|
+
import { createDocsModule } from './commands/docs.js';
|
|
22
|
+
const MODULES = [
|
|
23
|
+
leadsModule,
|
|
24
|
+
clientsModule,
|
|
25
|
+
tasksModule,
|
|
26
|
+
financeModule,
|
|
27
|
+
outboundModule,
|
|
28
|
+
marketingModule,
|
|
29
|
+
intelligenceModule,
|
|
30
|
+
operationsModule,
|
|
31
|
+
webhooksModule,
|
|
32
|
+
adminModule,
|
|
33
|
+
searchModule,
|
|
34
|
+
queryModule,
|
|
35
|
+
directoryModule,
|
|
36
|
+
topClientsModule,
|
|
37
|
+
];
|
|
38
|
+
async function main() {
|
|
39
|
+
const program = new Command();
|
|
40
|
+
const docsModule = createDocsModule(() => [...MODULES, docsModule]);
|
|
41
|
+
const allModules = [...MODULES, docsModule];
|
|
42
|
+
program
|
|
43
|
+
.name('badgie')
|
|
44
|
+
.description('Badgie CRM CLI — operate the CRM from your terminal or as an MCP tool server.')
|
|
45
|
+
.version('0.1.0')
|
|
46
|
+
.action(wrap(welcomeCommand));
|
|
47
|
+
program
|
|
48
|
+
.command('welcome')
|
|
49
|
+
.description('Show the welcome banner')
|
|
50
|
+
.action(wrap(welcomeCommand));
|
|
51
|
+
program
|
|
52
|
+
.command('login')
|
|
53
|
+
.description('Sign in with email + password')
|
|
54
|
+
.action(wrap(loginCommand));
|
|
55
|
+
program
|
|
56
|
+
.command('logout')
|
|
57
|
+
.description('Clear the persisted session')
|
|
58
|
+
.action(wrap(logoutCommand));
|
|
59
|
+
program
|
|
60
|
+
.command('whoami')
|
|
61
|
+
.description('Show the current auth mode and user')
|
|
62
|
+
.option('--pretty', 'human-readable output')
|
|
63
|
+
.action(wrap(whoamiCommand));
|
|
64
|
+
for (const m of allModules)
|
|
65
|
+
registerModule(program, m);
|
|
66
|
+
await program.parseAsync(process.argv);
|
|
67
|
+
}
|
|
68
|
+
function registerModule(program, module) {
|
|
69
|
+
const nodes = new Map();
|
|
70
|
+
nodes.set('', program);
|
|
71
|
+
// Pass 1: create every Command node (parent and leaf) exactly once.
|
|
72
|
+
for (const spec of module.specs) {
|
|
73
|
+
let parent = program;
|
|
74
|
+
for (let i = 0; i < spec.path.length; i++) {
|
|
75
|
+
const key = spec.path.slice(0, i + 1).join('.');
|
|
76
|
+
let node = nodes.get(key);
|
|
77
|
+
if (!node) {
|
|
78
|
+
node = parent
|
|
79
|
+
.command(spec.path[i])
|
|
80
|
+
.description(i === spec.path.length - 1 ? spec.summary : `${spec.path[i]} commands`);
|
|
81
|
+
nodes.set(key, node);
|
|
82
|
+
}
|
|
83
|
+
parent = node;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Pass 2: attach args, options, and action to each spec's node.
|
|
87
|
+
for (const spec of module.specs) {
|
|
88
|
+
const leaf = nodes.get(spec.path.join('.'));
|
|
89
|
+
attachLeaf(leaf, spec);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function attachLeaf(leaf, spec) {
|
|
93
|
+
leaf.description(spec.summary);
|
|
94
|
+
for (const a of spec.args ?? []) {
|
|
95
|
+
const tok = a.required ? `<${a.name}>` : `[${a.name}]`;
|
|
96
|
+
leaf.argument(tok, a.description);
|
|
97
|
+
}
|
|
98
|
+
for (const o of spec.options ?? []) {
|
|
99
|
+
if (o.collect) {
|
|
100
|
+
leaf.option(o.flag, o.description, (value, previous) => [...previous, value], []);
|
|
101
|
+
}
|
|
102
|
+
else if (o.defaultValue !== undefined) {
|
|
103
|
+
leaf.option(o.flag, o.description, String(o.defaultValue));
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
leaf.option(o.flag, o.description);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (spec.examples && spec.examples.length > 0) {
|
|
110
|
+
leaf.addHelpText('after', '\nExamples:\n' + spec.examples.map((e) => ' ' + e).join('\n'));
|
|
111
|
+
}
|
|
112
|
+
if (spec.description)
|
|
113
|
+
leaf.addHelpText('after', '\n' + spec.description);
|
|
114
|
+
leaf.action(async (...actionArgs) => {
|
|
115
|
+
const cmd = actionArgs[actionArgs.length - 1];
|
|
116
|
+
const opts = (cmd.optsWithGlobals
|
|
117
|
+
? cmd.optsWithGlobals()
|
|
118
|
+
: cmd.opts());
|
|
119
|
+
const argMap = {};
|
|
120
|
+
(spec.args ?? []).forEach((a, i) => {
|
|
121
|
+
argMap[a.name] = actionArgs[i];
|
|
122
|
+
});
|
|
123
|
+
try {
|
|
124
|
+
await spec.handler(argMap, opts);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
128
|
+
console.error(`Error: ${msg}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function wrap(fn) {
|
|
134
|
+
return async (opts) => {
|
|
135
|
+
try {
|
|
136
|
+
await fn(opts);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
140
|
+
console.error(`Error: ${msg}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
main().catch((err) => {
|
|
146
|
+
console.error(err);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { getAuthedClient } from '../core/supabase.js';
|
|
2
|
+
import { output, parseColumns, parseLimit, commonListOptions } from '../core/format.js';
|
|
3
|
+
import { callAppApi } from '../core/http.js';
|
|
4
|
+
async function listUsers(_a, opts) {
|
|
5
|
+
const { client } = await getAuthedClient();
|
|
6
|
+
let q = client
|
|
7
|
+
.from('team_members')
|
|
8
|
+
.select('id, email, full_name, role, is_active, last_login_at, created_at')
|
|
9
|
+
.order('created_at', { ascending: false })
|
|
10
|
+
.limit(parseLimit(opts));
|
|
11
|
+
if (opts.activeOnly !== undefined)
|
|
12
|
+
q = q.eq('is_active', true);
|
|
13
|
+
const { data, error } = await q;
|
|
14
|
+
if (error)
|
|
15
|
+
throw error;
|
|
16
|
+
output(data ?? [], { pretty: !!opts.pretty, columns: parseColumns(opts) });
|
|
17
|
+
}
|
|
18
|
+
async function createUser(_a, opts) {
|
|
19
|
+
const res = await callAppApi('/api/admin/create-user', {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
email: opts.email,
|
|
23
|
+
full_name: opts.fullName,
|
|
24
|
+
role: opts.role,
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
output(res, { pretty: !!opts.pretty });
|
|
28
|
+
}
|
|
29
|
+
async function deleteUser(args, opts) {
|
|
30
|
+
const res = await callAppApi('/api/admin/delete-user', {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
body: JSON.stringify({ id: args.id }),
|
|
33
|
+
});
|
|
34
|
+
output(res, { pretty: !!opts.pretty });
|
|
35
|
+
}
|
|
36
|
+
async function updateFinanceAccess(args, opts) {
|
|
37
|
+
const res = await callAppApi('/api/admin/update-finance-access', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
body: JSON.stringify({ id: args.id, allowed: opts.allowed === 'true' }),
|
|
40
|
+
});
|
|
41
|
+
output(res, { pretty: !!opts.pretty });
|
|
42
|
+
}
|
|
43
|
+
export const adminModule = {
|
|
44
|
+
name: 'admin',
|
|
45
|
+
summary: 'Admin operations — users, permissions (requires service key or admin user)',
|
|
46
|
+
specs: [
|
|
47
|
+
{
|
|
48
|
+
path: ['admin', 'users', 'list'],
|
|
49
|
+
summary: 'List team_members',
|
|
50
|
+
tags: ['read'],
|
|
51
|
+
options: [
|
|
52
|
+
{ flag: '--active-only', description: 'only active users' },
|
|
53
|
+
...commonListOptions(),
|
|
54
|
+
],
|
|
55
|
+
handler: listUsers,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
path: ['admin', 'users', 'create'],
|
|
59
|
+
summary: 'Create a team member (calls the Next app)',
|
|
60
|
+
tags: ['write', 'http'],
|
|
61
|
+
options: [
|
|
62
|
+
{ flag: '--email <email>', description: 'email (required)' },
|
|
63
|
+
{ flag: '--full-name <text>', description: 'display name' },
|
|
64
|
+
{ flag: '--role <role>', description: 'member role' },
|
|
65
|
+
{ flag: '--pretty', description: 'human output' },
|
|
66
|
+
],
|
|
67
|
+
handler: createUser,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
path: ['admin', 'users', 'delete'],
|
|
71
|
+
summary: 'Delete a team member (destructive, calls the Next app)',
|
|
72
|
+
tags: ['destructive', 'http'],
|
|
73
|
+
args: [{ name: 'id', required: true, description: 'team_members.id' }],
|
|
74
|
+
options: [{ flag: '--pretty', description: 'human output' }],
|
|
75
|
+
handler: deleteUser,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
path: ['admin', 'users', 'set-finance-access'],
|
|
79
|
+
summary: 'Toggle finance_access for a user (calls the Next app)',
|
|
80
|
+
tags: ['write', 'http'],
|
|
81
|
+
args: [{ name: 'id', required: true, description: 'team_members.id' }],
|
|
82
|
+
options: [
|
|
83
|
+
{ flag: '--allowed <bool>', description: 'true|false (required)' },
|
|
84
|
+
{ flag: '--pretty', description: 'human output' },
|
|
85
|
+
],
|
|
86
|
+
handler: updateFinanceAccess,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,KAAK,UAAU,SAAS,CAAC,EAAsC,EAAE,IAA6B;IAC5F,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,eAAe,EAAE,CAAA;IAC1C,IAAI,CAAC,GAAG,MAAM;SACX,IAAI,CAAC,cAAc,CAAC;SACpB,MAAM,CAAC,kEAAkE,CAAC;SAC1E,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;SACzC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;IAC1B,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;QAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,CAAA;IAC9D,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAA;IAC/B,IAAI,KAAK;QAAE,MAAM,KAAK,CAAA;IACtB,MAAM,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC5E,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,EAAsC,EAAE,IAA6B;IAC7F,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,wBAAwB,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,QAAQ;YACxB,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC;KACH,CAAC,CAAA;IACF,MAAM,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;AACxC,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,IAAwC,EAAE,IAA6B;IAC/F,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,wBAAwB,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;KACtC,CAAC,CAAA;IACF,MAAM,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;AACxC,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAwC,EAAE,IAA6B;IACxG,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,kCAAkC,EAAE;QAC/D,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;KACxE,CAAC,CAAA;IACF,MAAM,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;AACxC,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAe;IACrC,IAAI,EAAE,OAAO;IACb,OAAO,EAAE,4EAA4E;IACrF,KAAK,EAAE;QACL;YACE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC;YAChC,OAAO,EAAE,mBAAmB;YAC5B,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,mBAAmB,EAAE;gBAC3D,GAAG,iBAAiB,EAAE;aACvB;YACD,OAAO,EAAE,SAAS;SACnB;QACD;YACE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC;YAClC,OAAO,EAAE,2CAA2C;YACpD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YACvB,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,kBAAkB,EAAE;gBAC5D,EAAE,IAAI,EAAE,oBAAoB,EAAE,WAAW,EAAE,cAAc,EAAE;gBAC3D,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,aAAa,EAAE;gBACrD,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE;aAClD;YACD,OAAO,EAAE,UAAU;SACpB;QACD;YACE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC;YAClC,OAAO,EAAE,wDAAwD;YACjE,IAAI,EAAE,CAAC,aAAa,EAAE,MAAM,CAAC;YAC7B,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;YACtE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;YAC5D,OAAO,EAAE,UAAU;SACpB;QACD;YACE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,oBAAoB,CAAC;YAC9C,OAAO,EAAE,uDAAuD;YAChE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;YACvB,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;YACtE,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,kBAAkB,EAAE,WAAW,EAAE,uBAAuB,EAAE;gBAClE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE;aAClD;YACD,OAAO,EAAE,mBAAmB;SAC7B;KACF;CACF,CAAA"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { getAuthedClient } from '../core/supabase.js';
|
|
2
|
+
import { commonListOptions, output, parseColumns, parseLimit } from '../core/format.js';
|
|
3
|
+
async function listClients(_a, opts) {
|
|
4
|
+
const { client } = await getAuthedClient();
|
|
5
|
+
let q = client
|
|
6
|
+
.from('clients')
|
|
7
|
+
.select('id, name, email, phone, sport, city, type, status, assigned_to, created_at')
|
|
8
|
+
.order('created_at', { ascending: false })
|
|
9
|
+
.limit(parseLimit(opts));
|
|
10
|
+
if (typeof opts.sport === 'string')
|
|
11
|
+
q = q.ilike('sport', `%${opts.sport}%`);
|
|
12
|
+
if (typeof opts.city === 'string')
|
|
13
|
+
q = q.ilike('city', `%${opts.city}%`);
|
|
14
|
+
if (typeof opts.status === 'string')
|
|
15
|
+
q = q.eq('status', opts.status);
|
|
16
|
+
if (typeof opts.type === 'string')
|
|
17
|
+
q = q.eq('type', opts.type);
|
|
18
|
+
if (typeof opts.assignedTo === 'string')
|
|
19
|
+
q = q.eq('assigned_to', opts.assignedTo);
|
|
20
|
+
if (typeof opts.search === 'string')
|
|
21
|
+
q = q.or(`name.ilike.%${opts.search}%,email.ilike.%${opts.search}%`);
|
|
22
|
+
if (typeof opts.since === 'string')
|
|
23
|
+
q = q.gte('created_at', opts.since);
|
|
24
|
+
if (typeof opts.until === 'string')
|
|
25
|
+
q = q.lte('created_at', opts.until);
|
|
26
|
+
const { data, error } = await q;
|
|
27
|
+
if (error)
|
|
28
|
+
throw error;
|
|
29
|
+
output(data ?? [], { pretty: !!opts.pretty, columns: parseColumns(opts) });
|
|
30
|
+
}
|
|
31
|
+
async function getClient(args, opts) {
|
|
32
|
+
const { client } = await getAuthedClient();
|
|
33
|
+
const { data, error } = await client
|
|
34
|
+
.from('clients')
|
|
35
|
+
.select('*, assigned:team_members!clients_assigned_to_fkey(full_name, email)')
|
|
36
|
+
.eq('id', args.id)
|
|
37
|
+
.single();
|
|
38
|
+
if (error)
|
|
39
|
+
throw error;
|
|
40
|
+
output(data, { pretty: !!opts.pretty });
|
|
41
|
+
}
|
|
42
|
+
async function createClient_(_a, opts) {
|
|
43
|
+
const { client } = await getAuthedClient();
|
|
44
|
+
const { data, error } = await client
|
|
45
|
+
.from('clients')
|
|
46
|
+
.insert({
|
|
47
|
+
name: opts.name,
|
|
48
|
+
email: opts.email ?? null,
|
|
49
|
+
phone: opts.phone ?? null,
|
|
50
|
+
sport: opts.sport ?? null,
|
|
51
|
+
city: opts.city ?? null,
|
|
52
|
+
type: opts.type ?? null,
|
|
53
|
+
status: opts.status ?? null,
|
|
54
|
+
assigned_to: opts.assignedTo ?? null,
|
|
55
|
+
})
|
|
56
|
+
.select('*')
|
|
57
|
+
.single();
|
|
58
|
+
if (error)
|
|
59
|
+
throw error;
|
|
60
|
+
output(data, { pretty: !!opts.pretty });
|
|
61
|
+
}
|
|
62
|
+
async function listContacts(_a, opts) {
|
|
63
|
+
const { client } = await getAuthedClient();
|
|
64
|
+
let q = client
|
|
65
|
+
.from('contacts')
|
|
66
|
+
.select('id, first_name, last_name, email, phone, role, is_primary, client_id, created_at')
|
|
67
|
+
.order('created_at', { ascending: false })
|
|
68
|
+
.limit(parseLimit(opts));
|
|
69
|
+
if (typeof opts.clientId === 'string')
|
|
70
|
+
q = q.eq('client_id', opts.clientId);
|
|
71
|
+
const { data, error } = await q;
|
|
72
|
+
if (error)
|
|
73
|
+
throw error;
|
|
74
|
+
output(data ?? [], { pretty: !!opts.pretty, columns: parseColumns(opts) });
|
|
75
|
+
}
|
|
76
|
+
export const clientsModule = {
|
|
77
|
+
name: 'clients',
|
|
78
|
+
summary: 'Client & contact management',
|
|
79
|
+
specs: [
|
|
80
|
+
{
|
|
81
|
+
path: ['clients', 'list'],
|
|
82
|
+
summary: 'List clients (filter by sport, city, status, name)',
|
|
83
|
+
tags: ['read'],
|
|
84
|
+
options: [
|
|
85
|
+
{ flag: '--sport <name>', description: 'filter by sport (ilike)' },
|
|
86
|
+
{ flag: '--city <name>', description: 'filter by city (ilike)' },
|
|
87
|
+
{ flag: '--status <status>', description: 'filter by status' },
|
|
88
|
+
{ flag: '--type <type>', description: 'filter by client type' },
|
|
89
|
+
{ flag: '--assigned-to <uuid>', description: 'filter by KAM' },
|
|
90
|
+
{ flag: '--search <text>', description: 'search name or email (ilike)' },
|
|
91
|
+
{ flag: '--since <date>', description: 'created_at >= YYYY-MM-DD' },
|
|
92
|
+
{ flag: '--until <date>', description: 'created_at <= YYYY-MM-DD' },
|
|
93
|
+
...commonListOptions(),
|
|
94
|
+
],
|
|
95
|
+
examples: ['badgie-crm clients list --sport padel --pretty', 'badgie-crm clients list --search valencia', 'badgie-crm clients list --assigned-to <kam-uuid>'],
|
|
96
|
+
handler: listClients,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
path: ['clients', 'get'],
|
|
100
|
+
summary: 'Show one client by id',
|
|
101
|
+
tags: ['read'],
|
|
102
|
+
args: [{ name: 'id', required: true, description: 'client uuid' }],
|
|
103
|
+
options: [{ flag: '--pretty', description: 'human output' }],
|
|
104
|
+
handler: getClient,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
path: ['clients', 'create'],
|
|
108
|
+
summary: 'Create a client',
|
|
109
|
+
tags: ['write'],
|
|
110
|
+
options: [
|
|
111
|
+
{ flag: '--name <text>', description: 'client name (required)' },
|
|
112
|
+
{ flag: '--email <email>', description: 'primary email' },
|
|
113
|
+
{ flag: '--phone <phone>', description: 'primary phone' },
|
|
114
|
+
{ flag: '--sport <sport>', description: 'sport' },
|
|
115
|
+
{ flag: '--city <city>', description: 'city' },
|
|
116
|
+
{ flag: '--type <type>', description: 'client type' },
|
|
117
|
+
{ flag: '--status <status>', description: 'status' },
|
|
118
|
+
{ flag: '--assigned-to <uuid>', description: 'team_members.id' },
|
|
119
|
+
{ flag: '--pretty', description: 'human output' },
|
|
120
|
+
],
|
|
121
|
+
handler: createClient_,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
path: ['contacts', 'list'],
|
|
125
|
+
summary: 'List contacts (optionally filtered by client)',
|
|
126
|
+
tags: ['read'],
|
|
127
|
+
options: [
|
|
128
|
+
{ flag: '--client-id <uuid>', description: 'filter by client' },
|
|
129
|
+
...commonListOptions(),
|
|
130
|
+
],
|
|
131
|
+
handler: listContacts,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/commands/clients.ts"],"names":[],"mappings":"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"}
|