# Desplegar workers

> Despliega un archivo de worker, o todos los de ./workers, en tu workspace.

Fuente: https://developers.plazbot.com/cli/workers/deploy/

### Desplegar workers

Sube uno o varios archivos TypeScript de worker a tu workspace. Si no indicas un archivo, despliega todos los `*.ts` de la carpeta `./workers`. Si ya existe un worker con el mismo nombre, se actualiza y se guarda una versión nueva.

```bash
plazbot workers deploy [archivo] [options]
```

### Parámetros

| Parámetro | Flag | Requerido | Descripción |
|-----------|------|-----------|-------------|
| Archivo | `[archivo]` | No | Ruta al archivo `.ts` del worker. Si se omite, se despliegan todos los `*.ts` de `./workers` (se ignoran los `*.d.ts`), en orden alfabético. |
| Nombre | `-n, --name <name>` | No | Nombre del worker; reemplaza el `name` del archivo. Solo con un archivo. |
| Descripción | `--description <text>` | No | Descripción del worker; reemplaza la `reference` del archivo. Solo con un archivo. |
| JSON | `--json` | No | Imprime el resultado de cada archivo como un arreglo JSON. |

### Ejemplos

```bash
# Desplegar un solo archivo
plazbot workers deploy ./workers/get-stock.ts

# Desplegar todos los *.ts de ./workers
plazbot workers deploy

# Desplegar con nombre y descripción propios
plazbot workers deploy ./workers/cron.ts -n "daily-report" --description "Genera los KPIs del día"

# Desplegar todo y obtener el resultado por archivo en JSON (CI)
plazbot workers deploy --json
```

### Qué sucede al desplegar

1. El CLI lee cada archivo y envía solo ese código fuente. No se empaquetan imports relativos (`./utils`) ni dependencias de npm: todo el worker debe vivir en un único archivo que importe solo de `plazbot/workers`.
2. El servidor de Plazbot detecta el tipo por la función `define*` usada, extrae `name`, `reference`, `cron`, `timezone`, `method` y los parámetros, y compila el TypeScript a JavaScript.
3. Se crea o actualiza el worker (por nombre) y se guarda una versión.
4. Si es un webhook, se genera su URL pública y el CLI la muestra.

> **Importante:** `name`, `reference` y `cron` se leen del código como texto literal: escríbelos como strings (`name: 'get-stock'`), no como variables. El servidor rechaza código que use `eval()`, `new Function()`, `require()`, `import()` dinámico, `process.env` o `Deno.*`; para secretos usa `plz.env`. El compilador del servidor quita anotaciones de tipo simples (`: string`, `as any`); evita genéricos explícitos (`defineSync<...>`) y aserciones con tipos objeto (`as { ... }`).

### Resultado

```text
Deploying 2 workers
────────────────────────────────────────
✔ daily-report.ts → daily-report (schedule)
✔ get-stock.ts → get-stock (tool)

2 workers deployed
┌─────────────────┬──────────────┬──────────┬────────┬───────────┐
│ File            │ Name         │ Type     │ Status │ URL/Cron  │
├─────────────────┼──────────────┼──────────┼────────┼───────────┤
│ daily-report.ts │ daily-report │ schedule │ active │ 0 8 * * * │
├─────────────────┼──────────────┼──────────┼────────┼───────────┤
│ get-stock.ts    │ get-stock    │ tool     │ active │ —         │
└─────────────────┴──────────────┴──────────┴────────┴───────────┘
```

Para webhooks se añade una sección `Webhook URLs` con la URL de cada uno. Si algún archivo falla, los demás se despliegan igual y al final se listan los fallidos:

```text
✖ broken.ts: Codigo no permitido: Uso de eval() no permitido
...
1 of 3 workers failed to deploy
  broken.ts: Codigo no permitido: Uso de eval() no permitido
```

El comando termina con código 1 si al menos un archivo falla.

### Salida JSON

Un arreglo con un objeto por archivo. Los campos vacíos se omiten.

```json
[
{
  "file": "get-stock.ts",
  "name": "get-stock",
  "type": "tool",
  "status": "deployed",
  "workerStatus": "active"
},
{
  "file": "stripe-payments.ts",
  "name": "stripe-payments",
  "type": "webhook",
  "status": "deployed",
  "workerStatus": "active",
  "webhookUrl": "https://..."
},
{
  "file": "broken.ts",
  "status": "failed",
  "error": "Codigo no permitido: Uso de eval() no permitido"
}
]
```

### Errores comunes

| Mensaje | Causa | Solución |
|---------|-------|----------|
| `No worker files found to deploy.` | No pasaste archivo y `./workers` no existe o no tiene `*.ts`. | Pasa la ruta del archivo o crea los workers en `./workers`. |
| `File not found: <archivo>.` | La ruta indicada no es un archivo. | Revisa la ruta. |
| `--name and --description only apply to a single file, but N files were found in ./workers.` | Usaste `-n` o `--description` sin indicar un archivo. | Despliega un archivo a la vez: `plazbot workers deploy ./workers/my-tool.ts -n "my-tool"`. |
| `No se detecto un define* valido (...)` | El archivo no usa ninguna función `define*`. | Exporta el worker con `defineTool`, `defineWorker`, `defineSync`, `defineSchedule` o `defineWebhook`. |
| `El worker debe tener un nombre (...)` | No hay `name` literal en el archivo. | Agrega `name: 'mi-worker'` o usa `--name`. |
| `Codigo no permitido: ...` | El código usa una API bloqueada. | Reemplázala por el SDK (`plz.fetch`, `plz.env`). |
| `You are not signed in.` | No hay sesión. | Ejecuta [`plazbot login`](https://developers.plazbot.com/cli/login). |

## Tipos de worker

El archivo debe exportar por defecto el resultado de una función `define*` del paquete `plazbot/workers` (incluido en el SDK `plazbot`). Cada `handler` recibe el contexto `plz` con la API del workspace (`plz.contacts`, `plz.whatsapp`, `plz.fetch`, `plz.log`, `plz.env`, etc.; ver [Runtime: Contactos](https://developers.plazbot.com/cli/workers/runtime-contactos)).

| Función | Tipo | Se ejecuta cuando... |
|---------|------|----------------------|
| `defineTool` | `tool` | Un agente IA decide usarlo durante una conversación. |
| `defineWorker` | `worker` | Ocurre un evento de la plataforma (mensaje, contacto, conversación). |
| `defineSchedule` | `schedule` | Se cumple su expresión cron. |
| `defineSync` | `sync` | Se cumple su cron; trae registros externos y los aplica a contactos. |
| `defineWebhook` | `webhook` | Llega una petición HTTP a su URL pública. |

### defineTool

Herramienta que un agente IA ejecuta. El agente usa `reference` para decidir cuándo invocarla y extrae los `parameters` de la conversación. Tipos de parámetro: `string`, `number`, `boolean`, `date`, `email`, `phone`. El handler devuelve `{ result, data?, actions? }`.

```typescript
import { defineTool } from 'plazbot/workers'

export default defineTool({
name: 'get-stock',
reference: 'Consulta el stock de un producto',
parameters: {
  product: { type: 'string', description: 'Nombre del producto', required: true },
},
async handler(input, plz) {
  const res = await plz.fetch(`https://api.example.com/stock?q=${input.product}`, {
    headers: { Authorization: `Bearer ${plz.env.INVENTORY_API_KEY}` },
  })
  const data = await res.json()
  return {
    result: `Hay ${data.quantity} unidades de ${input.product}`,
    actions: [{ type: 'tag', value: 'consulto-stock' }],
  }
},
})
```

Acciones disponibles en `actions`: `tag`, `removeTag`, `setVariable` (con `code`), `setStage`, `setSegmentation`, `assignAgent`, `resolve`.

### defineWorker

Escucha eventos de la plataforma. Requiere al menos un evento en `events`: `message.received`, `message.image.received`, `message.audio.received`, `message.document.received`, `contact.created`, `contact.existing`, `contact.deleted`, `contact.blocked`, `contact.from.ad`, `conversation.assigned`, `conversation.resolved`, `conversation.reopened`.

```typescript
import { defineWorker } from 'plazbot/workers'

export default defineWorker({
name: 'welcome',
reference: 'Etiqueta a los contactos nuevos',
events: ['contact.created'],
async handler(event, plz) {
  const { contact } = event.data
  if (!contact) return
  await plz.contacts.addTag(contact.id, 'nuevo')
},
})
```

### defineSchedule

Tarea periódica con una expresión cron de 5 campos y `timezone` IANA opcional. El handler recibe solo `plz`.

```typescript
import { defineSchedule } from 'plazbot/workers'

export default defineSchedule({
name: 'daily-report',
reference: 'Cuenta los chats pendientes cada mañana',
cron: '0 8 * * *',
timezone: 'America/Lima',
async handler(plz) {
  const pending = await plz.contacts.list({ filter: { isSolved: false }, limit: 1000 })
  plz.log.info(`Chats pendientes: ${pending.length}`)
},
})
```

### defineSync

Sincroniza datos externos con contactos: `fetch` trae los registros, `map` convierte cada uno en `{ match, variables?, contact? }` (o `null` para omitirlo) y Plazbot crea o actualiza el contacto. Usa `schedule` (cron) y `source` (nombre del sistema origen). En `fetch`, `plz.cursor` y `plz.setCursor()` guardan el punto de la última sincronización.

```typescript
import { defineSync } from 'plazbot/workers'

export default defineSync({
name: 'crm-customers',
reference: 'Sincroniza el plan de los clientes del CRM',
source: 'CRM',
schedule: '0 * * * *',
async fetch(plz) {
  const res = await plz.fetch('https://api.example.com/customers')
  return res.json()
},
map(customer: any) {
  return {
    match: { email: customer.email },
    variables: { crm_plan: customer.plan },
  }
},
})
```

### defineWebhook

Endpoint HTTP público para recibir eventos de otras apps. `method` es `GET`, `POST`, `PUT`, `PATCH` o `DELETE`. El handler recibe `request` (`method`, `headers`, `query`, `body`, `url`) y devuelve `{ status, headers?, body? }`. La URL se muestra al desplegar y en [`plazbot workers list --json`](https://developers.plazbot.com/cli/workers/list).

```typescript
import { defineWebhook } from 'plazbot/workers'

export default defineWebhook({
name: 'payments',
reference: 'Recibe pagos confirmados',
method: 'POST',
async handler(request, plz) {
  const body = request.body as any
  if (body.email) {
    const [contact] = await plz.contacts.search({ email: body.email })
    if (contact) await plz.contacts.addTag(contact.id, 'cliente-pago')
  }
  return { status: 200, body: { received: true } }
},
})
```

> **Tip:** Si el worker usa secretos (`plz.env.MI_SECRET`), configúralos antes con [`plazbot workers secret set MI_SECRET`](https://developers.plazbot.com/cli/workers/secret-set). Después de desplegar, pruébalo con [`plazbot workers test`](https://developers.plazbot.com/cli/workers/test).
