Ctrl K
ComunidadGuías…

chattermate-test/journeys-demo/seed.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env node
// Sets up the Ledgerly demo (journeys-demo.html) against a ChatterMate backend:
// an agent with Ledgerly's in-app actions, the scenario journeys, a widget
// limited to the demo's origin, and a widget app whose key signs demo users in.
// Safe to run again: it updates what it made last time.
//
//   CM_EMAIL=you@example.com CM_PASSWORD=… node journeys-demo/seed.mjs
//
// Optional: CM_API (default http://localhost:8000/api/v1), DEMO_ORIGIN
// (default http://localhost:4100). Writes journeys-demo/.env.local (gitignored),
// which server.js reads to serve the page and sign demo users in.

import { readFile, writeFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { ACTIONS, AGENT, JOURNEYS } from './scenarios.mjs'

const here = dirname(fileURLToPath(import.meta.url))
const STATE_FILE = join(here, '.env.local')
const API = (process.env.CM_API || 'http://localhost:8000/api/v1').replace(/\/$/, '')
const ORIGIN = process.env.DEMO_ORIGIN || 'http://localhost:4100'

const { CM_EMAIL: email, CM_PASSWORD: password } = process.env
if (!email || !password) {
  console.error('Set CM_EMAIL and CM_PASSWORD (a dashboard login on this backend).')
  process.exit(1)
}

let token = null

async function call(method, path, body, { form = false, ok = [] } = {}) {
  const headers = token ? { Authorization: `Bearer ${token}` } : {}
  let payload
  if (body !== undefined) {
    if (form) {
      headers['Content-Type'] = 'application/x-www-form-urlencoded'
      payload = new URLSearchParams(body).toString()
    } else {
      headers['Content-Type'] = 'application/json'
      payload = JSON.stringify(body)
    }
  }
  const res = await fetch(`${API}${path}`, { method, headers, body: payload })
  const text = await res.text()
  if (!res.ok && !ok.includes(res.status)) throw new Error(`${method} ${path} → ${res.status}: ${text.slice(0, 300)}`)
  return text ? JSON.parse(text) : null
}

async function previousState() {
  try { return JSON.parse(await readFile(STATE_FILE, 'utf8')) } catch { return {} }
}

async function ensureAgent() {
  const agents = await call('GET', '/agent/list')
  const existing = agents.find((a) => a.name === AGENT.name)
  if (existing) {
    await call('PUT', `/agent/${existing.id}`, { instructions: AGENT.instructions })
    return existing.id
  }
  const created = await call('POST', '/agent', {
    name: AGENT.name, display_name: AGENT.displayName, agent_type: 'customer_support',
    instructions: AGENT.instructions, transfer_to_human: false,
  })
  return created.id
}

async function ensureWidget(agentId) {
  const widgets = await call('GET', `/widgets/agent/${agentId}`)
  const widget = widgets[0] ?? await call('POST', '/widgets', { name: 'Ledgerly demo', agent_id: agentId })
  // Actions and journeys only run on a site the widget is limited to.
  await call('PUT', `/widgets/${widget.id}/embed-settings`, { allowed_domains: [new URL(ORIGIN).host] })
  return widget.id
}

async function ensureActions(agentId) {
  const { actions } = await call('GET', `/agent/${agentId}/actions`)
  for (const action of ACTIONS) {
    const found = actions.find((a) => a.name === action.name)
    if (found) await call('PUT', `/agent/${agentId}/actions/${found.id}`, action)
    else await call('POST', `/agent/${agentId}/actions`, action)
  }
}

async function ensureJourneys(agentId) {
  const journeys = await call('GET', `/agent/${agentId}/journeys`)
  for (const journey of JOURNEYS) {
    const found = journeys.find((j) => j.key === journey.key)
    if (found) await call('PUT', `/agent/${agentId}/journeys/${found.id}`, journey)
    else await call('POST', `/agent/${agentId}/journeys`, journey)
  }
}

async function ensureApiKey(state) {
  // The key is shown once, so reuse the one saved last time while it still works.
  if (state.apiKey) {
    const check = await fetch(`${API}/widget-apps`, { headers: { Authorization: `Bearer ${token}` } })
    if (check.ok) return state.apiKey
  }
  const app = await call('POST', '/widget-apps', { name: `Ledgerly demo ${new Date().toISOString().slice(0, 16)}` })
  return app.api_key
}

const state = await previousState()
const login = await call('POST', '/users/login', { username: email, password }, { form: true })
token = login.access_token

const agentId = await ensureAgent()
const widgetId = await ensureWidget(agentId)
await ensureActions(agentId)
await ensureJourneys(agentId)
const apiKey = await ensureApiKey(state)

await writeFile(STATE_FILE, JSON.stringify({ apiUrl: API, origin: ORIGIN, agentId, widgetId, apiKey }, null, 2) + '\n')
console.log(`Agent ${agentId}, widget ${widgetId}: ${ACTIONS.length} actions, ${JOURNEYS.length} journeys.`)
console.log(`Saved to ${STATE_FILE}. Start the page with: PORT=${new URL(ORIGIN).port || 80} npm start`)
console.log(`Then open ${ORIGIN}/journeys-demo.html`)