chattermate-test/journeys-demo/seed.mjs
Rama main · réplica de lectura del repositorio original.
#!/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`)