plazbot-cli 0.3.7 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -2
- package/dist/commands/auth/browser-login.js +121 -0
- package/dist/commands/auth/index.js +5 -1
- package/dist/commands/auth/login.js +1 -3
- package/dist/commands/auth/logout.js +4 -3
- package/dist/commands/auth/status.js +8 -5
- package/dist/commands/auth/workspace.js +73 -0
- package/dist/commands/whatsapp/connect-flow.js +137 -0
- package/dist/commands/whatsapp/connect.js +26 -160
- package/dist/commands/whatsapp/index.js +4 -0
- package/dist/commands/whatsapp/setup.js +242 -0
- package/dist/schemas/agent.config.schema.json +24 -0
- package/dist/studio/components/App.js +1 -1
- package/dist/studio/runOneShot.js +2 -2
- package/dist/studio/runRepl.js +1 -1
- package/dist/utils/agent-errors.js +2 -2
- package/dist/utils/banner.js +10 -9
- package/dist/utils/credentials.js +31 -3
- package/dist/utils/open-browser.js +17 -0
- package/package.json +14 -6
- package/.claude/settings.local.json +0 -19
- package/CLAUDE.md +0 -121
- package/agent.config.json +0 -21
- package/dist/studio/components/AgentPanel.js +0 -35
- package/dist/studio/render/json.js +0 -16
- package/dist/studio/whatsapp/api.js +0 -63
- package/dist/studio/whatsapp/polling.js +0 -77
- package/dist/studio/whatsapp/types.js +0 -31
- package/src/cli.ts +0 -57
- package/src/commands/agent/ai-config.ts +0 -164
- package/src/commands/agent/chat.ts +0 -225
- package/src/commands/agent/copy.ts +0 -46
- package/src/commands/agent/create.ts +0 -103
- package/src/commands/agent/delete.ts +0 -71
- package/src/commands/agent/enable-widget.ts +0 -67
- package/src/commands/agent/export.ts +0 -430
- package/src/commands/agent/files.ts +0 -171
- package/src/commands/agent/get.ts +0 -233
- package/src/commands/agent/index.ts +0 -55
- package/src/commands/agent/list.ts +0 -58
- package/src/commands/agent/monitor.ts +0 -375
- package/src/commands/agent/on-message.ts +0 -82
- package/src/commands/agent/set.ts +0 -149
- package/src/commands/agent/templates.ts +0 -892
- package/src/commands/agent/tools.ts +0 -168
- package/src/commands/agent/update.ts +0 -61
- package/src/commands/agent/validate.ts +0 -135
- package/src/commands/agent/wizard.ts +0 -756
- package/src/commands/auth/index.ts +0 -10
- package/src/commands/auth/login.ts +0 -72
- package/src/commands/auth/logout.ts +0 -28
- package/src/commands/auth/status.ts +0 -28
- package/src/commands/portal/add-agent.ts +0 -66
- package/src/commands/portal/add-link.ts +0 -47
- package/src/commands/portal/clear-links.ts +0 -33
- package/src/commands/portal/create.ts +0 -58
- package/src/commands/portal/delete.ts +0 -70
- package/src/commands/portal/get.ts +0 -77
- package/src/commands/portal/index.ts +0 -32
- package/src/commands/portal/list.ts +0 -79
- package/src/commands/portal/update.ts +0 -93
- package/src/commands/whatsapp/broadcast.ts +0 -110
- package/src/commands/whatsapp/channels.ts +0 -109
- package/src/commands/whatsapp/chat.ts +0 -85
- package/src/commands/whatsapp/connect.ts +0 -220
- package/src/commands/whatsapp/delete-webhook.ts +0 -43
- package/src/commands/whatsapp/index.ts +0 -37
- package/src/commands/whatsapp/register-webhook.ts +0 -47
- package/src/commands/whatsapp/send-template.ts +0 -94
- package/src/commands/whatsapp/send.ts +0 -55
- package/src/commands/whatsapp/widget.ts +0 -75
- package/src/commands/workers/deploy.ts +0 -156
- package/src/commands/workers/index.ts +0 -30
- package/src/commands/workers/list.ts +0 -105
- package/src/commands/workers/logs.ts +0 -99
- package/src/commands/workers/remove.ts +0 -103
- package/src/commands/workers/secret.ts +0 -191
- package/src/commands/workers/test.ts +0 -145
- package/src/schemas/agent.config.schema.json +0 -569
- package/src/studio/api/sseClient.ts +0 -91
- package/src/studio/api/studioApi.ts +0 -27
- package/src/studio/api/types.ts +0 -96
- package/src/studio/components/App.tsx +0 -274
- package/src/studio/components/ChatLog.tsx +0 -121
- package/src/studio/components/ConnectCard.tsx +0 -171
- package/src/studio/components/Footer.tsx +0 -38
- package/src/studio/components/Header.tsx +0 -39
- package/src/studio/components/Input.tsx +0 -176
- package/src/studio/components/Message.tsx +0 -87
- package/src/studio/components/Suggestions.tsx +0 -26
- package/src/studio/components/ToolCall.tsx +0 -58
- package/src/studio/connect/api.ts +0 -98
- package/src/studio/connect/polling.ts +0 -94
- package/src/studio/connect/types.ts +0 -141
- package/src/studio/index.ts +0 -58
- package/src/studio/render/markdown.ts +0 -93
- package/src/studio/render/steps.ts +0 -57
- package/src/studio/runOneShot.ts +0 -114
- package/src/studio/runRepl.tsx +0 -82
- package/src/studio/slash/handlers.ts +0 -226
- package/src/studio/slash/parser.ts +0 -41
- package/src/studio/slash/registry.ts +0 -54
- package/src/studio/state/store.ts +0 -279
- package/src/types/agent.ts +0 -63
- package/src/types/auth.ts +0 -9
- package/src/types/common.ts +0 -10
- package/src/types/message.ts +0 -34
- package/src/types/portal.ts +0 -56
- package/src/types/workers.ts +0 -86
- package/src/utils/agent-errors.ts +0 -67
- package/src/utils/api.ts +0 -35
- package/src/utils/banner.ts +0 -99
- package/src/utils/credentials.ts +0 -38
- package/src/utils/help.ts +0 -51
- package/src/utils/logger.ts +0 -40
- package/src/utils/ui.ts +0 -109
- package/tsconfig.json +0 -18
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/** Abre una URL en el navegador del sistema. Devuelve false si no se pudo (p.ej. servidor sin GUI). */
|
|
3
|
+
export function openBrowser(url) {
|
|
4
|
+
try {
|
|
5
|
+
const [cmd, args] = process.platform === 'darwin' ? ['open', [url]] :
|
|
6
|
+
// En cmd.exe "&" separa comandos: se escapa con ^
|
|
7
|
+
process.platform === 'win32' ? ['cmd', ['/c', 'start', '', url.replace(/&/g, '^&')]] :
|
|
8
|
+
['xdg-open', [url]];
|
|
9
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
|
|
10
|
+
child.on('error', () => { });
|
|
11
|
+
child.unref();
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plazbot-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "CLI oficial de Plazbot: WhatsApp for Developers, agentes IA y workers desde tu terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"plazbot": "dist/cli.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
10
14
|
"scripts": {
|
|
11
15
|
"build": "tsc && mkdir -p dist/schemas && cp src/schemas/*.json dist/schemas/ && chmod +x dist/cli.js",
|
|
12
16
|
"start": "node dist/cli.js",
|
|
@@ -23,12 +27,15 @@
|
|
|
23
27
|
},
|
|
24
28
|
"repository": {
|
|
25
29
|
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/
|
|
30
|
+
"url": "git+https://github.com/plazbot/Plazbot-CLI-v2.0.git"
|
|
27
31
|
},
|
|
28
32
|
"keywords": [
|
|
29
33
|
"plazbot",
|
|
30
34
|
"cli",
|
|
31
|
-
"sdk"
|
|
35
|
+
"sdk",
|
|
36
|
+
"whatsapp",
|
|
37
|
+
"whatsapp-api",
|
|
38
|
+
"ai-agents"
|
|
32
39
|
],
|
|
33
40
|
"author": "Kristian Garcia <kristian@plazbot.com>",
|
|
34
41
|
"license": "MIT",
|
|
@@ -52,7 +59,7 @@
|
|
|
52
59
|
"marked": "^12.0.2",
|
|
53
60
|
"marked-terminal": "^7.2.1",
|
|
54
61
|
"ora": "^8.0.1",
|
|
55
|
-
"plazbot": "^2.
|
|
62
|
+
"plazbot": "^2.2.0",
|
|
56
63
|
"react": "^18.3.1",
|
|
57
64
|
"zustand": "^4.5.4"
|
|
58
65
|
},
|
|
@@ -64,5 +71,6 @@
|
|
|
64
71
|
},
|
|
65
72
|
"engines": {
|
|
66
73
|
"node": ">=18.17.0"
|
|
67
|
-
}
|
|
74
|
+
},
|
|
75
|
+
"homepage": "https://developers.plazbot.com/cli"
|
|
68
76
|
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(tree:*)",
|
|
5
|
-
"Bash(wc:*)",
|
|
6
|
-
"Bash(git -C /Users/kristian/Source/plazbot-v2/Plazbot-CLI-v2.0 log --oneline -10)",
|
|
7
|
-
"Bash(grep:*)",
|
|
8
|
-
"Bash(npm install:*)",
|
|
9
|
-
"Bash(npm run build:*)",
|
|
10
|
-
"Bash(node /Users/kristian/Source/plazbot-v2/Plazbot-CLI-v2.0/dist/cli.js:*)",
|
|
11
|
-
"Bash(npm ls:*)",
|
|
12
|
-
"Bash(python3:*)",
|
|
13
|
-
"WebFetch(domain:www.plazbot.com)",
|
|
14
|
-
"Bash(npx tsc:*)",
|
|
15
|
-
"Bash(ls:*)",
|
|
16
|
-
"Bash(xargs:*)"
|
|
17
|
-
]
|
|
18
|
-
}
|
|
19
|
-
}
|
package/CLAUDE.md
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
-
|
|
5
|
-
## Descripción del Proyecto
|
|
6
|
-
|
|
7
|
-
Plazbot CLI v2.0 es una interfaz de línea de comandos oficial para la plataforma Plazbot. Permite gestionar agentes de IA, portales web y comunicaciones vía WhatsApp desde la terminal.
|
|
8
|
-
|
|
9
|
-
- **Lenguaje**: TypeScript 5.0+
|
|
10
|
-
- **Runtime**: Node.js >=18.17.0 (ESM)
|
|
11
|
-
- **Framework CLI**: Commander.js
|
|
12
|
-
- **TUI**: Ink v5 + React 18 + zustand (solo para `plazbot studio`)
|
|
13
|
-
|
|
14
|
-
## Comandos de Desarrollo
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
# Compilar TypeScript
|
|
18
|
-
npm run build
|
|
19
|
-
|
|
20
|
-
# Modo watch (compilación continua)
|
|
21
|
-
npm run dev
|
|
22
|
-
|
|
23
|
-
# Ejecutar CLI localmente
|
|
24
|
-
npm start
|
|
25
|
-
# o directamente:
|
|
26
|
-
node dist/cli.js
|
|
27
|
-
|
|
28
|
-
# Versionado
|
|
29
|
-
npm run version:patch # Incrementar patch
|
|
30
|
-
npm run version:minor # Incrementar minor
|
|
31
|
-
npm run version:major # Incrementar major
|
|
32
|
-
|
|
33
|
-
# SDK local vs npm
|
|
34
|
-
npm run prepare:dev # Usar SDK local (desarrollo)
|
|
35
|
-
npm run prepare:prod # Usar SDK de npm
|
|
36
|
-
|
|
37
|
-
# Publicación
|
|
38
|
-
npm run publish:beta # Publicar beta
|
|
39
|
-
npm run publish:prod # Publicar producción
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Arquitectura
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
src/
|
|
46
|
-
├── cli.ts # Entry point - registra todos los comandos
|
|
47
|
-
├── commands/ # Comandos organizados por dominio
|
|
48
|
-
│ ├── agent/ # CRUD + chat interactivo de agentes
|
|
49
|
-
│ ├── auth/ # init (login) y logout
|
|
50
|
-
│ ├── portal/ # CRUD + gestión de enlaces
|
|
51
|
-
│ ├── whatsapp/ # Mensajes y webhooks
|
|
52
|
-
│ └── workers/ # Tools, syncs, schedules, webhooks
|
|
53
|
-
├── studio/ # REPL interactivo (Ink + SSE)
|
|
54
|
-
│ ├── index.ts # studioCommand + subcomando ask
|
|
55
|
-
│ ├── runRepl.tsx # Bootstrap Ink render(<App/>)
|
|
56
|
-
│ ├── runOneShot.ts # Modo no interactivo (stdout)
|
|
57
|
-
│ ├── api/ # types, studioApi, sseClient (fetch + ReadableStream)
|
|
58
|
-
│ ├── state/store.ts # zustand: messages, steps, agentConfig, usage
|
|
59
|
-
│ ├── slash/ # parser, registry, handlers (/help, /clear, /agents…)
|
|
60
|
-
│ ├── render/ # markdown (marked-terminal), steps, json
|
|
61
|
-
│ └── components/ # App, Header, ChatLog, Message, ToolCall, Input…
|
|
62
|
-
├── types/ # Interfaces TypeScript
|
|
63
|
-
└── utils/
|
|
64
|
-
├── logger.ts # Logging formateado (info, success, warning, error)
|
|
65
|
-
├── api.ts # axios con Authorization Bearer + x-workspace-id
|
|
66
|
-
└── credentials.ts # Persistencia en ~/.plazbot/config.json
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Studio (REPL interactivo)
|
|
70
|
-
|
|
71
|
-
`plazbot studio` abre una TUI estilo Claude Code que consume el endpoint SSE
|
|
72
|
-
`POST /api/agent/studio`. Permite crear, cargar, diagnosticar y guardar agentes
|
|
73
|
-
en conversación natural. Las tool_calls se renderizan como cards con spinner;
|
|
74
|
-
el `agentConfig` aparece en un panel lateral con tabs preview/json/diagnose.
|
|
75
|
-
|
|
76
|
-
- `plazbot studio` → REPL completo (Ink)
|
|
77
|
-
- `plazbot studio -m "..."` o `plazbot studio ask "..."` → one-shot a stdout
|
|
78
|
-
- `plazbot studio ask "..." --json` → NDJSON crudo de los chunks SSE
|
|
79
|
-
|
|
80
|
-
Atajos: `Tab` panel · `Esc` cancelar stream · `Ctrl+C` salir.
|
|
81
|
-
|
|
82
|
-
### Flujo de Comandos
|
|
83
|
-
|
|
84
|
-
1. `cli.ts` usa Commander.js para registrar grupos de comandos
|
|
85
|
-
2. Cada comando carga credenciales de `~/.plazbot/config.json`
|
|
86
|
-
3. Instancia el SDK (`plazbot`) con las credenciales
|
|
87
|
-
4. Ejecuta la operación contra el backend de Plazbot
|
|
88
|
-
5. Formatea la respuesta usando `logger.ts`
|
|
89
|
-
|
|
90
|
-
### SDK de Plazbot
|
|
91
|
-
|
|
92
|
-
El CLI depende del paquete `plazbot` que expone tres clases principales:
|
|
93
|
-
- **Agent** - Operaciones con agentes de IA
|
|
94
|
-
- **Portal** - Gestión de portales web
|
|
95
|
-
- **Message** - Envío de mensajes WhatsApp
|
|
96
|
-
|
|
97
|
-
### Credenciales
|
|
98
|
-
|
|
99
|
-
Se almacenan en `~/.plazbot/config.json`:
|
|
100
|
-
```json
|
|
101
|
-
{
|
|
102
|
-
"email": "string (opcional)",
|
|
103
|
-
"apiKey": "string (JWT)",
|
|
104
|
-
"workspace": "string",
|
|
105
|
-
"zone": "LA | EU",
|
|
106
|
-
"userId": "string (opcional, usado por studio como x-user-id)"
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
El `apiKey` es un JWT válido. El CLI lo envía como `Authorization: Bearer <jwt>`
|
|
111
|
-
y mantiene transitoriamente `x-api-key` para endpoints legacy.
|
|
112
|
-
|
|
113
|
-
### Flag --dev
|
|
114
|
-
|
|
115
|
-
Todos los comandos soportan `--dev` para apuntar al backend local (`http://localhost:5090`).
|
|
116
|
-
|
|
117
|
-
## Convenciones
|
|
118
|
-
|
|
119
|
-
- Cada grupo de comandos tiene un `index.ts` que exporta el comando padre con subcomandos
|
|
120
|
-
- Los tipos de opciones de comando se definen en `src/types/`
|
|
121
|
-
- El logger proporciona formato consistente: `logger.info()`, `logger.success()`, `logger.error()`
|
package/agent.config.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "agente-1",
|
|
3
|
-
"description": "Asistente que ayuda a resolver preguntas de la empresa",
|
|
4
|
-
"prompt": "Vas a ayudar a las personas a poder resolver sus preguntas de la empresa como informacion de contacto, horarios de atencion, etc. Nuestro horario de atencion es de 9:00 a 18:00 y trabajamos en Peru, España y Colombia.",
|
|
5
|
-
"zone": "LA",
|
|
6
|
-
"buffer": 5,
|
|
7
|
-
"color": "blue",
|
|
8
|
-
"question": "Como te podemos ayudar?",
|
|
9
|
-
"timezone": "America/Lima",
|
|
10
|
-
"showInChat": false,
|
|
11
|
-
"examples": [
|
|
12
|
-
{ "value": "quiero informacion de contacto", "color": "green" },
|
|
13
|
-
{ "value": "horarios de atencion", "color": "orange" }
|
|
14
|
-
],
|
|
15
|
-
"person": {
|
|
16
|
-
"name": "Leandro",
|
|
17
|
-
"role": "Asistente de Atencion al Cliente",
|
|
18
|
-
"speaksInFirstPerson": true,
|
|
19
|
-
"isHuman": false
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import { useStudioStore } from '../state/store.js';
|
|
4
|
-
import { JsonView, safeStringify } from '../render/json.js';
|
|
5
|
-
export function AgentPanel() {
|
|
6
|
-
const open = useStudioStore((s) => s.panelOpen);
|
|
7
|
-
const tab = useStudioStore((s) => s.panelTab);
|
|
8
|
-
const cfg = useStudioStore((s) => s.agentConfig);
|
|
9
|
-
if (!open)
|
|
10
|
-
return null;
|
|
11
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexGrow: 1, children: [_jsx(Box, { children: _jsx(Text, { bold: true, color: "green", children: "Agent panel" }) }), _jsxs(Box, { children: [_jsx(Tab, { active: tab === 'preview', children: "preview" }), _jsx(Text, { dimColor: true, children: " \u00B7 " }), _jsx(Tab, { active: tab === 'json', children: "json" }), _jsx(Text, { dimColor: true, children: " \u00B7 " }), _jsx(Tab, { active: tab === 'diagnose', children: "diagnose" })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: renderTab(tab, cfg) })] }));
|
|
12
|
-
}
|
|
13
|
-
function Tab({ active, children }) {
|
|
14
|
-
return active ? _jsx(Text, { color: "cyan", bold: true, children: children }) : _jsx(Text, { dimColor: true, children: children });
|
|
15
|
-
}
|
|
16
|
-
function renderTab(tab, cfg) {
|
|
17
|
-
if (!cfg) {
|
|
18
|
-
return _jsx(Text, { dimColor: true, children: "No agent loaded yet." });
|
|
19
|
-
}
|
|
20
|
-
if (tab === 'json') {
|
|
21
|
-
return _jsx(JsonView, { value: cfg });
|
|
22
|
-
}
|
|
23
|
-
if (tab === 'diagnose') {
|
|
24
|
-
return _jsx(Text, { dimColor: true, children: "Run `/diagnose` to analyze the current agent." });
|
|
25
|
-
}
|
|
26
|
-
// preview: key agent fields
|
|
27
|
-
const c = cfg;
|
|
28
|
-
const name = c?.name ?? '(unnamed)';
|
|
29
|
-
const description = c?.description ?? '';
|
|
30
|
-
const model = c?.model ?? c?.modelId ?? '';
|
|
31
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Name: " }), _jsx(Text, { children: name })] }), model ? _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Model: " }), _jsx(Text, { children: model })] }) : null, description ? (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, children: "Description:" }), _jsx(Text, { children: description })] })) : null, _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: truncate(safeStringify(c), 400) }) })] }));
|
|
32
|
-
}
|
|
33
|
-
function truncate(s, n) {
|
|
34
|
-
return s.length > n ? s.slice(0, n) + '…' : s;
|
|
35
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from 'ink';
|
|
3
|
-
import SyntaxHighlight from 'ink-syntax-highlight';
|
|
4
|
-
export function JsonView({ value, maxHeight }) {
|
|
5
|
-
const text = safeStringify(value);
|
|
6
|
-
return (_jsx(Box, { flexDirection: "column", height: maxHeight, children: _jsx(SyntaxHighlight, { code: text, language: "json" }) }));
|
|
7
|
-
}
|
|
8
|
-
function safeStringify(v) {
|
|
9
|
-
try {
|
|
10
|
-
return JSON.stringify(v, null, 2);
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return String(v);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
export { safeStringify };
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { getBaseUrl } from '../api/studioApi.js';
|
|
2
|
-
function headers(opts) {
|
|
3
|
-
const h = {
|
|
4
|
-
'Content-Type': 'application/json',
|
|
5
|
-
Authorization: `Bearer ${opts.apiKey}`,
|
|
6
|
-
'x-workspace-id': opts.workspaceId,
|
|
7
|
-
};
|
|
8
|
-
if (opts.userId)
|
|
9
|
-
h['x-user-id'] = opts.userId;
|
|
10
|
-
return h;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* GET /api/workspace/{workspaceId} — the API returns an array of workspaces
|
|
14
|
-
* (the front uses `[0]`). Returns the first element or null.
|
|
15
|
-
*/
|
|
16
|
-
export async function getWorkspaceById(opts, signal) {
|
|
17
|
-
const url = `${getBaseUrl(opts.zone, opts.dev)}/api/workspace/${encodeURIComponent(opts.workspaceId)}`;
|
|
18
|
-
const res = await fetch(url, { method: 'GET', headers: headers(opts), signal });
|
|
19
|
-
if (!res.ok) {
|
|
20
|
-
throw new Error(`GET workspace ${res.status} ${res.statusText}`);
|
|
21
|
-
}
|
|
22
|
-
const json = (await res.json());
|
|
23
|
-
if (!json)
|
|
24
|
-
return null;
|
|
25
|
-
if (Array.isArray(json))
|
|
26
|
-
return json[0] ?? null;
|
|
27
|
-
return json;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* POST /api/workspace/{workspaceId}/integrations/{integrationId}/activate
|
|
31
|
-
* Body: { isActive: true, platformCode: "whatsapp" }
|
|
32
|
-
*/
|
|
33
|
-
export async function activateIntegration(opts, integrationId, signal) {
|
|
34
|
-
const url = `${getBaseUrl(opts.zone, opts.dev)}/api/workspace/${encodeURIComponent(opts.workspaceId)}/integrations/${encodeURIComponent(integrationId)}/activate`;
|
|
35
|
-
const res = await fetch(url, {
|
|
36
|
-
method: 'POST',
|
|
37
|
-
headers: headers(opts),
|
|
38
|
-
body: JSON.stringify({ isActive: true, platformCode: 'whatsapp' }),
|
|
39
|
-
signal,
|
|
40
|
-
});
|
|
41
|
-
const text = await res.text();
|
|
42
|
-
let parsed = {};
|
|
43
|
-
if (text) {
|
|
44
|
-
try {
|
|
45
|
-
parsed = JSON.parse(text);
|
|
46
|
-
}
|
|
47
|
-
catch { /* not json */ }
|
|
48
|
-
}
|
|
49
|
-
if (!res.ok) {
|
|
50
|
-
return { success: false, message: parsed.message || `HTTP ${res.status} ${res.statusText}` };
|
|
51
|
-
}
|
|
52
|
-
return parsed.success === false ? parsed : { success: true, ...parsed };
|
|
53
|
-
}
|
|
54
|
-
/** Adapter: derives the workspace fetch options from a StudioStreamOptions. */
|
|
55
|
-
export function workspaceOptsFromStream(stream) {
|
|
56
|
-
return {
|
|
57
|
-
apiKey: stream.apiKey,
|
|
58
|
-
workspaceId: stream.workspaceId,
|
|
59
|
-
zone: stream.zone,
|
|
60
|
-
dev: stream.dev,
|
|
61
|
-
userId: stream.userId,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { activateIntegration, getWorkspaceById } from './api.js';
|
|
2
|
-
import { findNewWhatsappIntegration, WA_POLL_INTERVAL_MS, WA_POLL_MAX_ATTEMPTS, } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Starts the polling loop. Returns a handle to cancel it. The loop:
|
|
5
|
-
* 1. GETs the workspace every WA_POLL_INTERVAL_MS up to WA_POLL_MAX_ATTEMPTS.
|
|
6
|
-
* 2. Looks for a whatsapp integration whose id isn't in `existingIntegrationIds`.
|
|
7
|
-
* 3. When found, calls POST .../activate and reports the result.
|
|
8
|
-
*/
|
|
9
|
-
export function startWhatsappPolling(opts, linkData, cb) {
|
|
10
|
-
const knownIds = new Set(linkData.existingIntegrationIds ?? []);
|
|
11
|
-
const controller = new AbortController();
|
|
12
|
-
let attempts = 0;
|
|
13
|
-
let busy = false;
|
|
14
|
-
let stopped = false;
|
|
15
|
-
let timer = null;
|
|
16
|
-
const stop = () => {
|
|
17
|
-
stopped = true;
|
|
18
|
-
if (timer) {
|
|
19
|
-
clearInterval(timer);
|
|
20
|
-
timer = null;
|
|
21
|
-
}
|
|
22
|
-
controller.abort();
|
|
23
|
-
};
|
|
24
|
-
const tick = async () => {
|
|
25
|
-
if (busy || stopped)
|
|
26
|
-
return;
|
|
27
|
-
busy = true;
|
|
28
|
-
attempts++;
|
|
29
|
-
cb.onAttempt(attempts);
|
|
30
|
-
try {
|
|
31
|
-
const ws = await getWorkspaceById(opts, controller.signal);
|
|
32
|
-
const newIntegration = findNewWhatsappIntegration(ws?.integrations, knownIds);
|
|
33
|
-
if (newIntegration?.id) {
|
|
34
|
-
const number = newIntegration.cellphoneNumberFormat ||
|
|
35
|
-
newIntegration.cellphoneNumber ||
|
|
36
|
-
'';
|
|
37
|
-
cb.onActivating(newIntegration.id, number);
|
|
38
|
-
if (timer) {
|
|
39
|
-
clearInterval(timer);
|
|
40
|
-
timer = null;
|
|
41
|
-
}
|
|
42
|
-
const res = await activateIntegration(opts, newIntegration.id, controller.signal);
|
|
43
|
-
if (res.success !== false) {
|
|
44
|
-
cb.onConnected(number);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
cb.onError(res.message || 'Could not activate the integration.');
|
|
48
|
-
}
|
|
49
|
-
stopped = true;
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
if (err.name === 'AbortError')
|
|
55
|
-
return;
|
|
56
|
-
// Transient network errors: swallow and keep polling.
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
busy = false;
|
|
60
|
-
}
|
|
61
|
-
if (attempts >= WA_POLL_MAX_ATTEMPTS) {
|
|
62
|
-
if (timer) {
|
|
63
|
-
clearInterval(timer);
|
|
64
|
-
timer = null;
|
|
65
|
-
}
|
|
66
|
-
stopped = true;
|
|
67
|
-
cb.onTimeout();
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
timer = setInterval(() => { void tick(); }, WA_POLL_INTERVAL_MS);
|
|
71
|
-
// Trigger the first attempt immediately so the user sees activity right away.
|
|
72
|
-
void tick();
|
|
73
|
-
return {
|
|
74
|
-
cancel() { stop(); },
|
|
75
|
-
get stopped() { return stopped; },
|
|
76
|
-
};
|
|
77
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types and helpers for the "connect WhatsApp from Agent Studio" flow.
|
|
3
|
-
*
|
|
4
|
-
* The backend tool `connect_whatsapp` emits a `tool_result` chunk with
|
|
5
|
-
* `type: "whatsapp_link"`. When `data.shortUrl` is present we render an
|
|
6
|
-
* inline card with the onboarding link and start polling the workspace
|
|
7
|
-
* until the new integration appears. Baseline of "known" ids comes from
|
|
8
|
-
* the server (`existingIntegrationIds`) to avoid race conditions.
|
|
9
|
-
*
|
|
10
|
-
* Mirror of /Plazbot-Front-v2.0/src/modules/agentStudio/whatsappConnect.ts
|
|
11
|
-
*/
|
|
12
|
-
export const WA_POLL_INTERVAL_MS = 5000;
|
|
13
|
-
export const WA_POLL_MAX_ATTEMPTS = 60; // 5 minutes
|
|
14
|
-
/**
|
|
15
|
-
* Returns a new whatsapp integration whose id is NOT in the baseline.
|
|
16
|
-
* If multiple are new, picks the most recently created one.
|
|
17
|
-
*/
|
|
18
|
-
export function findNewWhatsappIntegration(integrations, knownIds) {
|
|
19
|
-
if (!integrations?.length)
|
|
20
|
-
return null;
|
|
21
|
-
const candidates = integrations.filter((i) => (i.type === 'whatsapp' || i.type === 'whatsapp_business') &&
|
|
22
|
-
!!i.id &&
|
|
23
|
-
!knownIds.has(i.id));
|
|
24
|
-
if (!candidates.length)
|
|
25
|
-
return null;
|
|
26
|
-
return candidates.reduce((latest, current) => {
|
|
27
|
-
const a = latest.creationDate ? new Date(latest.creationDate).getTime() : 0;
|
|
28
|
-
const b = current.creationDate ? new Date(current.creationDate).getTime() : 0;
|
|
29
|
-
return b > a ? current : latest;
|
|
30
|
-
});
|
|
31
|
-
}
|
package/src/cli.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { program } from 'commander';
|
|
3
|
-
import { readFileSync } from 'node:fs';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { dirname, join } from 'node:path';
|
|
6
|
-
import { portalCommands } from './commands/portal/index.js';
|
|
7
|
-
import { authCommands } from './commands/auth/index.js';
|
|
8
|
-
import { agentCommands } from './commands/agent/index.js';
|
|
9
|
-
import { whatsappCommands } from './commands/whatsapp/index.js';
|
|
10
|
-
import { workersCommands } from './commands/workers/index.js';
|
|
11
|
-
import { studioCommand } from './studio/index.js';
|
|
12
|
-
import { showBanner } from './utils/banner.js';
|
|
13
|
-
|
|
14
|
-
// Guard de runtime: Plazbot CLI v0.3+ requiere Node 18.17+
|
|
15
|
-
const [major, minor] = process.versions.node.split('.').map(Number);
|
|
16
|
-
if (major < 18 || (major === 18 && minor < 17)) {
|
|
17
|
-
console.error(`Plazbot CLI v0.3+ requires Node.js >=18.17.0. Current version: ${process.versions.node}`);
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Leer version desde package.json
|
|
22
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
-
const pkgPath = join(__dirname, '..', 'package.json');
|
|
24
|
-
const { version } = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
25
|
-
|
|
26
|
-
// Configuracion basica del CLI
|
|
27
|
-
program
|
|
28
|
-
.name('plazbot')
|
|
29
|
-
.description('Official Plazbot CLI - AI agents for WhatsApp, portals and developers')
|
|
30
|
-
.version(version);
|
|
31
|
-
|
|
32
|
-
// Registrar todos los comandos de autenticacion
|
|
33
|
-
authCommands.forEach(cmd => program.addCommand(cmd));
|
|
34
|
-
|
|
35
|
-
// Registrar todos los comandos de agente
|
|
36
|
-
program.addCommand(agentCommands);
|
|
37
|
-
|
|
38
|
-
// Registrar todos los comandos de portal
|
|
39
|
-
program.addCommand(portalCommands);
|
|
40
|
-
|
|
41
|
-
// Registrar todos los comandos de WhatsApp
|
|
42
|
-
program.addCommand(whatsappCommands);
|
|
43
|
-
|
|
44
|
-
// Registrar todos los comandos de Workers
|
|
45
|
-
program.addCommand(workersCommands);
|
|
46
|
-
|
|
47
|
-
// Registrar el comando Studio (REPL interactivo)
|
|
48
|
-
program.addCommand(studioCommand);
|
|
49
|
-
|
|
50
|
-
// Si no se pasan argumentos, mostrar banner
|
|
51
|
-
if (process.argv.length <= 2) {
|
|
52
|
-
showBanner().then(() => {
|
|
53
|
-
program.outputHelp();
|
|
54
|
-
});
|
|
55
|
-
} else {
|
|
56
|
-
program.parse(process.argv);
|
|
57
|
-
}
|